首页 > 编程语言 > 详细

Spring-AOP概述及简单实现-05

时间:2020-12-11 22:36:06      阅读:26      评论:0      收藏:0      [点我收藏+]

Spring AOP概述


Spring AOP实现是基于java的代理机制。spring支持:CGLIB代理机制、JDK代理机制。

Spring AOP和Spring IoC一样是Spring框架的核心内容。

AOP(Aspect Oriented Programming),即面向切面编程。

AOP是一种编程范式,一般适用于具有横切逻辑的场合,如访问控制、事务管理、性能监测、日志等。

目前有许多AOP框架,其中最流行的两个框架为Spring AOP和AspectJ。


基本概念:


切面(Aspect)

切面是一段程序代码,这段代码将被”植入“的程序流中。

连接点(join Point)

连接点是对象的一个操作,如:对象调用某个方法、读写对象的实例等。

切入点(pointcut)

切入点即切面与程序流的交叉点(切面注入到程序中的位置)

通知(advice)

通知是某个切入点被横切后所采取的处理逻辑,即在“切入点”处拦截程序后通过通知来执行切面。

通知分为前置、后置、异常、最终、环绕通知五类

  • 前置通知<aop:before>

  • 异常通知<aop:after-throwing>

  • 返回通知<aop:after-returning>

  • 环绕通知<aop:around>

  • 最终通知<aop:after>

目标对象(target)

所有被通知的对象|被代理的对象,都是目标对象。

织入(weaving)

织入是将切面功能应用到目标对象的过程,由代理工厂创建一个代理对象,这个代理对象可以为目标对象执行切面功能。

aop织入方式有3种:编译期织入、类加载期织入、执行期织入。springAOP多为执行期织入。

引入(introduction)

对一个已编译的类,在运行时期动态地向其中加载属性和方法。


AOP简单实现:


在spring的配置文件中引入aop命名空间:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
 
           http://www.springframework.org/schema/aop
           http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
</beans>

Spring提供了多种切面声明的方式:

  • 基于XML配置方式声明切面。

  • 基于注解方式声明切面。

  • 通过接口MethodInterceptor中的方法invoke中执行前置通知

在核心包的基础上,导入所需jar包:

  • spring-aop-4.3.17.RELEASE.jar
  • spring-aspects-4.3.17.RELEASE.jar
  • aopalliance-.jar

基于XML配置方式声明切面
  • 前置通知<aop:before >

  • 异常通知<aop:after-throwing >

  • 返回通知<aop:after-returning >

  • 环绕通知<aop:around >

  • 最终通知<aop:after >

spring配置文件applicationContext.xml

<!-- 实例化业务类的Bean -->
<bean id="productService" class="aop_xml.ProductServiceImpl"/>
<bean id="product" class="aop_xml.Product"/>

<!-- 实例化日志通知处理(切面)的Bean -->
<bean id="allLogAdvice" class="aop_xml.AllLogAdvice" />

<!-- 配置aop -->
<aop:config>
    <!-- 配置日志切面 -->
    <aop:aspect id="logaop" ref="allLogAdvice">

        <!-- 定制切入点,可采用正则表达式,含义是对browse方法,进行拦截 -->
        <aop:pointcut expression="execution(public void browse(String,String))" id="logpointcut"/>
            <!-- expression="execution(* com.ssm.service.*.*(..))" -->

        <!-- 将日志通知类中的myBeforeAdvice方法指定为前置通知 -->
        <aop:before method="myBeforeAdvice" pointcut-ref="logpointcut"/> 

        <!-- 将日志通知类中的myAfterReturnAdvice方法指定为返回通知 -->
        <aop:after-returning method="myAfterReturnAdvice" pointcut-ref="logpointcut"/> 

        <!-- 将日志通知类中的myThrowingAdvice方法指定为异常通知 -->
        <aop:after-throwing method="myThrowingAdvice" pointcut-ref="logpointcut" throwing="e"/> 

        <!-- 将日志通知类中的myAroundAdvice方法指定为环绕通知 -->
        <aop:around method="myAroundAdvice" pointcut-ref="logpointcut"/>
    </aop:aspect>		
</aop:config>

创建通知。

package aop_xml;

import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;

