深入浅出地讲解 java代理设计模式(附例子)

一、什么叫“代理”?

举一个生活中的小例子,小红今天身体不舒服,让小明上体育课的时候,替她向老师请假。

这时候,小明就是小红的代理,小红是被代理人。第三方(老师)接触到的人,是小明,即与第三方对接的是代理。

二、代理设计模式

上面的例子,翻译成代理模式术语,就是代理对象代替真实对象,与外界沟通。想想有什么职业很像是一个代理呢?没错,秘书。下面我们就以『客户通过秘书找老板谈判』为例,进行代理模式编程。

首先,定义谈判功能

public interface FunctionCommon {
	void negotiate();
}

其次,对于谈判一事,老板和秘书有不同的职责。
老板的职责就是和客户谈判

public class Boss implements FunctionCommon {
	private String name;
	public void negotiate() {
		System.out.println("谈判中。。。制定小目标...");
	}
}

秘书的职责,先为客户预约,到时间了叫老板出来谈判,最后整理会议记录

public class Secretary implements FunctionCommon {
	private Boss boss = new Boss("马化云");
	@Override
	public void negotiate() {
		System.out.println("预约时间");
		boss.negotiate();
		System.out.println("整理会议记录");
	}
}

这时候,客户来了,他不是直接去找老板,而是找秘书

public class Client {
	public static void main(String[] args) {
		Secretary secretary = new Secretary();
		secretary.negotiate();
	}
}

运行结果:

预约时间
制定小目标...谈判中。。。
整理会议记录

上述程序其实是一种静态代理

以上,我们可以总结出Proxy设计模式的特征:

  • 分为真实对象、代理对象和抽象对象(指功能)
  • 能够保护真实对象,使之不曝露在外
  • 让真实对象的职责更加明确

三、JDK动态代理

所谓动态代理,是说在编译时不需要定义代理类,而是在运行时创建。

抽象类

public interface Subject{
	void negotiate();
}

目标类(真实对象)

public class RealSubject implements Subject{
	@Override
	public void negotiate() {
		System.out.println("制定小目标...谈判中。。。");
	}
}

代理类(实现 InvocationHandler 接口)

public class JdkProxySecretary implements InvocationHandler {
	// 目标对象的引用
	private Object targetObject ;

    // 生成动态代理实例的方法
    public Object newProxy(Object targetObject) {        
        this.targetObject = targetObject;     
		// 参数1:反射时使用的类加载器
		// 参数2:目标类实现的接口列表
		// 参数3:通过接口对象调用方法时,最终需要调用哪个类的invoke方法
		//(即实现了InvocationHandler接口的对象)
        return Proxy.newProxyInstance(
        targetObject.getClass().getClassLoader(),
		targetObject.getClass().getInterfaces(),
 		this);
} 

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println("预约时间");
		Object result = method.invoke(targetObject, args);
		System.out.println("整理会议记录");
		return result;
	}
}

测试:

public class Client {
	public static void main(String[] args) {
		JdkProxySecretary jdkProxy = new JdkProxySecretary();
		// 目标对象
		Subject target= new RealSubject();
		// 获得目标对象的动态代理对象(jdk方式)
		Subject fc = (Subject) jdkProxy.newProxy(target);
		fc.negotiate();
	}
}

运行结果:

预约时间
制定小目标...谈判中。。。
整理会议记录

原理

Java Decompiler工具查看生成的代理类:

public final class $Proxy0 extends Proxy implements Subject

JDK 动态代理是通过继承 Proxy 类,实现被代理类的所有接口生成动态代理类的。

四、CGLIB 动态代理

public class RealSubject {
	public void negotiate() {
		System.out.println("谈判中。。。制定小目标...");
	}
}
public class CGLibProxySecretary implements MethodInterceptor {
	// 被代理对象的引用
    private Object targetObject; 

    // 生成动态代理对象的方法
    public Object createProxy(Object obj) {    
        this.targetObject = obj;    
        Enhancer enhancer = new Enhancer();    
        enhancer.setSuperclass(obj.getClass());    
        enhancer.setCallback(this);    
        Object proxyObj = enhancer.create();    
        return proxyObj;
    }    

	@Override
	public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
		System.out.println("预约时间");
		Object result = arg3.invokeSuper(arg0, arg2);
		System.out.println("整理会议记录");
		return result;
	}
}
public class Client {
	public static void main(String[] args) {
		CGLibProxySecretary cgLibProxy = new CGLibProxySecretary();
		RealSubject target = new RealSubject();
		// 获得目标对象的动态代理对象(cglib方式)
		RealSubject fci = (RealSubject) cgLibProxy.createProxy(target);
		fci.negotiate();
	}
}

可以看到,使用cglib动态代理时,目标对象不一定要实现接口

原理

public class RealSubject$$EnhancerByCGLib$$7e8b8caf
    extends RealSubject implements Factory

代理类继承目标类,实现Factory接口。因为目标类为父类,所以不能是final的

五、jdk和cglib动态代理的优缺点

1、jdk代理
优点:使用 jre自带类库,不需要额外导入 jar
缺点:真实对象必须实现接口;由于利用反射机制,效率不高
当试图把接口对象(Proxy)转换为具体真实对象时,会出现如下异常:
在这里插入图片描述
解决:使用cglib代理

2、cglib代理
优点:基于字节码生成真实对象的子类;运行效率高于JDK 代理;真实对象不需要实现接口
缺点:需要额外导入jar包

3、基于 Aspectj 实现动态代理
修改目标类的字节,织入代理的字节,在程序编译的时候插入动态代理的字节码,不会生成全新的Class。编译时便完成织入,本质上是静态代理。

六、SpringAOP配置

( AOP 底层使用动态代理技术 )

<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>

Spring默认 false,使用 jdk 代理;true 为 cglib 代理。
SpringBoot 2.x 默认改成了CGLIB

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值