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"/> 

posted @ 2020-05-18 18:21  CarBlack  阅读(179)  评论(0编辑  收藏  举报