重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
这篇文章主要介绍“java怎么实现从静态代理到动态代理”,在日常操作中,相信很多人在java怎么实现从静态代理到动态代理问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”java怎么实现从静态代理到动态代理”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!
创新互联是少有的网站设计制作、网站设计、营销型企业网站、微信小程序、手机APP,开发、制作、设计、卖链接、推广优化一站式服务网络公司,自2013年起,坚持透明化,价格低,无套路经营理念。让网页惊喜每一位访客多年来深受用户好评
引用网上的一段话 代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。
举个例子来说明:假如说我现在想买一辆二手车,虽然我可以自己去找车源,做质量检测等一系列的车辆过户流程,但是这确实太浪费我得时间和精力了。我只是想买一辆车而已为什么我还要额外做这么多事呢?于是我就通过中介公司来买车,他们来给我找车源,帮我办理车辆过户流程,我只是负责选择自己喜欢的车,然后付钱就可以了。用图表示如下:
代理对象:增强后的对象 目标对象:被增强的对象
先来假设一个场景,查询用户,在查询之前要写日志记录。
public class UserDaoImpl { public void query(){ System.out.println("查询用户信息"); } }
上面代码我们要实现对其功能的增强,可以通过修改代码来实现。
public class UserDaoImpl { public void query(){ System.out.println("log"); System.out.println("查询用户信息"); } }
这样实现可以达到效果,但是破坏了面向对象的开闭原则。况且有的时候,我们是不能拿到该Dao的源码,也没有办法对其修改。所以可以考虑代理模式来实现。
编写UserDaoImpl的一个子类,复写query方法.
public class ProxyUserDao extends UserDaoImpl{ @Override public void query() { System.out.println("log"); super.query(); } }
编写测试,执行测试。
public static void main(String[] args) { ProxyUserDao proxyUserDao = new ProxyUserDao(); proxyUserDao.query(); }
目标对象和代理对象都要实现同一接口。 编写UserDao接口
public interface UserDao { public void query(); }
UserDaoImpl实现
public class UserDaoImpl implements UserDao{ public void query(){ System.out.println("查询用户信息"); } }
编写代理类
public class ProxyUserDao implements UserDao{ //目标对象 UserDao userDao; //通过构造函数传入目标对象 public ProxyUserDao(UserDao userDao){ this.userDao = userDao; } @Override public void query() { System.out.println("log"); userDao.query(); } }
改写测试
ProxyUserDao proxyUserDao = new ProxyUserDao(new UserDaoImpl()); proxyUserDao.query();
执行后,也可以完成代理。上面实现的都是先打印log,在执行查询,那么要实现先执行查询,再打印log,我们就要再写一个代理类,岂不蛋疼。
现在我们来假设要实现多个功能的增强,以前是日志记录,log,我们还要家人时间记录time,那么这里就有多种组合方式。
那么有不同的需求就要有不同的实现方式,每一种都要编写不同的代理实现逻辑。就会写大量的java代理类。我们回头看一下继承的方式来实现的话,还更加麻烦,代理类会更多。
继承:代理类过多,比较复杂 聚合:也会有大量的代理类,不过相比继承要好很多。
所以,在不确定的情况下,就不要去使用静态代理,因为会编写大量的代理类来满足不同的需求。比较麻烦。这里模拟的还仅仅是一个UserDao,若有多个目标代理对象,那么通过硬编码的方式,就真的很不理智了。
我们可以通过动态代理来避免编写大量的代理对象的困扰。
我们先来分析一下,生成一个对象的途径。 第一步:编写.java文件
第二步:编译.java文件,获得.class文件 第三步:类记载机制加载.class文件,new出对象。
先来分析第一步:我们编写java文件以外,我们就是要为了不编写大量的类代码,所以这种方式无效,此外还可以通过编写程序实现输出一个java文件。 下面直接贴源码:
public class ProxyUtil { /** * content --->string * .java io * .class * .new 反射----》class * @return */ public static Object newInstance(Object target){ Object proxy=null; Class targetInf = target.getClass().getInterfaces()[0]; Method methods[] =targetInf.getDeclaredMethods(); String line="\n"; String tab ="\t"; String infName = targetInf.getSimpleName(); String content =""; //进行拼装代理类代码 String packageContent = "package com.google;"+line; String importContent = "import "+targetInf.getName()+";"+line; String clazzFirstLineContent = "public class $Proxy implements "+infName+"{"+line; String filedContent =tab+"private "+infName+" target;"+line; String constructorContent =tab+"public $Proxy ("+infName+" target){" +line +tab+tab+"this.target =target;" +line+tab+"}"+line; String methodContent = ""; for (Method method : methods) { String returnTypeName = method.getReturnType().getSimpleName(); String methodName =method.getName(); // Sting.class String.class Class args[] = method.getParameterTypes(); String argsContent = ""; String paramsContent=""; int flag =0; for (Class arg : args) { String temp = arg.getSimpleName(); //String //String p0,Sting p1, argsContent+=temp+" p"+flag+","; paramsContent+="p"+flag+","; flag++; } if (argsContent.length()>0){ argsContent=argsContent.substring(0,argsContent.lastIndexOf(",")-1); paramsContent=paramsContent.substring(0,paramsContent.lastIndexOf(",")-1); } //代理打印log日志功能(这里是写死的) methodContent+=tab+"public "+returnTypeName+" "+methodName+"("+argsContent+") {"+line +tab+tab+"System.out.println(\"log\");"+line +tab+tab+"target."+methodName+"("+paramsContent+");"+line +tab+"}"+line; } //类中的代码字符串 content=packageContent+importContent+clazzFirstLineContent+filedContent+constructorContent+methodContent+"}"; File file =new File("//$Proxy.java"); try { if (!file.exists()) { file.createNewFile(); } //将content写入java文件 FileWriter fw = new FileWriter(file); fw.write(content); fw.flush(); fw.close(); //进行java文件编译 java--->class JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null); Iterable units = fileMgr.getJavaFileObjects(file); JavaCompiler.CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units); t.call(); fileMgr.close(); // 类加载 URL[] urls = new URL[]{new URL("file:D:\\\\")}; URLClassLoader urlClassLoader = new URLClassLoader(urls); Class clazz = urlClassLoader.loadClass("com.google.$Proxy"); //获取构造函数 Constructor constructor = clazz.getConstructor(targetInf); /反射创建实例 proxy = constructor.newInstance(target); }catch (Exception e){ e.printStackTrace(); } return proxy; } }
上面是编写好的代理工具类,会实现java代理文件的生成,java的编译,加载,反射创建实例。调用newInstance就返回了代理对象。不过这里将代理功能写死的。
编写测试类。并执行
UserDao o = (UserDao) ProxyUtil.newInstance(new UserDaoImpl()); o.query();
目录下已经生成了对应的文件
查看生成的代理类代码
控制台打印的日志,实现了对方法加上日志打印
我们要实现对其他目标对象的代理,只需要调用newInstance传入目标对象即可。
如:我们要实现对订单OrderDao查询订单增加日志打印
//订单Dao接口 public interface OrderDao { public void query(); } //订单Dao接口实现 目标对象 public class OrderDaoImpl implements OrderDao { public void query(){ System.out.println("查询订单"); } } //测试函数 public static void main(String[] args) { OrderDao o = (OrderDao) ProxyUtil.newInstance(new OrderDaoImpl()); o.query(); }
只需测试,查询日志,达到了对订单接口的代理
通过动态代理我们可以在不手动编写代理对象的方式,实现对不同目标对象的代理。增强了代码的可扩展。ps:java底层也有动态代理的工具类proxy。他的实现原理也是相同的。
到此,关于“java怎么实现从静态代理到动态代理”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注创新互联网站,小编会继续努力为大家带来更多实用的文章!