700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > 代理设计模式(JDK动态代理)什么是代理 静态代理动态代理实现 分析JDK代理实现逻辑

代理设计模式(JDK动态代理)什么是代理 静态代理动态代理实现 分析JDK代理实现逻辑

时间:2023-11-02 11:59:34

相关推荐

代理设计模式(JDK动态代理)什么是代理 静态代理动态代理实现 分析JDK代理实现逻辑

什么是代理?

从字面意思来看,代理比较好理解,无非就是代为处理的意思。举个例子,现在哪吒有女助理了,所以很多事情都不用我自己去处理了,比如说去银行排队取钱,那么我就可以叫我的女助理去代替我取钱并交给我,那么我自己就可以去干其他的事情,那么此时女助理就充当了代理的作用,代替我取钱。

优点:

1.增强目标对象。可以在执行目标对象方法的前后或者其他地方加上验证、日志等等代码;(Spring框架中的AOP)

2.将调用对象和被调用对象分离,一定程度上降低了耦合度。扩展性好;

3.保护目标对象;

4.职责清晰。目标对象就是实现实际的业务逻辑,不用关心其他非本职责的事务,通过后期的代理完成,附带的结果就是编程简洁清晰。

缺点:

1.对象与对象调用之间增加了一层代理,可能会导致执行的速度变慢;

2.实现代理的代码有时会很复杂,添加了额外的工作量;

3.增加系统的复杂度。

代理模式有二类实现方式:

静态代理

代理类在程序运行前就编译好,因此称为静态代理。代理对象和被代理对象都要实现相同的接口或者继承相同的父类(除Object之外),一旦接口或者父类添加、修改方法,子类都要统一更改,违背开闭原则,因此静态代理具有一定的局限性,所以静态代理的实现有二种实现方式:

继承

我们假设一个场景,现在有个处理订单的业务,我们需要在处理订单方法前加日志记录怎么实现?

//公共接口public interface Utils {void operate();}//处理日志public class LogBusiness implements Utils {@Overridepublic void operate() {System.out.println("记录日志");}}//处理订单方法public class OmsBusiness extends LogBusiness implements Utils {@Overridepublic void operate() {//因为这里继承了LogBusiness所以这里调用父类的operate方法就可以记录日志super.operate();System.out.println("处理订单");}}//测试方法public class Test {public static void main(String[] args) {OmsBusiness oms = new OmsBusiness();oms.operate();}}//结果记录日志处理订单

这时候老板让在记录下时间呢?

//处理时间public class TimeBusiness implements Utils {@Overridepublic void operate() {System.out.println("记录时间");}}//处理日志,这里继承处理时间类。public class LogBusiness extends TimeBusiness implements Utils {@Overridepublic void operate() {//因为这里继承了TimeBusiness所以这里调用父类的operate方法就可以记录时间super.operate();System.out.println("记录日志");}}//结果记录时间记录日志处理订单

我们是先记录了时间在记录了日志,这时候老板让我们先记录日志在记录时间呢?

public void operate() {super.operate();System.out.println("记录日志");}

根据叠加业务及处理顺序不同,分别要创建不同的子类,这样就会造成类爆炸的问题还有代码冗余,就会有很多类来处理这种辅助功能,不好管理。但是聚合可以避免这个问题。

聚合

我们开看下通过聚合的方式怎么解决上边的问题。

//公共接口public interface Utils {void operate();}//处理日志public class LogBusiness implements Utils {@Overridepublic void operate() {System.out.println("记录日志");}}//处理时间public class TimeBusiness implements Utils {@Overridepublic void operate() {System.out.println("记录时间");}}//处理订单public class OmsBusiness implements Utils {@Overridepublic void operate() {System.out.println("处理订单");}}//代理对象public class OmsBusinessProxy implements Utils {private OmsBusiness omsBusiness;public OmsBusinessProxy(OmsBusiness omsBusiness) {this.omsBusiness = omsBusiness;}//在这里就可以调整增加逻辑的顺序@Overridepublic void operate() {TimeBusiness time = new TimeBusiness();LogBusiness log = new LogBusiness();time.operate();log.operate();omsBusiness.operate();}}//测试方法public class Test {public static void main(String[] args) {Utils oms = new OmsBusinessProxy(new OmsBusiness());oms.operate();}}//结果记录时间记录日志处理订单

很显然,聚合实现代理方式比继承代理方式更为灵活,代理之间可以互相组合,互相传递。

