zoukankan      html  css  js  c++  java
  • Spring 梳理-AOP

    1. 界面应用场景
      1. 日志、声明式事务、安全、缓存
    2. AOP功能演化图
      1.   
      2. 图片引用地址:https://www.cnblogs.com/best/p/5679656.html
    3. AOP设计模式-代理模式
      1. 静态代理:手动编写代理类,手动编写代码拦截每个方法。工作量极大。
      2. JDK代理:利用反射,实现InvocationHandler接口。 业务类需要实现“业务接口”,利用反射技术代理该“业务接口”
      3. 动态代理:利用cglib。cglib是一种动态代码生成器,可以在运行期动态生成代理类
    4. AOP术语
      1. 关注点:重复代码就叫做关注点;
      2. 切面: 关注点形成的类,就叫切面(类)!是通知和节点的集合,这个集合决定了切面:是什么,在何时和何处完成其功能。
      3. 通知(advice):切面类函数。切面要完成的工作(切面类中的方法)。通知定义了切面是什么以及何时使用。Spring的通知有5种类型:before、after、after-returning、after-throwing和around这五种类型。
      4. 连接点(joinpoint):程序事件。连接点表示在何种操作发生时应用切面。比如方法调用时、修改字段时和抛出异常时等。我们的应用可能有数以千计的时机,这些时机被称为连接点。连接点是应用执行过程中能够插入切面的一个点。
      5. 切点(拦截的作用)(pointcut): 执行目标对象方法,动态植入切面代码。可以通过切入点表达式,指定拦截哪些类的哪些方法; 给指定的类在运行的时候植入切面类代码。切点有助于缩小切面所通知的连接点的范围。如果说通知定义了切面“是什么”、“何时”,那么切点就定义了“何处”。
        1. 可能应用有很多事件(连接点)可以用来对外提供“触角”,但通过某种方式选定的连接点与通知发生作用,这个连接点成为切点。
    5. Spring AOP构建在动态代理基础之上,因此,Spring对AOP的支持局限于方法拦截
    6. Spring 项目与AspectJ项目 之间有大量的合作;Spring 使用 AspectJ项目的注解语法,实现Spring自己基于代理的AOP。也就是Spring与AspectJ在注解的语法上是相同的,但底层实现是不同的,Spring底层只支持方法拦截,无法在bean创建时应用通知将;,AspectJ却支持构造器、属性、方法拦截。
      1. Spring AOP的切面类的编写使用纯java语法;
      2. AspectJ AOP的切面类编写使用java语言扩展,自成一套新的语法。
      3. Spring在运行时才创建AOP代理对象。
    7. 联合使用@Component@Aspect,将切面类以一个普通的全局单实例bean的形式注入到容器中,;可以在其他业务类型的bean中使用@Autowired注解 将切面类bean注入到普通业务bean中,获取其运行时状态值。
    8. Spring使用AsjpectJ定义切点表达式
      1. 明确具体方法  execution( * cn.jt.ClassA.run(..))
      2. 限定切点所属的包  execution(* cn.jt.ClassA.run(..)) within(cn.*)
      3. 限定切点所在bean  execution(* cn.jt.ClassA.run(..)) and bean(‘beanID’)
    9. 使用AOP,为已封装好的Java类,添加新的方法,实现类似Ruby的动态类的概念。但实际上,Spring将一个bean分拆到多个类中:原有类方法在一个类中,新添加的方法在一个类中。
    10. XML方式实现AOP编程 :在没有源码,或者不想把AspectJ注解污染到代码之中的情况下使用
      1. 步骤
        1.          1) 引入jar文件  【aop 相关jar, 4个】

                   2) 引入aop名称空间

                   3)aop 配置

                             * 配置切面类 (重复执行代码形成的类)

                             * aop配置

                                      拦截哪些方法 / 拦截到方法后应用通知代

      2. 示例
        1.   
          <?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:p="http://www.springframework.org/schema/p"
              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">
              
              <!-- dao 实例   在这里配置后就不用在类中使用注解 -->
              <bean id="aDao" class="xx"></bean>
              <bean id="bDao" class="yy"></bean>
              
              <!-- 切面类 -->
              <bean id="aop" class="aopClass1"></bean>
              
              <!-- Aop配置 -->
              <aop:config>
                  <!-- 定义一个切入点表达式: 拦截哪些方法 -->
                  <aop:pointcut expression="execution(* aopClass1.*.*(..))" id="pt"/>
                  <!-- 切面 -->
                  <aop:aspect ref="aop">
                      <!-- 环绕通知 -->
                      <aop:around method="around" pointcut-ref="pt"/>
                      <!-- 前置通知: 在目标方法调用前执行 -->
                      <aop:before method="begin" pointcut-ref="pt"/>
                      <!-- 后置通知: -->
                      <aop:after method="after" pointcut-ref="pt"/>
                      <!-- 返回后通知 -->
                      <aop:after-returning method="afterReturning" pointcut-ref="pt"/>
                      <!-- 异常通知 -->
                      <aop:after-throwing method="afterThrowing" pointcut-ref="pt"/>
                      
                  </aop:aspect>
              </aop:config>
          </beans>
    11. 注解方式实现AOP编程
      1.  先引入aop相关jar文件                                                

                 spring-aop-3.2.5.RELEASE.jar  

            aopalliance.jar                      

              aspectjweaver.jar                    

            aspectjrt.jar

      2. bean.xml中引入aop名称空间       

      3. 开启aop注解                   

      4. 使用注解
        1.   

          @Aspect                                                              指定一个类为切面类             

          @Pointcut("execution(* cn.itcast.e_aop_anno.*.*(..))")  指定切入点表达式

          @Before("pointCut_()")                                    前置通知: 目标方法之前执行

          @After("pointCut_()")                                         后置通知:目标方法之后执行(始终执行)

          @AfterReturning("pointCut_()")                         返回后通知: 执行方法结束前执行(异常不执行)

          @AfterThrowing("pointCut_()")                            异常通知:  出现异常时候执行

          @Around("pointCut_()")                                      环绕通知: 环绕目标方法执行

      5. 关于@Pointcut注解
        1. 是一个节点表达式,通过在一个空函数run()上使用@Pointcut注解,我们实际上扩展了切点表达式语言,这样就可以在任何切点表达式中使用run(),否则,需要在每个切点表达式使用那个更长的表达式。例如:
        2.     @AspectJ
              public class Audience{
                  @Pointcut("execution(** cn.jt.run(..))")
                  public void run(){}
                  
                  @Before("run()")
                  public void beforeRun(ProceedingJoinPoint jp){
                      System.out.println("berfor");
                      jp.proceed();
                      System.out.println("after");
                  }
                  
                  @Around("run()")
                  public void watchRun(ProceedingJoinPoint jp){
                      System.out.println("berfor");
                      jp.proceed();
                      System.out.println("after");
                  }
          
              }


          相当于:

        3.     @AspectJ
              public class Audience{
                  @Before("execution(** cn.jt.run(..))")
                  public void beforeRun(){
                      System.out.println("berfor");
                  }
                  
                  @Around("execution(** cn.jt.run(..))")
                  public void watchRun(ProceedingJoinPoint jp){
                      System.out.println("berfor");
                      jp.proceed();
                      System.out.println("after");
                  }
          
              }
      6. 关于环绕通知:
        1. 环绕通知需要额外的ProceedingJoinPoint类型参数
        2. 如果不调用jp.proceed()方法,那么通知会阻塞对被通知方法的调用;或者调用多次,类似与“失败后重试”这样的业务逻辑。
      7. 将业务类方法中的参数,传递到通知(切面类方法)中。(使用 args 表达式)
        1. @Component
          @Aspect
          public class Audience {
              
              private Map<Integer, Integer> trackCounts = new HashMap<>();
              
              @Pointcut("execution(** concert.JayPerform.playTrack(int)) && args(trackNum)")  //<1>
              public void track(int trackNum) {}  //<2>
              
              @AfterReturning("track(trackNum)")  //<3>
              public void countTrack(int trackNum)    //<4>
              {
                  
                  int currentCount = getPlayCount(trackNum);
                  trackCounts.put(trackNum, currentCount+1);
                  System.out.println("------- 这首歌播放了"+(currentCount+1)+"");
              }
              
              public int getPlayCount(int trackNumber)
              {
                  return trackCounts.containsKey(trackNumber)?trackCounts.get(trackNumber):0;
              }
          }
        2. 代码中 <1>、<2>、<3>、<4> 处,int型参数的名称都是trackNum,这样保证了从命名切点到通知方法的参数转移。并且,这里的参数trackNum与concert.JayPerform.playTrack(int trackNum) 的参数命名相同。
          经过实验,发现这4处的参数名称与concert.JayPerform.playTrack(int trackNum)中的参数名称不必相同,只要<1>与<2>处参数名称相同、<3>与<4>处参数名称相同即可
      8. 示例
        1. bean.xml
          1.   
            <?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:p="http://www.springframework.org/schema/p"
                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="cn.jt"></context:component-scan>
                
                <!-- 开启aop注解方式 -->
                <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
            </beans>
        2. Java代码
          @Component  //加入IOC容器
          @Aspect  // 指定当前类为切面类
          public class Aop {
          
              // 指定切入点表达式: 拦截哪些方法; 即为哪些类生成代理对象
            //解释@Pointcut("execution(* cn.jt.*.*(..))")
            //@Pointcut("execution(*    切入点表达式固定写法, cn.itcast.e_aop_anno表示包.类名(可以用*表示包下所有的类).方法名(可以用*表示类下所有的方法)(..)表示参数可以用..
              @Pointcut("execution(* cn.jt.*.*(..))")
              public void pointCut_(){
              }
              
            //@Before("execution(* cn.jt.*.*(..))")每个方法需要写相同的引用,所以将相同的部分抽取到一个空的方法中pointCut_(),
              // 前置通知 : 在执行目标方法之前执行
              @Before("pointCut_()")
              public void begin(){
                  System.out.println("开始事务/异常");
              }
              
              // 后置/最终通知:在执行目标方法之后执行  【无论是否出现异常最终都会执行】
              @After("pointCut_()")
              public void after(){
                  System.out.println("提交事务/关闭");
              }
              
              // 返回后通知: 在调用目标方法结束后执行 【出现异常不执行】
              @AfterReturning("pointCut_()")
              public void afterReturning() {
                  System.out.println("afterReturning()");
              }
              
              // 异常通知: 当目标方法执行异常时候执行此关注点代码
              @AfterThrowing("pointCut_()")
              public void afterThrowing(){
                  System.out.println("afterThrowing()");
              }
              
              // 环绕通知:环绕目标方式执行
              @Around("pointCut_()")
              public void around(ProceedingJoinPoint pjp) throws Throwable{
                  System.out.println("环绕前....");
                  pjp.proceed();  // 执行目标方法
                  System.out.println("环绕后....");
              }
              
          }

          测试类

          public class App {
              
              ApplicationContext ac = 
                  new ClassPathXmlApplicationContext("cn/jt/bean.xml");
          
              // 目标对象有实现接口,spring会自动选择“JDK代理”
              @Test
              public void testApp() {
                  IUserDao userDao = (IUserDao) ac.getBean("userDao");
                  System.out.println(userDao.getClass());
                  userDao.save();
              }
              
              // 目标对象没有实现接口, spring会用“cglib代理”
              @Test
              public void testCglib() {
                  OrderDao orderDao = (OrderDao) ac.getBean("orderDao");
                  System.out.println(orderDao.getClass());
                  orderDao.save();
              }
            1.     
  • 相关阅读:
    【Shell】Shell编程之while循环命令
    【Shell】Shell编程之grep命令
    【Shell】Shell编程之awk命令
    【Shell】Shell编程之sed命令
    【MySQL】MySQL之示例数据库Sakila下载及安装
    关于JAVA项目报表选型过程
    关于HSQLDB访问已有数据库文件的操作说明
    为什么要用 C# 来作为您的首选编程语言
    ThinkPHP_5对数据库的CURL操作
    使用ECharts画K线图
  • 原文地址:https://www.cnblogs.com/jiangtao1218/p/9695015.html
Copyright © 2011-2022 走看看