public class AllLogAdvice {
	// 此方法作为前置通知
	public void myBeforeAdvice(JoinPoint joinPoint) {
		// 获取业务方法参数
		List<Object> args=Arrays.asList(joinPoint.getArgs());
		//日志格式的字符串
		String logInfoText="前置通知:"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
			.format(new Date())+" "+args.get(0).toString()+" 浏览商品 "+args.get(1).toString();
		//将日志信息输出到控制台
		System.out.println(logInfoText);
	}
	
	// 此方法作为返回通知
	public void myAfterReturnAdvice(JoinPoint joinPoint) {
		// 获取业务方法参数
		List<Object> args=Arrays.asList(joinPoint.getArgs());
		//日志格式的字符串
		String logInfoText="返回通知:"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
			.format(new Date())+" "+args.get(0).toString()+" 浏览商品 "+args.get(1).toString();
		//将日志信息输出到控制台
		System.out.println(logInfoText);
	}
	
	// 此方法作为异常通知
	public void myThrowingAdvice(JoinPoint joinPoint) {
		//获取被调用的类名
		String targetClassName=joinPoint.getTarget().getClass().getName();
		//获取被调用的方法名
		String targetMethodName=joinPoint.getSignature().getName();
		//日志格式的字符串
		String logInfoText="异常通知:执行"+targetClassName+"类中的"+targetMethodName+"方法时发生异常!";
		//将日志信息输出到控制台
		System.out.println(logInfoText);
	}
	
	// 此方法作为环绕通知
	public void myAroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
		long beginTime=System.currentTimeMillis();
		joinPoint.proceed();
		long endTime=System.currentTimeMillis();
		//获取被调用的方法名
		String targetMethodName=joinPoint.getSignature().getName();
		//日志格式的字符串
		String logInfoText="环绕通知:"+targetMethodName+"方法调用前时间"+beginTime+"毫秒,"
				+"调用后的时间"+endTime+"毫秒。共执行了"+(endTime-beginTime)+"毫秒。";
		//将日志信息输出到控制台
		System.out.println(logInfoText);
	}		
}

创建target,即被代理的目标对象。

package aop_xml;

public class Product {
	public void browse(String loginName, String productName) {
		System.out.println("执行业务方法browse…");
		//演示异常通知时,手动人为抛出异常
		//throw new RuntimeException("这是特意手动抛出的异常信息!");
		int i=100000000;
		while(i>0) {
			i--;
		}
	}
}

创建代理。

package aop_xml;

public interface ProductService {
	// 定义一个抽象方法browse,模拟用户浏览商品
	public void browse(String loginName,String productName);
}

代理对象为browse()提供一个代理方法。

package aop_xml;

public class ProductServiceImpl implements ProductService {
	// 实现方法browse,模拟用户浏览商品
	public void browse(String loginName, String productName) {
		System.out.println("执行业务方法browse…");
		//演示异常通知时,手动人为抛出异常
		//throw new RuntimeException("这是特意手动抛出的异常信息!");
		int i=100000000;
		while(i>0) {
			i--;
		}
	}
}

测试。

package aop_xml;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestAOP {

	public static void main(String[] args) {
		//初始化Spring容器,加载applicationContext.xml文件
		ApplicationContext ctx= new ClassPathXmlApplicationContext("applicationContext.xml");
		//通过容器获取配置中的productService的实例
		ProductService productService=(ProductService) ctx.getBean("productService");
		//调用productService中的browse方法
		productService.browse("张三", "spring-aop-xml-product");
		Product product=(Product) ctx.getBean("product");
		//调用productService中的browse方法
		product.browse("张三1", "spring-aop-xml-product");
	}
}
基于注解方式声明切面

在spring的配置文件中引入context命名空间:

<beans
	xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:p="http://www.springframework.org/schema/p"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 	
	http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
	http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">
</beans>

spring配置文件applicationContext.xml

<!-- 基于注解的AOP -->
<!-- 配置自动扫描的包 -->
<context:component-scan base-package="aop_annotation" />
<!-- 开启基于@AspectJ切面的注解处理器 -->
<aop:aspectj-autoproxy />

创建通知。

package aop_annotation;

