博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android进阶必学:自定义注解之动态代理
阅读量:4294 次
发布时间:2019-05-27

本文共 6389 字,大约阅读时间需要 21 分钟。

自定义注解是Android进阶的必学知识,从现在起我讲为大家带来四篇文章,让大家彻底学会自定义注解

静态代理大家都明白,就是相当于包装了一次,在包装这一次的时候可以加一些业务逻辑。同样静态代理的特点是一个接口对应一个代理类,当然委托类可以多个。

静态代理

/**  * 定义Demo接口  */  public interface Demo {
public void save(); }
/**  * DemoImpl实现Demo接口并覆写save()方法  * 真实主题,执行具体业务  */  public class DemoImpl implements Demo {
public void save() { System.out.println("调用save()方法"); } }
/**  * DemoImplProxy 也实现了Demo接口,并覆写了save()方法,增加了自己的业务   * 代理主题,负责其他业务的处理  */  public class DemoImplProxy implements Demo {
Demo demoImpl = new DemoImpl(); public void save() { System.out.println("开始记录日志"); demoImpl.save(); System.out.println("开始结束日志"); } }

静态代理的特点就是:一个萝卜一个坑,但是这样会产生大量的代理类

动态代理

动态代理的特点

动态代理是指在运行时动态生成代理类。即,代理类的字节码将在运行时生成并载入当前代理的 ClassLoader。

与静态处理类相比,动态类有诸多好处。

- 首先,不需要为真实主题写一个形式上完全一样的封装类,假如主题接口中的方法很多,为每一个接口写一个代理方法也很麻烦。如果接口有变动,则真实主题和代理类都要修改,不利于系统维护;
- 其次,使用一些动态代理的生成方法甚至可以在运行时制定代理类的执行逻辑,从而大大提升系统的灵活性。

实现步骤

- 新建代理类和委托类共同的接口以及委托类
- 实现InvocationHandler接口,这是负责连接代理类和委托类的中间类必须实现的接口
- 通过Proxy类新建代理类对象

第一步新建代理类和委托类共同的接口以及委托类

public interface IOperate {
public void operateMethod1(); public void operateMethod2(); public void operateMethod3();}
public class OperateImpl implements IOperate {
@Override public void operateMethod1() { System.out.println("Invoke operateMethod1"); } @Override public void operateMethod2() { System.out.println("Invoke operateMethod2"); } @Override public void operateMethod3() { System.out.println("Invoke operateMethod3"); }}

第二步:实现InvocationHandler接口

这一步提供了代理类应该怎么进行处理,重点在于处理

定义

/** * Object proxy:被代理的对象 * Method method:要调用的方法 * Object[] args:方法调用时所需要参数 */  public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }

实现

public class InvocationHandlerImpl implements java.lang.reflect.InvocationHandler {
private Object target; public InvocationHandlerImpl(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { long start = System.currentTimeMillis(); Object obj = method.invoke(target, args); System.out.println(method.getName() + " cost time is:" + (System.currentTimeMillis() - start)); return obj; }}

第三步:通过Proxy类新建代理类对象

接口

/** *CLassLoader loader:类的加载器 *Class
interfaces:得到全部的接口 *InvocationHandler h:得到InvocationHandler接口的子类的实例 */ public static Object newProxyInstance(ClassLoader loader, Class
[] interfaces, InvocationHandler h) throws IllegalArgumentException

实现

public class ExampleTest {
@Test public void testDumpClassInfo() throws Exception { InvocationHandlerImpl invocationHandler = new InvocationHandlerImpl(new OperateImpl()); IOperate iOperate = (IOperate) Proxy.newProxyInstance(IOperate.class.getClassLoader(),new Class[] {IOperate.class},invocationHandler); iOperate.operateMethod1(); iOperate.operateMethod2(); iOperate.operateMethod3(); }}

总结动态代理的使用

