代理模式以及实现(静态代理、动态代理)

6 篇文章 0 订阅
2 篇文章 0 订阅

一、定义

代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。

二、为什么要用代理模式?

  • 中介隔离作用:

在某些情况下,一个客户类不想或者不能直接引用一个委托对象,而代理类对象可以在客户类和委托对象之间起到中介的作用,其特征是代理类和委托类实现相同的接口。

  • 开闭原则,增加功能:

代理类除了是客户类和委托类的中介之外,我们还可以通过给代理类增加额外的功能来扩展委托类的功能,这样做我们只需要修改代理类而不需要再修改委托类,符合代码设计的开闭原则。代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后对返回结果的处理等。代理类本身并不真正实现服务,而是同过调用委托类的相关方法,来提供特定的服务。真正的业务功能还是由委托类来实现,但是可以在业务功能执行的前后加入一些公共的服务。例如我们想给项目加入缓存、日志这些功能,我们就可以使用代理类来完成,而没必要打开已经封装好的委托类。

三、有哪几种代理模式?

我们有多种不同的方式来实现代理。如果按照代理创建的时期来进行分类的话,可以分为两种:静态代理、动态代理。

  1. 静态代理是由程序员创建或特定工具自动生成源代码,在对其编译。在程序员运行之前,代理类.class文件就已经被创建了。

  2. 动态代理是在程序运行时通过反射机制动态创建的。

四、静态代理

1、实现

接口

public interface Person {
	void speak();
}

实现类

class Actor implements Person {
	private String content;

	public Actor(String content) {
		this.content = content;
	}

	@Override
	public void speak() {
		System.out.println(this.content);
	}
}

代理类

class Agent implements Person {
	private Actor actor;
	private String before;
	private String after;

	public Agent(Actor actor, String before, String after) {
		this.actor = actor;
		this.before = before;
		this.after = after;
	}

	@Override
	public void speak() {
		// before speak
		System.out.println("Before actor speak, Agent say: " + before);
		// real speak
		this.actor.speak();
		// after speak
		System.out.println("After actor speak, Agent say: " + after);
	}
}

测试

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Agent a = new Agent(new Actor("我是演员"), "我之前是一个演员", "我以后也是一个演员");
		a.speak();
	}

输出

Before actor speak, Agent say: 我之前是一个演员
我是演员
After actor speak, Agent say: 我以后也是一个演员
2、缺点

我们得为每一个服务都得创建代理类,工作量太大,不易管理。同时接口一旦发生改变,代理类也得相应修改。

五、JDK动态代理

1、 实现
public class DynamicProxyHandler implements InvocationHandler {

	private Object object;

	public DynamicProxyHandler(Object object) {
		super();
		this.object = object;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		// TODO Auto-generated method stub
		System.out.println("之前我是一个演员");
		Object result = method.invoke(object, args);
		System.out.println("之后我还是一个演员");
		return result;
	}
}
public static void main(String[] args) {
	Person p = new Actor("我是演员");
	Person agentP = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class[]{Person.class}, new DynamicProxyHandler(p));
	agentP.speak();
}
2、原理
关于InvocationHandler

每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。我们来看看InvocationHandler这个接口的唯一一个方法 invoke 方法:

Object invoke(Object proxy, Method method, Object[] args) throws Throwable

我们看到这个方法一共接受三个参数,那么这三个参数分别代表如下:

  1. proxy:指代JDK动态生成的最终代理对象
  2. method:指代的是我们所要调用真实对象的某个方法的Method对象
  3. args:指代的是调用真实对象某个方法时接受的参数
关于Proxy

Proxy这个类的作用就是用来动态创建一个代理对象的类,它提供了许多的方法,但是我们用的最多的就是 newProxyInstance 这个方法:

public static Object newProxyInstance(ClassLoader loader,
    Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException

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

  1. loader:一个ClassLoader对象,定义了由哪个ClassLoader来对生成的代理对象进行加载
  2. interfaces:一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
  3. InvocationHandler:表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
源码部分
    @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        if (h == null) {
            throw new NullPointerException();
        }
        // 获得所有实现的接口
        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * Look up or generate the designated proxy class.
         */
        // 生成代理对象的class文件(其内部实现是先生成字节码文件,然后通过类加载加载生成class实例)
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            // 使用构造器以及传入的InvocationHandler来实例化代理对象
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
                // create proxy instance with doPrivilege as the proxy class may
                // implement non-public interfaces that requires a special permission
                return AccessController.doPrivileged(new PrivilegedAction<Object>() {
                    public Object run() {
                        return newInstance(cons, ih);
                    }
                });
            } else {
                return newInstance(cons, ih);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString());
        }
    }
关于生成的代理类:
  1. 通过jdk动态代理生成的代理对象会继承Proxy,并实现被代理的接口,由于java不能多继承,所以JDK的动态代理不支持对实现类的代理,只支持接口的代理。
  2. 生成静态代码块来初始化接口中方法的Method对象,以及Object类的equals、hashCode、toString方法。(使用了反射)
  3. 提供了一个使用InvocationHandler作为参数的构造方法,代理类内部的方法的实现其实都是调用了InvocationHandler的invoke方法。

详细参考:https://blog.csdn.net/zhangdefeng2008/article/details/79399898

六、cglib动态代理

1、实现
ublic class CGlibAgent implements MethodInterceptor {

	private Object proxy;

	public Object getInstance(Object proxy) {
		this.proxy = proxy;
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(this.proxy.getClass());
		// 回调方法
		enhancer.setCallback(this);
		// 创建代理对象
		return enhancer.create();
	}

	@Override
	public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
		// TODO Auto-generated method stub
		System.out.println("之前我是一个码农");
		Object result = methodProxy.invokeSuper(o, objects);
		System.out.println("之后我就是一个rapper了");
		return result;
	}

}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		CGlibAgent cGlibAgent = new CGlibAgent();
		Rapper rapper = (Rapper) cGlibAgent.getInstance(new Rapper());
		rapper.speak();
	}
2、原理

CGLIB是一种字节码增强库,利用其提供的字节码技术可以实现动态代理。其底层依赖ASM字节码技术。

通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用。(即MethodInterceptor的intercept方法)

七、JDK动态代理和Gglib动态代理的区别

  1. JDK动态代理是实现了被代理对象的接口,因此被代理的对象必须实现了某个接口,而Cglib是继承了被代理对象,因此不能代理final修饰的类。
  2. JDK和Cglib都是在运行期生成字节码,JDK是直接写Class字节码,Cglib使用ASM框架写Class字节码,Cglib代理实现更复杂,生成代理类比JDK效率低。
  3. JDK调用代理方法,是通过反射机制调用,Cglib是通过FastClass机制直接调用方法,Cglib执行效率更高。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值