紫玉坏小子

导航

Spring学习二

AOP

  • 概念

    • 面向切面编程(方面),利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发效率

    • 通俗描述:不通过修改源代码方式,在主干功能里面添加新功能

  • AOP底层使用动态代理

    • 有两种情况动态代理

      • 第一种:有接口情况,使用JDK动态代理

        • 创建接口实现类代理对象,增强类的方法

        • public class SpringTest {
            public static void main(String[] args) {

                Class[] interfaces = {UserDao.class};
                UserDao userDao = new UserDaoImpl();
                UserDao userDao1 = (UserDao)Proxy.newProxyInstance(SpringConfig.class.getClassLoader(), interfaces, new UserProxy(userDao));
                userDao1.say();
            }
          }
          class UserProxy implements InvocationHandler{

            private Object obj;
            public UserProxy(Object o){
                this.obj = o;
            }
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("方法执行之前>>>>>");
                method.invoke(obj);
                System.out.println("方法执行之后");
                return UserDao.class;
            }
          }
      • 第二种:没有接口情况,使用CGLIB动态代理

        • 创建子类的代理对象,增强类的方法

  • //AOP 术语解释
    public class User {
       //增
       public  int add(){ return 0; };
       //删
       public  int del(){ return 0; };
       //改
       public  int update(){ return 0; };
       //查
       public  int select(){ return 0; };
       /**
        * 在一个类中 哪些方法可以被增强 那这些方法就叫连接点
        * add、del、update、select方法都可以做连接点
        */
       /**
        * 实际被真正增加的方法 叫做切入点
        */
       /**
        * 实际增强的逻辑部分称为通知(增强)
        * 前置通知
        * 后置通知
        * 环绕通知
        * 异常通知
        * 最终通知
        */
       /**
        * 把通知应用到切入点的过程 叫做 切面
        */
    }
  • 切入点表达式

    • execution([权限修饰符][返回类型][类全路径][方法名称][参数列表])

      列如:execution(* com.dao.User.add(..)) 对add方法增强

      列如:execution(* com.dao.User.*(..)) 对User类里面所有方法进行增强

  • springmvc的配置文件中加入AOP的配置,即扫描AOP的包以及让AOP生效

    • <!-- AOP 注解方式 ;定义Aspect -->
         <!-- 激活组件扫描功能,在包"com.example.aop及其子包下面自动扫描通过注解配置的组件-->
         <context:component-scan base-package="com.example.aop"/>
         <!-- 启动AspectJ支持   只对扫描过的bean有效-->
         <aop:aspectj-autoproxy proxy-target-class="true" />
    • AOP逻辑处理类

      package com.example.aop;

      import java.util.Arrays;
      import java.util.List;

      import org.aspectj.lang.JoinPoint;
      import org.aspectj.lang.ProceedingJoinPoint;
      import org.aspectj.lang.annotation.After;
      import org.aspectj.lang.annotation.AfterReturning;
      import org.aspectj.lang.annotation.AfterThrowing;
      import org.aspectj.lang.annotation.Around;
      import org.aspectj.lang.annotation.Aspect;
      import org.aspectj.lang.annotation.Before;
      import org.aspectj.lang.annotation.Pointcut;
      import org.springframework.core.annotation.Order;
      import org.springframework.stereotype.Component;

      @Aspect    //该标签把LoggerAspect类声明为一个切面
      @Order(1)  //设置切面的优先级:如果有多个切面,可通过设置优先级控制切面的执行顺序(数值越小,优先级越高)
      @Component //该标签把LoggerAspect类放到IOC容器中
      public class LoggerAspect {

         /**
          * 定义一个方法,用于声明切入点表达式,方法中一般不需要添加其他代码
          * 使用@Pointcut声明切入点表达式
          * 后面的通知直接使用方法名来引用当前的切点表达式;如果是其他类使用,加上包名即可
          */
         @Pointcut("execution(public * com.example.controller.*Controller.*(..))")
         public void declearJoinPointExpression(){}

         /**
          * 前置通知
          * @param joinPoint
          */
         @Before("declearJoinPointExpression()") //该标签声明次方法是一个前置通知:在目标方法开始之前执行
         public void beforMethod(JoinPoint joinPoint){
             String methodName = joinPoint.getSignature().getName();
             List<Object> args = Arrays.asList(joinPoint.getArgs());
             System.out.println("this method "+methodName+" begin. param<"+ args+">");
        }
         /**
          * 后置通知(无论方法是否发生异常都会执行,所以访问不到方法的返回值)
          * @param joinPoint
          */
         @After("declearJoinPointExpression()")
         public void afterMethod(JoinPoint joinPoint){
             String methodName = joinPoint.getSignature().getName();
             System.out.println("this method "+methodName+" end.");
        }
         /**
          * 返回通知(在方法正常结束执行的代码)
          * 返回通知可以访问到方法的返回值!
          * @param joinPoint
          */
         @AfterReturning(value="declearJoinPointExpression()",returning="result")
         public void afterReturnMethod(JoinPoint joinPoint,Object result){
             String methodName = joinPoint.getSignature().getName();
             System.out.println("this method "+methodName+" end.result<"+result+">");
        }
         /**
          * 异常通知(方法发生异常执行的代码)
          * 可以访问到异常对象;且可以指定在出现特定异常时执行的代码
          * @param joinPoint
          * @param ex
          */
         @AfterThrowing(value="declearJoinPointExpression()",throwing="ex")
         public void afterThrowingMethod(JoinPoint joinPoint,Exception ex){
             String methodName = joinPoint.getSignature().getName();
             System.out.println("this method "+methodName+" end.ex message<"+ex+">");
        }
         /**
          * 环绕通知(需要携带类型为ProceedingJoinPoint类型的参数)
          * 环绕通知包含前置、后置、返回、异常通知;ProceedingJoinPoin 类型的参数可以决定是否执行目标方法
          * 且环绕通知必须有返回值,返回值即目标方法的返回值
          * @param point
          */
         @Around(value="declearJoinPointExpression()")
         public Object aroundMethod(ProceedingJoinPoint point){

             Object result = null;
             String methodName = point.getSignature().getName();
             try {
                 //前置通知
                 System.out.println("The method "+ methodName+" start. param<"+ Arrays.asList(point.getArgs())+">");
                 //执行目标方法
                 result = point.proceed();
                 //返回通知
                 System.out.println("The method "+ methodName+" end. result<"+ result+">");
            } catch (Throwable e) {
                 //异常通知
                 System.out.println("this method "+methodName+" end.ex message<"+e+">");
                 throw new RuntimeException(e);
            }
             //后置通知
             System.out.println("The method "+ methodName+" end.");
             return result;
        }
      }

       

    • 在类上加上注解Aspect声明这个类是一个切面,加上component注解加入IOC容器中。Order注解是优先级,如果只有一个切面类可以不用。注解pointcut是声明注解的作用范围。execution(public * com.example.controller.*Controller.*(..))我这里用的execution表达式,代表作用com.example.controller包下所有以Controller结尾类的所有Public方法。@pointcut所描述的方法里面通常不需要内容。切面通知包含:

      前置通知(@Before,在执行方法之前,参数为JoinPoint)

      后置通知(@After,无论方法抛不抛异常都会执行,所以获取不到方法的返回值。参数为JoinPoint)

      返回通知(@AfterReturning,在方法正常结束后执行,可以获取到方法的返回值。参数为JoinPoint和result(Object))

      异常通知(@AfterThrowing,在方法抛出异常时执行,可以获取到异常对象,且可以指定在出现特定异常时执行的代码,参数为JoinPoint何Exception)

      环绕通知(@Around,环绕通知需要携带的类型为ProceedingJoinPoint类型的参数, 环绕通知包含前置、后置、返回、异常通知;ProceedingJoinPoin 类型的参数可以决定是否执行目标方法,且环绕通知必须有返回值,返回值即目标方法的返回值)
    •  

posted on 2021-01-17 22:11  紫玉坏小子  阅读(107)  评论(0编辑  收藏  举报