zoukankan      html  css  js  c++  java
  • Spring基础知识之Aop动态代理

    代理模式:

      代理模式是常用的Java设计模式,它的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息,代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务,简单的说就是,我们在访问实际对象时,是通过代理对象来访问的.代理模式就是在访问实际对象时,引入一定程度的间接性,因为这种间接性,可以附加多种用途.

    代理模式结构图(图片来自<<大话设计模式>>)

     静态代理:

      静态代理:由程序员创建或特定工具自动生成源代码,也就是在编译时就已经将接口,被代理类,代理类等确定下来,在程序运行之前,代理类.class文件已经生成.

    静态代理简单实现:

      1.首先创建一个接口,这个接口是代理类和被代理类公共的接口,它们有一个共同的实现方法.

    1 public interface Person {
    2     // 上交班费
    3     void giveMoney();
    4 }

      2.创建两个实现类

     1 public class Student implements Person {
     2     private String name;
     3     public Student(String name){
     4         this.name = name;
     5     }
     6 
     7     public void giveMoney() {
     8         System.out.println("student: "+name+" giveMoney");
     9     }
    10 }
     1 public class StudentProxy implements Person {
     2     //被代理的学生
     3     Student student;
     4     public StudentProxy(Person student){
     5         // 只代理学生对象
     6         if(student.getClass() == Student.class){
     7             this.student = (Student)student;
     8         }
     9     }
    10     // 实现被代理类的方法
    11     public void giveMoney() {
    12         student.giveMoney();
    13     }
    14 }

      3.测试类

     1 public class Test {
     2     public static void main(String[] args) {
     3         //被代理对象
     4         Person student = new Student("张三");
     5         //生成代理对象,并将张三传给代理对象
     6         Person monitor = new StudentProxy(student);
     7         //代理对象调用方法,间接调用被代理对象的方法
     8         monitor.giveMoney();
     9     }
    10 }

      4.测试结果

       这里并没有直接通过张三(被代理对象)来执行上交班费的行为,而是通过班长(代理对象)来代理执行了。这就是代理模式.

      代理模式最主要的就是有一个公共接口(Person),一个具体的类(Student),一个代理类(StudentsProxy),代理类持有具体类的实例,代为执行具体类实例方法。上面说到,代理模式就是在访问实际对象时引入一定程度的间接性,因为这种间接性,可以附加多种用途。这里的间接性就是指不直接调用实际对象的方法,那么我们在代理过程中就可以加上一些其他用途。

     1 public class StudentProxy implements Person {
     2     //被代理的学生
     3     Student student;
     4     public StudentProxy(Person student){
     5         // 只代理学生对象
     6         if(student.getClass() == Student.class){
     7             this.student = (Student)student;
     8         }
     9     }
    10     // 实现被代理类的方法
    11     public void giveMoney() {
    12         System.out.println("在执行被代理类方法之前执行");
    13         student.giveMoney();
    14     }
    15 }

      可以看到,只需要在代理类中执行被代理方法之前,执行其他操作就可以了。这种操作,也是使用代理模式的一个很大的优点。最直白的就是在Spring中的面向切面编程(AOP),我们能在一个切点之前执行一些操作,在一个切点之后执行一些操作,这个切点就是一个个方法。这些方法所在类肯定就是被代理了,在代理过程中切入了一些其他操作。

    动态代理:

       代理类在程序运行时创建的代理方式被称为动态代理。动态代理,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。

    动态代理的实现:

      在java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和这个接口可以生成JDK动态代理类和动态代理对象。

      1.创建一个接口

    1 public interface Person {
    2     void giveMoney();
    3 }

      2.创建一个需要被代理的实际类

    public class Student implements Person {
        private String name;
    
        public Student(String name) {
            this.name = name;
        }
    
        public void giveMoney() {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("student "+name +" giveMoney");
        }
    }

      3定义一个检测方法执行时间的工具类

     1 public class MonitorUtil {
     2     private static ThreadLocal<Long> t1 = new ThreadLocal<Long>();
     3     public static void start(){
     4         t1.set(System.currentTimeMillis());
     5     }
     6     //结束打印时耗时
     7     public static void finish(String methodName){
     8         long finishTime = System.currentTimeMillis();
     9         System.out.println(methodName+"方法耗时:"+(finishTime - t1.get())+"ms");
    10     }
    11 }

      4.创建StudentInvocationHandler类,实现InvocationHandler接口,这个类中持有一个被代理对象的实例target。InvocationHandler中有一个invoke方法,所有执行代理对象的方法都会被替换成执行invoke方法。再在invoke方法中执行被代理对象target的相应方法.

     1 public class StudentInvocationHandler<T> implements InvocationHandler {
     2     // invocationHandler持有的被代理对象
     3     T target;
     4 
     5     public StudentInvocationHandler(T target) {
     6         this.target = target;
     7     }
     8 
     9     /**
    10      *
    11      * @param proxy 代表动态代理对象
    12      * @param method 代表正在执行的方法
    13      * @param args 代表调用目标方法时传入的实参
    14      * @return
    15      * @throws Throwable
    16      */
    17     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    18         System.out.println("代理执行"+method.getName()+"方法");
    19         //代理过程插入检测方法,计算方法耗时
    20         MonitorUtil.start();
    21         Object result = method.invoke(target,args);
    22         MonitorUtil.finish(method.getName());
    23         return result;
    24     }
    25 }

      5.创建测试类:

     1 public class Test {
     2     public static void main(String[] args) {
     3         //创建一个实例对象,这个对象是被代理的对象
     4         Person student = new Student("张三");
     5         //创建一个与代理对象相关联的invocationHandler
     6         InvocationHandler stuHandler = new StudentInvocationHandler<Person>(student);
     7         //创建一个代理对象stuProxy来代理student,代理对象的每个执行方法都会替换执行InvocationHandler的invoke()方法
     8         Person stuProxy = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(),new Class[]{Person.class},stuHandler);
     9         //代理执行方法
    10         stuProxy.giveMoney();
    11     }
    12 }

      6.上面也有说到所有执行代理对象的方法都会被替换成执行invoke方法,也就是说,最后执行的是StuInvocationHandler中的invoke方法.

       7.总结:

      动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。是因为所有被代理执行的方法,都是通过在InvocationHandler中的invoke方法调用的,所以我们只要在invoke方法中统一处理,就可以对所有被代理的方法进行相同的操作了,动态代理的过程中,我们并没有实际看到代理类,也没有很清晰地的看到代理类的具体样子,因为动态代理中被代理对象和代理对象是通过InvocationHandler来完成的代理过程的

      Java动态代理只能对接口进行代理,Java的继承机制注定了这些动态代理类们无法实现对class的动态代理。

    静态代理和动态代理的区别:

      静态代理:编译时将增强代码植入class文件,因为是编译期进行的增强,所以代码运行时效率比动态代理高,

      动态代理:运行时生成代理类并加载,效率比静态代理要低,spring中使用了JDK动态代理和cglib两种动态代理的方式来实现代理类的生成.

    CGlib代理:

      CGLIB(Code Generation Library)是一个开源项目!是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口

      CGLIB是一个强大的高性能的代码生成包。它广泛的被许多AOP的框架使用,例如Spring AOP为他们提供方法的interception(拦截)。CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。

      CGLIB是无法代理final修饰的方法的.

    CGlib的简单实现:

      1.创建一个业务类,这个类没有实现任何接口.

     1 public class HelloService {
     2  
     3     public HelloService() {
     4         System.out.println("HelloService构造");
     5     }
     6  
     7     /**
     8      * 该方法不能被子类覆盖,Cglib是无法代理final修饰的方法的
     9      */
    10     final public String sayOthers(String name) {
    11         System.out.println("HelloService:sayOthers>>"+name);
    12         return null;
    13     }
    14  
    15     public void sayHello() {
    16         System.out.println("HelloService:sayHello");
    17     }
    18 }

      2.自定义一个MethodInterceptor方法实现MethodInterceptor接口;

     1 public class MyMethodInterceptor implements MethodInterceptor{
     2  
     3     /**
     4      * sub:cglib生成的代理对象
     5      * method:被代理对象方法
     6      * objects:方法入参
     7      * methodProxy: 代理方法
     8      */
     9     @Override
    10     public Object intercept(Object sub, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    11         System.out.println("======插入前置通知======");
    12         Object object = methodProxy.invokeSuper(sub, objects);
    13         System.out.println("======插入后者通知======");
    14         return object;
    15     }
    16 }

      3.测试类:

     1 public class Client {
     2     public static void main(String[] args) {
     3         // 代理类class文件存入本地磁盘方便我们反编译查看源码
     4         System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\code");
     5         // 通过CGLIB动态代理获取代理对象的过程
     6         Enhancer enhancer = new Enhancer();
     7         // 设置enhancer对象的父类
     8         enhancer.setSuperclass(HelloService.class);
     9         // 设置enhancer的回调对象
    10         enhancer.setCallback(new MyMethodInterceptor());
    11         // 创建代理对象
    12         HelloService proxy= (HelloService)enhancer.create();
    13         // 通过代理对象调用目标方法
    14         proxy.sayHello();
    15     }
    16 }

      4.运行结果:

       5.总结:

      实现CGLIB动态代理必须实现MethodInterceptor(方法拦截器)接口,这个接口只有一个intercept()方法,这个方法有4个参数:

      1)obj表示增强的对象,即实现这个接口类的一个对象;

      2)method表示要被拦截的方法;

      3)args表示要被拦截方法的参数;

      4)proxy表示要触发父类的方法对象;

      在上面的Client代码中,通过Enhancer.create()方法创建代理对象
    JDK动态代理和CGlib代理的区别:

      JDK动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理.

      CGlib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理.

    AOP的实现:

      1.如果目标对象实现了接口,默认情况下会采用JDK动态代理实现AOP

      2.如果目标对象实现了接口,可以强制使用CGlib实现AOP,实现方法

        1.添加CGlib库,SPRING_HOME/cglib/*.jar

        2,在spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class="true"/>

      3.如果目标对象没有实现接口,必须采用CGlib库,Spring会自动在JDK动态代理和CGlib之间转换.

  • 相关阅读:
    netty 解决TCP粘包与拆包问题(二)
    Socket通讯-Netty框架实现Java通讯
    SpringBoot集成Swagger,Postman,newman,jenkins自动化测试.
    Java Console/控制台 打印表格
    mysql控制台的一些技巧,显示,输入换行,语法正则等
    “妈妈,我不想学了”,你的回答改变孩子一生(怅然若失,说了一句话:“为什么当初没有人逼我?”。我不愿意学,你就不让我学啦!那时候我还小,我还不懂事,难道你也不懂事吗。要做成一件事,就必须有延迟满足的能力)
    “太子”显然沉稳不足,急于把大权抓在自己手里,做的第一件事居然是想尽一切办法铲除父亲在公司的亲信(我很久以来的疑惑,看了这段才明白,相比之下,经过玄武门之变的李世民仍然忍了裴寂三年时间是何等的聪明和了不起)
    如何分析和提高大型项目(C/C++)的编译速度?(VS2015特有的:/LTCG:incremental选项)
    Duilib学习之基础(一个SDK程序)
    使用 QWorker 做为计划任务引擎
  • 原文地址:https://www.cnblogs.com/wk-missQ1/p/12643353.html
Copyright © 2011-2022 走看看