JAVA动态代理

0. 前言

学习Spring的时候,我们知道Spring主要有两大思想,一个是IoC,另一个就是AOP,对于IoC,它利用的是反射机制,依赖注入就不用多说了,而对于Spring的核心AOP来说,使用了动态代理,所以本篇随笔就是对java的动态代理进行一个回顾

1. 代理模式

代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等

2. 静态代理

由程序员创建或特定工具自动生成源代码,也就是在编译时就已经将接口,被代理类,代理类等确定下来。在程序运行之前,代理类的.class文件就已经生成。缺点:代理类和被代理类编译期间都写死了,不够灵活

package com.zad.java;

/**
 *
 * 静态代理举例
 * 代理类和被代理类编译期间都写死了,不够灵活
 */

interface smile{
    void produceCloth();
}

/**代理类**/
class ProxyClothFactory implements smile{

    private smile factory;//用被代理类对象进行实例化

    public ProxyClothFactory(smile factory){
        this.factory = factory;
    }

    @Override
    public void produceCloth() {
        System.out.println("你真的");
        factory.produceCloth();
        System.out.println("是不是");
    }

}

//被代理类
class realsmile implements smile{

    @Override
    public void produceCloth() {
        System.out.println("很开心");
    }
}


public class StaticProxyTest {
    public static void main(String[] args) {
        //创建被代理类对象
        realsmile real = new realsmile();
        //创建代理类对象
        ProxyClothFactory proxyClothFactory = new ProxyClothFactory(real);
        proxyClothFactory.produceCloth();
    }
}

3. 动态代理

Java反射提供了一种类动态代理机制,可以通过代理接口实现类来完成程序无侵入式扩展。

Java动态代理主要使用场景:

  1. 统计方法执行所耗时间。
  2. 在方法执行前后添加日志。
  3. 检测方法的参数或返回值。
  4. 方法访问权限控制。
  5. 方法Mock测试。

创建动态代理类会使用到java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口。java.lang.reflect.Proxy主要用于生成动态代理类Class、创建代理类实例。proxy类实现了java.io.Serializable接口

InvocationHandler接口

java.lang.reflect.InvocationHandler接口用于调用Proxy类生成的代理类方法,该类只有一个invoke方法,那么也必须要实现该方法,入口参数Object proxy即为要被代理的对象,method为被调用的方法,args为代理实例的方法参数数组,若没参数则为null

 proxy类

 

 

 

 该类中我们用的最多的就是 newProxyInstance 这个方法,这个方法的作用就是得到一个动态的代理对象,其接收三个参数,我们来看看这三个参数所代表的含义

 

 

 

loader: 一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载

interfaces: 一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了

h: 一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上

 

 

 

package com.zad.java;

import org.omg.CORBA.SystemException;
import org.omg.CORBA.portable.InputStream;
import org.omg.CORBA.portable.InvokeHandler;
import org.omg.CORBA.portable.OutputStream;
import org.omg.CORBA.portable.ResponseHandler;
import sun.awt.AWTAccessor;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 动态代理举例
 *
 *
 * **/

interface Human{
    String getBelief();
    void eat(String food);
}
//被代理类
class SuperMan implements Human{

    @Override
    public String getBelief() {
        return "I believe I can fly!";
    }

    @Override
    public void eat(String food) {
        System.out.println("我喜欢吃"+food);
    }
}

/**
 *
 * proxy:代表动态代理对象
 * method:代表正在执行的方法
 * args:代表调用目标方法时传入的实参
 *
 * 实现动态代理需要解决的问题:
 * 问题1:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象。
 * 问题2:当通过代理类的对象调用方法a时,如何动态的去调用被代理类中的同名方法a
 */

class ProxyFactory{
    //调用此方法,返回一个代理类的对象,解决了问题1
    public static Object getProxychInstance(Object obj){//obj:被代理类的对象
        MyInvocationHandler handler = new MyInvocationHandler();
        handler.bind(obj);
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler);
    }
}
class MyInvocationHandler implements InvocationHandler {
    private Object obj;
    public void bind (Object obj){
        this.obj = obj;
    }

    //当我们通过代理类的对象,调用方法a,就会自动调用如下方法:invoke()
    //将被代理类要执行的方法a的功能就声明在invoke()中
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //method:就是代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法
        Object returnValue = method.invoke(obj, args);
        return returnValue;
    }

}
public class ProxyTest {
    public static void main(String[] args) {
        SuperMan superMan = new SuperMan();
        Human proxyInstance = (Human) ProxyFactory.getProxychInstance(superMan);
        proxyInstance.getBelief();
        proxyInstance.eat("麻辣烫");

    }
}

 

cglib代理

这里我们先简单说一下这两种代理方式最大的区别,JDK动态代理是基于接口的方式,换句话来说就是代理类和目标类都实现同一个接口,那么代理类和目标类的方法名就一样了,这种方式上一篇说过了;CGLib动态代理是代理类去继承目标类,然后重写其中目标类的方法啊,这样也可以保证代理类拥有目标类的同名方法

cglib特点:

  1. 可以代理没有实现接口的类
  2. 可以在运行期扩展java类与实现java接口,被许多aop框架所使用,例如spring AOP和dynaop提供方法的interception拦截

cglib和动态代理的区别

  • 使用动态代理必须实现接口
  • cglib无需实现接口,达到代理类无侵入

 引入cglib的jar包(https://repo1.maven.org/maven2/cglib/cglib/3.2.5/cglib-3.2.5.jar)

<dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.2.5</version>
</dependency>

男的写了

posted @ 2020-12-30 00:19  -Zad-  阅读(242)  评论(0编辑  收藏  举报
jQuery火箭图标返回顶部代码