但是又问题来了,这个是处理订单的业务逻辑的增强,那么现在又有处理库存等等的业务逻辑需要增强,那么就需要每个业务逻辑增加一个代理类,代理类仍然会爆炸。

可以通过动态代理来解决这个问题。

动态代理

上边说到的静态代理都有一个致命的类爆炸问题,那动态代理怎么解决呢?我们大胆的猜想下能不能不创建Java类用代码实现动态创建?想想应该是可以的,为什么呢?因为Java类文件也是字符串,我们只是用代码编写了这个字符串,然后生成.class文件然后再生成class对象最后通过反射实现生成Java对象,有思路了那就开始干。

//接口public interface Person {void eat();}//动态生成类对象public class MyObject {private static final String ENTER = "\r\n";private static final String PAKAGE = MyObject.class.getPackage().toString()+";";private static final String CLASS_NAME = "$Proxy";//防止并发产生相同文件名private static final AtomicInteger NUMBER= new AtomicInteger(0);public static void main(String[] args) throws Exception {//生成Java文件String className = CLASS_NAME+NUMBER.getAndIncrement();String javaString = createJavaString(new Class<?>[]{Person.class}, className);String parentPath = MyObject.class.getResource("").getPath();File file = new File(parentPath,className+".java" );FileWriter writer = new FileWriter(file);writer.write(javaString);writer.flush();writer.close();//编译成class文件JavaCompiler systemJavaCompiler = ToolProvider.getSystemJavaCompiler();StandardJavaFileManager standardFileManager = systemJavaCompiler.getStandardFileManager(null, null, null);Iterable<? extends JavaFileObject> javaFileObjects = standardFileManager.getJavaFileObjects(file);pilationTask task = systemJavaCompiler.getTask(null, standardFileManager, null, null, null, javaFileObjects);task.call();standardFileManager.close();//通过反射创建实例,CLClassLoader这个目前不用管就是生成class对象的工具,这里是先把.class文件转成byte字节码然后生成class对象Class<?> aClass = new CLClassLoader().findClass(className);Person instance = (Person) aClass.newInstance();instance.eat();}//通过StringBuffer拼接代码private static String createJavaString(Class<?>[] interfaces , String className){StringBuffer buffer = new StringBuffer();buffer.append(PAKAGE+ENTER);StringBuffer interfaceString= new StringBuffer();int length= interfaces.length;for (int i = 0; i<length ; ++i){interfaceString.append(interfaces[i].getName());if (i!=length-1){interfaceString.append(",");}}buffer.append("public final class ");buffer.append(className);buffer.append(" implements ");buffer.append(interfaceString);buffer.append(" {"+ENTER);buffer.append("public "+className+"() {}"+ENTER);for (int i =0 ;i < length;++i){Class<?> clazz= interfaces[i];Method[] methods = clazz.getMethods();for (Method method : methods){String returnTypeString = method.getReturnType().getName();int modifiers = method.getModifiers();if (Modifier.isPublic(modifiers)){buffer.append("public");}else if (Modifier.isPrivate(modifiers)){buffer.append("private");}else if (Modifier.isProtected(modifiers)){buffer.append("protected");}buffer.append(" final "+returnTypeString+" "+ method.getName()+"(" + "){"+ ENTER);buffer.append("try{"+ENTER);buffer.append("System.out.println(\"dynamic object!\");"+ENTER);buffer.append("}catch(Throwable e){"+ENTER);buffer.append("e.printStackTrace();"+ENTER);buffer.append("}"+ENTER);if (!"void".equals(returnTypeString)){buffer.append("return null;"+ENTER);}buffer.append("}"+ENTER);}}buffer.append("}");return buffer.toString();}}

//ClassLoader工具类public class CLClassLoader extends ClassLoader {private File classPathFile;public CLClassLoader(){String classPath = CLClassLoader.class.getResource("").getPath();this.classPathFile= new File(classPath);}@Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException {String className = CLClassLoader.class.getPackage().getName()+"."+name;if (classPathFile!= null ){File classFile = new File(classPathFile, name.replace("\\.", "/") + ".class");if (classFile.exists()){FileInputStream inputStream =null;ByteArrayOutputStream outputStream = null;try{inputStream=new FileInputStream(classFile);outputStream= new ByteArrayOutputStream();byte[] bytes = new byte[1024];int len;while ((len=inputStream.read(bytes))!=-1){outputStream.write(bytes,0,len);}return defineClass(className,outputStream.toByteArray(),0,outputStream.size());}catch (Exception e){e.printStackTrace();}finally {if (inputStream!= null){try {inputStream.close();} catch (IOException e) {e.printStackTrace();}}if (outputStream!=null){try {outputStream.close();} catch (IOException e) {e.printStackTrace();}}}}}return super.findClass(name);}}

