
点击上方蓝字关注我!
什么是代理模式
我们现在有这样一个场景。有一个简单的手机类,只能打电话
public class Phone {
public void call() {
System.out.println("打电话");
}
}
现在我们要改需求了,我们想要手机在打电话的时候可以开启录音
public class Phone {
public void call() {
System.out.println("开启了录音...");
System.out.println("打电话");
}
}
但是追求优雅的程序员是不会这么写的,修改源代码不就破坏了面向对象的开闭原则了么。那么我们创建一个子类去继承Phone
public class RecordPhone extends Phone {
public void call() {
System.out.println("开启了录音...");
System.out.println("打电话");
}
}
这样我们也不用修改源代码了,如果需要可录音的电话,直接使用RecordPhone
就可以了。受到上面的启发,我们继续改进
抽象出一个Phone
接口
public interface IPhone {
void call();
}
实现这个接口
public class Phone implements IPhone {
public void call() {
System.out.println("打电话");
}
}
我们再创建一个代理类也实现IPhone
接口
public class PhoneProxy implements IPhone {
private IPhone phone;
public PhoneProxy(){
this.phone = new Phone();
}
@Override
public void call() {
System.out.println("开启了录音...");
phone.call();
}
}
我们直接调用这个代理类
public class Test {
public static void main(String[] args) {
PhoneProxy proxy = new PhoneProxy();
proxy.call();
}
}
结果
开启了录音...
打电话
上面就是使用了代理模式,我们抽象出接口让程序更具备扩展性。
但有个问题如果手机的游戏方法也需要增加录音功能,我们需要在代理类中重写游戏方法增加录音功能,这个还好办,毕竟是同一个类。如果不是同一个类呢,如果微信类,QQ类也需要增加录音功能,那岂不是还要写微信代理类,QQ代理类么。这样也太麻烦了。
因此我们需要一个动态的代理类,这个代理类并不是一开始就创建的,而是在调用的时候创建。
Java中的动态代理有动态代理和动态代理,这两种动态代理在Spring的代理模式中有用到。
Spring中这两种动态代理的区别为:
“
(1)当目标对象实现了接口,默认使用JDK动态代理,也可以强制使用CGLIB动态代理。
(2)当目标对象没有实现接口,必须使用CGLIB动态代理。
”
JDK动态代理
代码
public class JdkProxy implements InvocationHandler {
//需要代理的目标对象
private Object target;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("JDK动态代理,监听开始...");
Object invoke = method.invoke(target, args);
System.out.println("JDK动态代理,监听结束...");
return invoke;
}
public Object getJdkProxy(Object targetObject) {
this.target = targetObject;
//实例化
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),targetObject.getClass().getInterfaces(),this);
}
}
测试
@org.junit.Test
public void testJdkProxy() {
JdkProxy jdkProxy = new JdkProxy();
UserService userService = (UserService) jdkProxy.getJdkProxy(new UserServiceImpl());
userService.addUser("lvshen","123456");
}
JDK动态代理,监听开始...
调用addUser()...
参数为:name[lvshen],password[123456]
JDK动态代理,监听结束...
核心代码在这里
UserService userService = (UserService) jdkProxy.getJdkProxy(new UserServiceImpl());
这里的userService
实际上不是原本的userService
,而是一个代理的userService
。我们debug调试
如上图userService
是代理方式生成的,userService1
是自己new
出来的。可以看到(1)和(2)的区别。当调用userService.addUser("xxx")
,实际上是进入了JdkProxy
的invoke
方法。
Object invoke = method.invoke(target, args);
就是执行的userService
本身的addUser()
方法,我们在method.invoke(target, args)
前后进行方法增强。
jdkProxy.getJdkProxy()
可以塞入其他的类,从而获得对应类的代理类。这样就避免了静态代理的弊端:每个类都要写死对应的代理类。
jdkProxy.getJdkProxy()
方法返回代码
publiProxy.newProxyInstance(targetObject.getClass().getClassLoader(),targetObject.getClass().getInterfaces(),this);
参数中有targetObject.getClass().getInterfaces()
,返回的代理对象需要获取到目标对象的接口,所以说JDK动态代理目标对象需要有接口才能生成代理对象。
继续跟代码newProxyInstance()
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h){
...
Class<?> cl = getProxyClass0(loader, intfs); //通过接口获取代理类
...
}
我们来看看getProxyClass0()
方法,代理类是从缓存中获取的
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// If the proxy class defined by the given loader implementing
// the given interfaces exists, this will simply return the cached copy;
// otherwise, it will create the proxy class via the ProxyClassFactory
return proxyClassCache.get(loader, interfaces); //缓存
}
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
在缓存的get()
方法中有段代码
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
subKeyFactory.apply()
实际是调用的ProxyClassFactory
的apply
方法。ProxyClassFactory
是Proxy
的内部类。apply
方法就是生成代理类的方法。
生成代理类的时序图如下
CGLIB动态代理
代码
public class CglibProxy implements MethodInterceptor {
private Object target;
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("CGLIB动态代理,监听开始...");
Object invoke = method.invoke(target, objects);
System.out.println("CGLIB动态代理,监听结束...");
return invoke;
}
public Object getCglibProxy(Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();
//指定父类
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
Object result = enhancer.create();
return result;
}
}
测试
@org.junit.Test
public void testCglibProxy() {
CglibProxy cglibProxy = new CglibProxy();
UserService service = (UserService) cglibProxy.getCglibProxy(new UserServiceImpl());
service.addUser("zhouzhou","654321");
}
CGLIB动态代理,监听开始...
调用addUser()...
参数为:name[zhouzhou],password[654321]
CGLIB动态代理,监听结束..
同样我们来看看这两种不同创建方式(通过代理创建,自己创建)。图上图,Enhancer
类似JDK动态代理的Proxy
。
CGLIB动态代理需要实现MethodInterceptor
接口。增强的方法就是写在intercept()
中,这个方法有4个参数。
“
1)
Object o
表示增强的对象,即实现这个接口类的一个对象;2)
Method method
表示要被拦截的方法;3)
Object[] objects
表示要被拦截方法的参数;4)
MethodProxy methodProxy
表示要触发父类的方法对象;”
最后,我们发现生成代理类的方法在Enhancer.nextInstance()
中。
protected Object nextInstance(Object instance) {
EnhancerFactoryData data = (EnhancerFactoryData) instance;
if (classOnly) {
return data.generatedClass;
}
Class[] argumentTypes = this.argumentTypes;
Object[] arguments = this.arguments;
if (argumentTypes == null) {
argumentTypes = Constants.EMPTY_CLASS_ARRAY;
arguments = null;
}
return data.newInstance(argumentTypes, arguments, callbacks);
}
调用过程如下
生成代理类的时序图如下
最后总结,如果目标对象存在接口,可以通过JDK和CGLIB生成代理对象;如果目标对象没有接口,则只能通过CGLIB生成代理对象。
JDK生成的代理对象与目标对象平级;CGLIB生成的代理对象继承目标对象,并且使用CGLIB生成代理对象时,目标类不能是final修饰的。
关于性能,网上有相关的结论,在JDK1.8以前:
“
1、CGLIB所创建的动态代理对象在实际运行时候的性能要比JDK动态代理高不少,有研究表明,大概要高10倍;
2、但是CGLIB在创建对象的时候所花费的时间却比JDK动态代理要多很多,有研究表明,大概有8倍的差距;
3、因此,对于singleton的代理对象或者具有实例池的代理,因为无需频繁的创建代理对象,所以比较适合采用CGLIB动态代理,反正,则比较适用JDK动态代理。
”
然而,JDK1.8以后,两者都优化的很不错,不要再纠结使用哪种性能更好了。
往期推荐


扫码二维码
获取更多精彩
Lvshen_9


原文始发于微信公众号(Lvshen的技术小屋):面试必问的动态代理,我们来看看它们的源码
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/262690.html