给某一个对象提供一个代理,并由代理对象来控制对真实对象的访问。代理模式是一种结构型设计模式。
根据代理类的创建时机和创建方式的不同,可以将其分为静态代理和动态代理两种形式:
在程序运行前即编译时就存在的代理类是为静态代理
在程序运行时根据需要动态创建代理类及其实例为动态代理
静态代理,必需手动创建N个代理类,这显然让人相当不爽。
动态代理则可以简单地为各个主题类分别生成代理类,共享“预处理,后处理”功能,这样可以大大减小程序规模,这也是动态代理的一大亮点。
在动态代理中,代理类是在运行时期生成的。因此,相比静态代理,动态代理可以很方便地对委托类的相关方法进行统一增强处理,如添加方法调用次数、添加日志功能等等
// 方法 1: 该方法用于获取指定代理对象所关联的
InvocationHandler static InvocationHandler getInvocationHandler(Object proxy)
// 方法 2:该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象
static Class getProxyClass(ClassLoader loader, Class[] interfaces)
// 方法 3:该方法用于判断指定类是否是一个动态代理类
static boolean isProxyClass(Class cl)
// 方法 4:该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
static Object newProxyInstance(ClassLoader loader, Class[] interfaces,InvocationHandler h)
Object invoke(Object proxy, Method method, Object[] args)
该方法是代理类完整逻辑的集中体现。在被监控行为将要执行时,会被JVM拦截。被监控行为和行为实现方法会被作为参数输送invoke,通常通过反射完成对具体角色业务逻辑的调用,并对其进行增强。
第一个参数既是代理类实例。
第二个参数是被调用的方法对象。
第三个方法是调用参数。
public void proxyForPeople(){
User user = new User();
People userProxy = (People) Proxy.newProxyInstance(user.getClass().getClassLoader(),
user.getClass().getInterfaces(), (proxy, methods, args) -> {
if(methods.getName().equals("toExcute")){
System.out.println("preparing......");
return methods.invoke(user, args);
}
if(methods.getName().equals("getStatus")){
System.out.println("status types [coding, rest, learning]");
return methods.invoke(user, args);
}
return null;
});
userProxy.toExcute();
userProxy.getStatus();
}
其实大概就是把接口复制出来,通过这些接口和类加载器,拿到这个代理类cl。然后通过反射的技术复制拿到代理类的构造函数(这部分代码在Class类中的getConstructor0方法),最后通过这个构造函数new个一对象出来,同时用InvocationHandler绑定这个对象。
为了让生成的代理类与目标对象(真实主题角色)保持一致性,从现在开始将介绍以下两种最常见的方式:
通过实现接口的方式 -> JDK动态代理
通过继承类的方式 -> CGLIB动态代理
JDK代理需要一组需要实现的接口,然后通过这些接口获取构造方法,用这个构造方法和InvocationHandler,实例化一个对象出来。所以JDK的方式是基于接口的。
而CGLIB的代理是基于类的,用目标类生成一个子类,子类重写父类的方法,从而达到动态代理的效果。
一般情况下使用jdk动态代理,如果目标对象的代理至少实现了一个接口,那么就用JDK动态代理,所有由目标对象实现的接口将全部都被代理。如果目标对象没有实现任何接口,那么就用CGLIB代理。
比较灵活,可以在运行的时候才切入改变类的方法,而不需要预先定义它。
动态代理一般我们比较少去手写,但我们用得其实非常多。在Spring项目中用的注解,例如依赖注入的@Bean、@Autowired,事务注解@Transactional等都有用到,换言之就是Srping的AOP(切面编程)。
这种场景的使用是动态代理最佳的落地点,可以非常灵活地在某个类,某个方法,某个代码点上切入我们想要的内容,就是动态代理其中的内容。
参考:
https://www.jianshu.com/p/95970b089360
https://www.jianshu.com/p/4dcc74b63f1c