import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class AllLogAdvice {
	// 定义切入点表达式
	@Pointcut("execution(* com.ssm.service.*.*(..))")
	public void allMethod() {
		//使用一个返回值为void,方法体为空的方法来命名切入点
	}
	
	// 此方法作为前置通知
	@Before("allMethod()")
	public void myBeforeAdvice(JoinPoint joinPoint) {
		// 获取业务方法参数
		List<Object> args=Arrays.asList(joinPoint.getArgs());
		//日志格式的字符串
		String logInfoText="前置通知:"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
			.format(new Date())+" "+args.get(0).toString()+" 浏览商品 "+args.get(1).toString();
		//将日志信息输出到控制台
		System.out.println(logInfoText);
	}
	
	// 此方法作为返回通知
	@AfterReturning("allMethod()")
	public void myAfterReturnAdvice(JoinPoint joinPoint) {
		// 获取业务方法参数
		List<Object> args=Arrays.asList(joinPoint.getArgs());
		//日志格式的字符串
		String logInfoText="返回通知:"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
			.format(new Date())+" "+args.get(0).toString()+" 浏览商品 "+args.get(1).toString();
		//将日志信息输出到控制台
		System.out.println(logInfoText);
	}
	
	// 此方法作为异常通知
	@AfterThrowing(pointcut="allMethod()",throwing="e")
	public void myThrowingAdvice(JoinPoint joinPoint) {
		//获取被调用的类名
		String targetClassName=joinPoint.getTarget().getClass().getName();
		//获取被调用的方法名
		String targetMethodName=joinPoint.getSignature().getName();
		//日志格式的字符串
		String logInfoText="异常通知:执行"+targetClassName+"类中的"+targetMethodName+"方法时发生异常!";
		//将日志信息输出到控制台
		System.out.println(logInfoText);
	}
	
	// 此方法作为环绕通知
	@Around("allMethod()")
	public void myAroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
		long beginTime=System.currentTimeMillis();
		joinPoint.proceed();
		long endTime=System.currentTimeMillis();
		//获取被调用的方法名
		String targetMethodName=joinPoint.getSignature().getName();
		//日志格式的字符串
		String logInfoText="环绕通知:"+targetMethodName+"方法调用前时间"+beginTime+"毫秒,"
				+"调用后的时间"+endTime+"毫秒。共执行了"+(endTime-beginTime)+"毫秒。";
		//将日志信息输出到控制台
		System.out.println(logInfoText);
	}		
}

创建代理。

package aop_annotation;

public interface ProductService {
	// 定义一个抽象方法browse,模拟用户浏览商品
	public void browse(String loginName,String productName);
}
package aop_annotation;
import org.springframework.stereotype.Component;

// 实例化业务类的Bea
@Component("productService2")
public class ProductServiceImpl implements ProductService {
	// 实现方法browse,模拟用户浏览商品
	public void browse(String loginName, String productName) {
		System.out.println("执行业务方法browse…");
		//演示异常通知时,手动人为抛出异常
		//throw new RuntimeException("这是特意手动抛出的异常信息!");
		int i=100000000;
		while(i>0) {
			i--;
		}
	}
}

测试。

package aop_annotation;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestAOP {

	public static void main(String[] args) {

		//初始化Spring容器,加载applicationContext.xml文件
		ApplicationContext ctx= new ClassPathXmlApplicationContext("applicationContext.xml");
		//通过容器获取配置中的productService的实例
		ProductService productService=(ProductService) ctx.getBean("productService2");
		//调用productService中的browse方法
		productService.browse("张三", "spring-aop-annotation-product");
	}
}
基于接口MethodInterceptor

通过接口MethodInterceptor中的方法invoke中执行前置通知


创建Target,即被代理的目标对象。

public class Target{
    //程序执行的方法
    public void execute(String name){
        System.out.println("程序执行。。。"+name);
    }
}

创建通知。

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class LoggerExecute implements MethodInterceptor{
    public Object invoke(MethodInvocation invocation) throws Throwable{
        //执行前置通知
        before();
        //proceed()方法,执行目标对象的execute()方法
        invocation.proceed();
        return null;
    }
    //前置通知
    private void before(){
        System.out.println("前置通知!");
    }
}

创建代理。

import org.springframework.aop.framework.ProxyFactory;

public class Manager{
    //创建代理
    public static main(String[] args){
        Target target = new Target();
        //Spring中代理对象可通过xml配置方式获得,也可通过ProxyFactory手动编程方式创建对象。
        //代理对象未指定接口,使用CGLIB生成代理类-
        ProxyFactory factory = new ProxyFactory();
        factory.addAdivce(new LoggerExecute());
        factory.setTarget(target);
        Target proxy = (Targer) id.getProxy();
        //执行execute方法
        proxy.execute("AOP简单实现")
    }
}

Spring-AOP概述及简单实现-05

原文:https://www.cnblogs.com/2020-6-12/p/14122930.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!