美文网首页
Java Spring-AOP动态代理

Java Spring-AOP动态代理

作者: 鄢栋云 | 来源:发表于2020-01-30 10:16 被阅读0次

    代理设计模式的原理:使用一个代理将对象包装起来,然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。

    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);
    }
    
    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;
        }
    }
    
    public class ArithmeticCalculatorLoggingProxy {
        //要代理的对象
        private ArithmeticCalculator target;
        public ArithmeticCalculatorLoggingProxy(ArithmeticCalculator target){
            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:正在返回的那个代理函数。一般情况下,在invoke方法中都不使用该对象。
                method:正在被调用的方法
                args:调用方法时,传入的参数
                */
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    String methodName = method.getName();
                    //日志
                    System.out.println("The method " + methodName + " begins with " + Arrays.asList(args));
                    //执行方法
                    Object result = method.invoke(target,args);
                    //日志
                    System.out.println("The method " + methodName + " ends with " + result);
                    return result;
                }
            };
            proxy = (ArithmeticCalculator) Proxy.newProxyInstance(loader,interfaces,h);
            return proxy;
        }
    }
    
    public class Main {
        public static void main(String[] args) {
    //        ArithmeticCalculator arithmeticCalculator = null;
    //        arithmeticCalculator = new ArithmeticCalculatorImpl();
    
            ArithmeticCalculator target = new ArithmeticCalculatorImpl();
            ArithmeticCalculator proxy = new ArithmeticCalculatorLoggingProxy(target).getLoggingProxy();
            int result = proxy.add(1,2);
            System.out.println("-->"+result);
            proxy.div(4,2);
            System.out.println("-->"+result);
        }
    }
    //输出
    The method add begins with [1, 2]
    The method add ends with 3
    -->3
    The method div begins with [4, 2]
    The method div ends with 2
    -->3
    

    AOP(Aspect-Oriented Programming,面向切面编程):是一种新的方法论,是对传统OOP(Object-Oriented-Programming,面向对象编程)的补充。AOP的主要编程对象是切面(aspect),而切面模块化横切关注点。在应用AOP编程时,任然需要定义公共功能,但可以明确的定义这个功能在哪里,以什么方式应用,并且不必修改受影响的类,这样一来横切关注点就被模块化到特殊的对象(切面)里。AOP的好处:

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

    SpringAOP

    1.导入aspectjweaver.jar和aspectjrt.jar

    2.在配置文件中加入aop的命名空间

    3.基于注解的方式

    1.在配置文件中加入以下配置

        <!-- 使AspjectJ 注解起作用:自动为匹配的类生成代理对象 -->
        <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    

    2.把横切关注点的代码抽象到切面的类中

    i.切面首先是一个IOC中的bean,即加入@Component注解

    ii.切面还需要加入@Aspect注解

    3.在类中声明各种通知:
    i.声明一个方法
    ii.在方法前加入@Before注释

    4.可以在通知方法中声明一个类型为JointPoint的参数。然后就能访问链接细节。如方法名称和参数值。

    //把这个类声明为一个切面:需要把该类放入到IOC容器中、再声明为一个切面
    @Aspect
    @Component
    public class LoggingAspect {
        //声明该方法是一个前置通知:在目标方法开始之前执行
        @Before("execution(public int com.cloud.spring.aop.impl.ArithmeticCalculator.*(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);
        }
    }
    

    例如:

    <?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: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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <!-- 配置自动扫描的包 -->
        <context:component-scan base-package="com.cloud.spring.aop.impl"></context:component-scan>
    
        <!-- 使AspjectJ 注解起作用:自动为匹配的类生成代理对象 -->
        <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    </beans>
    
    
    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);
    }
    
    @Component
    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;
        }
    }
    
    //把这个类声明为一个切面:需要把该类放入到IOC容器中、再声明为一个切面
    @Aspect
    @Component
    public class LoggingAspect {
        //声明该方法是一个前置通知:在目标方法开始之前执行
        @Before("execution(public int com.cloud.spring.aop.impl.ArithmeticCalculator.*(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);
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            //1.创建Spring的IOC容器
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            //2.从IOC容器中获取bean的实例
            ArithmeticCalculator arithmeticCalculator = context.getBean(ArithmeticCalculator.class);
            //3.使用bean
            int result = arithmeticCalculator.add(3,6);
            System.out.println("result:" + result);
    
            result = arithmeticCalculator.div(9,3);
            System.out.println("result:" + result);
        }
    }
    
    //输出
    The method add begins with[3, 6]
    result:9
    The method div begins with[9, 3]
    result:3
    

    后置通知:在目标方法执行后(无论是否发生异常),执行的通知,在后置通知中还不能访问目标方法执行的结果。

    //后置通知:在目标方法执行后(无论是否发生异常),执行的通知
    //在后置通知中还不能访问目标方法执行的结果
    @After("execution(* com.cloud.spring.aop.impl.*.*(int,int))")
    public void afterMethod(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("The method " + methodName + " ends");
    }
        
    //输出
    The method add begins with[3, 6]
    The method add ends
    result:9
    The method div begins with[9, 3]
    The method div ends
    result:3
    

    后置通知是在连接点完成之后执行的,即连接点返回结果或者抛出异常的时候,下面的后置通知记录了方法的终止,一个切面可以包括一个或者多个通知。

    返回通知:在方法正常结束受执行的代码,返回通知是可以访问到方法的返回值的。

        /*
        * 在方法正常结束之后执行的代码
        * 返回通知是可以访问到方法的返回值的!
        */
        @AfterReturning(value = "execution(* com.cloud.spring.aop.impl.*.*(..))",returning = "result")
        public void afterReturing(JoinPoint joinPoint,Object result){
            String methodName = joinPoint.getSignature().getName();
            System.out.println("The method " + methodName + " ends with " + result);
        }
    //输出
    The method add begins with[3, 6]
    The method add ends
    The method add ends with 9
    result:9
    The method div begins with[9, 3]
    The method div ends
    The method div ends with 3
    result:3
    

    异常通知:在方法抛出异常之后

         /*
         *  在目标方法出现异常时会执行的代码
         *  可以访问到异常对象,且可以指定在出现特定异常时再执行通知代码
         */
        @AfterThrowing(value = "execution(* com.cloud.spring.aop.impl.*.*(..))",throwing = "ex")
        public void afterThrowing(JoinPoint joinPoint,Exception ex){
            String  methodName = joinPoint.getSignature().getName();
            System.out.println("The method " + methodName + " occurs exception with " + ex);
        }
    //输出
    The method add begins with[3, 6]
    Exception in thread "main" java.lang.ArithmeticException: / by zero
    The method add ends
    The method add ends with 9
        at com.cloud.spring.aop.impl.ArithmeticCalculatorImpl.div(ArithmeticCalculatorImpl.java:32)
    result:9
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    The method div begins with[9, 0]
    The method div ends
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    The method div occurs exception with java.lang.ArithmeticException: / by zero
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
    

    环绕通知:围绕着方法执行

        public static void main(String[] args) {
            //1.创建Spring的IOC容器
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            //2.从IOC容器中获取bean的实例
            ArithmeticCalculator arithmeticCalculator = context.getBean(ArithmeticCalculator.class);
            //3.使用bean
            int result = arithmeticCalculator.add(3,6);
            System.out.println("result:" + result);
    
            result = arithmeticCalculator.div(9,3);
            System.out.println("result:" + result);
        }
    /*
        * 环绕通知需要携带ProceedingJointPoint 类型的参数
        * 环绕通知类似于动态代理的全过程:ProceedingJointPoint 类型的参数可以决定是否执行目标方法。
        * 且环绕通知必须有返回值,返回值即为目标方法的返回值
        */
        @Around("execution(* com.cloud.spring.aop.impl.*.*(..))")
        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 + " ends with " + result);
            }catch (Throwable e){
                //异常通知
                System.out.println("The method " + methodName + " occurs exception: " + e);
                throw new RuntimeException(e);
            }
            System.out.println("The method " + methodName + " ends ");
            return result;
        }
    //输出
    The method add begins with [3, 6]
    The method add ends with 9
    The method add ends 
    result:9
    The method div begins with [9, 3]
    The method div ends with 3
    The method div ends 
    result:3
    
    
        public static void main(String[] args) {
            //1.创建Spring的IOC容器
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            //2.从IOC容器中获取bean的实例
            ArithmeticCalculator arithmeticCalculator = context.getBean(ArithmeticCalculator.class);
            //3.使用bean
            int result = arithmeticCalculator.add(3,6);
            System.out.println("result:" + result);
    
            result = arithmeticCalculator.div(9,0);
            System.out.println("result:" + result);
        }
        
    /*
        * 环绕通知需要携带ProceedingJointPoint 类型的参数
        * 环绕通知类似于动态代理的全过程:ProceedingJointPoint 类型的参数可以决定是否执行目标方法。
        * 且环绕通知必须有返回值,返回值即为目标方法的返回值
        */
        @Around("execution(* com.cloud.spring.aop.impl.*.*(..))")
        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 + " ends with " + result);
            }catch (Throwable e){
                //异常通知
                System.out.println("The method " + methodName + " occurs exception: " + e);
                throw new RuntimeException(e);
            }
            System.out.println("The method " + methodName + " ends ");
            return result;
        }
    //输出
    The method add begins with [3, 6]
    The method add ends with 9
    The method add ends 
    result:9
    The method div begins with [9, 0]
    The method div occurs exception: java.lang.ArithmeticException: / by zero
    

    相关文章

      网友评论

          本文标题:Java Spring-AOP动态代理

          本文链接:https://www.haomeiwen.com/subject/akkithtx.html