1.引言
在开发中过程中,我们经常性的会对一个对象的方法进行增强,当然如果要增强一个对象的方法,我们会有很多种方式文章源自JAVA秀-https://www.javaxiu.com/507.html
那么动态代理肯定是其中的一种方式,但是很多情况下,我们只会去使用动态代理,对动态代理的实现原理并不是很清晰,那这里我们就可以来聊一聊JDK动态代理的底层原理文章源自JAVA秀-https://www.javaxiu.com/507.html
2.需求
首先我们先有一个接口叫UserService,接口中有一个save的保存方法文章源自JAVA秀-https://www.javaxiu.com/507.html
public interface UserService { public abstract void save(); }
接着我们给这个接口一个实现类叫UserServiceImpl实现save的保存方法文章源自JAVA秀-https://www.javaxiu.com/507.html
public class UserServiceImpl implements UserService { public void save() { System.out.println("保存方法...."); } }
3.实现代码
那么接下来我的需求就是要对这个UserServiceImpl里面的目标方法save方法进行增强,如何增强呢,我们先使用jdk提供的动态代理来进行编写增强,代码实现下:文章源自JAVA秀-https://www.javaxiu.com/507.html
public class Demo{ @Test public void test1() { final UserServiceImpl userServiceImpl=new UserServiceImpl(); UserService userProxy= (UserServive)Proxy.newProxyInstance(userServiceImpl.getClass().getClassLoader(), userServiceImpl.getClass().getInterfaces(), new InvocationHandler()new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) { System.out.println("保存方法之前增强..."); return method.invoke(userServiceImpl, args); System.out.println("保存方法之后增强..."); } }); userProxy.save(); } }
4.源码解析
执行以上代码会发现,对于UserServiceImpl对象的save保存方法确实进行了前后的增强, 用起来是比较简单,但是如果能知道它背后做了些什么手脚,那就更好不过了。首先来看一下JDK是怎样生成代理对象的。既然生成代理对象是用的Proxy类的静态方newProxyInstance,那么我们就去它的源码里看一下它到底都做了些什么?文章源自JAVA秀-https://www.javaxiu.com/507.html
// loader:类加载器 // interfaces:目标对象实现的接口 //h:InvocationHandler的实现类 public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)throws IllegalArgumentException{ // 这里如果没有传递InvocationHandler的实现类就抛空指针 if (h == null) { throw new NullPointerException(); } // 这里会返回代理对象字节码文件 Class cl = getProxyClass(loader, interfaces); // 调用代理对象的构造方法 Constructor cons = cl.getConstructor(constructorParams); // 生成代理类的实例并把InvocationHandler的实例传给它的构造方法 return (Object) cons.newInstance(new Object[] { h });
Class cl = getProxyClass(loader, interfaces); 这句代码会返回代理对象字节码文件文章源自JAVA秀-https://www.javaxiu.com/507.html
我们可以看一看它是如何返回的文章源自JAVA秀-https://www.javaxiu.com/507.html
public static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) throws IllegalArgumentException { // 如果目标类实现的接口数大于65535个则抛出异常 if (interfaces.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); } // 如果没有 声明代理对象所代表的Class类型 Class proxyClass = null; // 申明一个数组用来表示实现的接口数 String[] interfaceNames = new String[interfaces.length]; // 声明一个集合 Set interfaceSet = new HashSet(); // 遍历目标类所实现的接口 for (int i = 0; i < interfaces.length; i++) { // 拿到目标类实现的接口的名称 String interfaceName = interfaces[i].getName(); Class interfaceClass = null; try { // 加载目标类实现的接口到内存中 interfaceClass = Class.forName(interfaceName, false, loader); } catch (ClassNotFoundException e) { } if (interfaceClass != interfaces[i]) { throw new IllegalArgumentException( interfaces[i] + " is not visible from class loader"); } // 把目标类实现的接口代表的Class对象放到Set中 interfaceSet.add(interfaceClass); interfaceNames[i] = interfaceName; } // 把目标类实现的接口名称作为缓存(Map)中的key Object key = Arrays.asList(interfaceNames); Map cache; synchronized (loaderToCache) { // 获取map cache = (Map) loaderToCache.get(loader); if (cache == null) { // 如果获取不到,则新建地个HashMap实例 cache = new HashMap(); // 把HashMap实例和当前加载器放到缓存中 loaderToCache.put(loader, cache); } } synchronized (cache) { do { // 根据接口的名称从map中获取对象 Object value = cache.get(key); if (value instanceof Reference) { proxyClass = (Class) ((Reference) value).get(); } if (proxyClass != null) { // 如果代理对象的Class实例已经存在,则直接返回 return proxyClass; } else if (value == pendingGenerationMarker) { try { cache.wait(); } catch (InterruptedException e) { } continue; } else { cache.put(key, pendingGenerationMarker); break; } } while (true); } try { byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces); try { // 创建代理的Class对象 proxyClass = defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); } catch (ClassFormatError e) { throw new IllegalArgumentException(e.toString()); } } proxyClasses.put(proxyClass, null); } return proxyClass; }
5.总结
通过以上的源码分析,我们可以看到newProxyInstance的底层首先会判断有没有接收到InvocationHandler的实现类,如果没有就会抛空指针,如果有就调用Class cl = getProxyClass(loader, interfaces)方法返回代理对象字节码文件,在这个方法中对代理类的接口做了个数限制并且还维护了一个代理类的缓存集合,如果有缓存就直接返回,否则就通过ProxyClassFactory去创建,ProxyClassFactory是Proxy的一个静态内部类,在这个静态内部类中会调用一个ProxyGenerator.generateProxyClass的方法,这个方法的内部生成了class文件,然后返回在通过cl.getConstructor(constructorParams)方法反射构造器生成了代理对象,到这里我们就能看到代理对象是如何产生出来的,希望以上的代码分析能够帮助我们学习动态代理…文章源自JAVA秀-https://www.javaxiu.com/507.html

评论