java代理模式(Java)

 2025-08-23 08:06:01  阅读 707  评论 0

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

什么是代理

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

Java 代理模式及动态代理详解

代理模式UML

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

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

静态代理

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

继承实现静态代理

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

1.创建接口

publicinterfaceIUserDao{
Stringselect(Stringname);
}

2.创建接口的实现

publicclassUserServiceimplementsIUserDao{
@Override
publicStringselect(Stringname){
returnname;
}
}

3.创建代理类

publicclassUserServiceLogextendsUserService{

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

4.测试类以及测试

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

测试结果:

Java 代理模式及动态代理详解

测试结果

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

聚合实现静态代理

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

publicclassUserServiceLogPolymerizationimplementsIUserDao{
privateIUserDaouserDao;

publicUserServiceLogPolymerization(IUserDaouserDao){
this.userDao=userDao;
}

@Override
publicStringselect(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的反射技术(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文件。

Java 代理模式及动态代理详解

测试结果:

Java 代理模式及动态代理详解

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

深度模拟

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

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

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

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

实现类

publicclassUserServiceLogProxyInvocationHandlerimplementsIProxyInvocationHandler{
//目标类
privateObjecttarget;
publicUserServiceLogProxyInvocationHandler(Objecttarget){
this.target=target;
}
@Override
publicObjectinvoke(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");
}
}

Java 代理模式及动态代理详解

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

Java 代理模式及动态代理详解

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

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

总结

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

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

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

标签:java代理模式

发表评论:

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

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

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

陕ICP备14005772号-15