前言:
今天看官们对“java的provider”大概比较注重,咱们都想要学习一些“java的provider”的相关资讯。那么小编也在网上收集了一些关于“java的provider””的相关内容,希望姐妹们能喜欢,各位老铁们一起来学习一下吧!很多java优秀框架都应用了java里的高级特性,比如java动态代理等。
为了更好地方便大家理解 我后续将会写的文章内容,咱们先来理解下这些特性。
秉承先简后难的原则,这篇文章,先简单手写一个java动态代理。
这是什么意思? 买房子找中介,那么中介就是代理人,卖房人就是被代理的人。
java代理中涉及两个名词:代理对象(增强后的对象)以及目标对象(被代理的对象)
先了解一下静态代理的实现方式
一 java静态代理
1 新建一个 目标对象的接口类 ObjService,接口方法 echoUserName
/** * 目标对象接口。 */public interface ObjService { void echoUserName(String name);}
2 新建一个 实现类,ObjServiceImpl
/** * 目标对象实现类。 */public class ObjServiceImpl implements ObjService{ @Override public void echoUserName(String name) { System.out.println( "hello,"+name); }}
3 目标对象有了,接下来,就是创建一个 同样实现ObjService接口的 代理类 LogProxyServiceImpl,在构造方法中传入要代理的目标对象。在方法中加入增强的代码逻辑。
/** * 日志代理类 */public class LogProxyServiceImpl implements ObjService{ private ObjService objService; /** * 将 目标对象 通过构造函数的方式 加到代理类里 * @param objService */ public LogProxyServiceImpl(ObjService objService){ this.objService = objService; } @Override public void echoUserName(String name) { //增强的逻辑 System.out.println("log log log start $$$"); objService.echoUserName(name); System.out.println("log log log end $$$"); }}
4 测试一下结果
public class ProxyTest { public static void main(String[] args) { // 创建目标对象 ObjService objService = new ObjServiceImpl(); //为对象加代理类 ObjService logProxy = new LogProxyServiceImpl(objService); logProxy.echoUserName("小码哥"); }}
结果如下。代理成功了。
可以看到 每实现一个对象的代理类,都得新建一个,如果需要记录执行时间,那只能再加一个 执行时间的代理类。所以一个对象有多个代理类,多个对象呢,类就泛滥了。
public static void main(String[] args) { // 创建目标对象 ObjService objService = new ObjServiceImpl(); //为对象加日志代理类 ObjService logProxy = new LogProxyServiceImpl(objService); //再加入时间代理类 ObjService timeProxy = new TimeProxyServiceImpl(logProxy); timeProxy.echoUserName("小码哥"); }
二 java动态代理
大家可以想象一下,执行一个java的方法,需要做哪些工作呢?
第一,需要新建一个java文件;第二,编译成.class文件 ;第三,新建获取对象;最后运行。
动态生成java代理类,也是需要这么做的。接下来,咱们就来通过动态代理的方式来实现上述ObjService目标对象的日志代理类。
下面是重点,大家打起十二分精神。
1 新建一个 动态代理生成类DynamicProxy, 方法名称 newProxyInstance
public static Object newProxyInstance(Object target) throws Exception { // 获取目标对象的接口,接口可以多个。这里简单点,取第一个 Class targetInfo = target.getClass().getInterfaces()[0]; // (1)通过目标对象 构建代理类文件,返回文件地址 File file = getProxyFile(target,targetInfo); //(2)编译上面的java文件 compile(file); //(3)通过类加载器加载类,并构建代理对象 URL[] urls = new URL[]{new URL("file:d:\\\\tmp\\\\")}; URLClassLoader urlClassLoader = new URLClassLoader(urls); Class cl = urlClassLoader.loadClass("com.example.proxy.$Proxy"); Constructor constructor = cl.getConstructor(targetInfo); Object result = constructor.newInstance(target); return result; }
2 通过目标对象 构建代理类文件,返回文件地址。getProxyFile方法的代码如下,里面是通过反射的方式获取的。
private static File getProxyFile(Object target,Class targetInfo) throws Exception{ String content = ""; //java文件的内容。 String packageContent = "package com.example.proxy;"; //文件包名 //接口名 ObjService String targetInfoName = targetInfo.getSimpleName(); // import ObjService 路径 String importContent = "import " + targetInfo.getName() + ";"; //声明代理类名$Proxy,并实现 ObjService接口 String classContent = "public class $Proxy implements " + targetInfoName + "{"; // 声明目标对象 private ObjService target String fieldContent = "private " + targetInfoName + " target;"; //构造方法. public $Proxy(ObjService target){ this.target = target} String constructContent = "public $Proxy(" + targetInfoName + " target){" + "this.target = target;" + "}"; //方法内容 String methodsContent = ""; //获取ObjService接口的所有方法 Method[] methods = targetInfo.getDeclaredMethods(); for(Method method:methods){ //方法名 echoUserName String methodName = method.getName(); //返回类型 void Class returnType = method.getReturnType(); //参数类型 这里是String Class<?>[] paramTypes = method.getParameterTypes(); String argsContent = ""; String argsNames = ""; int i=0; for(Class<?> paramType:paramTypes){ String simpleName = paramType.getSimpleName(); argsContent += simpleName+" a"+i+","; argsNames+= "a"+i+","; i++; } if(argsContent.length()>0){ //去掉最后的 逗号 argsContent = argsContent.substring(0,argsContent.length()-1); argsNames = argsNames.substring(0,argsNames.length()-1); } //生成文件 methodsContent += "public "+returnType+" "+methodName+"("+argsContent+"){" +"System.out.println(\"log log log start $$$\");" +"target."+methodName+"("+argsNames+");" +"System.out.println(\"log log log end $$$\");" +"}"; } //把所有的组件内容 合起来 content+=packageContent+importContent+classContent+fieldContent+constructContent+methodsContent+"}"; //生成 $Proxy.java文件。 File file = new File("D:\\tmp\\com\\example\\proxy\\$Proxy.java"); if(!file.exists()){ file.createNewFile(); } FileWriter fileWriter = new FileWriter(file); fileWriter.write(content); fileWriter.flush(); fileWriter.close(); return file; }
3 编译java文件。代码如下。这里用系统自带的,不太建议使用,cglib 有更好的方式,大家可以先了解下这个。
private static void compile(File file) throws Exception{ JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager fileManager = compiler.getStandardFileManager(null,null,null); Iterable units = fileManager.getJavaFileObjects(file); JavaCompiler.CompilationTask task = compiler.getTask(null,fileManager,null,null,null,units); task.call(); fileManager.close(); }
经过上面两步,我们这里生产的是这样的文件。
4 通过类加载器 URLClassLoader 加载 class文件。
URL[] urls = new URL[]{new URL("file:d:\\\\tmp\\\\")}; URLClassLoader urlClassLoader = new URLClassLoader(urls); Class cl = urlClassLoader.loadClass("com.example.proxy.$Proxy"); Constructor constructor = cl.getConstructor(targetInfo); Object result = constructor.newInstance(target); return result;
5 修改测试代码如下。
public class ProxyTest { public static void main(String[] args) throws Exception{ // 创建目标对象 ObjService objService = new ObjServiceImpl();// //为对象加日志代理类// ObjService logProxy = new LogProxyServiceImpl(objService);// //再加入时间代理类// ObjService timeProxy = new TimeProxyServiceImpl(logProxy); //使用动态代理生成类. ObjService proxy = (ObjService) DynamicProxy.newProxyInstance(objService); proxy.echoUserName("小码哥"); }}
运行结果如下。符合预期。
log log log start $$$hello,小码哥log log log end $$$
上面我们尽量用最简单的方式 写了一个 java动态代理类。真正用于生产实践 里面还有很多难点需要考虑处理。但万变不离其宗。
喜欢的小伙伴多多 关注 点赞 转发。后续会提供更多的技术文章。也希望大家多给点意见。咱们共同进步。
标签: #java的provider