java代理(Java)

 2025-08-10 18:48:02  阅读 723  评论 0

摘要:什么是代理代理是设计模式的一种,其原理就是通过代理对象去访问目标对象,而外部只能访问到代理对象。代理模式的UML图如下:代理模式UML为了保持行为的一致性,代理类和委托类通常会实现相同的接口,所以在访问者看来两者没有丝毫的区别。通过代理类这中间一层,能有效控制对

什么是代理

代理是设计模式的一种,其原理就是通过代理对象去访问目标对象,而外部只能访问到代理对象。代理模式的UML图如下:

代理模式UML

为了保持行为的一致性,代理类和委托类通常会实现相同的接口,所以在访问者看来两者没有丝毫的区别。通过代理类这中间一层,能有效控制对委托类对象的直接访问,也可以很好地隐藏和保护委托类对象,同时也为实施不同控制策略预留了空间,从而在设计上获得了更大的灵活性。

在Java中实现代理有两种方式分别为静态代理和动态代理。

静态代理

所谓的静态代理,即在项目中手动的创建代理类,并且创建的代理类会一直存在于项目中。静态代理的实现分为继承和聚合两种方式。

继承实现静态代理

废话不多说,直接上代码,大家就能明白。

1.创建接口

publicinterfaceIUserDao{Stringselect(Stringname);}

2.创建接口的实现

publicclassUserServiceimplementsIUserDao{@OverridepublicStringselect(Stringname){returnname;}}

3.创建代理类

publicclassUserServiceLogextendsUserService{@OverridepublicStringselect(Stringname){System.out.println("======log======");returnsuper.select(name);}}

4.测试类以及测试

publicclassMain{publicstaticvoidmain(String[]args){UserServiceservice=newUserServiceLog();Stringselect=service.select("select");System.out.println(select);}}

测试结果:

测试结果

我们从结果中可以看到,代理类确实完成了目标类的逻辑,并且对目标类进行了日志逻辑的增强。但是这种方式在目标类非常多或者需要增强的逻辑比较复杂时,就会在我们的项目中产生非常多的代理类,导致类爆炸。

聚合实现静态代理

接口和目标类仍然使用上面的代码,下面我们主要实现代理类。

publicclassUserServiceLogPolymerizationimplementsIUserDao{privateIUserDaouserDao;publicUserServiceLogPolymerization(IUserDaouserDao){this.userDao=userDao;}@OverridepublicStringselect(Stringname){System.out.println("聚合代理log日志");returnuserDao.select(name);}}

测试

publicclassMain{publicstaticvoidmain(String[]args){IUserDaouserDao=newUserService();UserServiceLogPolymerizationusp=newUserServiceLogPolymerization(userDao);System.out.println(usp.select("usp"));}}

测试结果:

从结果中,我们看到聚合也完成了代理,但是它与继承完成代理一样,在目标类过多时,也会导致类爆炸。

既然静态代理的缺陷是会导致类爆炸,那我们能不能在不产生实际类的情况下来完成代理呢,答案是可以,这就引出了动态代理。

动态代理

动态代理利用Java的反射技术(Java Reflection)生成字节码,在运行时创建一个实现某些给定接口的新类(也称"动态代理类")及其实例。下面我们利用JDK自带的JavaCompiler来模拟上面聚合模式实现的静态代理。

「注」:JavaCompiler就是一个将.java文件编译问.class文件的工具

模拟动态代理