//生成的文件$Proxy0.javapackage com.donny.erp.proxy.mydynamicobject;public final class $Proxy0 implements com.donny.erp.proxy.mydynamicobject.Person {public $Proxy0() {}public final void eat(){try {System.out.println("dynamic object!");} catch(Throwable e){e.printStackTrace();}}}//生成的文件$Proxy0.class//// Source code recreated from a .class file by IntelliJ IDEA// (powered by FernFlower decompiler)//package com.donny.erp.proxy.mydynamicobject;public final class $Proxy0 implements Person {public $Proxy0() {}public final void eat() {try {System.out.println("dynamic object!");} catch (Throwable var2) {var2.printStackTrace();}}}

可以看到这里我们是能成功的通过代码的形式生成类对象并且正常的执行eat方法。但是我们的方法体中写死了业务逻辑代码打印了"dynamic object!",那么怎么实现动态业务呢?我们先来看看jdk的动态代理是怎么实现的。

//接口public interface Utils {public void operate();}//目标对象public class SourceObject implements Utils{@Overridepublic void operate() {System.out.println("处理订单");}}//invocationhandler实现类public class TimeInvocationHandler implements InvocationHandler {private Object source;public TimeInvocationHandler(Object source) {this.source = source;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//增强System.out.println("处理时间");//执行目标对象的方法Object invoke = method.invoke(source, args);return invoke;}public Object createObject(){Class<?> aClass = source.getClass();//生成代理对象Object o = Proxy.newProxyInstance(aClass.getClassLoader(), aClass.getInterfaces(), this);return o;}}//测试方法public class Test {public static void main(String[] args) {//保存代理对象到磁盘System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");TimeInvocationHandler handler = new TimeInvocationHandler(new SourceObject());Utils o = (Utils)handler.createObject();o.operate();}}

可以看到创建代理对象就这一行代码。

Object o = Proxy.newProxyInstance(aClass.getClassLoader(), aClass.getInterfaces(), this);

这个方法和我们的对比差了一个参数,InvocationHandler的实现类,InvocationHandler接口只有一个invoke方法,可以看到增强逻辑都是在这个方法中执行的。那么也就是说我们在代理对象中执行传进来的InvocationHandler实现类的invoke方法就可以实现增强逻辑,看看JDK是怎么做的?

