摘要
相比于静态代理,动态代理避免了开发人员编写各个繁锁的静态代理类,只需简单地指定一组接口及目标类对象就能动态的获得代理对象。
代理模式
使用代理模式必须要让代理类和目标类实现相同的接口,客户端通过代理类来调用目标方法,代理类会将所有的方法调用分派到目标对象上反射执行,还可以在分派过程中添加"前置通知"和后置处理(如在调用目标方法前校验权限,在调用完目标方法后打印日志等)等功能。
使用动态代理的五大步骤
1.通过实现InvocationHandler接口来自定义自己的InvocationHandler;
2.通过Proxy.getProxyClass获得动态代理类
3.通过反射机制获得代理类的构造方法,方法签名为getConstructor(InvocationHandler.class)
4.通过构造函数获得代理对象并将自定义的InvocationHandler实例对象传为参数传入
5.通过代理对象调用目标方法
动态代理的使用
例1(方式一)
public class MyProxy { public interface IHello{ void sayHello(); } static class Hello implements IHello{ public void sayHello() { System.out.println("Hello world!!"); } } //自定义InvocationHandler static class HWInvocationHandler implements InvocationHandler{ //目标对象 private Object target; public HWInvocationHandler(Object target){ this.target = target; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("------插入前置通知代码-------------"); //执行相应的目标方法 Object rs = method.invoke(target,args); System.out.println("------插入后置处理代码-------------"); return rs; } } public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetExc eption, InstantiationException { //生成$Proxy0的class文件 System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); //获取动态代理类 Class proxyClazz = Proxy.getProxyClass(IHello.class.getClassLoader(),IHello.class); //获得代理类的构造函数,并传入参数类型InvocationHandler.class Constructor constructor = proxyClazz.getConstructor(InvocationHandler.class); //通过构造函数来创建动态代理对象,将自定义的InvocationHandler实例传入 IHello iHello = (IHello) constructor.newInstance(new HWInvocationHandler(new Hello())); //通过代理对象调用目标方法 iHello.sayHello(); } }</div>
输出:
------插入前置通知代码-------------
Hello world!!
------插入后置处理代码-------------
Proxy类中还有个将2~4步骤封装好的简便方法来创建动态代理对象,其方法签名为:newProxyInstance(ClassLoader loader,Class<?>[] instance, InvocationHandler h),如下例:
(方式二)
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { //生成$Proxy0的class文件 System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); IHello ihello = (IHello) Proxy.newProxyInstance(IHello.class.getClassLoader(), //加载接口的类加载器 new Class[]{IHello.class}, //一组接口 new HWInvocationHandler(new Hello())); //自定义的InvocationHandler ihello.sayHello(); }</div>
输出结果一样.
下面以newProxyInstance方法为切入点来剖析代理类的生成及代理方法的调用
(为了篇幅整洁去掉了次要的代码)
public static Object newProxyInstance(ClassLoader loader, Class<!--?-->[] interfaces, InvocationHandler h) throws IllegalArgumentException { if (h == null) { //如果h为空直接抛出异常,所以InvocationHandler实例对象是必须的 throw new NullPointerException(); } //对象的拷贝,暂不知道这里拷贝下的意义是啥? final Class<!--?-->[] intfs = interfaces.clone(); //一些安全的权限检查 final SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } //产生代理类 Class<!--?--> cl = getProxyClass0(loader, intfs); //获取代理类的构造函数对象 //参数constructorParames为常量值:private static final Class<!--?-->[] constructorParams = { InvocationHandler.class }; final Constructor<!--?--> cons = cl.getConstructor(constructorParames); final InvocationHandler ih = h; //根据代理类的构造函数对象来创建代理类对象 return newInstance(cons, ih); }</div>
这段代码就是对代理类对象的创建,就是对例1中34~38行封装,其中getProxyClass0就是生成代理类的方法
getProxyClass0方法剖析
private static Class<!--?--> getProxyClass0(ClassLoader loader, Class<!--?-->... interfaces) { //接口数不得超过65535个 if (interfaces.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); } //代理类缓存,如果缓存中有代理类了直接返回,否则将由ProxyClassFactory创建代理类 return proxyClassCache.get(loader, interfaces); }</div>
看看ProxyClassFactory是怎样生成代理类的?
private static final class ProxyClassFactory implements BiFunction<classloader, class<?="">[], Class<!--?-->> { //统一代理类的前缀名都以$Proxy开关 private static final String proxyClassNamePrefix = "$Proxy"; //使用唯一的编号给作为代理类名的一部分,如$Proxy0,$Proxy1等 private static final AtomicLong nextUniqueNumber = new AtomicLong(); @Override public Class<!--?--> apply(ClassLoader loader, Class<!--?-->[] interfaces) { Map<class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length); for (Class<!--?--> intf : interfaces) { //验证指定的类加载器(loader)加载接口所得到的Class对象(interfaceClass)是否与intf对象相同 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"); } //验证该Class对象是不是接口 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; /*验证你传入的接口中是否有非public接口,只要有一个接口是非public的,那么这些接口都必须在同一包中 这里的接口修饰符直接影响到System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true")所生成 的代理类的路径,往下看!!*/ for (Class<!--?--> intf : interfaces) { int flags = intf.getModifiers(); if (!Modifier.isPublic(flags)) { String name = intf.getName(); int n =