动态代理

动态代理

1.代理模式

1.1什么是代理模式

  • 代理模式是指,为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,
    一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户类和目标对象之
    间起到中介的作用。

  • 例如: 有 A, B(代理类), C (目标类)三个类, A 原来可以调用 C 类的方法, 现在因为某种原因 C 类不允许A 类调用其方法,但 B 类可以调用 C 类的方法。 A 类通过 B 类调用 C 类的方法。这里 B 是 C
    的代理。 A 通过代理 B 访问 C 。

1.2代理模式作用

  • 功能增强: 在原有的功能上,增加了额外的功能。 新增加的功能,叫做功能增强。
  • 控制访问: 代理类不让你直接访问目标,例如中介不让租户直接访问房东。

1.3代理模式分类

  • 静态代理

  • 动态代理

2.静态代理

2.1什么是静态代理

  • 静态代理是指,代理类在程序运行前就已经定义好.java 源文件,其与目标类的关系在
    程序运行前就已经确立。 在程序运行前代理类已经编译为.class 文件。
  • (1)代理类是自己手工实现的,自己手动创建一个java类,表示代理类。
    (2)同时你所要代理的目标类是确定的。
    特点: 1)实现简单 2)容易理解。

2.2静态代理缺点

  • 当一个项目中,目标类和代理类很多时候,有以下的缺点:

(1)代码复杂,难于管理

代理类和目标类实现了相同的接口, 每个代理都需要实现目标类的方法, 这样就出现了大量的代
码重复。如果接口增加一个方法,除了所有目标类需要实现这个方法外,所有代理类也需要实现
此方法。增加了代码维护的复杂度 ,耦合度也高。

(2)代理类依赖目标类,代理类过多

代理类只服务于一种类型的目标类,如果要服务多个类型。势必要为每一种目标类都进行代理,

静态代理在程序规模稍大时就无法胜任了,代理类数量过多。

3.动态代理

3.1什么是动态代理

  • 动态代理是指代理类对象在程序运行时由JVM 根据反射机制动态生成的。 动态代理不
    需要定义代理类的.java 源文件。
    动态代理其实就是 jdk 运行期间, 动态创建 class 字节码并加载到 JVM。

3.2动态代理的实现方式

  • jdk动态代理(理解): 使用java反射包中的类和接口实现动态代理的功能。是基于接口的代理类
    反射包 java.lang.reflect , 里面有三个类 : InvocationHandler , Method, Proxy。

  • cglib动态代理(了解): cglib是第三方的工具库, 创建代理对象。

    cglib的原理是继承,cglib通过继承目标类,创建它的子类,在子类中重写父类中同名的方法, 实现功能的修改。因为cglib是继承,重写方法,所以要求目标类不能是final的, 方法也不能是final的。cglib的要求目标类比较宽松, 只要能继承就可以了。cglib在很多的框架中使用, 比如 mybatis ,spring框架中都有使用。

3.3jdk动态代理需要用到的方法和类

  • (1) InvocationHandler 接口

    通 过 代 理 对 象 执 行 目 标 接 口 中 的 方 法 , 会 把 方 法 的 调 用 分 派 给 调 用 处 理 器
    (InvocationHandler)的实现类,执行实现类中的 invoke()方法,我们需要把功能代理写在 invoke()方法中

    public interface InvocationHandler {//代理类必须实现这个接口,并重写invoke()方法
         /**
         * 该方法在目标类的方法被执行的时候,会被调用
         * 调用目标类方法时,会先执行该invoke方法
         * @param proxy 真正的代理对象,即Proxy.newProxyInstance()方法的返回结果
         * @param method 目标类中的方法,jdk自动帮我们提供对象
         * @param args 目标类中方法的参数(可变参数),也是jdk自动帮我们的提供的
         * @return null
         * @throws Throwable
         */
         public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable;
        
    }
    
  • (2) Method 类

    表示目标类中的方法

    InvocationHandler实现类的invoke()方法的第二个参数为 Method 类对象,该类有一个方法也叫 invoke(),可以调用目标方法。这两个 invoke()方法,虽然同名,但无关

     public Object invoke(Object obj, Object... args){}
     //obj:表示目标对象
     //args:表示目标方法参数,就是InvocationHandler实现类的invoke方法的第三个参数(可变参数)
    

    该方法的作用是:调用执行 obj 对象所属类的方法,这个方法由其调用者 Method 对象确定。
    在代码中,一般的写法为
    method.invoke(target, args);
    其中, method 为上一层 invoke 方法的第二个参数。这样,即可调用目标类的目标方法。

  • ( 3) Proxy 类

    通 过 JDK 的 java.lang.reflect.Proxy 类 实 现 动 态 代 理 , 使 用 其 静 态 方 法newProxyInstance(),依据目标对象、业务接口及调用处理器三者,自动生成一个动态代理对象。

    public static Object newProxyInstance(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler h) {}
    //loader:目标类的类加载器,通过目标对象的反射可获取
    //interfaces:目标类实现的接口数组,通过目标对象的反射可获取
    //handler: 调用处理器。
    

3.4 jdk 动态代理实现

​ jdk 动态代理是代理模式的一种实现方式,其只能代理接口。实现步骤:
​ 1、新建一个接口,作为目标接口
​ 2、为接口创建一个实现类,是目标类
​ 3、创建类实现 java.lang.reflect.InvocationHandler 接口,调用目标方法并增加其他功能代码
​ 4、 创建动态代理对象,使用 Proxy.newProxyInstance()方法,并把返回值强制转为接口类型。

3.5jdk动态代理的例子

  • 目标接口
public interface TargetClass { 
    void sayHello();
}
  • 目标接口的实现类

    public class TargetClassImpl implements TargetClass{    
        @Override    
        public void sayHello() {        
            System.out.println("Hi,dynamic proxy");
        }
    }
    
  • 创建类实现 java.lang.reflect.InvocationHandler 接口

    /**
     * ProxyClass是动态代理的一部分,还不是真正的代理类,该类是协助真正的代理类去工作
     */
    public class ProxyClass implements InvocationHandler {
        private Object target;//这里要传入一个目标类
    
        public ProxyClass(Object target) {
            this.target = target;
        }
    
        /**
         * 该方法在目标类的方法被执行的时候,会被调用
         * 调用目标类方法时,会先执行该invoke方法
         * @param proxy 真正的代理对象,即Proxy.newProxyInstance()方法的返回结果
         * @param method 目标类中的方法,jdk自动帮我们提供对象
         * @param args 目标类中方法的参数(可变参数),也是jdk自动帮我们的提供的
         * @return
         * @throws Throwable
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("目标方法执行前。。。。。。");
    
            //此代码是真正的目标方法sayHello()
            Object result = method.invoke(target,args);
            
    		//这些增加的方法表示增强功能作用
            System.out.println("目标方法执行后。。。。。。");
            return result;
        }
    }
    
  • 创建动态代理对象来调用目标类

    public class Client {
        public static void main(String[] args) {
    
            //使用代理模式
            //Proxy.newProxyInstance()这个方法的返回结果才是我们的真正的代理对象
            TargetClass targetClass = (TargetClass)Proxy.newProxyInstance(TargetClass.class.getClassLoader(),
                                    new Class<?>[] {TargetClass.class},
                                    new ProxyClass(new TargetClassImpl()));
            targetClass.sayHello();
        }
    }
    
上一篇:代理模式——静态代理和动态代理


下一篇:mybatis的sql语句中报系统异常,参数中没有此属性异常 MyBatisSystemException .NoSuchPropertyException