什么是代理模式
我们现在有这样一个场景。有一个简单的手机类,只能打电话
publicclassPhone{
publicvoidcall(){
System.out.println("打电话");
}
}
现在我们要改需求了,我们想要手机在打电话的时候可以开启录音
publicclassPhone{
publicvoidcall(){
System.out.println("开启了录音...");
System.out.println("打电话");
}
}
但是追求优雅的程序员是不会这么写的,修改源代码不就破坏了面向对象的开闭原则了么。那么我们创建一个子类去继承Phone
publicclassRecordPhoneextendsPhone{
publicvoidcall(){
System.out.println("开启了录音...");
System.out.println("打电话");
}
}
这样我们也不用修改源代码了,如果需要可录音的电话,直接使用RecordPhone就可以了。受到上面的启发,我们继续改进
抽象出一个Phone接口
publicinterfaceIPhone{
voidcall();
}
实现这个接口
publicclassPhoneimplementsIPhone{
publicvoidcall(){
System.out.println("打电话");
}
}
我们再创建一个代理类也实现IPhone接口
publicclassPhoneProxyimplementsIPhone{
privateIPhonephone;
publicPhoneProxy(){
this.phone=newPhone();
}
@Override
publicvoidcall(){
System.out.println("开启了录音...");
phone.call();
}
}
我们直接调用这个代理类
publicclassTest{
publicstaticvoidmain(String[]args){
PhoneProxyproxy=newPhoneProxy();
proxy.call();
}
}
结果
开启了录音...
打电话
上面就是使用了代理模式,我们抽象出接口让程序更具备扩展性。
但有个问题如果手机的游戏方法也需要增加录音功能,我们需要在代理类中重写游戏方法增加录音功能,这个还好办,毕竟是同一个类。如果不是同一个类呢,如果微信类,QQ类也需要增加录音功能,那岂不是还要写微信代理类,QQ代理类么。这样也太麻烦了。
因此我们需要一个动态的代理类,这个代理类并不是一开始就创建的,而是在调用的时候创建。
Java中的动态代理有动态代理和动态代理,这两种动态代理在Spring的代理模式中有用到。
Spring中这两种动态代理的区别为:
“
(1)当目标对象实现了接口,默认使用JDK动态代理,也可以强制使用CGLIB动态代理。
(2)当目标对象没有实现接口,必须使用CGLIB动态代理。
”
JDK动态代理

代码
publicclassJdkProxyimplementsInvocationHandler{
//需要代理的目标对象
privateObjecttarget;
@Override
publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{
System.out.println("JDK动态代理,监听开始...");
Objectinvoke=method.invoke(target,args);
System.out.println("JDK动态代理,监听结束...");
returninvoke;
}
publicObjectgetJdkProxy(ObjecttargetObject){
this.target=targetObject;
//实例化
returnProxy.newProxyInstance(targetObject.getClass().getClassLoader(),targetObject.getClass().getInterfaces(),this);
}
}
测试
@org.junit.Test
publicvoidtestJdkProxy(){
JdkProxyjdkProxy=newJdkProxy();
UserServiceuserService=(UserService)jdkProxy.getJdkProxy(newUserServiceImpl());
userService.addUser("lvshen","123456");
}
JDK动态代理,监听开始...
调用addUser()...
参数为:name[lvshen],password[123456]
JDK动态代理,监听结束...
核心代码在这里
UserServiceuserService=(UserService)jdkProxy.getJdkProxy(newUserServiceImpl());
这里的userService实际上不是原本的userService,而是一个代理的userService。我们debug调试

如上图userService是代理方式生成的,userService1是自己new出来的。可以看到(1)和(2)的区别。当调用userService.addUser("xxx"),实际上是进入了JdkProxy的invoke方法。

Objectinvoke=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
publicstaticObjectnewProxyInstance(ClassLoaderloader,
Class<?>[]interfaces,
InvocationHandlerh){
...
Class<?>cl=getProxyClass0(loader,intfs);//通过接口获取代理类
...
}
我们来看看getProxyClass0()方法,代理类是从缓存中获取的
privatestaticClass<?>getProxyClass0(ClassLoaderloader,
Class<?>...interfaces){
if(interfaces.length>65535){
thrownewIllegalArgumentException("interfacelimitexceeded");
}
//Iftheproxyclassdefinedbythegivenloaderimplementing
//thegiveninterfacesexists,thiswillsimplyreturnthecachedcopy;
//otherwise,itwillcreatetheproxyclassviatheProxyClassFactory
returnproxyClassCache.get(loader,interfaces);//缓存
}
proxyClassCache=newWeakCache<>(newKeyFactory(),newProxyClassFactory());
在缓存的get()方法中有段代码
ObjectsubKey=Objects.requireNonNull(subKeyFactory.apply(key,parameter));
subKeyFactory.apply()实际是调用的ProxyClassFactory的apply方法。ProxyClassFactory是Proxy的内部类。apply方法就是生成代理类的方法。
生成代理类的时序图如下

CGLIB动态代理
代码
publicclassCglibProxyimplementsMethodInterceptor{
privateObjecttarget;
@Override
publicObjectintercept(Objecto,Methodmethod,Object[]objects,MethodProxymethodProxy)throwsThrowable{
System.out.println("CGLIB动态代理,监听开始...");
Objectinvoke=method.invoke(target,objects);
System.out.println("CGLIB动态代理,监听结束...");
returninvoke;
}
publicObjectgetCglibProxy(Objecttarget){
this.target=target;
Enhancerenhancer=newEnhancer();
//指定父类
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
Objectresult=enhancer.create();
returnresult;
}
}
测试
@org.junit.Test
publicvoidtestCglibProxy(){
CglibProxycglibProxy=newCglibProxy();
UserServiceservice=(UserService)cglibProxy.getCglibProxy(newUserServiceImpl());
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()中。
protectedObjectnextInstance(Objectinstance){
EnhancerFactoryDatadata=(EnhancerFactoryData)instance;
if(classOnly){
returndata.generatedClass;
}
Class[]argumentTypes=this.argumentTypes;
Object[]arguments=this.arguments;
if(argumentTypes==null){
argumentTypes=Constants.EMPTY_CLASS_ARRAY;
arguments=null;
}
returndata.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以后,两者都优化的很不错,不要再纠结使用哪种性能更好了。