基于AspectJ注解方式实现AOP
作者:96岁对抗java
基于AspectJ实现AOP(注解方式)
这里我们采用的是非完全注解方式
1. 创建类, 在类里面定义方法(连接点)
package com.ffyc.spring.aop; //被增强的类(也就是被代理的类) public class User { //被增强的方法(也就是被代理的方法) public void add(){ System.out.println("add..."); } }
2. 创建增强类, 编写增强逻辑
- 注意: 此时我们并没有给增强类添加注解来生成代理对象, 也没有配置通知(增强的逻辑)
package com.ffyc.spring.aop; //增强类 public class UserProxy { //前置通知 public void before(){ System.out.println("before"); } //这里还有后置通知, 异常通知, 环绕通知, 最终通知等, 现在逻辑先省略 }
3. 进行通知的配置
①: 在spring配置文件中, 开启注解扫描与开启注解自动生成代理对象
- 注解扫描大家都知道: 就是componentScan
- 注解自动生成代理对象是什么?
- 就是开启了注解自动生成代理对象之后当SpringIOC容器扫描basePackages的时候如果扫描到某个类上面有@Aspect注解, 则会生成该类的代理对象放到SpringIOC容器中由SpringIOC容器管理
- 所以@Aspect注解就是在增强(通知)类上添加的
- 就是开启了注解自动生成代理对象之后当SpringIOC容器扫描basePackages的时候如果扫描到某个类上面有@Aspect注解, 则会生成该类的代理对象放到SpringIOC容器中由SpringIOC容器管理
<!-- 开启组件扫描, 开启了组件扫描之后我们的spring容器就会在对应的base-package位置进行一个扫描--> <!-- 注意: 其实我们的组件扫描也是可以通过使用注解的方式进行配置--> <context:component-scan base-package="com.ffyc.spring"></context:component-scan> <!-- 开启Aspectj自动生成代理对象--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
②: 使用注解创建User和UserProxy类的对象
- 也就是让SpringIOC容器帮我们创建
- 在User类和UserProxy类上加上@Component注解即可
package com.ffyc.spring.aop; import org.springframework.stereotype.Component; //被增强的类(也就是被代理的类) @Component public class User { //被增强的方法(也就是被代理的方法) public void add(){ System.out.println("add..."); } }
package com.ffyc.spring.aop; import org.springframework.stereotype.Component; //增强类 @Component public class UserProxy { //前置通知 public void before(){ System.out.println("before"); } }
③: 在增强类上面添加注解@Aspect
- 这里注意: @Aspect注解要和@Component注解一起使用, 原因在最后
package com.ffyc.spring.aop; import org.aspectj.lang.annotation.Aspect; import org.springframework.stereotype.Component; //增强类 @Component @Aspect public class UserProxy { //前置通知 public void before(){ System.out.println("before"); } }
4. 配置不同类型的通知
- 在增强类里面, 在作为通知的方法上面添加对应的通知类型的注解, 并使用切入点表达式将对应的通知配置到某个切入点上
- 这样Spring底层就会帮我们根据我们添加的注解来创建代理对象
package com.ffyc.spring.aop; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; //增强类 @Component @Aspect public class UserProxy { @Pointcut(value = "execution(* com.ffyc.spring.aop.User.add(..))") private void method(){ } //前置通知 @Before(value = "execution (* com.ffyc.spring.aop.User.add(..))") public void before(){ System.out.println("before..."); } @AfterReturning(value = "method()") public void afterReturning(){ System.out.println("afterReturning..."); } @After(value = "execution(* com.ffyc.spring.aop.User.add(..))") public void after(){ System.out.println("after..."); } @AfterThrowing(value = "method()") public void afterThrowing(){ System.out.println("afterThrowing..."); } @Around(value = "method()") public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System.out.println("环绕前..."); proceedingJoinPoint.proceed(); System.out.println("环绕之后..."); } }
- @PointCut注解用来创建一个切入点, 该注解的value属性(String类型)为切入点表达式
- @Around, @Before, @After, @AfterReturning, @AfterThrowing注解的value属性(String类型)都为切入点表达式
- @AfterThrowing针对的是被代理方法抛出异常, 如果异常在内部被解决, 那么并不会执行@AfterThrowing通知
- 我们通过JVM的学习可以知道, 如果方法是通过异常结束(指抛出异常), 那么肯定就不是正常退出(return), 那么也就意味着@AfterReturning(后置通知), 和@AfterThrowing(异常通知)只能同时触发一个
- @Around注解使用的时候要在参数位置预留一个ProceedingJoinPoint类型的参数, 最终会通过这个通知类构建一个被代理类的代理对象, 这个代理对象是由Spring帮我们创建的, 所以这个通知类也是Spring容器帮我们去扫描的, 对应的@Around注解标注的方法肯定也是由Spring帮我们调用的, 而Spring调用的方法我们可以在参数位置写一个形参, 这个时候Spring默认会对该参数类型进行一个自动装配(基于类型的自动装配), 这里就会由Spring帮我们装配一个ProceedingJoinPoint对象, 我们可以调用其中的proceed()方法, 调用proceed()方法就会执行切入点方法(被增强的方法)
- ProceedingJoinPoint : 正在进行的连接点
5. 测试:
package com.ffyc.spring.aop; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestAop { @Test public void testAop(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml"); User user = applicationContext.getBean("user", User.class); System.out.println(user); user.add(); } }
- 当我们调用user对象的add()方法的时候其实会执行代理对象的add()方法
各个通知的执行顺序:
环绕前[@Around(前)] --> 前置通知[@Before] --> 被增强方法(切入点) --> 环绕后(@Around(后)) --> 最终通知(@After) --> 后置通知[@AfterReturning] (或者异常通知[@AfterThrowing])
补充:
@Aspect注解为什么要和@Component注解一起使用, 按理将使用一个@Aspect注解不是就已经是由SpringIOC荣IQ创建代理对象并交由SpringIOC容器管理了?
**官方文档解释如下: **
You may register aspect classes as regular beans in your Spring XML configuration, or autodetect them through classpath scanning - just like any other Spring-managed bean. However, note that the @Aspect annotation is not sufficient for autodetection in the classpath: For that purpose, you need to add a separate @Component annotation (or alternatively a custom stereotype annotation that qualifies, as per the rules of Spring’s component scanner).
翻译:
您可以在Spring XML配置中注册aspect类,或者通过类路径扫描自动检测它们,就像任何其他Spring管理bean一样。但是,请注意,@aspect注释对于在类路径中自动检测是不够的:为了达到这个目的,您需要添加一个单独的@component注解(或者根据Spring的组件扫描器的规则来定义一个定制的原型注解)
到此这篇关于基于AspectJ注解方式实现AOP的文章就介绍到这了,更多相关AspectJ实现AOP内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!