• 06 Spring_AOP


    目录

    一、SpringAOP_AOP简介

    二、SpringAOP_术语

     AOP的相关术语:(前4个常用,后3个不常用)

    三、SpringAOP_AOP入门 

    四、SpringAOP_五种通知类型

     AspectJ实现AOP共有五种通知类型:

    测试五种通知类型

    五、SpringAOP_切点表达式

    六、SpringAOP_多切面配置

    七、SpringAOP_注解配置AOP

    1.Spring可以使用注解代替配置文件配置切面:

    2.如何为一个类下的所有方法统一配置切点:

    3.配置类如何代替xml中AOP注解支持?

    八、知识点整理:


    一、SpringAOP_AOP简介

     AOP的全称是Aspect Oriented Programming,即面向切面编程。是实现功能统一维护的一种技术,它将业务逻辑的各个部分进行隔离,使开发人员在编写业务逻辑时可以专心于核心业务,从而提高了开发效率。

    作用:在不修改源码的基础上,对已有方法进行增强。
    实现原理:动态代理技术。
    优势:减少重复代码、提高开发效率、维护方便
    应用场景:事务处理、日志管理、权限控制、异常处理等方面。

    二、SpringAOP_术语

      AOP的相关术语:(前4个常用,后3个不常用)

    名称说明
    JoinPoint(连接点)指能被拦截到的点,在Spring中只有方法能被拦截
    Pointcut(切点)指要对那些连接点进行拦截,即被增强的方法
    Advice(通知)指拦截后要做的事情,即切点被拦截后执行的(后置、前置、、)方法
    Aspect(切面)切点+通知称为切面
    Target(目标)

    被代理的对象

    Proxy(代理)代理对象
    Weaving(织入)生成代理对象的过程

    以下一段配置文件的嵌套代码更好地理解上述的相关术语: 

    1. <bean id="myAspectJAdvice" class="com.itbaizhan.advice.MyAspectAdvice">bean>
    2. <aop:config >
    3. <aop:aspect ref="myAspectJAdvice">
    4. <aop:pointcut id="myPointcut" expression="execution(* com.itbaizhan.dao.UserDao.*(..))"/>
    5. <aop:after-returning method="myAfterReturning" pointcut-ref="myPointcut">aop:after-returning>
    6. aop:aspect>
    7. aop:config>

    三、SpringAOP_AOP入门 

    AspectJ是一个基于Java语言的AOP框架,在Spring框架中建议使用AspectJ实现AOP
    接下来我们写一个 AOP 入门案例: dao 层的每个方法结束后都可以打印一条日志

    1.引入依赖:

    1. <dependencies>
    2. <dependency>
    3. <groupId>org.springframeworkgroupId>
    4. <artifactId>spring-contextartifactId>
    5. <version>5.3.13version>
    6. dependency>
    7. <dependency>
    8. <groupId>org.aspectjgroupId>
    9. <artifactId>aspectjweaverartifactId>
    10. <version>1.8.7version>
    11. dependency>
    12. <dependency>
    13. <groupId>junitgroupId>
    14. <artifactId>junitartifactId>
    15. <version>4.12version>
    16. <scope>testscope>
    17. dependency>
    18. dependencies>

    2.编写连接点

    1. @Repository("userDao")//注解方式将UserDao放入Spring容器中
    2. public class UserDao {
    3. public void add(){
    4. System.out.println("添加方法");
    5. }
    6. public void delete(){
    7. System.out.println("删除方法");
    8. }
    9. public void update(){
    10. int i=1/0;
    11. System.out.println("修改方法");
    12. }
    13. }

    3.编写通知类

    1. //通知类
    2. public class MyAspectJAdvice {
    3.    // 后置通知
    4.    public void myAfterReturning() {
    5.        System.out.println("打印日志...");
    6.   }
    7. }

    4.配置文件中配置切面

    1. <beans xmlns="http://www.springframework.org/schema/beans"
    2. xmlns:context="http://www.springframework.org/schema/context"
    3. xmlns:aop="http://www.springframework.org/schema/aop"
    4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    5. xsi:schemaLocation="http://www.springframework.org/schema/beans
    6. http://www.springframework.org/schema/beans/spring-beans.xsd
    7. http://www.springframework.org/schema/context
    8. http://www.springframework.org/schema/context/spring-context.xsd
    9. http://www.springframework.org/schema/aop
    10. http://www.springframework.org/schema/aop/spring-aop.xsd">
    11. <context:component-scan base-package="com.itbaizhan">context:component-scan>
    12. <bean id="myAspectJAdvice" class="com.itbaizhan.advice.MyAspectAdvice">bean>
    13. <aop:config >
    14. <aop:aspect ref="myAspectJAdvice">
    15. <aop:pointcut id="myPointcut" expression="execution(* com.itbaizhan.dao.UserDao.*(..))"/>
    16. <aop:after-returning method="myAfterReturning" pointcut-ref="myPointcut">aop:after-returning>
    17. aop:aspect>
    18. beans>

    5.测试:

    1. public class UserDaoTest {
    2. @Test
    3. public void testAdd(){
    4. ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
    5. UserDao userDao = (UserDao)ac.getBean("userDao");
    6. userDao.add();
    7. }
    8. @Test
    9. public void testDelete(){
    10. ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
    11. UserDao userDao = (UserDao)ac.getBean("userDao");
    12. userDao.delete();
    13. }
    14. @Test
    15. public void testUpdate(){
    16. ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
    17. UserDao userDao = (UserDao)ac.getBean("userDao");
    18. userDao.update();
    19. }

    6.测试testAdd方法结果:

    如上所示:我们可以看到,执行userDao.add()方法本来只会打印添加方法,可最后却打印出了MyAspectJAdvice类中的方法“打印日志”语句,说明AOP后置通知切入到切点中了。

    也就表明,AOP可以在不修改源码的情况下对已有方法进行增强。

    四、SpringAOP_五种通知类型

     AspectJ实现AOP共有五种通知类型:

    通知类型描述
    前置通知在方法(切点)执行前添加功能
    后置通知在方法(切点)正常执行后添加功能
    异常通知在方法抛出异常后添加功能
    最终通知无论方法是否抛出异常,都会执行该通知,相当于finally
    环绕通知在方法执行前后添加功能

    测试五种通知类型

    1.编写通知方法

    1. //通知类
    2. public class MyAspectAdvice {
    3. // 后置通知
    4. public void myAfterReturning(JoinPoint joinPoint){
    5. System.out.println("切点方法名"+joinPoint.getSignature().getName());
    6. System.out.println("目标对象"+joinPoint.getTarget());
    7. System.out.println("打印日志"+joinPoint.getSignature().getName()+"方法被执行了!");
    8. }
    9. // 前置通知
    10. public void myBefore(){
    11. System.out.println("前置通知。。。");
    12. }
    13. // 异常通知
    14. public void myAfterThrowing(Exception ex){
    15. System.out.println("异常通知。。。");
    16. System.err.println(ex.getMessage());//err:打印红色字体
    17. }
    18. // 最终通知
    19. public void myAfter(){
    20. System.out.println("最终通知");
    21. }
    22. // 环绕通知
    23. public Object myAround(ProceedingJoinPoint proceedingJoinPoint)throws Throwable{
    24. System.out.println("环绕前");
    25. Object obj = proceedingJoinPoint.proceed();//执行方法
    26. System.out.println("环绕后");
    27. return obj;
    28. }
    29. }

    2.配置文件bean.xml中配置切面

    1. <bean id="myAspectJAdvice" class="com.itbaizhan.advice.MyAspectAdvice">bean>
    2. <aop:config >
    3. <aop:aspect ref="myAspectJAdvice">
    4. <aop:pointcut id="myPointcut" expression="execution(* com.itbaizhan.dao.UserDao.*(..))"/>
    5. <aop:before method="myBefore" pointcut-ref="myPointcut">aop:before>
    6. <aop:after-returning method="myAfterReturning" pointcut-ref="myPointcut">aop:after-returning>
    7. <aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointcut" throwing="ex">aop:after-throwing>
    8. <aop:after method="myAfter" pointcut-ref="myPointcut">aop:after>
    9. <aop:around method="myAround" pointcut-ref="myPointcut">aop:around>
    10. aop:aspect>
    11. aop:config>

    3.测试方法

    1. public class UserDaoTest {
    2. @Test
    3. public void testAdd(){
    4. ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
    5. UserDao userDao = (UserDao)ac.getBean("userDao");
    6. userDao.add();
    7. }
    8. @Test
    9. public void testDelete(){
    10. ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
    11. UserDao userDao = (UserDao)ac.getBean("userDao");
    12. userDao.delete();
    13. }
    14. @Test
    15. public void testUpdate(){
    16. ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
    17. UserDao userDao = (UserDao)ac.getBean("userDao");
    18. userDao.update();
    19. }

    五、SpringAOP_切点表达式

    使用AspectJ需要使用切点表达式配置切点位置,写法如下:
    标准写法:访问修饰符 返回值 包名.类名.方法名(参数列表)
    注意要点:
    1.访问修饰符可以省略。
    2.返回值使用 * 代表任意类型。
    3.包名使用 * 表示任意包,多级包结构要写多个 * ,使用 *.. 表示任意包结构
    4.类名和方法名都可以用 * 实现通配。
    5.参数列表基本数据类型直接写类型,如:int、float
                     引用类型写 包名.类名      ,如:java.lang.String
                     * 表示匹配一个任意类型参数
                     .. 表示匹配任意类型任意个数的参数
    6.全通配: * *..*.*(..)
    1. <bean id="myAspectJAdvice" class="com.itbaizhan.advice.MyAspectAdvice">bean>
    2. <aop:config >
    3. <aop:aspect ref="myAspectJAdvice">
    4. <aop:pointcut id="myPointcut" expression="execution(* com.itbaizhan.dao.UserDao.*(..))"/>
    5. <aop:after-returning method="myAfterReturning" pointcut-ref="myPointcut">aop:after-returning>
    6. aop:aspect>

    如上切点表达式中表示:com.itbaizhan.dao.UserDao类下任意返回值类型的方法的所有方法任意参数类型

    int com.itbaizhan.dao.UserDao.*(..)表示只有返回值为int类型的方法可以被拦截

    * com.*.*UserDao.*(int) 表示com包下的任意包下的UserDao类的任意返回值类型参数类型为int方法

    * *..UserDao.*(..)表示任意包结果下的UserDao类下的所有任意类型任意参数的方法

    六、SpringAOP_多切面配置

     我们可以为切点配置多个通知,形成多切面,比如希望dao层的每个方法结束后都可以打印日志并发送邮件:

    1.编写发送邮件的通知类

    1. //通知类2
    2. public class MyAspectAdvice2 {
    3. // 后置通知
    4. public void myAfterReturning2(){
    5. System.out.println("发送邮件");
    6. }
    7. }

    2.编写打印日志的通知类

    1. public class MyAspectAdvice {
    2. // 后置通知
    3. public void myAfterReturning(JoinPoint joinPoint){
    4. System.out.println("打印日志。。");
    5. }
    6. }

    3.配置多切面

    1. <bean id="myAspectJAdvice" class="com.itbaizhan.advice.MyAspectAdvice">bean>
    2. <bean id="myAspectJAdvice2" class="com.itbaizhan.advice.MyAspectAdvice2">bean>
    3. <aop:config >
    4. <aop:aspect ref="myAspectJAdvice">
    5. <aop:pointcut id="myPointcut" expression="execution(* com.itbaizhan.dao.UserDao.*(..))"/>
    6. <aop:after-returning method="myAfterReturning" pointcut-ref="myPointcut">aop:after-returning>
    7. aop:aspect>
    8. <aop:aspect ref="myAspectJAdvice2">
    9. <aop:pointcut id="myPointcut2" expression="execution(* com.itbaizhan.dao.UserDao.*(..))"/>
    10. <aop:after-returning method="myAfterReturning2" pointcut-ref="myPointcut2">aop:after-returning>
    11. aop:aspect>
    12. aop:config>

    4.测试

    1. @Test
    2. public void testAdd(){
    3. ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
    4. UserDao userDao = (UserDao)ac.getBean("userDao");
    5. userDao.add();
    6. }

    5.测试结果

    七、SpringAOP_注解配置AOP

    1.Spring可以使用注解代替配置文件配置切面:

    1.在xml中开启AOP注解支持
    1. <aop:aspectj-autoproxy>aop:aspectj-autoproxy>

    2.在通知类上方加入注解 @Aspect

    3.在通知方法上方加入注解@Before/@AfterReturning/@AfterThrowing/@After/@Around

    1. @Aspect//注解方式配置通知需要在类上方标明@Aspect
    2. @Component//把此类放入容器中
    3. public class MyAspectAdvice {
    4. // 后置通知
    5. @AfterReturning("execution(* com.itbaizhan.dao.UserDao.*(..))")
    6. public void myAfterReturning(JoinPoint joinPoint){
    7. System.out.println("切点方法名"+joinPoint.getSignature().getName());
    8. System.out.println("目标对象"+joinPoint.getTarget());
    9. System.out.println("打印日志"+joinPoint.getSignature().getName()+"方法被执行了!");
    10. }
    11. // 前置通知
    12. @Before("execution(* com.itbaizhan.dao.UserDao.*(..))")
    13. public void myBefore(){
    14. System.out.println("前置通知。。。");
    15. }
    16. // 异常通知
    17. @AfterThrowing(value = "execution(* com.itbaizhan.dao.UserDao.*(..))",throwing = "ex")
    18. public void myAfterThrowing(Exception ex){
    19. System.out.println("异常通知。。。");
    20. System.err.println(ex.getMessage());//err:打印红色字体
    21. }
    22. // 最终通知
    23. @After("execution(* com.itbaizhan.dao.UserDao.*(..))")
    24. public void myAfter(){
    25. System.out.println("最终通知");
    26. }
    27. // 环绕通知
    28. @Around("execution(* com.itbaizhan.dao.UserDao.*(..))")
    29. public Object myAround(ProceedingJoinPoint proceedingJoinPoint)throws Throwable{
    30. System.out.println("环绕前");
    31. Object obj = proceedingJoinPoint.proceed();//执行方法
    32. System.out.println("环绕后");
    33. return obj;
    34. }
    35. }

    4.测试方法:

    1. @Test
    2. public void testAdd2(){
    3. ApplicationContext ac = new ClassPathXmlApplicationContext("bean1.xml");
    4. UserDao userDao = (UserDao)ac.getBean("userDao");
    5. userDao.add();
    6. }

    2.如何为一个类下的所有方法统一配置切点:

    1.在通知类中添加方法配置切点

    2.在通知方法上使用定义好的切点

    1. @Aspect//注解方式配置通知需要在类上方标明@Aspect
    2. @Component//把此类放入容器中
    3. public class MyAspectAdvice {
    4. // 切点方法
    5. @Pointcut("execution(* com.itbaizhan.dao.UserDao.*(..))")
    6. public void pointCut(){
    7. }
    8. // 后置通知
    9. @AfterReturning("pointCut()")
    10. public void myAfterReturning(JoinPoint joinPoint){
    11. System.out.println("切点方法名"+joinPoint.getSignature().getName());
    12. System.out.println("目标对象"+joinPoint.getTarget());
    13. System.out.println("打印日志"+joinPoint.getSignature().getName()+"方法被执行了!");
    14. }
    15. // 前置通知
    16. @Before("pointCut()")
    17. public void myBefore(){
    18. System.out.println("前置通知。。。");
    19. }
    20. // 异常通知
    21. @AfterThrowing(value = "pointCut()",throwing = "ex")
    22. public void myAfterThrowing(Exception ex){
    23. System.out.println("异常通知。。。");
    24. System.err.println(ex.getMessage());//err:打印红色字体
    25. }
    26. }

    3.配置类如何代替xmlAOP注解支持?

    1. <aop:aspectj-autoproxy>aop:aspectj-autoproxy>
    在配置类上方添加@EnableAspectJAutoProxy即可
    1. @Configuration//标明此类为配置类
    2. @ComponentScan("com.itbaizhan")//包扫描
    3. @EnableAspectJAutoProxy//用注解的方式开启配置类代替xml文件配置AOP
    4. public class SpringConfig {
    5. }

    测试方法:

    1. @Test
    2. public void testAdd3(){
    3. ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfig.class);
    4. UserDao userDao = (UserDao)ac.getBean("userDao");
    5. userDao.add();
    6. }

    八、知识点整理:

    1.AOP的实现原理是“动态代理技术”

    2.AOP的作用是“在不修改源代码的情况下对已有方法进项增强”

    3.在AOP中,切面指“切点+通知”

    4.在AOP中,通知指“切点被拦截后执行的方法”

    5.AOP五种通知类型“前置通知”、“后置通知”、“异常通知”、“最终通知”、“环绕通知”

    6.AOP切点表达式的标准写法是“访问修饰符 返回值类型 包名.类名.方法名(参数类型)”

    7.使用AspectJ实现AOP时,使用标签配置切面

    8.在xml中开启AOP注解支持的标签为:“-autoproxy>

  • 相关阅读:
    std::future, std::async, std::promise
    C语言——程序环境和预处理
    【代码随想录】LC 27. 移除元素
    关于HTTP模块访问之后响应网页
    21 搜索二维矩阵 II
    C++图书资源管理系统
    什么是代理IP池?如何判断IP代理商的IP池是否真实优质?
    Linux系统下实现开机自动加载驱动模块
    vue + element UI【实战】打字闯关(含按键监听、按键音效、字符匹配、动态样式、结果判定、数据统计、音效获取和剪辑等实用技巧)
    【学习笔记】计算几何
  • 原文地址:https://blog.csdn.net/m0_51697147/article/details/126117461