对于java代理和反射之间的理解
首先需要明白概念:什么为反射,反射为一种机制,我们可以动态的获取类的所有参数
反射可以用来做什么?现在最常用的设计模式之一的代理模式底层就是通过代理来实现的。
代理:用一个类来代替另一个类执行操作。
静态代理和动态代理的区别就是一个是因为有代理类,在编译期就已经确定了这个类为谁的代理类;
动态代理则是可以随着程序员的选择手动选择代理某个类。
首先先讲一下静态代理。为了符合面向接口编程,创建一个接口来抽象规定用户的某个行为
public interface User { void eat(); }
现在有两个普通的User类实现这个接口。
public class Rose implements User { @Override public void eat() { System.out.println("我是Rose,我在吃早餐"); } }
public class Tom implements User { @Override public void eat() { System.out.println("我是tom,我在吃苹果"); } }
如果想执行这两个用户的操作那肯定就是new Tom().eat之类的,如果现在有需求,要在执行eat方法前先洗手,静态代理的做法如下
先创建一个类来代理tom
public class TomProxy implements User { private Tom tom; public TomProxy(Tom tom) { this.tom = tom; } @Override public void eat() { //执行前置加强 System.out.println("先洗手"); //执行委托类的方法 tom.eat(); //进行后置加强 System.out.println("吃饱了呀"); } }
创建代理类的时候传入需要代理类的参数,这样需要Tom吃饭的时候直接创建代理类对象来替他执行
new TomProxy(new Tom()).eat;这样会先执行前置加强再执行代理类的方法,最后执行后置加强
因为手动创建了一个类,其实编译期在编译期就已经知道了哪些类是代理类,这就是静态代理。
动态代理:
动态代理也叫jdk代理,我们将User接口不变,Rose和Tom类也不变,新建一个类LoadHandler,对外提供一个构造方法,参数为需要代理的类实例
public class LoadHandler { public Object newProxyInstance(Object targetObject) { //代理的是接口,不是实现类 /** @param loader :the class loader to define the proxy class * @param interfaces :the list of interfaces for the proxy class * to implement * @param h :the invocation handler to dispatch method invocations to */ return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), new InvocationHandler() { @Override /** * @param proxy: the proxy instance that the method was invoked on * * @param method: the {@code Method} instance corresponding to * the interface method invoked on the proxy instance. The declaring * class of the {@code Method} object will be the interface that * the method was declared in, which may be a superinterface of the * proxy interface that the proxy class inherits the method through. * * @param args: an array of objects containing the values of the * arguments passed in the method invocation on the proxy instance, * or {@code null} if interface method takes no arguments. * Arguments of primitive types are wrapped in instances of the * appropriate primitive wrapper class, such as * {@code java.lang.Integer} or {@code java.lang.Boolean}. */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("先洗手"); //通过代理,调用动态代理对象要执行的方法 //如果有返回值,这里Object为空 Object o=method.invoke(targetObject, args); //这里写方法执行之后的操作 System.out.println("吃饱了"); //没有返回值,直接return return null; } }); } }
这个动态代理的用法我们肯定知道了,创建一个LoadHandler,传入需要代理的类的实例,返回对象直接调用方法就行了
new LoadHandler().newProxyInstance(new TomProxy()).eat();即可
现在我们来分析里面的参数,方法名字可以看出为创建一个代理实例并返回
我们现在分析一下参数
loader :the class loader to define the proxy class
loader参数定义的类的类加载器,可以通过对象.getclass().getClassLoader()来获得
interfaces :the list of interfaces for the proxy class to implement
interfaces参数为代理类要实现的接口列表
h:invocations to the specified invocation handler.
h参数为调用处理程序,将方法调用分派到指定的调用处理程序。
怎么理解?就是使用我们自己写的调用处理程序来代替方法执行,之前的方法会分派到我们写的调用处理程序里
new InvocationHandler(); 这是一个接口,需要重写invoke方法,有三个参数
@param proxy :the proxy instance that the method was invoked on
proxy:调用方法的代理实例 在这里就是targetObject
@param method:the {@code Method} instance corresponding to
the interface method invoked on the proxy instance.
与代理实例上调用的接口方法相对应的实例,在这里就是eat了。
@param args :an array of objects containing the values of the
* arguments passed in the method invocation on the proxy instance,
* or {@code null} if interface method takes no arguments.
* Arguments of primitive types are wrapped in instances of the
* appropriate primitive wrapper class, such as
* {@code java.lang.Integer} or {@code java.lang.Boolean}.
一个对象数组,包含代理实例的方法调用中传递的参数值,如果接口方法不接受任何参数,就为null
而且原始类型的参数要包装,比如Integer。什么意思?就是执行eat方法需要调用的参数,没参数就为Null
java.lang.reflect.Method里对这个invoke的解释如下
Invokes the underlying method represented by this {@code Method}
* object, on the specified object with the specified parameters.
* Individual parameters are automatically unwrapped to match
* primitive formal parameters, and both primitive and reference
* parameters are subject to method invocation conversions as
* necessary.
在具有指定参数的指定对象上调用此method对象表示的基础方法,各个参数将自动解包以匹配原始形式参数
这里method其实就等于eat方法,method.invoke(对象,参数)可以理解为eat.invoke(对象,参数),这里为 什么对象执行eat方法,有什么参数。
附上源码解读
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { //判断h对象是否为空,可以理解为有没有指定的处理程序 Objects.requireNonNull(h); //重点在这,动态代理是根据反射来创建的接口实例 final Class<?>[] intfs = interfaces.clone(); //获取程序的安全管理器 final SecurityManager sm = System.getSecurityManager(); if (sm != null) { //检查创建代理类所需的权限 checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } //生成指定的代理类,是根据接口来生成的哦, 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; } }); } //创建实例返回 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); } }