• spring AOP


    什么叫AOP?

      这种在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。

     面向切面编程(AOP是Aspect Oriented Program的首字母缩写) ,我们知道,面向对象的特点是继承、多态和封装。而封装就要求将功能分散到不同的对象中去,这在软件设计中往往称为职责分配。
    实际上也就是说,让不同的类设计不同的方法。这样代码就分散到一个个的类中去了。这样做的好处是降低了代码的复杂程度,使类可重用。 但是人们也发现,在分散代码的同时,也增加了代码的
    重复性。什么意思呢?比如说,我们在两个类中,可能都需要在每个方法中做日志。按面向对象的设计方法,我们就必须在两个类的方法中都加入日志的内容。也许他们是完全相同的,但就是因为面向对象
    的设计让类与类之间无法联系,而不能将这些重复的代码统一起来。 也许有人会说,那好办啊,我们可以将这段代码写在一个独立的类独立的方法里,然后再在这两个类中调用。但是,这样一来,这两
    个类跟我们上面提到的独立的类就有耦合了,它的改变会影响这两个类。那么,有没有什么办法,能让我们在需要的时候,随意地加入代码呢?这种在运行时,动态地将代码切入到类的指定方法、指定位置
    上的编程思想就是面向切面的编程。 一般而言,我们管切入到指定类指定方法的代码片段称为切面,而切入到哪些类、哪些方法则叫切入点。有了AOP,我们就可以把几个类共有的代码,抽取到一
    个切片中,等到需要时再切入对象中去,从而改变其原有的行为。这样看来,AOP其实只是OOP的补充而已。OOP从横向上区分出一个个的类来,而AOP则从纵向上向对象中加入特定的代码。有了AOP,OOP
    变得立体了。如果加上时间维度,AOP使OOP由原来的二维变为三维了,由平面变成立体了。从技术上来说,AOP基本上是通过代理机制实现的。

    1.代理模式readMe:

     1 代理设计模式: 是java中常用的设计模式!
     2 
     3    特点:
     4        01.委托类和代理类有相同的接口或者共同的父类!
     5        02.代理类为委托类负责处理消息,并将消息转发给委托类!
     6        03.委托类和代理类对象通常存在关联关系!
     7           一个代理类对象与一个委托类对象关联!
     8        04.代理类本身并不是真正的实现者!而是通过调用委托类的方法,
     9           来实现功能!
    10 
    11 
    12    按照代理类创建的时机,代理类分为两种:
    13    01.静态代理:由我们程序猿或者特定的工具自动生成了源代码,
    14                  在程序运行之前,.class文件已经存在了!
    15       (serviceImpl 调用了 dao层的方法! 真正的实现是Dao)
    16    02.动态代理:在程序运行期间,通过反射的方式动态的创建出来!
    17 
    18 
    19    按照我们的使用方式:  是由共同的接口还是公共的父类?
    20 
    21      01.jdk动态代理 (接口)
    22           必须知道一个类和一个接口
    23            001.InvocationHandler接口只有一个方法
    24 
    25             public Object invoke(Object proxy, Method method, Object[] args)
    26                    throws Throwable;
    27 
    28            proxy:代理类对象
    29            method:被代理的方法
    30            args:被代理的方法的参数列表
    31 
    32           002.Proxy 类:
    33            public static Object newProxyInstance(ClassLoader loader,
    34                   Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException
    35               loader:类加载器
    36               interfaces:代理类实现的所有接口
    37               h:InvocationHandler接口的一个实例   this当前对象
    38                  因为我们想使用jdk动态代理 必须是 代理类 实现 InvocationHandler!
    39                   它让我们传递父接口 我们传递 自身!
    40 
    41      02.cglib动态代理(接口+父类)
    42          必须知道一个类和一个接口
    43            001.MethodInterceptor接口
    44 
    45            public Object intercept(Object obj,
    46               Method method,Object[] args,MethodProxy proxy) throws Throwable;
    47 
    48         intercept是所有拦截器执行的方法,类似于jdk动态代理中的invoke
    49 
    50 
    51           002. Enhancer类
    52 
    53            设置委托类和代理类的公共接口或者公共的父类
    54            public void setSuperclass(Class superclass) {
    55                   if (superclass != null && superclass.isInterface()) {
    56                       setInterfaces(new Class[]{ superclass });
    57                   } else if (superclass != null && superclass.equals(Object.class)) {
    58                       // affects choice of ClassLoader
    59                       this.superclass = null;
    60                   } else {
    61                       this.superclass = superclass;
    62                   }
    63               }
    64 
    65                代理类执行完毕 通知委托类
    66                public void setCallback(final Callback callback) {
    67                       setCallbacks(new Callback[]{ callback });
    68                   }
    69 
    70 
    71                 在Enhancer类的父类AbstractClassGenerator中有一个方法
    72                   创建我们需要的代理类
    73                   protected Object create(Object key)

    2.静态代理:

    01.接口代码:

    package cn.pb.dao;
    
    /**
     * 动物类 父接口
     */
    public interface Animal {
        //主业务
        void eat();
        void sleep();
    }

    02.实现类代码:

    package cn.pb.dao.impl;
    /**
     * 狗狗类 实现了Animal接口
     */
    
    import cn.pb.dao.Animal;
    
    public class Dog implements Animal {
        public void eat() {
            System.out.println("狗狗在啃骨头!");
        }
    
        public void sleep() {
            System.out.println("狗狗在午休!");
        }
    }

    03.静态代理类:

    package cn.pb.staticproxy;
    
    import cn.pb.dao.Animal;
    import cn.pb.dao.impl.Dog;
    
    /**
     * 狗狗的静态代理类
     */
    public class AnimalStaticProxy implements Animal {
    
        private Dog dog;
    
    
        public void sleep() {
            System.out.println("主人在召唤");  //系统级业务
            dog.sleep();
            System.out.println("主人离开"); //系统级业务
        }
    
        public void eat() {
            System.out.println("主人在召唤"); //系统级业务
            dog.eat();
            System.out.println("主人离开"); //系统级业务
        }
    
        public Dog getDog() {
            return dog;
        }
    
        public void setDog(Dog dog) {
            this.dog = dog;
        }
    
    
        /**
         * 我们发现的问题
         * 01:代码冗余
         * 02:把冗余的代码提取成公共的方法
         * 03:有可能小猫咪也有这些方法
         * 04:提取成一个工具类中的方法
         * 05:现在有一个小猫咪 也需要执行 sleep和eat 以及系统级业务方法
         * 06:我们又得创建一个小猫咪对应的代理类
         * 07:动物有很多  ,难道需要我们创建N个代理类吗??肯定!
         */
    }

    04.测试类代码:

    /**
         * 静态代理的测试方法
         */
        @Test
        public void testStaticProxy(){
            AnimalStaticProxy proxy = new AnimalStaticProxy();
            Dog dog=new Dog();
            proxy.setDog(dog);
    
    
            proxy.eat();
            System.out.println("*************");
            proxy.sleep();
        }

    3.JDK动态代理:

    01.接口代码:

    package cn.pb.dao;
    
    /**
     * 动物类 父接口
     */
    public interface Animal {
        //主业务
        void eat();
        void sleep();
    }

    02.实现类代码:

    package cn.pb.dao.impl;
    /**
     * 狗狗类 实现了Animal接口
     */
    
    import cn.pb.dao.Animal;
    
    public class Dog implements Animal {
        public void eat() {
            System.out.println("狗狗在啃骨头!");
        }
    
        public void sleep() {
            System.out.println("狗狗在午休!");
        }
    }

    03.动态代理类代码:

    package cn.pb.jdkdynamicproxy;
    /**
     * JDK的动态代理类
     */
    
    import cn.pb.dao.Animal;
    import cn.pb.dao.impl.Dog;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class AnimalJdkDynamicProxy implements InvocationHandler {
    
        /**
         *  01.我们不确定委托类是谁?委托类的类型 是Object
         *   和委托类建立关联关系
         */
        private Object target;
    
    
        /**
         * 02.给我一个委托类,我返回一个代理类对象
         */
        public Object createProxy(Object target){
            //根据传递的参数 进行对象的关联
            this.target=target;
            return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                    target.getClass().getInterfaces(), this);
        }
    
        /**
         *
         * @param proxy :代理对象
         * @param method :方法名
         * @param args : 参数列表
         * @return
         */
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("主人在召唤"); //系统级业务   开始事务
            Object result= method.invoke(target,args);  // 主业务
            System.out.println("主人离开"); //系统级业务     日志处理  关闭事务
            return result;
        }
    
    
        //创建测试方法
        public static void main(String[] args) {
            AnimalJdkDynamicProxy proxy=new AnimalJdkDynamicProxy();
            Animal dog= (Animal) proxy.createProxy(new Dog());
            dog.eat();
            System.out.println("**************************");
            dog.sleep();
    
        }
    }

    04.测试代码:

    @Test
        public void testJdkDynamicProxy(){
            AnimalJdkDynamicProxy proxy=new AnimalJdkDynamicProxy();
            Animal dog= (Animal) proxy.createProxy(new Dog());
            dog.eat();
            System.out.println("**************************");
            dog.sleep();
        }

    4.cglib动态代理:

    01.接口代码:

    package cn.pb.dao;
    
    /**
     * 动物类 父接口
     */
    public interface Animal {
        //主业务
        void eat();
        void sleep();
    }

    02.实现类代码:

    package cn.pb.dao.impl;
    /**
     * 狗狗类 实现了Animal接口
     */
    
    import cn.pb.dao.Animal;
    
    public class Dog implements Animal {
        public void eat() {
            System.out.println("狗狗在啃骨头!");
        }
    
        public void sleep() {
            System.out.println("狗狗在午休!");
        }
    }

    03.动态代理类代码:

    package cn.pb.cglibdynamicproxy;
    
    
    /**
     * Cglib动态代理
     */
    import cn.pb.dao.Animal;
    import cn.pb.dao.impl.Dog;
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    import java.lang.reflect.Method;
    
    public class AnimalCglibDynamicProxy implements MethodInterceptor {
    
        /**
         * 在enhancer中有一个setCallBack(this)
         * 这样就实现了代理类和委托类的关联
         */
        private Enhancer enhancer=new Enhancer();
    
        /**
         *  创建代理类对象
         */
        public  Object  createProxy(Class clazz){
            //设置公共的接口或者公共的类
            enhancer.setSuperclass(clazz);
            //建立关联关系
            enhancer.setCallback(this);
            return enhancer.create();
        }
    
        /**
         * 类似于我们jdk中的invoke
         */
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            System.out.println("主人在召唤"); //系统级业务   开始事务
            Object result= proxy.invokeSuper(obj,args);  // 主业务
            System.out.println("主人离开"); //系统级业务     日志处理  关闭事务
            return result;
        }
    
    
        //创建测试方法
        public static void main(String[] args) {
            AnimalCglibDynamicProxy proxy=new AnimalCglibDynamicProxy();
            //这里的参数可以传三种形式01:new Dog().getClass()
            // 02:Class.forName("cn.pb.dao.impl.Dog") 03.Dog.class
            Animal dog= (Animal) proxy.createProxy(new Dog().getClass());
            dog.eat();
            System.out.println("**************************");
            dog.sleep();
        }
    }

    04.测试代码:

    @Test
        public void testCglibDynamicProxy(){
            AnimalJdkDynamicProxy proxy=new AnimalJdkDynamicProxy();
            Animal dog= (Animal) proxy.createProxy(new Dog());
            dog.eat();
            System.out.println("**************************");
            dog.sleep();
        }

    就是在方法前或后要做的一些事情。AOP中官方叫法为“切面” 如果写到方法中。后期维护很难。

    1:核心代码

     1 package com.spring.aop;
     2 
     3 public interface ArithmeticCalculator {
     4 
     5     int add(int i, int j);
     6     int sub(int i, int j);
     7     
     8     int mul(int i, int j);
     9     int div(int i, int j);
    10     
    11 }
     1 package com.spring.aop;
     2 
     3 import org.springframework.stereotype.Component;
     4 
     5 @Component("arithmeticCalculator")
     6 public class ArithmeticCalculatorImpl implements ArithmeticCalculator {
     7 
     8     public int add(int i, int j) {
     9         int result = i + j;
    10         return result;
    11     }
    12 
    13     public int sub(int i, int j) {
    14         int result = i - j;
    15         return result;
    16     }
    17 
    18     public int mul(int i, int j) {
    19         int result = i * j;
    20         return result;
    21     }
    22 
    23     public int div(int i, int j) {
    24         int result = i / j;
    25         return result;
    26     }
    27 
    28 }

    2:写一个切面的类

    package com.spring.aop;

    import java.util.Arrays;

    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.AfterReturning;
    import org.aspectj.lang.annotation.AfterThrowing;
    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;

    /**
     * 可以使用 @Order 注解指定切面的优先级, 值越小优先级越高
     */
    @Order(1)
    @Aspect
    @Component
    public class LoggingAspect {
        
        /**
         * 定义一个方法, 用于声明切入点表达式. 一般地, 该方法中再不需要添入其他的代码.
         * 使用 @Pointcut 来声明切入点表达式.
         * 后面的其他通知直接使用方法名来引用当前的切入点表达式.
         */
        @Pointcut("execution(public int com.atguigu.spring.aop.ArithmeticCalculator.*(..))")
        public void declareJointPointExpression(){}
        
        /**
         * 在 com.atguigu.spring.aop.ArithmeticCalculator 接口的每一个实现类的每一个方法开始之前执行一段代码
         */
        @Before("declareJointPointExpression()")
        public void beforeMethod(JoinPoint joinPoint){
            String methodName = joinPoint.getSignature().getName();
            Object [] args = joinPoint.getArgs();
            
            System.out.println("The method " + methodName + " begins with " + Arrays.asList(args));
        }
        
        /**
         * 在方法执行之后执行的代码. 无论该方法是否出现异常
         */
        @After("declareJointPointExpression()")
        public void afterMethod(JoinPoint joinPoint){
            String methodName = joinPoint.getSignature().getName();
            System.out.println("The method " + methodName + " ends");
        }
        
        /**
         * 在方法法正常结束受执行的代码
         * 返回通知是可以访问到方法的返回值的!
         */
        @AfterReturning(value="declareJointPointExpression()",
                returning="result")
        public void afterReturning(JoinPoint joinPoint, Object result){
            String methodName = joinPoint.getSignature().getName();
            System.out.println("The method " + methodName + " ends with " + result);
        }
        
        /**
         * 在目标方法出现异常时会执行的代码.
         * 可以访问到异常对象; 且可以指定在出现特定异常时在执行通知代码
         */
        @AfterThrowing(value="declareJointPointExpression()",
                throwing="e")
        public void afterThrowing(JoinPoint joinPoint, Exception e){
            String methodName = joinPoint.getSignature().getName();
            System.out.println("The method " + methodName + " occurs excetion:" + e);
        }
        
        /**
         * 环绕通知需要携带 ProceedingJoinPoint 类型的参数.
         * 环绕通知类似于动态代理的全过程: ProceedingJoinPoint 类型的参数可以决定是否执行目标方法.
         * 且环绕通知必须有返回值, 返回值即为目标方法的返回值
         */
        /*
        @Around("execution(public int com.atguigu.spring.aop.ArithmeticCalculator.*(..))")
        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;
        }
        */
    }

    3:配置文件中配置上使用注解和AOP

    <?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:aop="http://www.springframework.org/schema/aop"
        xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
                            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
                            ">
        <!-- 自动扫描的包 -->
        <context:component-scan base-package="com.spring.aop"></context:component-scan>
    
        <!-- 使 AspectJ 的注解起作用 -->
        <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    </beans>

     ***:小知识:先执行哪个切面类

    @Order(1)  值越小越先 执行

     4:基于xml文件配置的方式

    <!-- 配置目标方法的类的bean -->
            <bean id="arithmeticCalculator"
                    class="com.spring.aop.ArithmeticCalculatorImpl"
            ></bean>
            <!-- 配置切面类的bean -->
            <bean id="laoggingAspect"
                    class="com.spring.aop.LoggingAspect"
            ></bean>
            <!-- 配置AOP -->
            <aop:config>
            <!-- 切点表达式  就是配置哪个方法是切面 -->
            <aop:pointcut expression="execution(* com.spring.aop.ArithmeticCalculator.*(int, int))" id="pointcut"/>
            <!-- 调用方法的时候先执行切面 -->
            <aop:aspect ref="laoggingAspect">
                <aop:before method="beforeMethod" pointcut-ref="pointcut"/>
            </aop:aspect>
            </aop:config>
  • 相关阅读:
    Power Apps 创建响应式布局
    SharePoint Online 软件边界限制
    Power Apps 中人员选择器的使用
    Power Apps 中修改 SharePoint Online 数据
    Power Apps 中调用 Automate 工作流
    如何查看你的Office 365 账号的订阅
    Microsoft Teams 中嵌入SharePoint Online 页面
    SharePoint Online 触发Outlook邮件内审批
    Linux查看实时网卡流量的几种方式
    Linux性能优化和监控系列(三)——分析Memory使用状况
  • 原文地址:https://www.cnblogs.com/bulrush/p/7912356.html
Copyright © 2020-2023  润新知