zoukankan      html  css  js  c++  java
  • 解析Spring第三天(面向切面AOP)

    面向切面:AOP

    在不修改源代码的基础上,对方法进行增强。AOP的底层原理就是代理技术(第一种:jdk的动态代理(编写程序必须要有接口)。第二种:cglib代理技术(生成类的子类)。如果编写的程序有借口,则spring框架会自动使用jdk的动态代理技术增强,)。

    Joinpoint(连接点) 所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点

    Pointcut(切入点) -- 所谓切入点是指我们要对哪些Joinpoint进行拦截的定义

    Advice(通知/增强)-- 所谓通知是指拦截到Joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)

    Target(目标对象)-- 代理的目标对象

    Weaving(织入)-- 是指把增强应用到目标对象来创建新的代理对象的过程

    Proxy(代理)-- 一个类被AOP织入增强后,就产生一个结果代理类

    Aspect(切面)-- 是切入点和通知的结合,以后自己来编写和配置的

     创建一个普通的Maven项目工程引入坐标

     1 <dependencies>
     2     <dependency>
     3         <groupId>org.springframework</groupId>
     4         <artifactId>spring-context</artifactId>
     5         <version>5.0.2.RELEASE</version>
     6     </dependency>
     7     <dependency>
     8         <groupId>commons-logging</groupId>
     9         <artifactId>commons-logging</artifactId>
    10         <version>1.2</version>
    11     </dependency>
    12     <dependency>
    13         <groupId>log4j</groupId>
    14         <artifactId>log4j</artifactId>
    15         <version>1.2.12</version>
    16     </dependency>
    17     <dependency>
    18         <groupId>org.springframework</groupId>
    19         <artifactId>spring-test</artifactId>
    20         <version>5.0.2.RELEASE</version>
    21     </dependency>
    22     <dependency>
    23         <groupId>junit</groupId>
    24         <artifactId>junit</artifactId>
    25         <version>4.12</version>
    26     </dependency>
    27   
    28         <!-- AOP联盟 -->
    29         <dependency>
    30             <groupId>aopalliance</groupId>
    31             <artifactId>aopalliance</artifactId>
    32             <version>1.0</version>
    33         </dependency>
    34         <!-- Spring Aspects -->
    35         <dependency>
    36             <groupId>org.springframework</groupId>
    37             <artifactId>spring-aspects</artifactId>
    38             <version>5.0.2.RELEASE</version>
    39         </dependency>
    40         <!-- aspectj -->
    41         <dependency>
    42             <groupId>org.aspectj</groupId>
    43             <artifactId>aspectjweaver</artifactId>
    44             <version>1.8.3</version>
    45         </dependency>
    46   </dependencies>
    • 创建Spring的配置文件,引入具体的AOP的schema约束

       1 <?xml version="1.0" encoding="UTF-8"?>
       2 <beans xmlns="http://www.springframework.org/schema/beans"
       3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       4        xmlns:context="http://www.springframework.org/schema/context"
       5        xmlns:aop="http://www.springframework.org/schema/aop"
       6        xsi:schemaLocation="
       7                 http://www.springframework.org/schema/beans
       8                 http://www.springframework.org/schema/beans/spring-beans.xsd
       9                 http://www.springframework.org/schema/context
      10                 http://www.springframework.org/schema/context/spring-context.xsd
      11                 http://www.springframework.org/schema/aop
      12                 http://www.springframework.org/schema/aop/spring-aop.xsd">
      13     
      14 </beans>
    • 创建包结构,编写具体的接口和实现类
      1 package cn.tx.demo2;
      2 public class UserServiceImpl implements UserService {
      3 4     @Override
      5     public void save() {
      6         System.out.println("业务层:保存用户...");
      7     }
      8 9 }
    • 将目标类配置到Spring中
      <bean id="userService" class="cn.tx.demo2.UserServiceImpl"/>
    • 自定义切面类
       1 package cn.tx.demo2;
       2  3 /**
       4  * 自定义切面类 = 切入点(表达式) + 通知(增强的代码)
       5  */
       6 public class MyXmlAspect {
       7  8     /**
       9      * 通知
      10      */
      11     public void log(){
      12         // 发送手机短信
      13         // 发送邮件/记录日志/事务管理
      14 15         System.out.println("增强的方法执行了...");
      16     }
      17 18 }
    • 在配置文件中定义切面类

      1 <bean id="myXmlAspect" class="cn.tx.demo2.MyXmlAspect"/>
    • 在配置文件中完成aop的配置

      1   <!--配置AOP的增强-->
      2     <aop:config>
      3         <!--配置切面 = 切入点 + 通知组成-->
      4         <aop:aspect ref="myXmlAspect">
      5             <!--前置通知:UserServiceImpl的save方法执行前,会增强-->
      6             <aop:before method="log" pointcut="execution(public void cn.tx.demo2.UserServiceImpl.save())" />
      7         </aop:aspect>
      8     </aop:config>
    • 对增强进行测试
      package cn.tx.test;
      ​
      import cn.tx.demo2.UserService;
      import org.junit.Test;
      import org.junit.runner.RunWith;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.test.context.ContextConfiguration;
      import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
      
      @RunWith(SpringJUnit4ClassRunner.class)
      @ContextConfiguration("classpath:applicationContext_demo2.xml")
      public class Demo2 {
      ​
          @Autowired
          private UserService userService;
      ​
          /**
           * 测试
           */
          @Test
          public void run1(){
              userService.save();
          }
      ​
      }
    • 切入点的表达式格式:
      • execution([修饰符] 返回值类型 包名.类名.方法名(参数))

      • 修饰符可以省略不写,不是必须要出现的。

      • 返回值类型是不能省略不写的,根据你的方法来编写返回值。可以使用 * 代替。

      • 包名例如:com.tx.demo3.BookDaoImpl

        • 首先com是不能省略不写的,但是可以使用 * 代替
        • 中间的包名可以使用 * 号代替
        • 如果想省略中间的包名可以使用 ..
      • 类名也可以使用 * 号代替,也有类似的写法:*DaoImpl

      • 方法也可以使用 * 号代替

      • 参数如果是一个参数可以使用 * 号代替,如果想代表任意参数使用 ..

    • AOP的通知方式
       1 <?xml version="1.0" encoding="UTF-8"?>
       2 <beans xmlns="http://www.springframework.org/schema/beans"
       3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       4        xmlns:context="http://www.springframework.org/schema/context"
       5        xmlns:aop="http://www.springframework.org/schema/aop"
       6        xsi:schemaLocation="
       7                 http://www.springframework.org/schema/beans
       8                 http://www.springframework.org/schema/beans/spring-beans.xsd
       9                 http://www.springframework.org/schema/context
      10                 http://www.springframework.org/schema/context/spring-context.xsd
      11                 http://www.springframework.org/schema/aop
      12                 http://www.springframework.org/schema/aop/spring-aop.xsd">
      13 
      14     <!--bean管理-->
      15     <bean id="userService" class="cn.tx.demo1.UserServiceImpl" />
      16 
      17     <!--=================编写AOP配置文件====================-->
      18     <!--先配置切面类-->
      19     <bean id="myXmlAspect" class="cn.tx.demo1.MyXmlAspect" />
      20 
      21     <!--配置AOP的增强-->
      22     <aop:config>
      23         <!--正在配置切面,引入真正切面对象-->
      24         <aop:aspect ref="myXmlAspect">
      25             <!--配置的是前置通知:目标对象方法执行前,先增强。method="切面类中通知的方法" pointcut="切入点的表达式"-->
      26             <!--
      27                 切入点的表达式
      28                     execution() 写法是固定的
      29                     public  可以省略不写的
      30                     方法返回值   void int String * 通用的写法
      31                     包名  * 通用的写法
      32                     类名  * 推荐的写法 *ServiceImpl 例如:UserServiceImpl DeptServiceImpl
      33                     方法名称    *  推荐写法:save*
      34                     方法参数列表  .. == Object... obj Object类型的可变参数
      35                     <aop:before method="log" pointcut="execution(* cn.tx.*.*ServiceImpl.save*(..))" />
      36             -->
      37 
      38             <!--
      39                 通知类型
      40                     前置通知:目标对象方法执行前,先增强。
      41                     <aop:before method="log" pointcut="execution(* cn.tx.*.*ServiceImpl.save*(..))" />
      42 
      43                     最终通知:目标对象方法执行成功或者失败,都会增强。finally
      44                     <aop:after method="log" pointcut="execution(* cn.tx.*.*ServiceImpl.save*(..))" />
      45 
      46                     后置通知:目标对象方法执行成功,才会增强。
      47                     <aop:after-returning method="log"  pointcut="execution(* cn.tx.*.*ServiceImpl.save*(..))" />
      48 
      49                     异常通知:目标对象方法执行失败,才会增强。
      50                     <aop:after-throwing method="log"  pointcut="execution(* cn.tx.*.*ServiceImpl.save*(..))" />
      51 
      52                     <aop:before method="begin" pointcut="execution(* cn.tx.*.*ServiceImpl.save*(..))" />
      53                     <aop:after-returning method="commit"  pointcut="execution(* cn.tx.*.*ServiceImpl.save*(..))" />
      54                     <aop:after-throwing method="rollback"  pointcut="execution(* cn.tx.*.*ServiceImpl.save*(..))" />
      55                     <aop:after method="close" pointcut="execution(* cn.tx.*.*ServiceImpl.save*(..))" />
      56 
      57                     环绕通知:自己决定增强的位置。使用了环绕通知,目标对象的方法默认没有执行的,需要自己手动执行目标对象方法。
      58             -->
      59 
      60             <aop:around method="logAroud"  pointcut="execution(* cn.tx.*.*ServiceImpl.save*(..))" />
      61 
      62         </aop:aspect>
      63     </aop:config>
      64 
      65 </beans>

      Spring的AOP技术-注解方式

    • 同样创建一个普通的Maven工程,导入坐标,编写接口,同上
    • 编写一个切面类,给切面类添加注解 @Aspect,编写增强的方法,使用通知类型注解声明
       1 package cn.tx.demo3;
       2  3 import org.aspectj.lang.annotation.Aspect;
       4 import org.aspectj.lang.annotation.Before;
       5 import org.springframework.stereotype.Component;
       6 
      12 @Component  // 把该类交给IOC去管理
      13 @Aspect     // 声明是切面类  == <aop:aspect ref="myXmlAspect">
      14 public class MyAnnoAspect {
      15 16     /**
      17      * 通知的方法
      18      */
      19     // @Before(value = "切入点的表达式")
      20     @Before(value = "execution(public * cn.tx.demo3.OrderServiceImpl.save(..))")
      21     public void log(){
      22         System.out.println("增强了...");
      23     }
      24 25 }
      26
    • 配置文件中开启自动代理

      <aop:aspectj-autoproxy/>
    • 测试方法

       1  
       2 package cn.tx.test;
       3  4 import cn.tx.demo2.UserService;
       5 import cn.tx.demo3.OrderService;
       6 import org.junit.Test;
       7 import org.junit.runner.RunWith;
       8 import org.springframework.beans.factory.annotation.Autowired;
       9 import org.springframework.test.context.ContextConfiguration;
      10 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
      11 
      12 
      13 @RunWith(SpringJUnit4ClassRunner.class)
      14 @ContextConfiguration("classpath:applicationContext_demo3.xml")
      15 public class Demo3 {
      16 17     @Autowired
      18     private OrderService orderService;
      19 20     /**
      21      * 测试
      22      */
      23     @Test
      24     public void run1(){
      25         orderService.save();
      26     }
      27 28 }
      • @RunWith(SpringJUnit4ClassRunner.class)    //在使用所有注释前必须使用@RunWith(SpringJUnit4ClassRunner.class),让测试运行于spring测试环境
      •   @ContextConfiguration 用来指定加载的Spring配置文件位置,会加载默认配置文件。

           @ContextConfiguration有两个常用的属性,locations、inheritLocations

           locations:可以通过该属性手工指定spring配置文件所在的位置,可以指定一个或多个spring的配置文件,要使用【,】隔开。 例如:@ContextConfiguration(locations={“aa/aa.xml”,” aa/bb.xml”})

              inheritLocations:是否要继承父测试用例中的Spring 配置文件,默认是true。

      • 拓展注解解析:
      • @DirtiesContext(classMode = ClassMode.AFTER_CLASS): //@DirtiesContext 在测试方法上出现这个注解时,表明底层Spring容器在该方法的执行中被“污染”,从而必须在方法执行结束后重新创建(无论该测试是否通过)。
      • @TransactionConfiguration( transactionManager = "transactionManager" , defaultRollback = false) 

          @TransactionConfiguration为配置事物性测试定义了类级别的元数据。PlatformTransactionManager默认的实例叫transactionManager,如果需要的PlatformTransactionManager不                                   是“transactionManager”的话,那么可以显示配置驱动事物的PlatformTransactionManager的bean的名字。此外,可以将defaultRollback标志改为false,表示不回滚。通常,                               @TransactionConfiguration与@ContextConfiguration搭配使用。

          @ContextConfiguration
          @TransactionConfiguration(transactionManager="transactionManager", defaultRollback=false)】

    • 通知类型注解

      1 @Before    -- 前置通知
      2 
      3 ​    @AfterReturing    -- 后置通知
      4 
      5 ​    @Around    -- 环绕通知(目标对象方法默认不执行的,需要手动执行)
      6 
      7 ​    @After    -- 最终通知
      8 
      9 ​    @AfterThrowing    -- 异常抛出通知
    • 纯注解的方式

      package cn.tx.demo3;
      ​
      import org.springframework.context.annotation.ComponentScan;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.context.annotation.EnableAspectJAutoProxy;
      ​
      
      @Configuration      // 配置类
      @ComponentScan(value = "cn.tx.demo3")   // 扫描包
      @EnableAspectJAutoProxy     // 开启自动代理 == <aop:aspectj-autoproxy />
      public class SpringConfig {
          
      }
  • 相关阅读:
    laravel底层源码解析:pipeline,db,console
    composer命令清单
    composer使用笔记
    git常见问题
    JS阻止冒泡和取消默认事件(默认行为)
    vue项目构建:vue-cli+webpack常用配置
    MVC和三层架构
    SSM框架初始配置
    Java对象间的关系
    Spring框架
  • 原文地址:https://www.cnblogs.com/LBJLAKERS/p/11750094.html
Copyright © 2011-2022 走看看