代理模式
代理模式(Proxy Pattern)是设计模式中的一种,它为其他对象提供一个代理以控制对这个对象的访问。代理对象在客户端和目标对象之间起到中介的作用,并且可以通过代理对象对目标对象进行额外的操作或处理,如访问控制、日志记录、事务管理等
静态代理
在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。静态代理的缺点是,如果我们的接口一旦增加新的方法,除了所有实现类需要增加此方法外,所有代理类也需要增加此方法。这样就增加了代码维护的复杂度
动态代理
在程序运行时,动态地创建代理类。动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java反射机制可以生成任意类型的动态代理类
JDK动态代理
JDK动态代理基于接口实现,要求目标对象要有实现的接口才能代理其方法
代码实现
目标类接口
/**
* 目标类接口
*/
public interface Persion {
/**
* 被代理方法1
*/
void talk();
/**
* 被代理方法2
*/
void sing();
}
目标类
/**
* 目标类
*/
public class Student implements Persion{
@Override
public void talk() {
System.out.println("俺是学生");
}
@Override
public void sing() {
System.out.println("我唱七里香");
}
}
代理逻辑handler
public class ProxyHandler implements InvocationHandler {
/**
* 目标对象
*/
private final Object target;
public ProxyHandler(Object target){
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("前置操作。。");
Object invoke = method.invoke(target, args);
System.out.println("后置操作。。");
return invoke;
}
}
创建代理对象并执行代理方法
public static void main(String[] args) {
//目标对象
Persion target = new Student();
//代理对象
ProxyHandler proxyHandler = new ProxyHandler(target);
//创建代理类对象
//JDK会通过传入的参数信息动态地在内存中创建和.class文件等同的字节码
//然后会根据相应的字节码转换成对应的class
//最后创建代理类实例
Persion proxy = (Persion) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), proxyHandler);
proxy.talk();
System.out.println("------------------------");
proxy.sing();
}
执行结果,实现了对Student类的方法增强
源码分析
我们查看Proxy.newProxyInstance方法源码
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
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);
}
}
Class<?> cl = getProxyClass0(loader, intfs);
它的作用是查找或生成指定的代理类,getProxyClass0()
方法源码:
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
return proxyClassCache.get(loader, interfaces);
}
直接从 proxyClassCache
中获取缓存的代理类副本,若没有将通过 ProxyClassFactory
创建代理类,获取到代理类后,执行 逻辑 final Constructor<?> cons = cl.getConstructor(constructorParams);
获取对应的构造方法,代理类构造函数的参数 constructorParams
是一个class数组
private static final Class<?>[] constructorParams = { InvocationHandler.class }
InvocationHandler
就是我们代理类 ProxyHandler
实现的接口
这个代理类的构造方法是怎么来的呢?
回到上面从缓存
proxyClassCache
的get
方法逻辑,缓存没有则调用subKeyFactory.apply(key,parameter)
生成,这里的key
与我们传入的类加载器相关,parameter
就是我们传入的接口class数subKeyFactory.apply(key, parameter)
根据规则生成一个类路径,为com.sun.proxy.$Proxy0
,后面的0是一个原子自增的数字,创建一个代理类则递增, 最终会调用一个native方法defineClass0
生成class,这就是我们动态生成的class,因此获取带有constructorParams
参数的构造方法应该是在native方法中生成的,这个在运行中生成的代理类class与我们的目标类都实现了相同的接口
通过对 newProxyInstance
方法的源码两行逻辑:Class<?> cl = getProxyClass0(loader, intfs);
final Constructor<?> cons = cl.getConstructor(constructorParams);
我们了解这个运行时生成的代理类的类路径为 com.sun.proxy.$Proxy0
(第一个代理类为0,后面依次递增), 了解带 constructorParams
参数的构造方法来源。constructorParams
参数中有 InvocationHandler
接口的 class
,接下来就是将目标对象的方法逻辑和 ProxyHandler类
的 invoke
一起作为代理类接口的具体实现。第三行核心逻辑 cons.newInstance(new Object[]{h});
使用反射通过有参构造方法创建实例,这里的 h
参数就是我们的 ProxyHandler
实例, 与第二行核心逻辑获取的有参构造一致,它在动态生成类class时已经组装完成。通过反射创建类路径为 com.sun.proxy.$Proxy0
实现了 Persion
接口的 两个方法,有个成员变量为接口的数组,通过有参构造赋值, 两个方法的实现就是调用这个成员变量 InvocationHandler
接口实例的 invoke
方法。这种方式的动态代理是通过Java反射机制实现的,代理类必须基于统一的接口,这就是JDK动态代理。
字节码文件
在main方法开头添加 System.getProperties().setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
方法修改系统变量,可以保存生成的字节码文件
public final class $Proxy0 extends Proxy implements Persion {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m4;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void talk() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void sing() throws {
try {
super.h.invoke(this, m4, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("tao.Persion").getMethod("talk");
m4 = Class.forName("tao.Persion").getMethod("sing");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
简单说明关键内容:
- 代理类继承Proxy类,实现目标类接口(Persion接口),重写equals、hashCode、toString的方法
- 类和方法都被public final 修饰,所以类和方法只能使用,不能继承和重写
- 方法的实现最终会调用到代理调用处理器对象(ProxyHandler类实例)的invoke方法
CGLIB动态代理
代码实现
引入Maven依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
创建一个User类,不实现任何接口
public class User {
public void study(){
System.out.println("执行 study 方法 。。");
}
public void work(){
System.out.println("执行 work 方法 。。");
}
}
创建一个 UserInterceptor类 ,实现MethodInterceptor接口,用于方法的拦截回调
public class UserInterceptor implements MethodInterceptor {
/**
* @param o 表示要进行增强的对象
* @param method 表示拦截的方法
* @param objects 数组表示参数列表,基本数据类型需要传入其包装类型,如int-->Integer、long-Long、double-->Double
* @param methodProxy 表示对方法的代理,invokeSuper方法表示对被代理对象方法的调用
* @return 执行结果
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("前置通知");
// 注意这里是调用 invokeSuper 而不是 invoke,否则死循环,methodProxy.invokesuper执行的是原始类的方法,method.invoke执行的是子类的方法
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("后置通知");
return result;
}
}
创建代理类,执行测试
public class ProxyDemo {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
//设置超类,cglib是通过继承来实现的
enhancer.setSuperclass(User.class);
enhancer.setCallback(new UserInterceptor());
//创建代理类
User user = (User) enhancer.create();
//执行测试
user.study();
System.out.println("-------------------");
user.work();
}
}
打印结果
源码分析
查看 Enhancer
类,这里只保留重点学习的部分
public class Enhancer extends AbstractClassGenerator {
private static final CallbackFilter ALL_ZERO = new CallbackFilter() {
public int accept(Method method) {
return 0;
}
};
private static final Source SOURCE;
private Class[] interfaces;
private Callback[] callbacks;
private Class superclass;
public Enhancer() {
super(SOURCE);
}
}
Enhancer
继承了类 AbstractClassGenerator
,创建 Enhancer
实例,会调用无参构造方法,无参构造方法中使用 super()
调用父类的构造方法 。 SOURCE
是一个静态成员变量,数据结构如下
protected static class Source {
String name;
Map cache = new WeakHashMap();
public Source(String name) {
this.name = name;
}
}
看一下 AbstractClassGenerator
类的构造方法
protected AbstractClassGenerator(AbstractClassGenerator.Source source) {
this.strategy = DefaultGeneratorStrategy.INSTANCE;
this.namingPolicy = DefaultNamingPolicy.INSTANCE;
this.useCache = true;
this.source = source;
}
设置了四个属性,source
是 Enhancer
子类的成员变量;useCache
设置为true表示使用缓存;namingPolicy
设置为 DefaultNamingPolicy.INSTANCE
, 设置类的命名策略,查看一下 DefaultNamingPolicy
,详细逻辑这里跳过
public class DefaultNamingPolicy implements NamingPolicy {
public static final DefaultNamingPolicy INSTANCE = new DefaultNamingPolicy();
public DefaultNamingPolicy() {
}
public String getClassName(String prefix, String source, Object key, Predicate names) {
if (prefix == null) {
prefix = "net.sf.cglib.empty.Object";
} else if (prefix.startsWith("java")) {
prefix = "$" + prefix;
}
String base = prefix + "$$" + source.substring(source.lastIndexOf(46) + 1) + this.getTag() + "$$" + Integer.toHexString(key.hashCode());
String attempt = base;
for(int var7 = 2; names.evaluate(attempt); attempt = base + "_" + var7++) {
}
return attempt;
}
}
了解了 Enhancer
类实例化的过程,再看一下 enhancer.setSuperclass(User.class);
方法实现
public void setSuperclass(Class superclass) {
if (superclass != null && superclass.isInterface()) {
this.setInterfaces(new Class[]{superclass});
} else if (superclass != null && superclass.equals(class$java$lang$Object == null ? (class$java$lang$Object = class$("java.lang.Object")) : class$java$lang$Object)) {
this.superclass = null;
} else {
this.superclass = superclass;
}
}
判断传入的class不为空且为接口的条件下,设置 enhancer.interfaces
的值,如果不是接口的则设置 enhancer.superclass
的值为传入的class,也就是 User.class
继续查看 enhancer.setCallback(new UserInterceptor());
方法的实现
public void setCallback(Callback callback) {
this.setCallbacks(new Callback[]{callback});
}
public void setCallbacks(Callback[] callbacks) {
if (callbacks != null && callbacks.length == 0) {
throw new IllegalArgumentException("Array cannot be empty");
} else {
this.callbacks = callbacks;
}
}
就是将入参包装成数组并赋值给成员变量,接下来看核心的 enhancer.create()
public Object create() {
this.classOnly = false;
this.argumentTypes = null;
return this.createHelper();
}
设置 classOnly = false
,继续看 this.createHelper();
private Object createHelper() {
this.validate();
if (this.superclass != null) {
this.setNamePrefix(this.superclass.getName());
} else if (this.interfaces != null) {
this.setNamePrefix(this.interfaces[ReflectUtils.findPackageProtected(this.interfaces)].getName());
}
return super.create(KEY_FACTORY.newInstance(this.superclass != null ? this.superclass.getName() : null, ReflectUtils.getNames(this.interfaces), this.filter, this.callbackTypes, this.useFactory, this.interceptDuringConstruction, this.serialVersionUID));
}
this.validate();
校验并赋值,这里不过多说明;接着if判断区分了类和接口,为两种情况设置不同的 enhancer.namePrefix
,我们测试代码中是类,此时设置的 值为 com.tao.proxy.User
;最后调用父类的 create
方法,KEY_FACTORY.newInstance
方法生成了一个 Object
类型的key作为 create
方法的入参,create
方法与JDK动态代理从缓存中获取代理类相似。 具体逻辑也是从缓存中获取,如果没有,回去加载前面生成的类路径,这里会抛出异常 ClassNotFoundException
,最终调用反射中的native方法 newInstance
动态生成代理类
字节码文件
在 main 方法开头加上 System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, ".\\");
,第二个参数为路径,表示与src同级目录
public class User$$EnhancerByCGLIB$$e488937e extends User implements Factory {
//省略属性字段....
final void CGLIB$study$0() {
super.study();
}
public final void study() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
var10000.intercept(this, CGLIB$study$0$Method, CGLIB$emptyArgs, CGLIB$study$0$Proxy);
} else {
super.study();
}
}
final void CGLIB$work$1() {
super.work();
}
public final void work() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
var10000.intercept(this, CGLIB$work$1$Method, CGLIB$emptyArgs, CGLIB$work$1$Proxy);
} else {
super.work();
}
}
//其余部分省略...
}
上述代码只保留了我们定义的 study
和 work
方法,这个新生成的类继承了我们的 User
类,简单分析一下 study
方法(work
方法原理一样)
- 首先给
var10000
赋值,它是一个MethodInterceptor
类型, 也就是前面的UserInterceptor
- 然后
var10000
不为null则调用var10000.intercept()
- 如果
var10000
为null则调用super.study();
我们最初目标类的方法
总结
学习了两种动态代理的使用方式,通过源码的阅读简单了解了两种方式的实现原理和区别