Java两种方式实现动态代理
一、JDK动态代理
Java在java.lang.reflect包中有自己的代理支持,该类(Proxy.java)用于动态生成代理类,只需传入目标接口、目标接口的类加载器以及InvocationHandler便可为目标接口生成代理类及代理对象。我们称这个Java技术为:动态代理
@CallerSensitive publicstaticObjectnewProxyInstance(ClassLoaderloader, Class>[]interfaces, InvocationHandlerh) throwsIllegalArgumentException { //... }
在Java中规定,要想产生一个对象的代理对象,那么这个对象必须要有一个接口,因此interfaces必须是一个接口。
在动态代理技术里,由于不管用户调用代理对象的什么方法,都是调用开发人员编写的InvocationHandler的invoke方法(这相当于invoke方法拦截到了代理对象的方法调用)。
因此JDK动态代理的整体流程为:
- 实现InvocationHandler,用来处理对象拦截后的逻辑。(该对象必须是接口,或者父类是接口)
- 使用Proxy.newProxyInstance产生代理对象。
以下是一个用JDK动态代码实现AOP的具体例子:
1.目标(Target)类
publicinterfaceUserService{ voideat(); }
publicclassUserServiceImplimplementsUserService{ @Override publicvoideat(){ System.out.println("吃东西"); } }
2.切面(Aspect)类
publicclassMyAspect{ /** *前置通知 */ publicvoidbefore(){ System.out.print("先洗手再"); } }
3.织入(Weaving)过程
/** *产生代理对象的工厂类 */ publicclassMyFactoryBean{ privateMyFactoryBean(){ } publicstaticUserServicegetInstance(){ //target:目标类 finalUserServiceuserService=newUserServiceImpl(); //Aspect:切面类 finalMyAspectmyAspect=newMyAspect(); //Weaving:织入,也就是产生代理的过程 UserServiceproxyInstance=(UserService)Proxy.newProxyInstance(UserService.class.getClassLoader(), newClass[]{UserService.class},(Objectproxy,Methodmethod,Object[]args)->{ //模拟切点-pointcut if("eat".equals(method.getName())){ myAspect.before(); } returnmethod.invoke(userService,args); }); returnproxyInstance; } }
publicstaticvoidmain(String[]args){ UserServiceuserService=MyFactoryBean.getInstance(); //先洗手再吃东西 userService.eat(); }
想想看,这其实跟我们平常使用的AOP已经很相似了,Spring里面定义了前置通知(@Before)、异常通知(@AfterThrowing)等等,Spring只是换成了甄别这些注解来选择什么时候调用通知方法,另外,Spring还通过切点表达式来选择目标类和切入点。
二、CGLIB动态代理
CGLIB动态代理需要引入第三方的库,它通过修改代理对象生成子类的方式来实现调用拦截,代理对象不需要实现接口,但是代理类不能是final,代理的方法也不能是final。
/** *产生代理对象的工厂类 */ publicclassMyFactoryBean{ privateMyFactoryBean(){ } publicstaticUserServicegetInstance(){ //target:目标类 finalUserServiceuserService=newUserServiceImpl(); //Aspect:切面类 finalMyAspectmyAspect=newMyAspect(); //Weaving:织入,也就是产生代理的过程 Enhancerenhancer=newEnhancer(); enhancer.setSuperclass(userService.getClass()); enhancer.setUseCache(false); enhancer.setCallback(newMethodInterceptor(){ @Override publicObjectintercept(Objecto,Methodmethod,Object[]objects,MethodProxymethodProxy)throwsThrowable{ //模拟pointcut-切点 if("eat".equals(method.getName())){ myAspect.before(); } returnmethodProxy.invokeSuper(o,objects); } }); return(UserService)enhancer.create(); } publicstaticvoidmain(String[]args){ UserServiceproxyInstance=MyFactoryBean.getInstance(); //先洗手再吃东西 proxyInstance.eat(); } }
三、总结
在JDK中实现动态代理时,要求代理类必须是接口或继承接口的类,因为JDK最后生成的proxyclass其实就是实现了代理类所代理的接口并且继承了java中的Proxy类(继承Proxy类是为了判断该类是否为代理类),通过反射找到接口的方法,调用InvocationHandler的invoke方法实现拦截。
CGLIB字节码增强是JDK动态代理的一个很好的补充,CGLIB中最后生成的proxyclass是一个继承代理类所代理的class,通过重写被代理类中的非final的方法实现代理。
总结为:
- JDK动态代理:代理类必须是接口或继承接口的类。
- CGLIB字节码增强:代理类不能是final,代理的方法也不能是final(继承限制)。
关于在Spring的AOP中采用何种代理手段,我们不强加限制的话,会根据类是否有接口来区别对待:
- 当一个类有接口的时候,就会选用JDK的动态代理。
- 当一个类没有实现接口的时候,就会选用CGLIB代理的方式。
以上就是Java两种方式实现动态代理的详细内容,更多关于Java动态代理的资料请关注毛票票其它相关文章!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。