zoukankan      html  css  js  c++  java
  • Java基础——动态代理

    1、动态代理

    动态代理的目标就是在运行时改变目标类,对目标类的参数或者方法做扩展

    当想要给实现了某个接口的类中的方法,加一些额外的处理。比如说加日志,加事务等。可以给这个类创建一个代理,故名思议就是创建一个新的类,这个类不仅包含原来类方法的功能,而且还在原来的基础上添加了额外处理的新类。这个代理类并不是定义好的,是动态生成的。具有解耦意义,灵活,扩展性强

    Java中常用的动态代理的实现方式:一种是利用JDK反射机制生成代理,另外一种是使用CGLIB代理

    JDK代理必须要提供接口,而CGLIB则不需要,可以直接代理类

    反射机制:程序在运行期间可以访问、检测和修改其本身状态或行为的一种能力,使用反射我们可以调用任意一个类对象,以及类对象中包含的属性及方法

    动态代理使用场景:Spring 的 AOP
             JavaBean和jsp之间调用
               Struts的 FormBean 和页面之间调用
               JDBC 的 classForName()
             Hibernate的 find(Class clazz) 

             ................

    2、JDK反射机制

    JDK Proxy 是 Java 语言自带的功能,无需通过加载第三方类实现

    Java 对 JDK Proxy 提供了稳定的支持,并且会持续的升级和更新 JDK Proxy,例如 Java 8 版本中的 JDK Proxy 性能相比于之前版本提升了很多

    JDK Proxy 是通过拦截器加反射的方式实现的

    JDK Proxy 只能代理继承接口的类

    JDK Proxy 实现和调用起来比较简单

    JDK反射实现:实现 InvocationHandler 接口,重写 invoke() 方法

     1 //定义方法接口
     2 public interface IA {
     3     public void A1(String s);
     4     public void A2(String s);
     5 }
     6 
     7 //实现IA接口
     8 public class IATest implements IA{
     9     @Override
    10     public void A1(String s) {
    11         System.out.println("this is IATest implements IA method A1() &&"+s);
    12     }
    13 
    14     @Override
    15     public void A2(String s) {
    16 
    17         System.out.println("this is IATest implements IA method A2() &&"+s);
    18     }
    19 }
    20 
    21 //重写 Invocation 接口的invoke()方法
    22  public class IBTest implements InvocationHandler {
    23     private Object object;//代理对象
    24 
    25     //  构造方法,给我们要代理的真实对象赋初值
    26     public Object IBTest(Object object){
    27         this.object = object;
    28         //获得代理对象
    29         Object o = Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), this);
    30         return  o;
    31     }
    32     @Override
    33     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    34          Object o ;
    35         //  在代理真实对象前我们可以添加一些自己的操作
    36         System.out.println("Method:" + method.getName());
    37         //      如果方法是 A1
    38         if (method.getName().equals("A1")) {
    39             String r = "B1";
    40             o = method.invoke(object, r);
    41             System.out.println("IB after IA.A1");
    42 
    43         } else {
    44             //    当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
    45             String r = "B2";
    46             o = method.invoke(object, r);
    47             System.out.println("IB after IA.A2");
    48 
    49         }
    50         System.out.println("IB is end");
    51         return o;
    52     }
    53 }
    54 
    55 //测试代码
    56 public static void main (String[] args){
    57         IBTest ibTest = new IBTest();
    58         IA ia = (IA)ibTest.IBTest(new IATest());
    59         ia.A1("A1");
    60         ia.A2("A2");
    61     }


    运行结果:

    Method:A1
    this is IATest implements IA method A1() &&B1
    IB after IA.A1
    IB is end

    Method:A2
    this is IATest implements IA method A2() &&B2
    IB after IA.A2
    IB is end

    //InvocationHandler 接口源码
    public interface InvocationHandler {
      public Object invoke(Object proxy, Method method, Object[] args)
              throws Throwable;
    }
    
    在动态代理中有一个重要的角色——代理器,它用于统一管理被代理的对象,InvocationHandler 就是这个代理器,而 invoke() 方法则是触发代理的执行方法,我们通过实现 Invocation 接口来拥有动态代理的能力

    3、CGLIB代理

    CGLib 是第三方提供的工具,基于 ASM 实现的,性能比较高
    CGLib 无需通过接口来实现,它是通过实现子类的方式来完成调用的

    //先引入第三方辅助
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.3.0</version>
    </dependency>
    

      

    package com.lagou.interview;
    
    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 CGLibExample {
    
        static class Car {
            public void running() {
                System.out.println("The car is running.");
            }
        }
    
        /**
         * CGLib 代理类
         */
        static class CGLibProxy implements MethodInterceptor {
            private Object target; // 代理对象
    
            public Object getInstance(Object target) {
                this.target = target;
                Enhancer enhancer = new Enhancer();
                // 设置父类为实例类
                enhancer.setSuperclass(this.target.getClass());
                // 回调方法
                enhancer.setCallback(this);
                // 创建代理对象
                return enhancer.create();
            }
    
            @Override
            public Object intercept(Object o, Method method,
                                    Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("方法调用前业务处理.");
                Object result = methodProxy.invokeSuper(o, objects); // 执行方法调用
                return result;
            }
        }
    
        // 执行 CGLib 的方法调用
        public static void main(String[] args) {
            // 创建 CGLib 代理类
            CGLibProxy proxy = new CGLibProxy();
            // 初始化代理对象
            Car car = (Car) proxy.getInstance(new Car());
            // 执行方法
            car.running();
    

    CGLib 在初始化被代理类时,是通过 Enhancer 对象把代理对象设置为被代理类的子类来实现动态代理的。因此被代理类不能被关键字 final 修饰,如果被 final 修饰,再使用 Enhancer 设置父类时会报错,动态代理的构建会失败

    4、其他

    静态代理其实就是事先写好代理类,可以手工编写也可以使用工具生成,但它的缺点是每个业务类都要对应一个代理类,特别不灵活也不方便,于是就有了动态代理

    Spring 框架中同时使用了两种动态代理 JDK Proxy 和 CGLib,当 Bean 实现了接口时,Spring 就会使用 JDK Proxy,在没有实现接口时就会使用 CGLib,我们也可以在配置中指定强制使用 CGLib,只需要在 Spring 配置中添加 <aop:aspectj-autoproxy proxy-target-class="true"/> 

  • 相关阅读:
    Daily Scrum 10.24
    Daily Srum 10.22
    TFS的使用
    Daily Srum 10.21
    Scrum Meeting 报告
    团队博客作业Week4 --- 学霸网站--NABC
    团队博客作业Week3 --- 项目选择&&需求疑问
    团队博客作业Week2 --- 学长学姐访谈录
    团队博客作业Week1 --- 团队成员简介
    js将数组中一个或多个字段相同的子元素中合并
  • 原文地址:https://www.cnblogs.com/carblack/p/12906719.html
Copyright © 2011-2022 走看看