  1. 包装需要被代理类的实现
InvocationHandler handler = new InvocationHandlerImpl(..);
  1. 通过Proxy类生成代理类
Class clazz = Proxy.getProxyClass(classLoader, new Class[] { Interface.class, ... });

原理

Proxy.newProxyInstance(...)

public static Object newProxyInstance(ClassLoader loader, Class
[] interfaces, InvocationHandler invocationHandler) throws IllegalArgumentException { if (invocationHandler == null) { throw new NullPointerException("invocationHandler == null"); } Exception cause; try { return getProxyClass(loader, interfaces)//得到代理的Class对象 .getConstructor(InvocationHandler.class)//最终调用Class.getDeclaredConstructorInternal() .newInstance(invocationHandler);//生成实例对象并返回 } catch (Exception e) { cause = e; } AssertionError error = new AssertionError(); error.initCause(cause); throw error;}

传递进来的是:

- 类加载器
- 委托类实现的所有接口

public static Class
getProxyClass(ClassLoader loader, Class
... interfaces) throws IllegalArgumentException { if (loader == null) { loader = ClassLoader.getSystemClassLoader(); } ... //给委托类实现的接口排序添加到interfaceList中 final List
> interfaceList = new ArrayList
>(interfaces.length); Collections.addAll(interfaceList, interfaces); //去重 final Set
> interfaceSet = new HashSet
>(interfaceList); if (interfaceSet.contains(null)) { throw new NullPointerException("interface list contains null: " + interfaceList); } //有重复则抛出异常 if (interfaceSet.size() != interfaces.length) { throw new IllegalArgumentException("duplicate interface in list: " + interfaceList); } //如果在类加载器中有这些代理缓存,则直接返回,注意的是多个接口对应一个类 synchronized (loader.proxyCache) { Class
proxy = loader.proxyCache.get(interfaceList); if (proxy != null) { return proxy; } } String commonPackageName = null; //拿到每一个接口Class对象 for (Class
c : interfaces) { ... if (!Modifier.isPublic(c.getModifiers())) { String packageName = c.getPackageName$(); if (packageName == null) { packageName = ""; } //这些接口必须在同一个包下 if (commonPackageName != null && !commonPackageName.equals(packageName)) { throw new IllegalArgumentException( "non-public interfaces must be in the same package"); } commonPackageName = packageName; } } //得到接口的Methods集合 List
methods = getMethods(interfaces); Collections.sort(methods, ORDER_BY_SIGNATURE_AND_SUBTYPE); validateReturnTypes(methods); List
[]> exceptions = deduplicateAndGetExceptions(methods); Method[] methodsArray = methods.toArray(new Method[methods.size()]); Class
[][] exceptionsArray = exceptions.toArray(new Class
[exceptions.size()][]); String baseName = commonPackageName != null && !commonPackageName.isEmpty() ? commonPackageName + ".$Proxy" : "$Proxy"; Class
result; synchronized (loader.proxyCache) { result = loader.proxyCache.get(interfaceList); if (result == null) { String name = baseName + nextClassNameIndex++; //得到代理对象 result = generateProxy(name, interfaces, loader, methodsArray, exceptionsArray); loader.proxyCache.put(interfaceList, result);//添加到缓存 } } return result;}

转载地址:http://mpuws.baihongyu.com/

你可能感兴趣的文章
sdc时序约束
查看>>
Xilinx Jtag Access/svf文件/BSCANE2
查看>>
NoC片上网络
查看>>
开源SoC整理
查看>>
【2020-3-21】Mac安装Homebrew慢,解决办法
查看>>
influxdb 命令行输出时间为 yyyy-MM-dd HH:mm:ss(年月日时分秒)的方法
查看>>
已知子网掩码,确定ip地址范围
查看>>
判断时间或者数字是否连续
查看>>
docker-daemon.json各配置详解
查看>>
Mac 下docker路径 /var/lib/docker不存在问题
查看>>
Docker(一)使用阿里云容器镜像服务
查看>>
Docker(二) 基础命令
查看>>
Docker(三) 构建镜像
查看>>
Spring 全家桶注解一览
查看>>
JDK1.8-Stream API使用
查看>>
cant connect to local MySQL server through socket /tmp/mysql.sock (2)
查看>>
vue中的状态管理 vuex store
查看>>
Maven之阿里云镜像仓库配置
查看>>
Maven:mirror和repository 区别
查看>>
微服务网关 Spring Cloud Gateway
查看>>