一、Spring对AOP的支持
AOP并不是Spring框架特有的,Spring只是支持AOP编程的框架之一,每一个框架对AOP的支持各有特点,有些AOP能够对方法的参数进行拦截,有些AOP对方法进行拦截。而Spring AOP是一只基于方法拦截的AOP,换句话说Spring只能支持方法拦截的AOP。
在Spring中有4种方式去实现AOP的拦截功能:
1、使用ProxyFactoryBean和对应的接口实现AOP
2、使用XML配置AOP
3、使用@AspectJ注解驱动切面
4、使用Aspect注入切面
Spring AOP 的拦截方式中,真正常用的是用@AspectJ注解的方式实现的切面,有时候XML配置也有一定的辅助作用
spring AOP使用@AspectJ注解:https://www.cnblogs.com/weibanggang/p/10137217.html
二、面向切面编程的术语
1、切面(ASPECT)
切面就是一套规范的流程,可以理解为一个拦截器,能定义被拦截方法执行前后可以执行的操作。(即,它是一个类。)
2、通知(Advice)
通知就是切面中的方法,它根据在代理真实对象方法调用前、后的顺序和业务逻辑进行区分,Spring AOP中有四种通知:(即,它是类中的一个方法。)
- 前置通知(before):在动态代理反射原有对象方法或者执行环绕通知前执行的通知功能;
- 后置通知(after):在动态代理反射原有对象方法或者执行环绕通知后执行的通知功能,不管是否返回异常都会被执行;
- 返回通知(afterReturning):在动态代理反射原有对象方法或者执行环绕通知正常返回无异常时执行的通知功能;
- 异常通知(afterThrowing):在动态代理反射原有对象方法或者执行环绕通知产生异常时执行的通知功能;
- 环绕通知(around):在动态代理中,它可以取代当前被拦截对象的方法,提供回调原有被拦截对象的方法;
3、引入(Introduction)
引入允许我们在现有类里添加的自定义类或方法。比如我们要对一个被代理对象的逻辑进行优化,但是不能修改原方法时,可以使用引入来进行增强;
4、切点(Pointcut)
切点就是告诉Spring AOP什么时候启动拦截器并织入对应流程中。
5、连接点(join point)
连接点对应的是具体需要拦截的东西,比如通过切点的正则表达式去判断哪些方法是连接点,从而织入对应的通知;
6、织入(Weaving)
织入是一个生成代理对象,并且将切面内容放入到流程中的过程,它的实现方式就是动态代理
SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice:
一、基于XML配置的Spring AOP
1、创建要被代理的Math类,代码如下
创建类:Math
package com.wbg.springAOP.spring; public class Math { //加 public int add(int n1,int n2){ int result=n1+n2; System.out.println(n1+"+"+n2+"="+result); return result; } //减 public int sub(int n1,int n2){ int result=n1-n2; System.out.println(n1+"-"+n2+"="+result); return result; } //乘 public int mut(int n1,int n2){ int result=n1*n2; System.out.println(n1+"X"+n2+"="+result); return result; } //除 public int div(int n1,int n2){ int result=n1/n2; System.out.println(n1+"/"+n2+"="+result); return result; } }
2、编辑AOP中需要使用到的通知类Advices.java代码如下:
package com.wbg.springAOP.spring; import org.aspectj.lang.JoinPoint; public class Advices { public void before(JoinPoint jp){ System.out.println("----------前置通知----------"); System.out.println(jp.getSignature().getName()); } public void after(JoinPoint jp){ System.out.println("----------最终通知----------"); } }
3、配置容器初始化时需要的XML文件,aop01.xml文件内容如下:
<?xml version="1.0" encoding="UTF-8"?> <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.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!--被代理对象--> <bean id="math" class="com.wbg.springAOP.spring.Math"/> <!--通知--> <bean id="advices" class="com.wbg.springAOP.spring.Advices"/> <!--aop配置--> <aop:config proxy-target-class="true"> <!--切面--> <aop:aspect ref="advices"> <!--切点--> <aop:pointcut id="pointcur1" expression="execution(* com.wbg.springAOP.spring.Math.*(..))"/> <!--连接通知方法与切点--> <aop:before method="before" pointcut-ref="pointcur1"/> <aop:after method="after" pointcut-ref="pointcur1"/> </aop:aspect> </aop:config> </beans>
4、测试:
ApplicationContext context = new ClassPathXmlApplicationContext("aop01.xml"); Math math = context.getBean("math",Math.class); int n=100; int m=5; math.add(n,m); math.sub(n,m); math.mut(n,m); math.div(n,m);
二、使用注解配置AOP
1、创建代理类:Math2 ,类上注解了@Service并命名bean为math2。
代码:
package com.wbg.springAOP.spring.annotation; import org.springframework.stereotype.Component; import org.springframework.stereotype.Service; import java.util.Random; /** * 被代理的目标类 */ @Service("math2") public class Math2 { //加 public int add(int n1,int n2){ //开始时间 long start = getTime(); delay(); int result=n1+n2; System.out.println(n1+"+"+n2+"="+result); System.out.println("共用时:" + (getTime() - start)); return result; } //减 public int sub(int n1,int n2){ //开始时间 long start = getTime(); delay(); int result=n1*n2; System.out.println(n1+"-"+n2+"="+result); System.out.println("共用时:" + (getTime() - start)); return result; } //乘 public int mut(int n1,int n2){ //开始时间 long start = getTime(); delay(); int result=n1*n2; System.out.println(n1+"X"+n2+"="+result); System.out.println("共用时:" + (getTime() - start)); return result; } //除 public int div(int n1,int n2){ //开始时间 long start = getTime(); delay(); int result=n1/n2; System.out.println(n1+"/"+n2+"="+result); System.out.println("共用时:" + (getTime() - start)); return result; } public static long getTime() { return System.currentTimeMillis(); } public static void delay(){ try { int n = (int) new Random().nextInt(500); Thread.sleep(n); } catch (InterruptedException e) { e.printStackTrace(); } } }
2、创建Advices2类
@Component表示该类的实例会被Spring IOC容器管理
@Aspect表示声明一个切面
@Before表示before为前置通知,通过参数execution声明一个切点
代码:
execution(<修饰符模式>?<返回类型模式><方法名模式>(<参数模式>)<异常模式>?)切点函数,可以满足多数需求。
package com.wbg.springAOP.spring.annotation; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; @Component @Aspect public class Advices2 { @Before("execution(* com.wbg.springAOP.spring.annotation.Math2.*(..))") public void before(JoinPoint joinPoint){ System.out.println("----------前置通知----------"); System.out.println(joinPoint.getSignature().getName()); } @After("execution(* com.wbg.springAOP.spring.annotation.Math2.*(..))") public void after(JoinPoint joinPoint){ System.out.println("----------最终通知----------"); } }
3、创建xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <context:component-scan base-package="com.wbg.springAOP.spring.annotation"/> <!-- 在配置IOC的基础上增加了aop:aspectj-autoproxy节点 Spring框架会自动为与AspectJ切面配置的Bean创建代理 proxy-target-class="true"属性表示被代理的目标对象是一个类 而非实现了接口的类,主要是为了选择不同的代理方式 --> <aop:aspectj-autoproxy proxy-target-class="true"/> </beans>
4、测试
ApplicationContext context = new ClassPathXmlApplicationContext("aop2.xml"); Math2 math = context.getBean("math2",Math2.class); int n=100; int m=5; math.add(n,m); math.sub(n,m); math.mut(n,m); math.div(n,m);