publicclassProxyUtil{privatestaticfinalStringNEW_LINE="\r\n";privatestaticfinalStringTAB="\t";/***产生代理对象*@paramtarget*@return*/publicstaticObjectnewInstance(Objecttarget)throwsIOException,ClassNotFoundException,NoSuchMethodException,IllegalAccessException,InvocationTargetException,InstantiationException{//获取代理对象的接口ClassanInterface=target.getClass().getInterfaces()[0];//获取接口中的所有方法Method[]methods=anInterface.getDeclaredMethods();//开始组装java文件的字符串Stringcontent="packagecom.proxy;"+NEW_LINE+"import"+anInterface.getName()+";"+NEW_LINE+"publicclass$Proxy01implements"+anInterface.getSimpleName()+"{"+NEW_LINE+TAB+"private"+anInterface.getSimpleName()+"target;"+NEW_LINE+TAB+"public$Proxy01("+anInterface.getSimpleName()+"target){"+NEW_LINE+TAB+TAB+"this.target=target;"+NEW_LINE+TAB+"}"+NEW_LINE;//拼接接口中的所有方法for(Methodmethod:methods){//获取接口中的参数类Class[]parameterTypes=method.getParameterTypes();Stringparams="";StringtrueParams="";if(parameterTypes.length>0){for(inti=0;i0){params=params.substring(0,params.lastIndexOf(",")-1);trueParams=trueParams.substring(0,trueParams.lastIndexOf(",")-1);}content+=TAB+"public"+method.getReturnType().getSimpleName()+""+method.getName()+"("+params+"){"+NEW_LINE;;content+=TAB+TAB+"System.out.println(\"聚合代理log日志\");"+NEW_LINE;content+=TAB+TAB;if(!method.getReturnType().getSimpleName().equals("void")){content+="return";}content+="target."+method.getName()+"("+trueParams+");"+NEW_LINE;content+=TAB+"}"+NEW_LINE;}content+="}"+NEW_LINE;//将字符转写入文件Stringfilename="F://com/proxy/$Proxy01.java";Filefile=newFile(filename);if(!file.exists()){file.createNewFile();}FileWriterfw=newFileWriter(file);fw.write(content);fw.flush();fw.close();//使用JavaCompile将java文件编译成.class文件JavaCompilercomplier=ToolProvider.getSystemJavaCompiler();//获得文件管理者StandardJavaFileManagerfileMgr=complier.getStandardFileManager(null,null,null);Iterableits=fileMgr.getJavaFileObjects(filename);//编译任务JavaCompiler.CompilationTasktask=complier.getTask(null,fileMgr,null,null,null,its);//开始编译,执行完可在当前目录下看到.class文件task.call();fileMgr.close();//load到内存URL[]urls=newURL[]{newURL("file:F:\\\\")};URLClassLoaderurlClassLoader=newURLClassLoader(urls);Classcls=urlClassLoader.loadClass("com.proxy.$Proxy01");//反射生成对象Constructorconstructor=cls.getDeclaredConstructor(anInterface);returnconstructor.newInstance(target);}}

测试

publicclassMain{publicstaticvoidmain(String[]args)throwsException{IUserDaouserDao=(IUserDao)ProxyUtil.newInstance(newUserService());System.out.println(userDao.select("$proxy"));userDao.insert(555);}}

运行后,我们可以看到在F盘的com.proxy下生成了$Proxy01.java和$Proxy01.class文件。

测试结果:

到这里我们模拟了JDK的动态代理,并且实现了任意接口任意方法的代理,但是这里有两个问题就,一是在获取接口时,获取的是第一个基础的接口,即target.getClass().getInterfaces()[0];但是传入的类实现了多个接口呢?二是现在的代理增强的逻辑都是写死的,我们如何实现动态的处理增强的逻辑呢?

深度模拟

既然要实现我们的增强逻辑,那么这些逻辑必定是我们需要定义的。那么我们可以定义一个接口,接口中定义一个方法,让增加的逻辑类去实现这个方法,编写自己的增强逻辑。

首先我们定义一个接口,让我们需要处理的业务逻辑类来实现这个接口,接口定义如下:

publicinterfaceIProxyInvocationHandler{/***@parammethod:要代理的方法*@paramargs:方法的参数*@return*@throwsException*/Objectinvoke(Methodmethod,Object[]args)throwsException;}

当我们需要使用的动态代理时,就是通过重写这个钩子函数,将逻辑动态的传递进去,并且可以选择在适当的地方让目标方法执行。这里的思想就是通过反射,来调用目标对象中的方法,因此IProxyInvocationHandler的实现类中,必须要有目标类。

实现类

publicclassUserServiceLogProxyInvocationHandlerimplementsIProxyInvocationHandler{//目标类privateObjecttarget;publicUserServiceLogProxyInvocationHandler(Objecttarget){this.target=target;}@OverridepublicObjectinvoke(Methodmethod,Object[]args)throwsInvocationTargetException,IllegalAccessException{//增强的业务逻辑System.out.println("增强代理的业务逻辑");//通过反射调用目标方法Objectinvoke=method.invoke(target,args);System.out.println(invoke);returninvoke;}}

这里实现类中必须传递目标对象进来,是因为需要通过反射的方式调用目标对象中的方法,这点应该很好理解。

代理生成类

publicclassProxyUtil{privatestaticfinalStringNEW_LINE="\r\n";privatestaticfinalStringTAB="\t";/***产生代理对象**@return*/publicstaticObjectnewInstance(IProxyInvocationHandlerinvocationHandler,Class[]interfaces)throwsException{//开始组装java文件的字符串StringBuildercontent=newStringBuilder("packagecom.proxy;"+NEW_LINE);StringBuilderimportContent=newStringBuilder("import"+IProxyInvocationHandler.class.getName()+";"+NEW_LINE+"importjava.lang.reflect.Method;"+NEW_LINE+"importjava.lang.Exception;"+NEW_LINE);StringBuilderclassFirstLine=newStringBuilder();//获取代理对象的接口for(ClassanInterface:interfaces){importContent.append("import").append(anInterface.getName()).append(";").append(NEW_LINE);classFirstLine.append("publicclass$Proxy01implements").append(anInterface.getSimpleName()).append(",");};if(classFirstLine.length()>0){classFirstLine=newStringBuilder(classFirstLine.substring(0,classFirstLine.lastIndexOf(",")-1));}content.append(importContent).append(classFirstLine).append("{").append(NEW_LINE).append(TAB).append("privateIProxyInvocationHandlerhandler;").append(NEW_LINE).append(TAB).append("public$Proxy01(IProxyInvocationHandlerhandler){").append(NEW_LINE).append(TAB).append(TAB).append("this.handler=handler;").append(NEW_LINE).append(TAB).append("}").append(NEW_LINE);//拼接接口中的所有方法for(ClassanInterface:interfaces){for(Methodmethod:anInterface.getDeclaredMethods()){//获取接口中的参数类StringBuilderparameterStr=newStringBuilder();StringBuilderpsType=newStringBuilder();StringBuilderpname=newStringBuilder();for(Parameterp:method.getParameters()){parameterStr.append(p).append(",");psType.append(p.getType().getName()).append(".class,");pname.append(p.getName()).append(",");}if(!parameterStr.toString().equals("")){parameterStr=newStringBuilder(parameterStr.substring(0,parameterStr.length()-1));}content.append(TAB+"public").append(method.getReturnType().getSimpleName()).append("").append(method.getName()).append("(").append(parameterStr).append("){").append(NEW_LINE);content.append(TAB+TAB+"try{"+NEW_LINE);content.append(TAB+TAB+TAB+"Methodmethod=").append(anInterface.getName()).append(".class.getMethod(\"").append(method.getName()).append("\"");if(!psType.toString().equals("")){psType=newStringBuilder(psType.substring(0,psType.length()-1));content.append(",").append(psType);}content.append(");").append(NEW_LINE);if(pname.length()>0){pname=newStringBuilder(pname.substring(0,pname.length()-1));}StringBuilderreturnStr=newStringBuilder(TAB+TAB+TAB);if(!"void".equals(method.getReturnType().getName())){returnStr.append("return(").append(method.getReturnType().getName()).append(")");}content.append(returnStr).append("this.handler.invoke(method,");if(pname.length()==0){content.append("null");}else{content.append("newObject[]{").append(pname).append("});").append(NEW_LINE);}content.append(TAB+TAB+"}catch(Exceptione){"+NEW_LINE+TAB+TAB+TAB+"thrownewRuntimeException(e);"+NEW_LINE+TAB+TAB+"}"+NEW_LINE);content.append(TAB+"}"+NEW_LINE);}}content.append("}"+NEW_LINE);//将字符转写入文件Stringfilename="F://com/proxy/$Proxy01.java";Filefile=newFile(filename);if(!file.exists()){file.createNewFile();}FileWriterfw=newFileWriter(file);fw.write(content.toString());fw.flush();fw.close();//使用JavaCompile将java文件编译成.class文件JavaCompilercomplier=ToolProvider.getSystemJavaCompiler();//获得文件管理者StandardJavaFileManagerfileMgr=complier.getStandardFileManager(null,null,null);Iterableits=fileMgr.getJavaFileObjects(filename);//编译任务JavaCompiler.CompilationTasktask=complier.getTask(null,fileMgr,null,null,null,its);//开始编译,执行完可在当前目录下看到.class文件task.call();fileMgr.close();//load到内存URL[]urls=newURL[]{newURL("file:F:\\\\")};URLClassLoaderurlClassLoader=newURLClassLoader(urls);Classcls=urlClassLoader.loadClass("com.proxy.$Proxy01");//反射生成对象Constructorconstructor=cls.getDeclaredConstructor(IProxyInvocationHandler.class);returnconstructor.newInstance(invocationHandler);}}

测试结果

publicclassMain{publicstaticvoidmain(String[]args)throwsException{IUserDaoinstance=(IUserDao)ProxyUtil.newInstance(newUserServiceLogProxyInvocationHandler(newUserService()),newClass[]{IUserDao.class});instance.select("123");}}

我们可以看到测试结果完全正常。最后我们看看生成的代理类

至此,我们可以看到生成代理的类以后就不用改变,我们要代理就只需要实现IProxyInvocationHandler接口,自定义业务逻辑了。

但是我们可能有个疑惑,为什么需要IProxyInvocationHandler接口,为什么不在生成代理类时将方法传递进去呢?这是因为我们的代理类是通过拼接字符串组成的Java文件,我们在字符串中如果直接使用method.invoke()显然是不能成功的,所以需要传递一个对象,通过反射来实例化对象继而调用对象中的方法。

总结

到此我们一步一步的分析了JDK的动态代理,明白了JDK的动态代理是怎么工作运行的,但是这只是我们的模拟,具体JDK动态代理中的一些细节大家有兴趣的可以去看看,希望这篇文章能够对大家理解JDK动态代理有所帮助,不足的地方也请大家多多指正。

版权声明:我们致力于保护作者版权,注重分享,被刊用文章【java代理(Java)】因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理!;

原文链接:https://www.yxiso.com/zhishi/2056278.html

标签:java代理

发表评论:

关于我们
院校搜的目标不仅是为用户提供数据和信息,更是成为每一位学子梦想实现的桥梁。我们相信,通过准确的信息与专业的指导,每一位学子都能找到属于自己的教育之路,迈向成功的未来。助力每一个梦想,实现更美好的未来!
联系方式
电话:
地址:广东省中山市
Email:beimuxi@protonmail.com

Copyright © 2022 院校搜 Inc. 保留所有权利。 Powered by BEIMUCMS 3.0.3

页面耗时0.0404秒, 内存占用1.94 MB, 访问数据库24次

陕ICP备14005772号-15