Java开发中用到的静态代理和动态代理

本文详细介绍了Java中的代理模式,包括静态代理与动态代理的概念及应用。通过实例对比静态代理和两种动态代理(JDK代理与Cglib代理)的特点与适用场景。

一、什么是代理?

    在现实生活中,大家都对代理商不陌生,代理商就是在其行业管理范围内接受他人委托,为他人促成或缔结交易的一般代理人。而在Java编程中,代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式,即通过代理对象访问目标对象,这样就可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。简单来说就是你想要做的事情我替你来完成,并且在完成你的事情的同时,我还可以做些其他事情,更加完美的完成这个事情。

二、静态代理与动态代理

    静态代理是在程序运行前,这个代理类已经生成了,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类,然后通过调用相同的方法来调用目标对象的方法,所以缺点就很明显,因为代理对象需要与目标对象实现一样的接口,所以会产生很多代理类,还有就是一旦接口增加方法,目标对象与代理对象都要维护。

    动态代理是在程序运行的时候,通过反射机制动态创建而成。无需coder手写它的源代码,这样不仅简化了编程工作,而且还提高了软件系统的可扩展性。动态代理可以说是为了弥补静态代理的缺点而生的。

三、举例说明

    假设演员演戏就是一个目标对象,而经纪人就是一个代理对象,演员演好戏,对导演做好服务就行了,像演出费、合同之类的事情就交给经纪人来做。

    1、静态代理

    首先要有一个唱歌的接口:

public interface IActor {
	void action();
}

    再来一个德艺双馨的明星演员:

public class FanActor implements IActor{
	@Override
	public void action() {
		System.out.println("好开心,好激动,又开始演戏了。。。");
	}
}

    只演戏可不行呀,还得签合同、谈费用和吃喝拉撒睡呢,这是经纪人出场,也就是代理对象:

public class MuActorProxy implements IActor{
	private IActor iActor;
	public MuActorProxy(IActor iActor) {
		this.iActor = iActor;
	}
	@Override
	public void action() {
		System.out.println("签个合同吧!一大一小,导演您懂得!");
		iActor.action();
		System.out.println("我们至少要住五星级的,演出完了,还要坐飞机回去!");
	}
}

    测试一下:

public class MuActorProxyTest {
	public static void main(String[] args) {
		IActor actor = new FanActor();
		MuActorProxy proxy = new MuActorProxy(actor);
		proxy.action();
	}
}

    结果如下:

签个合同吧!一大一小,导演您懂得!
好开心,好激动,又开始演戏了。。。
我们至少要住五星级的,演出完了,还要坐飞机回去!

    不错,经纪人做了静态代理出色的完成了任务,但是有很多的演员,这样就会有很多的经纪人,并且演员有个其他事情或者发个小脾气,都会连累经纪人。下面就介绍另外一种经纪方式,统一的经纪公司,负责所有的演员,也就是动态代理。

2、动态代理之基于接口的JDK代理

    JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数,完整的写法是:

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

该方法是在Proxy类中是静态方法,且接收的三个参数依次为:


  1. ClassLoader loader:指定当前目标对象使用类加载器,获取加载器的方法是固定的

  2. Class<?>[] interfaces:目标对象实现的接口的类型,使用泛型方式确认类型

  3. InvocationHandler h:事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入

建造一个经纪公司:

public class ActorFactoryProxy {
	private Object target;

	public ActorFactoryProxy(Object target) {
		this.target = target;
	}
	public Object getProxyInstance(){
		return Proxy.newProxyInstance(
				target.getClass().getClassLoader(),
				target.getClass().getInterfaces(),
				new InvocationHandler() {
					@Override
					public Object invoke(Object proxy, Method method, Object[] args)
							throws Throwable {
						System.out.println("签个合同吧!一大一小,导演您懂得!");
						Object returnValue = method.invoke(proxy, args);
						System.out.println("我们至少要住五星级的,演出完了,还要坐飞机回去!");
						return returnValue;
					}
				});
	}
}

测试一下:

public class ActorFactoryProxyTest {
	public static void main(String[] args) {
		IActor actor = new FanActor();
		IActor actorProxy = (IActor) new ActorFactoryProxy(actor).getProxyInstance();
		actorProxy.action();
	}
}

结果如下:

签个合同吧!一大一小,导演您懂得!
好开心,好激动,又开始演戏了。。。
我们至少要住五星级的,演出完了,还要坐飞机回去!

代理对象不需要实现接口,但是目标对象一定要实现接口,否则不能用动态代理。也就是演员得一定会演戏呀,要不经纪公司找你干嘛。

3、动态代理之基于子类的Cglib代理

    Cglib是针对类进行动态代理的,原理是对指定的业务类生成一个子类,通过代理类实现业务类中的业务,并且附加一些其他业务的实现。因为此时业务类相当于父类,代理类相当于子类,这是继承关系,所以不能对final修饰的类进行代理。

    这是实现业务类方法的代理类方法:

public Object intercept(Object obj, Method method, Object[] args,MethodProxy proxy) throws Throwable

    首先创建一个演员,不用实现接口,天生会表演:

public class WangActor {
	public void action(){
		System.out.println("我天生会表演。。。");
	}
}

创建一个代理工厂,也就是经纪公司:

public class CgProxyFactory implements MethodInterceptor{
	private Object target;
	public CgProxyFactory(Object target) {
		this.target = target;
	}
	@Override
	public Object intercept(Object obj, Method method, Object[] args,
			MethodProxy proxy) throws Throwable {
		System.out.println("合同和演出费你们看着办。。。");
		Object returnValue = method.invoke(target, args);
		System.out.println("演完了,我走了。。。");
		return returnValue;
	}
	public Object getProxyInstance(){
		//通过Enhancer创建代理对象
		Enhancer hancer = new Enhancer();
		//设置创建子类的类
		hancer.setSuperclass(target.getClass());
		hancer.setCallback(this);
		return hancer.create();
	}
}

测试一下:

public class CgProxyTest {
	public static void main(String[] args) {
		WangActor actor = new WangActor();
		WangActor proxy = (WangActor) new CgProxyFactory(actor).getProxyInstance();
		proxy.action();
	}
}

结果如下:

合同和演出费你们看着办。。。
我天生会表演。。。
演完了,我走了。。。
四、总结

    静态代理是通过在代码中显式定义一个业务实现类的一个代理,在代理类中对同名的业务方法进行包装,程序通过通过代理类调用被包装过的方法;

    JDK动态代理是基于接口的,通过接口中的方法名,在动态生成的代理类中调用业务实现类的同名方法;Cglib动态代理是基于子类的,通过继承业务类,动态生成代理类;一般来说,如果一个类实现了接口,就用JDK动态代理,如果没有实现接口,就用Cglib动态代理。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猿人小郑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值