public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException{private static final Class<?>[] constructorParams = {InvocationHandler.class };//这里我只copy了重要的代码//这个其实和我们上边实现逻辑的类似(但是实现方式不同,我们是先生成文件再加载为class,JDK是生成byte[]字节再加载成class,当然JDK的实现更快),只是它多了有一个InvocationHandler为参数的构造函数,并且方法全部都调用了InvocationHandler.invoke方法。下边可以看生成的反编译下class文件Class<?> cl = getProxyClass0(loader, intfs);//拿到一个InvocationHandler参数的构造函数的类对象final Constructor<?> cons = cl.getConstructor(constructorParams);//通过构造函数的类对象反射出对象return cons.newInstance(new Object[]{h});}//代理对象.class文件public final class $Proxy0 extends Proxy implements Utils {public $Proxy0(InvocationHandler var1) throws {super(var1);}public final void operate() throws {try {super.h.invoke(this, m3, (Object[])null);} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);}}}/*这里说下面试会问到为什么JDK的动态代理只能代理基于接口的目标类。看到上边反编译的.class文件就可以解释这个问题了。首先JDK代理会默认继承Proxy,其次Java是单继承。所以只能代理基于接口的目标类。再看下operate()方法,里边其实就是执行了InvocationHandler.invoke,其实就是我们实现的InvocationHandler接口。这样看下来还是挺简单的吧。我们继续把下边代码看了。*/

private static Class<?> getProxyClass0(ClassLoader loader,Class<?>... interfaces) {return proxyClassCache.get(loader, interfaces);}public V get(K key, P parameter) {Objects.requireNonNull(parameter);expungeStaleEntries();//查询缓存Object cacheKey = CacheKey.valueOf(key, refQueue);ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);if (valuesMap == null) {ConcurrentMap<Object, Supplier<V>> oldValuesMap= map.putIfAbsent(cacheKey,valuesMap = new ConcurrentHashMap<>());if (oldValuesMap != null) {valuesMap = oldValuesMap;}}//创建Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));Supplier<V> supplier = valuesMap.get(subKey);Factory factory = null;//返回while (true) {if (supplier != null) {V value = supplier.get();if (value != null) {return value;}}if (factory == null) {factory = new Factory(key, parameter, subKey, valuesMap);}if (supplier == null) {supplier = valuesMap.putIfAbsent(subKey, factory);if (supplier == null) {supplier = factory;}} else {if (valuesMap.replace(subKey, supplier, factory)) {supplier = factory;} else {supplier = valuesMap.get(subKey);}}}}//重点 = subKeyFactory.apply(key, parameter)public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);//校验接口for (Class<?> intf : interfaces) {Class<?> interfaceClass = null;try {interfaceClass = Class.forName(intf.getName(), false, loader);} catch (ClassNotFoundException e) {}if (interfaceClass != intf) {throw new IllegalArgumentException(intf + " is not visible from class loader");}if (!interfaceClass.isInterface()) {throw new IllegalArgumentException(interfaceClass.getName() + " is not an interface");}if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {throw new IllegalArgumentException("repeated interface: " + interfaceClass.getName());}}String proxyPkg = null;int accessFlags = Modifier.PUBLIC | Modifier.FINAL;//非public接口,代理类的包名与接口的包名相同for (Class<?> intf : interfaces) {int flags = intf.getModifiers();if (!Modifier.isPublic(flags)) {accessFlags = Modifier.FINAL;String name = intf.getName();int n = name.lastIndexOf('.');String pkg = ((n == -1) ? "" : name.substring(0, n + 1));if (proxyPkg == null) {proxyPkg = pkg;} else if (!pkg.equals(proxyPkg)) {throw new IllegalArgumentException("non-public interfaces from different packages");}}}if (proxyPkg == null) {proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";}//代理名称long num = nextUniqueNumber.getAndIncrement();String proxyName = proxyPkg + proxyClassNamePrefix + num;//创建字节数据byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);try {//调用底层native方法生成class对象return defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);} catch (ClassFormatError e) {throw new IllegalArgumentException(e.toString());}}}

知道JDK实现逻辑接下来我们自己实现下动态业务的动态代理吧

//接口public interface Person {void eat();String call(String name);}//测试使用目标类class Donny implements Person {@Overridepublic void eat() {System.out.println("吃东西");}@Overridepublic String call(String name) {return name;}}//自定义InvocationHandlerpublic interface CLInvocationHandler {public Object invoke(Object proxy, Method method, Object[] args)throws Throwable;}public class CLProxy {private static final String ENTER = "\r\n";private static final String PAKAGE = CLProxy.class.getPackage().toString()+";";private static final String CLASS_NAME = "$Proxy";//防止并发产生相同文件名private static final AtomicInteger NUMBER= new AtomicInteger(0);public static Object newProxyInstance(CLClassLoader classLoader, Class<?>[] interfaces,CLInvocationHandler h) throws Exception{String className = CLASS_NAME+NUMBER.getAndIncrement();//遍历所有的接口生成java 文件String javaString = createJavaString(interfaces, className);String parentPath = CLProxy.class.getResource("").getPath();File file = new File(parentPath,className+".java" );FileWriter writer = new FileWriter(file);writer.write(javaString);writer.flush();writer.close();//编译JavaCompiler systemJavaCompiler = ToolProvider.getSystemJavaCompiler();StandardJavaFileManager standardFileManager = systemJavaCompiler.getStandardFileManager(null, null, null);Iterable<? extends JavaFileObject> javaFileObjects = standardFileManager.getJavaFileObjects(file);pilationTask task = systemJavaCompiler.getTask(null, standardFileManager, null, null, null, javaFileObjects);task.call();standardFileManager.close();//创建实例Class<?> aClass = classLoader.findClass(className);Constructor<?> constructor = aClass.getConstructor(CLInvocationHandler.class);Object instance = constructor.newInstance(h);//file.delete();return instance;}/*** 生成java 文件* @param interfaces* @return*/private static String createJavaString(Class<?>[] interfaces , String className){StringBuffer buffer = new StringBuffer();buffer.append(PAKAGE+ENTER);buffer.append("import java.lang.reflect.Method;"+ ENTER);StringBuffer interfaceString= new StringBuffer();int length= interfaces.length;for (int i = 0; i<length ; ++i){interfaceString.append(interfaces[i].getName());if (i!=length-1){interfaceString.append(",");}}buffer.append("public final class ");buffer.append(className);buffer.append(" implements ");buffer.append(interfaceString);buffer.append(" {"+ENTER);//CLInvocationHandler成员变量buffer.append("private CLInvocationHandler handler;"+ENTER);//构造函数buffer.append("public "+className+"(CLInvocationHandler handler) {"+ENTER);buffer.append(" this.handler= handler;"+ENTER);buffer.append("}"+ENTER);for (int i =0 ;i < length;++i){Class<?> clazz= interfaces[i];Method[] methods = clazz.getMethods();for (Method method : methods){String returnTypeString = method.getReturnType().getName();Class<?>[] parameterTypes = method.getParameterTypes();StringBuffer paramTypeString = new StringBuffer();StringBuffer methodParamString = new StringBuffer();StringBuffer invokeParamString = new StringBuffer();paramTypeString.append("new Class[]{");int paramLength= parameterTypes.length;//拼接参数for (int j =0 ; j<paramLength ;++j){Class<?> paramClazz= parameterTypes[j];paramTypeString.append(paramClazz.getName()+".class");String paramFieldName = "var"+j;methodParamString.append(paramClazz.getName() +" "+paramFieldName);invokeParamString.append(paramFieldName);if (j!= paramLength-1){paramTypeString.append(",");methodParamString.append(",");invokeParamString.append(",");}}paramTypeString.append("}");//修饰符int modifiers = method.getModifiers();if (Modifier.isPublic(modifiers)){buffer.append("public");}else if (Modifier.isPrivate(modifiers)){buffer.append("private");}else if (Modifier.isProtected(modifiers)){buffer.append("protected");}buffer.append(" final "+returnTypeString+" "+ method.getName()+"("+methodParamString+"){"+ ENTER);buffer.append("try{"+ENTER);buffer.append("Method method = "+clazz.getName()+".class.getMethod(\""+method.getName()+"\","+paramTypeString+" );"+ENTER);if (!"void".equals(returnTypeString)){buffer.append("return ("+returnTypeString+")");}if (invokeParamString.toString().length()==0){invokeParamString.append("null");}else{invokeParamString = new StringBuffer("new Object[]{"+invokeParamString.toString()+"}");}buffer.append("this.handler.invoke(this,method,"+invokeParamString+");"+ENTER);buffer.append("}catch(Throwable e){"+ENTER);buffer.append("e.printStackTrace();"+ENTER);buffer.append("}"+ENTER);if (!"void".equals(returnTypeString)){buffer.append("return null;"+ENTER);}buffer.append("}"+ENTER);}}buffer.append("}");return buffer.toString();}//InvocationHandler实现static class MyCLInvocationHandler implements CLInvocationHandler{private Object source;public MyCLInvocationHandler(Object source) {this.source = source;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//增强System.out.println("before");Object result= method.invoke(source, args);System.out.println("after");return result;}}//测试方法public static void main(String[] args) throws Exception {Person person = (Person)CLProxy.newProxyInstance(new CLClassLoader(),Donny.class.getInterfaces(),new MyCLInvocationHandler(new Donny()));String donny = person.call("donny");System.out.println(donny);person.eat();}}

//ClassLoader工具类public class CLClassLoader extends ClassLoader {private File classPathFile;public CLClassLoader(){String classPath = CLClassLoader.class.getResource("").getPath();this.classPathFile= new File(classPath);}@Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException {String className = CLClassLoader.class.getPackage().getName()+"."+name;if (classPathFile!= null ){File classFile = new File(classPathFile, name.replace("\\.", "/") + ".class");if (classFile.exists()){FileInputStream inputStream =null;ByteArrayOutputStream outputStream = null;try{inputStream=new FileInputStream(classFile);outputStream= new ByteArrayOutputStream();byte[] bytes = new byte[1024];int len;while ((len=inputStream.read(bytes))!=-1){outputStream.write(bytes,0,len);}return defineClass(className,outputStream.toByteArray(),0,outputStream.size());}catch (Exception e){e.printStackTrace();}finally {if (inputStream!= null){try {inputStream.close();} catch (IOException e) {e.printStackTrace();}}if (outputStream!=null){try {outputStream.close();} catch (IOException e) {e.printStackTrace();}}}}}return super.findClass(name);}}

这样应该就很清楚JDK是怎么实现动态代理的,为了后边Spring学习打基础,每天进步一点点 。

代理设计模式(JDK动态代理)什么是代理 静态代理动态代理实现 分析JDK代理实现逻辑 手动实现JDK代理逻辑。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。