java之动态代理
摘要
相比于静态代理,动态代理避免了开发人员编写各个繁锁的静态代理类,只需简单地指定一组接口及目标类对象就能动态的获得代理对象。
JDK动态代理
使用
1、定义一个接口:
package com.zze.service; public interface IWaiter { void service(); }
com.zze.service.IWaiter
2、定义它的实现类:
package com.zze.service.impl; import com.zze.service.IWaiter; public class Waiter implements IWaiter { public void service(){ System.out.println("正在服务"); } }
com.zze.service.impl.Waiter
3、使用 JDK 提供的动态代理:
@Test public void test() { IWaiter waiter = new Waiter(); Class<?>[] interfaces = Waiter.class.getInterfaces(); IWaiter waiterProxy = (IWaiter) Proxy.newProxyInstance(Waiter.class.getClassLoader(), interfaces, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object obj = null; if (method.getName().equalsIgnoreCase("service")) { System.out.println("服务之前"); obj = method.invoke(waiter, args); System.out.println("服务之后"); } return obj; } }); waiterProxy.service(); /* 服务之前 正在服务 服务之后 */ }
JDK 动态代理只能为实现了接口的类产生代理对象。
源码分析
从 newProxyInstance 方法看起:
@CallerSensitive public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { Objects.requireNonNull(h); final Class<?>[] intfs = interfaces.clone(); final SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } /* * 生成代理类的 class 文件 */ Class<?> cl = getProxyClass0(loader, intfs); try { if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); } /* * 获取代理类构造函数 */ final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (!Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { cons.setAccessible(true); return null; } }); } /* 使用代理类的构造器,传入参数 h(即我们实现的InvocationHandler类实例)创建代理类的实例并返回 */ return cons.newInstance(new Object[]{h}); } catch (IllegalAccessException|InstantiationException e) { throw new InternalError(e.toString(), e); } catch (InvocationTargetException e) { Throwable t = e.getCause(); if (t instanceof RuntimeException) { throw (RuntimeException) t; } else { throw new InternalError(t.toString(), t); } } catch (NoSuchMethodException e) { throw new InternalError(e.toString(), e); } }
java.lang.reflect.Proxy.newProxyInstance
该方法的返回值是第 41 行返回的代理类实例,而这个代理类字节码文件创建工作都是在 18 行的 getProxyClass0 方法完成:
private static Class<?> getProxyClass0(ClassLoader loader, // 如果被代理类实现的接口超出 65535 个则抛出异常 if (interfaces.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); } return proxyClassCache.get(loader, interfaces); }
java.lang.reflect.Proxy.getProxyClass0
接着查看第 7 行 proxyClassCache.get 方法:
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); } } } }
java.lang.reflect.WeakCache.get
直接从 22 行开始看,入眼就是一个死循环,它的出口在 26 行,当 supplier.get() 不为空时返回它的值,而 supplier 的赋值操作是在第 34-38 行,赋值后就会执行 22 行 supplier.get 方法:
@Override public synchronized V get() { Supplier<V> supplier = valuesMap.get(subKey); if (supplier != this) { return null; } V value = null; try { value = Objects.requireNonNull(valueFactory.apply(key, parameter)); } finally { if (value == null) { valuesMap.remove(subKey, this); } } assert value != null; CacheValue<V> cacheValue = new CacheValue<>(value); if (valuesMap.replace(subKey, this, cacheValue)) { reverseMap.put(cacheValue, Boolean.TRUE); } else { throw new AssertionError("Should not reach here"); } return value; }
java.lang.reflect.WeakCache.Factory.get
这个方法的返回值在第 10 行,它的值为 valueFactory.apply(key, parameter) 的返回值,而此时 valueFactory 是 java.lang.reflect.Proxy.ProxyClassFactory 的实例,查看该实例的 apply 方法:
@Override 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; 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 { return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); } catch (ClassFormatError e) { throw new IllegalArgumentException(e.toString()); } }
java.lang.reflect.Proxy.ProxyClassFactory.apply
从 26-47 行实际上就是在拼接代理类包名,保存在变量 proxyPkg 中,在 50 行拼接出代理类全路径。
此处 52 行的 ProxyGenerator.generateProxyClass 方法才是真正生成代理类字节码文件的地方,将其保存在名为 proxyClassFile 的字节数组中。查看 ProxyGenerator.generateProxyClass 方法:
public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) { ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2); final byte[] var4 = var3.generateClassFile(); if (saveGeneratedFiles) { AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { try { int var1 = var0.lastIndexOf(46); Path var2; if (var1 > 0) { Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar)); Files.createDirectories(var3); var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class"); } else { var2 = Paths.get(var0 + ".class"); } Files.write(var2, var4, new OpenOption[0]); return null; } catch (IOException var4x) { throw new InternalError("I/O exception saving generated file: " + var4x); } } }); } return var4; }
sun.misc.ProxyGenerator.generateProxyClass
在第 3 行通过 generateClassFile 方法创建代理类字节码文件,保存在字节数组。查看 generateClassFile 方法:
private byte[] generateClassFile() { this.addProxyMethod(hashCodeMethod, Object.class); this.addProxyMethod(equalsMethod, Object.class); this.addProxyMethod(toStringMethod, Object.class); Class[] var1 = this.interfaces; int var2 = var1.length; int var3; Class var4; for(var3 = 0; var3 < var2; ++var3) { var4 = var1[var3]; Method[] var5 = var4.getMethods(); int var6 = var5.length; for(int var7 = 0; var7 < var6; ++var7) { Method var8 = var5[var7]; this.addProxyMethod(var8, var4); } } Iterator var11 = this.proxyMethods.values().iterator(); List var12; while(var11.hasNext()) { var12 = (List)var11.next(); checkReturnTypes(var12); } Iterator var15; try { this.methods.add(this.generateConstructor()); var11 = this.proxyMethods.values().iterator(); while(var11.hasNext()) { var12 = (List)var11.next(); var15 = var12.iterator(); while(var15.hasNext()) { ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next(); this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10)); this.methods.add(var16.generateMethod()); } } this.methods.add(this.generateStaticInitializer()); } catch (IOException var10) { throw new InternalError("unexpected I/O Exception", var10); } if (this.methods.size() > 65535) { throw new IllegalArgumentException("method limit exceeded"); } else if (this.fields.size() > 65535) { throw new IllegalArgumentException("field limit exceeded"); } else { this.cp.getClass(dotToSlash(this.className)); this.cp.getClass("java/lang/reflect/Proxy"); var1 = this.interfaces; var2 = var1.length; for(var3 = 0; var3 < var2; ++var3) { var4 = var1[var3]; this.cp.getClass(dotToSlash(var4.getName())); } this.cp.setReadOnly(); ByteArrayOutputStream var13 = new ByteArrayOutputStream(); DataOutputStream var14 = new DataOutputStream(var13); try { var14.writeInt(-889275714); var14.writeShort(0); var14.writeShort(49); this.cp.write(var14); var14.writeShort(this.accessFlags); var14.writeShort(this.cp.getClass(dotToSlash(this.className))); var14.writeShort(this.cp.getClass("java/lang/reflect/Proxy")); var14.writeShort(this.interfaces.length); Class[] var17 = this.interfaces; int var18 = var17.length; for(int var19 = 0; var19 < var18; ++var19) { Class var22 = var17[var19]; var14.writeShort(this.cp.getClass(dotToSlash(var22.getName()))); } var14.writeShort(this.fields.size()); var15 = this.fields.iterator(); while(var15.hasNext()) { ProxyGenerator.FieldInfo var20 = (ProxyGenerator.FieldInfo)var15.next(); var20.write(var14); } var14.writeShort(this.methods.size()); var15 = this.methods.iterator(); while(var15.hasNext()) { ProxyGenerator.MethodInfo var21 = (ProxyGenerator.MethodInfo)var15.next(); var21.write(var14); } var14.writeShort(0); return var13.toByteArray(); } catch (IOException var9) { throw new InternalError("unexpected I/O Exception", var9); } } }
sun.misc.ProxyGenerator.generateClassFile
在起始 2-4 行可以看到,它还帮我们额外的代理了 hashCode、equals、toString 方法。
接着看 sun.misc.ProxyGenerator.generateProxyClass 的第 4 行,条件 saveGeneratedFiles 是一个布尔值,用于指定是否执行下面代码块的保存 class 文件到硬盘的功能,默认是 false。而 saveGeneratedFiles 的值实际上是取自
private static final boolean saveGeneratedFiles = (Boolean)AccessController.doPrivileged(new GetBooleanAction("sun.misc.ProxyGenerator.saveGeneratedFiles"));
所以我们只要指定了它为 true,它就会帮我们保存字节码文件。
在 src 根目录下运行以下代码:
import com.zze.dao.impl.Waiter; import com.zze.service.IWaiter; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class Test { public static void main(String[] args) { System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); IWaiter waiter = new Waiter(); Class<?>[] interfaces = Waiter.class.getInterfaces(); IWaiter waiterProxy = (IWaiter) Proxy.newProxyInstance(Waiter.class.getClassLoader(), interfaces, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object obj = null; if (method.getName().equalsIgnoreCase("service")) { System.out.println("服务之前"); obj = method.invoke(waiter, args); System.out.println("服务之后"); } return obj; } }); waiterProxy.service(); } }
Test
接着在项目根目录下就会生成如下文件:
package com.sun.proxy; import com.zze.service.IWaiter; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; public final class $Proxy0 extends Proxy implements IWaiter { private static Method m1; private static Method m2; private static Method m3; private static Method m0; public $Proxy0(InvocationHandler var1) throws { super(var1); } public final boolean equals(Object var1) throws { try { return (Boolean)super.h.invoke(this, m1, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final String toString() throws { try { return (String)super.h.invoke(this, m2, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final void service() throws { try { super.h.invoke(this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final int hashCode() throws { try { return (Integer)super.h.invoke(this, m0, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m2 = Class.forName("java.lang.Object").getMethod("toString"); m3 = Class.forName("com.zze.service.IWaiter").getMethod("service"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } }
com.sun.proxy.$Proxy0
我们最后使用的代理对象就是 com.sun.proxy.$Proxy0 类的实例。
cglib动态代理
简介
CGLIB(Code Generation Library) 是一个开源项目!它是一个强大的,高性能,高质量的 Code 生成类库,它可以在运行期扩展 Java 类与实现 Java 接口。Hibernate 支持它来实现 PO(Persistent Object 持久化对象) 字节码的动态生成。
使用
1、引入 cglib 支持 jar,点击下载。
2、编写被代理类:
package com.zze.service; public class Waiter { public void service() { System.out.println("正在服务"); } }
com.zze.service.Waiter
3、使用 CGLIB 提供的动态代理:
@Test public void test() {// 创建核心类对象 Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(Waiter.class); enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { // 判断方法是否是 save if ("service".equals(method.getName())) { // 增强,权限校验 System.out.println("权限校验..."); } return methodProxy.invokeSuper(o, args); } }); Waiter customerDaoProxy = (Waiter) enhancer.create(); customerDaoProxy.service(); }
java之动态代理的更多相关文章
- java的动态代理机制详解
在学习Spring的时候,我们知道Spring主要有两大思想,一个是IoC,另一个就是AOP,对于IoC,依赖注入就不用多说了,而对于Spring的核心AOP来说,我们不但要知道怎么通过AOP来满足的 ...
- java中动态代理实现机制
前言: 代理模式是常用的java设计模式,它的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等.代理类与委托类之间通常会存在关联关系 ...
- Java特性-动态代理
代理在开发中无处不在: 我们完成一个接口开发A,接口下有很多个实现类,这些类有些共同要处理的部分,比如每一个类都定义了接口A中的方法getXX(String name).我现在想把每次调用某个实现类的 ...
- java --- 设计模式 --- 动态代理
Java设计模式——动态代理 java提供了动态代理的对象,本文主要探究它的实现, 动态代理是AOP(面向切面编程, Aspect Oriented Programming)的基础实现方式, 动态代理 ...
- java的动态代理机制
前几天看到java的动态代理机制,不知道是啥玩意,然后看了看.死活不知道 invoke(Object proxy, Method m, Object[] args)种的proxy是个什么东西,放在这里 ...
- java中动态代理
一.在java中怎样实现动态代理 1.我们要有一个接口,还要有一个接口的实现类,而这个实现类呢就是我们要代理的对象 接口: package org.dynamicproxy.test; public ...
- Java的动态代理机制详解(转)
在学习Spring的时候,我们知道Spring主要有两大思想,一个是IoC,另一个就是AOP,对于IoC,依赖注入就不用多说了,而对于Spring的核心AOP来说,我们不但要知道怎么通过AOP来满足的 ...
- (转)java的动态代理机制详解
原文出自:http://www.cnblogs.com/xiaoluo501395377/p/3383130.html 在学习Spring的时候,我们知道Spring主要有两大思想,一个是IoC,另一 ...
- [转载] java的动态代理机制详解
转载自http://www.cnblogs.com/xiaoluo501395377/p/3383130.html 代理模式 代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代 ...
- 【译】11. Java反射——动态代理
原文地址:http://tutorials.jenkov.com/java-reflection/dynamic-proxies.html 博主最近比较忙,争取每周翻译四篇.等不急的请移步原文网页. ...
随机推荐
- np.corrcoef()方法计算数据皮尔逊积矩相关系数(Pearson's r)
上一篇通过公式自己写了一个计算两组数据的皮尔逊积矩相关系数(Pearson's r)的方法,但np已经提供了一个用于计算皮尔逊积矩相关系数(Pearson's r)的方法 np.corrcoef() ...
- nginx实现限速
项目中有一个需求,需要限制每个容器的网速,避免某些容器占用太多资源,导致其他容器无法使用,但是docker对于网速的限制支持的有点弱,由于容器中的所有进程和APP的交互都是通过nginx的,所以就想到 ...
- python线程池(threadpool)模块使用笔记 .python 线程池使用推荐
一.安装与简介 pip install threadpool pool = ThreadPool(poolsize) requests = makeRequests(some_callable, li ...
- 第四百零六节,自定义用户表类来继承Django的用户表类,
第四百零六节,自定义用户表类来继承Django的用户表类, models.py from django.db import models # Create your models here. from ...
- 某公司面试java试题之【一】,看看吧,说不定就是你将要做的题
- Linux 依据关键字查找正在运行的进程
ps aux |grep tm1s
- linux 使用不安全的sprintf函数,存储字符越界导致程序莫名崩溃问题
linux c++编程 问题背景: 在处理一个公共模块的代码中,其中有以下代码片段 //代码片段-组合一组字符串并存放到szSignKey数组中 ] = {}; sprintf(szSignKey, ...
- Windows下Codeblocks调试Cocos2d-x项目体验(一次失败的体验)
很久之前的一篇文章有介绍过在Ubuntu下安装Cocos2d-x3.11并使用Codeblock调试Cocos2d-x程序:http://www.cnblogs.com/moonlightpoet/p ...
- Qt编写自定义控件10-云台仪表盘
前言 做过安防视频监控的同学都清楚,在视频监控系统软件上都可以看到一个云台控制区域,可以对球机进行下下左右等八个方位的运动控制,还可以进行复位,一般都是美工作图好,然后贴图的形式加入到软件中,好处是程 ...
- javascript--面试题
(1)javaScript怎么清空数组? 如 var arrayList = ['a','b','c','d','e','f']; 怎么清空 arrayList 方法1:直接改变arrayList所指 ...