zoukankan      html  css  js  c++  java
  • Spring_AOP_AspectJ支持的通知注解

    1.AOP前奏: 使用动态代理解决日志需求

    ArithmeticCalculator.java

    package com.aff.spring.aop.helloworld;
    public interface ArithmeticCalculator {
        int add(int i,int j);
        int sub(int i,int j);
        int mul(int i,int j);
        int div(int i,int j);
    }

    ArithmeticCalculatorImpl.java

    package com.aff.spring.aop.helloworld;
    public class ArithmeticCalculatorImpl implements ArithmeticCalculator {
    
        @Override
        public int add(int i, int j) {
            int result = i + j;
            return result;
        }
    
        @Override
        public int sub(int i, int j) {
            int result = i - j;
            return result;
        }
    
        @Override
        public int mul(int i, int j) {
            int result = i * j;
            return result;
        }
    
        @Override
        public int div(int i, int j) {
            int result = i / j;
            return result;
        }
    }

    ArithmeticCalculatorLoggingProxy.java

    package com.aff.spring.aop.helloworld;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.util.Arrays;
    
    public class ArithmeticCalculatorLoggingProxy {
        //要代理的对象
        private ArithmeticCalculator target;
        
        public ArithmeticCalculatorLoggingProxy(ArithmeticCalculator target) {
            super();
            this.target = target;
        }
    
        //返回代理对象
        public ArithmeticCalculator getLoggingProxy(){
            ArithmeticCalculator proxy = null;
            
            ClassLoader loader = target.getClass().getClassLoader();
            Class [] interfaces = new Class[]{ArithmeticCalculator.class};
            InvocationHandler h = new InvocationHandler() {
                /**
                 * proxy: 代理对象。 一般不使用该对象
                 * method: 正在被调用的方法
                 * args: 调用方法传入的参数
                 */
                @Override
                public Object invoke(Object proxy, Method method, Object[] args)
                        throws Throwable {
                    String methodName = method.getName();
                    //打印日志
                    System.out.println("[before] The method " + methodName + " begins with " + Arrays.asList(args));
                    
                    //调用目标方法
                    Object result = null;
                    
                    try {
                        //前置通知
                        result = method.invoke(target, args);
                        //返回通知, 可以访问到方法的返回值
                    } catch (NullPointerException e) {
                        e.printStackTrace();
                        //异常通知, 可以访问到方法出现的异常
                    }
                    
                    //后置通知. 因为方法可以能会出异常, 所以访问不到方法的返回值
                    
                    //打印日志
                    System.out.println("[after] The method ends with " + result);
                    
                    return result;
                }
            };
            
            /**
             * loader: 代理对象使用的类加载器。 
             * interfaces: 指定代理对象的类型. 即代理代理对象中可以有哪些方法. 
             * h: 当具体调用代理对象的方法时, 应该如何进行响应, 实际上就是调用 InvocationHandler 的 invoke 方法
             */
            proxy = (ArithmeticCalculator) Proxy.newProxyInstance(loader, interfaces, h);
            
            return proxy;
        }
    }

    Main.java

    package com.aff.spring.aop.helloworld;
    
    public class Main {
        public static void main(String[] args) {
    
            ArithmeticCalculator arithmeticCalculator = new ArithmeticCalculatorImpl();
    
            arithmeticCalculator = new ArithmeticCalculatorLoggingProxy(arithmeticCalculator).getLoggingProxy();
    
            int result = arithmeticCalculator.add(11, 12);
            System.out.println("result:" + result);
    
            result = arithmeticCalculator.div(21, 3);
            System.out.println("result:" + result);
    
        }
    }

    结果如下

    [before] The method add begins with [11, 12]
    [after] The method ends with 23
    result:23
    [before] The method div begins with [21, 3]
    [after] The method ends with 7
    result:7

    2.AOP简介

    ①AOP 的好处:
        每个事物逻辑位于一个位置, 代码不分散, 便于维护和升级
        业务模块更简洁, 只包含核心业务代码

    ②.AOP术语

    切面(Aspect): 横切关注点(跨越应用程序多个模块的功能)被模块化的特殊对象
    通知(Advice): 切面必须要完成的工作
    目标(Target): 被通知的对象
    代理(Proxy): 向目标对象应用通知之后创建的对象
    连接点(Joinpoint):程序执行的某个特定位置:如类某个方法调用前、调用后、方法抛出异常后等。

    连接点由两个信息确定:方法表示的程序执行点;相对点表示的方位。例如 ArithmethicCalculator#add() 方法执行前的连接点,执行点为 ArithmethicCalculator#add(); 方位为该方法执行前的位置
    切点(pointcut):每个类都拥有多个连接点:例如 ArithmethicCalculator 的所有方法实际上都是连接点,即连接点是程序类中客观存在的事务。AOP 通过切点定位到特定的连接点。类比:连接点相当于数据库中的记录,切点相当于查询条件。切点和连接点不是一对一的关系,一个切点匹配多个连接点,切点通过 org.springframework.aop.Pointcut 接口进行描述,它使用类和方法作为连接点的查询条件。

    切面(Aspect): 横切关注点(跨越应用程序多个模块的功能)被模块化的特殊对象
    通知(Advice): 切面必须要完成的工作
    目标(Target): 被通知的对象
    代理(Proxy): 向目标对象应用通知之后创建的对象
    连接点(Joinpoint):程序执行的某个特定位置:如类某个方法调用前、调用后、方法抛出异常后等。连接点由两个信息确定:方法表示的程序执行点;相对点表示的方位。例如 ArithmethicCalculator#add() 方法执行前的连接点,执行点为 ArithmethicCalculator#add(); 方位为该方法执行前的位置
    切点(pointcut):每个类都拥有多个连接点:例如 ArithmethicCalculator 的所有方法实际上都是连接点,即连接点是程序类中客观存在的事务。AOP 通过切点定位到特定的连接点。类比:连接点相当于数据库中的记录,切点相当于查询条件。切点和连接点不是一对一的关系,一个切点匹配多个连接点,切点通过 org.springframework.aop.Pointcut 接口进行描述,它使用类和方法作为连接点的查询条件。

    ③用 AspectJ 注解声明切面

    要在 Spring 中声明 AspectJ 切面, 只需要在 IOC 容器中将切面声明为 Bean 实例. 当在 Spring IOC 容器中初始化 AspectJ 切面之后, Spring IOC 容器就会为那些与 AspectJ 切面相匹配的 Bean 创建代理.
    在 AspectJ 注解中, 切面只是一个带有 @Aspect 注解的 Java 类.
    通知是标注有某种注解的简单的 Java 方法.
    AspectJ 支持 5 种类型的通知注解:
    @Before: 前置通知, 在方法执行之前执行
    @After: 后置通知, 在方法执行之后执行
    @AfterRunning: 返回通知, 在方法返回结果之后执行
    @AfterThrowing: 异常通知, 在方法抛出异常之后
    @Around: 环绕通知, 围绕着方法执行

    LoggingAspect.java

    package com.aff.spring.aop.imlp;
    
    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.springframework.stereotype.Component;
    
    //把这个类声明为切面,需要把这个类放到IOC容器中, 再声明为一个切面
    @Aspect
    @Component
    public class LoggingAspect {
        //声明该方法是一个前置通知, 在目标方法开始之前执行
        @Before("execution(* com.aff.spring.aop.imlp.*.*(int , int ))")
        public void  beforeMethod(JoinPoint joinPoint){
            String methodName =  joinPoint.getSignature().getName();
            List<Object> args =  Arrays.asList(joinPoint.getArgs());
            System.out.println("The method " +methodName+"begins  with"+args);
            
        }
        //后置通知:在目标方法执行后(无论 是否发生异常) ,执行的通知
        //在后置通知中还不能访问目标方法执行的结果
        //第一个*:任意返回值类型;   第二个*:包下的任意类;  第三个*:任意方法; .. : 任意参数
        @After("execution(* com.aff.spring.aop.imlp.*.*(..))")
        public void afterMethod(JoinPoint joinPoint){
            String methodName =  joinPoint.getSignature().getName();
            System.out.println("The method " +methodName+"ends ");
        }
        
        
        /**
        *  返回通知
         * 在方法正常结束后执行的代码
         * 返回通知是可以访问到方法的返回值
         * @param joinPoint
         */
        @AfterReturning(value="execution(* com.aff.spring.aop.imlp.*.*(..))",returning="result")
        public  void  afterReturning(JoinPoint joinPoint,Object result ){
            String methodName =  joinPoint.getSignature().getName();
            System.out.println("The method " +methodName+"ends with "+result);
        }
        
        /**
         * 异常通知
         * 在目标方法出现异常时 会执行的代码
         * 可以访问到异常对象,且可以指定出现特定异常时在执行通知代码
         * @param joinPoint
         * @param ex
         */
        @AfterThrowing(value="execution(* com.aff.spring.aop.imlp.*.*(..))",throwing="e")
        public void  afterThrowing(JoinPoint joinPoint,Exception e){
            String methodName =  joinPoint.getSignature().getName();
            System.out.println("The method " +methodName+"ocurs  exception :"+e);
        }        
        
        /**
         * 转绕通知需要携带 ProceedingJoinPoint 类型的参数
         * 环绕通知类似于动态代理的全过程: ProceedingJoinPoint 类型的参数可以决定是否执行目标方法
         * 且环绕通知必须有返回值
         * @param pjd
         */
        @Around("execution(* com.aff.spring.aop.imlp.*.*(..))")
        public  Object  aroundMethod(ProceedingJoinPoint pjd){
            Object result =null;
            String methodName=   pjd.getSignature().getName();
            //执行目标方法
            try {
                //前置通知
                System.out.println("The method " +methodName+"begins  with"+Arrays.asList(pjd.getArgs()));
                
                //执行目标方法
                result = pjd.proceed();
                
                //返回通知
                System.out.println("The method "+methodName +"end with"+result);
            } catch (Throwable e) {
                //异常通知
                System.out.println("The method"+methodName+" ocurs  exception :"+e);
                throw new RuntimeException(e);
            }
            System.out.println("The method "+methodName +"ends");
            return result;
        }
        
    }

    Main

    package com.aff.spring.aop.imlp;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class Main {
        public static void main(String[] args) {
            ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
            
            ArithmeticCalculator arithmeticCalculator = ctx.getBean(ArithmeticCalculator.class);
            
            int result = arithmeticCalculator.add(2, 3);
            System.out.println("result:"+result);
            
            int result2 = arithmeticCalculator.div(2, 0);
            System.out.println("result2:"+result2);
            
        }
    }

    效果如下

    The method addbegins  with[2, 3]
    The method addends 
    The method addends with 5
    result:5
    The method divbegins  with[2, 1]
    The method divends 
    The method divends with 2
    result2:2

     目录

    3.@Order()注解指定切面的优先级,值越小优先级越高

       如:@Order(1)高于@Order(2)

    4.重用切点表达式

    //把这个类声明为切面,需要把这个类放到IOC容器中, 再声明为一个切面
    @Aspect
    @Component
    public class LoggingAspect {
        
        /**
         * 定义一个方法, 用于声明切入点表达式,一般,该方法中不再需要添加其他代码
         * 
         * @Pointut 声明切点表达式
         *    后面的其他通知 直接使用方法名来引用当前的切点表达式 
         */
        @Pointcut("execution(* com.aff.spring.aop.imlp.*.*(int , int ))")
        public void  declareJointPointExpression(){
            
        }
        
        //声明该方法是一个前置通知, 在目标方法开始之前执行
        @Before("declareJointPointExpression()")
        public void  beforeMethod(JoinPoint joinPoint){
            String methodName =  joinPoint.getSignature().getName();
            List<Object> args =  Arrays.asList(joinPoint.getArgs());
            System.out.println("The method " +methodName+"begins  with"+args);
        }
    }
    All that work will definitely pay off
  • 相关阅读:
    [翻译]XNA 3.0 Game Programming Recipes之thirteen
    [翻译]XNA 3.0 Game Programming Recipes之sixteen
    [翻译]XNA 3.0 Game Programming Recipes之fourteen
    [翻译]XNA 3.0 Game Programming Recipes之nineteen
    [翻译]XNA 3.0 Game Programming Recipes之fifteen
    [翻译]XNA 3.0 Game Programming Recipes之twenty
    正则表达式关于电话和邮箱在RegularExpressionValidator 中的验证
    automation服务器不能创建对象
    常用的正则表达式
    谈论URL伪装
  • 原文地址:https://www.cnblogs.com/afangfang/p/12986741.html
Copyright © 2011-2022 走看看