Spring AOP
本文记述AOP相关的代理模式
静态代理
1.定义
为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。通过代理对象访问目标对象,防止直接访问目标对象造成系统复杂性提升.
2.原理

Subject : 接口,为RealSubject和ProxySubject提供一致性的接口
RealSubject : 目标访问类,实际的主体,实现Subject接口的类
ProxySubject : 代理类,处理Client的请求,只有当代理类无法回复请求时,才会调用目标访问类
Client : 请求者
3.适用场景
Virtual Proxy : 对于一些占用系统资源较多或者加载时间较长的对象,可以给这些对象提供一个虚拟代理。在真实对象创建成功之前虚拟代理扮演真实对象的替身,而当真实对象创建之后,虚拟代理将用户的请求转发给真实对象。
Remote Proxy : 远程代理使得客户端可以访问远程主机上的对象,远程代理可以把网络的细节隐藏起来,使得客户端不必考虑网络的存在,客户端完全可以认为调用的远程代理对象在本地,而不是在远程
Cache Proxy : 某一个操作的结果提供临时的缓存存储空间,以便在后续使用中能够共享这些结果,从而可以避免某些方法的重复执行,优化系统性能。
Access Proxy : 可以在调用RealSubject时做访问限制,通过代理过滤不可以访问的对象
4.实现
Printable.java
public interface Printable {
void setPrintName(String name);
String getPrintName();
void print(String str);
}
Printer.java
public class Printer implements Printable {
private String name;
public Printer() {
heavyJob("正在生成Printer的实例");
}
public Printer(String name) {
this.name = name;
heavyJob("正在生成(Printer)" + name + "的实例");
}
@Override
public void setPrintName(String name) {
this.name = name;
}
@Override
public String getPrintName() {
return this.name;
}
@Override
public void print(String str) {
System.out.println("==" + name + "==");
System.out.println(str);
}
public void heavyJob(String msg) {
System.out.println(msg);
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.print(".");
}
System.out.println("over");
}
}
PrintProxy.java
public class PrintProxy implements Printable {
private String name;
private Printer real;
public PrintProxy(String name) {
this.name = name;
}
@Override
public synchronized void setPrintName(String name) {
if (real != null) {
real.setPrintName(name);
}
this.name = name;
}
@Override
public String getPrintName() {
return this.name;
}
@Override
public void print(String str) {
realize();
real.print(str);
}
public synchronized void realize() {
if (real == null) {
real = new Printer(name);
}
}
}
Main.java
public class Main {
public static void main(String[] args) {
Printable p = new PrintProxy("kristin");
System.out.println("name: " + p.getPrintName());
p.setPrintName("kkk");
System.out.println("new name: " + p.getPrintName());
p.print("hello world");
}
}
Printable相当于Subject
Printer相当于RealSubject
PrinterProxy相当于ProxySubject
Main相当于Client
5.优缺点
优点:
协调调用者与被调用者,降低耦合度
作为客户端对象与目标对象的中介,可以有效地保护目标对象
缺点:
在客户端与目标对象之间增加了代理对象,处理请求的速度可能会变慢
如果代理的实现复杂,可能会增加系统实现的复杂性
如果想要为多个类进行代理,需要创建多个代理类,维护难度加大
JDK动态代理
1.定义
代理类在程序运行时被创建,并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的.
2.原理
Java 动态代理机制的出现,使得 Java 开发人员不用手工编写代理类,只要简单地指定一组接口及委托类对象,便能动态地获得代理类。
代理类会负责将所有的方法调用分派到委托对象上反射执行,在分派执行的过程中,开发人员还可以按需调整委托类对象及其功能,这是一套非常灵活有弹性的代理框架。在java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和这个接口可以生成JDK动态代理类和动态代理对象。
3.应用
如果想对代理类的所有方法都加上日志,可以通过动态代理可以对代理类的所有方法进行统一的处理,而不用一一更改每一个方法.
UserDao.java
/**
* 真实类的接口
*/
public interface UserDao {
void add();
}
UserDaoImpl.java
/**
* 真实类
*/
public class UserDaoImpl implements UserDao {
@Override
public void add() {
System.out.println("this is method-add running");
}
}
MyInvocationHandler.java
/**
* 运行时生成代理类的Handler
*/
public class MyInvocationHandler implements InvocationHandler {
/**
* 真实类对象
*/
private Object target;
/**
* 将真实类的实例对象设置到自定义InvocationHandler中
*
* @param target
*/
public MyInvocationHandler(Object target) {
this.target = target;
}
/**
* 运行时生成真实类的代理类
*
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("-------------------before------------------");
Object result = method.invoke(target, args);
System.out.println("--------------------after-------------------");
return result;
}
}
Main.java
public class Main {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
UserDao user = new UserDaoImpl(); //实例化真实类
MyInvocationHandler handler = new MyInvocationHandler(user);
//保存class文件到本地,可以在项目根目录下的com.sun.proxy文件夹中看到$Proxy.class
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
//运行时生成代理类
/**
* 方法一:
*/
Class classProxy = Proxy.getProxyClass(user.getClass().getClassLoader(), UserDao.class);
Constructor constructor = classProxy.getConstructor(InvocationHandler.class);
UserDao userDao = (UserDao) constructor.newInstance(new MyInvocationHandler(user));
userDao.add();
/**
* 方法二
*/
// UserDao userDao = (UserDao) Proxy.newProxyInstance(user.getClass().getClassLoader(), user.getClass().getInterfaces(), handler);
// UserDao userDao = (UserDao) Proxy.newProxyInstance(UserDao.class.getClassLoader(), new Class[]{UserDao.class}, handler);
// userDao.add(); //调用代理类的方法
}
}
4.实现原理
4.1相关的类和接口
Class : java.lang.reflect.Proxy
Interface : java.lang.reflect.InvocationHandler
4.2代理步骤
1.Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)</p>
方法参数:
1. loader : 类加载器
2. interfaces : 目标对象实现的接口
3. h : InvocationHandler的实现类
以下是Proxy.java的newProxyInstance方法
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h); // 如果h为null,就会抛出空指针异常,所以一定要有InvocationHandler的实现类
final Class<?>[] intfs = interfaces.clone(); //拷贝目标对象所实现的接口
final SecurityManager sm = System.getSecurityManager(); //进行一些安全检查
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* 根据类加载器和接口生成代理类
*/
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
//获取代理类的构造函数对象
//参数constructorParames为常量值:private static final Class<?>[] constructorParams = { InvocationHandler.class };
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);
}
}
以下是Proxy类的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);
}
以下是Proxy的内部类ProxyClassFactory,看看ProxyClassFactory是怎样生成的代理类
/**
* A factory function that generates, defines and returns the proxy class given
* the ClassLoader and array of interfaces.
*/
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
// 所有代理类的名称前缀为"$Proxy"
private static final String proxyClassNamePrefix = "$Proxy";
// 使用唯一的编号作为代理类名称的一部分
private static final AtomicLong nextUniqueNumber = new AtomicLong();
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
Map<Class<?>, Boolean> interfaceSet = new IdentityMyMyHashMap<>(interfaces.length);
for (Class<?> intf : interfaces) {
/*
* 验证指定类加载器加载的Class对象是否与intf加载的Class对象相同
*/
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; // 声明代理类所在包
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
/*
* 验证传入的接口是否为非public的,保证非public接口的代理类将被定义在同一个包中
*/
for (Class<?> intf : interfaces) {
int flags = intf.getModifiers();
if (!Modifier.isPublic(flags)) {
accessFlags = Modifier.FINAL;
String name = intf.getName();
int n = name.lastIndexOf('.');
String pkg = ((n == -1) ? "" : name.substring(0, n + 1)); //截取完整的包名
if (proxyPkg == null) {
proxyPkg = pkg;
} else if (!pkg.equals(proxyPkg)) {
throw new IllegalArgumentException(
"non-public interfaces from different packages");
}
}
}
if (proxyPkg == null) {
// 如果没有非public接口,那么这些代理类都放在com.sun.proxy包中
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
/*
* 将nextUniqueNumber通过cas加1,对proxy类名编号进行generate,初始为1
*/
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num; //代理类的全限定名,如com.sun.proxy.$Proxy0.calss,
/*
* 生成代理类的字节码文件
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
/*
* A ClassFormatError here means that (barring bugs in the
* proxy class generation code) there was some other
* invalid aspect of the arguments supplied to the proxy
* class creation (such as virtual machine limitations
* exceeded).
*/
throw new IllegalArgumentException(e.toString());
}
}
}
再看一看是怎样生成字节码文件的呢?
以下是sun.misc.ProxyGenerator的静态方法generateProxyClass
public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
// 真正生成代理类字节码文件的方法
final byte[] var4 = var3.generateClassFile();
// 保存字节码文件
if (saveGeneratedFiles) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
try {
int var1 = var0.lastIndexOf(46);
Path var2;
if (var1 > 0) {
Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));
Files.createDirectories(var3);
var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
} else {
var2 = Paths.get(var0 + ".class");
}
Files.write(var2, var4, new OpenOption[0]);
return null;
} catch (IOException var4x) {
throw new InternalError("I/O exception saving generated file: " + var4x);
}
}
});
}
return var4;
}
以下是sun.misc.ProxyGenerator的内部静态方法,也是真正生成代理类字节码文件的方法
private byte[] generateClassFile() {
/* 将Object的三个方法,hashCode、equals、toString添加到代理类容器中
* hashCodeMethod方法位于静态代码块中通过Object对象获得,
* hashCodeMethod=Object.class.getMethod("hashCode",new Class[0]),
* 相当于从Object中继承过来了这三个方法equalsMethod,toStringMethod
*/
this.addProxyMethod(hashCodeMethod, Object.class);
this.addProxyMethod(equalsMethod, Object.class);
this.addProxyMethod(toStringMethod, Object.class);
Class[] var1 = this.interfaces;
int var2 = var1.length;
int var3;
Class var4;
//将所有接口中的所有方法添加到代理方法中
for(var3 = 0; var3 < var2; ++var3) {
var4 = var1[var3];
Method[] var5 = var4.getMethods();
int var6 = var5.length;
for(int var7 = 0; var7 < var6; ++var7) {
Method var8 = var5[var7];
this.addProxyMethod(var8, var4);
}
}
Iterator var11 = this.proxyMethods.values().iterator();
List var12;
while(var11.hasNext()) {
var12 = (List)var11.next();
checkReturnTypes(var12); //验证具有相同方法签名的的方法的返回值类型是否一致,因为不可能有两个方法名相同,参数相同,而返回值却不同的方法
}
//以下步骤是写代理类文件
Iterator var15;
try {
this.methods.add(this.generateConstructor()); //生成代理类构造函数
var11 = this.proxyMethods.values().iterator();
while(var11.hasNext()) {
var12 = (List)var11.next();
var15 = var12.iterator();
while(var15.hasNext()) {
ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next();
/* 将代理字段声明为Method,10为ACC_PRIVATE和ACC_STATAIC的与运算,表示该字段的修饰符为private static
* 所以代理类的字段都是private static Method XXX
*/
this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10));
//生成代理类的代理方法
this.methods.add(var16.generateMethod());
}
}
//为代理类生成静态代码块,对一些字段进行初始化
this.methods.add(this.generateStaticInitializer());
} catch (IOException var10) {
throw new InternalError("unexpected I/O Exception", var10);
}
if (this.methods.size() > 65535) { //代理方法数量超过65535将抛出异常
throw new IllegalArgumentException("method limit exceeded");
} else if (this.fields.size() > 65535) { //代理类的字段数量超过65535将抛出异常
throw new IllegalArgumentException("field limit exceeded");
} else { //从这里开始就是写代理类字节码文件啦
this.cp.getClass(dotToSlash(this.className));
this.cp.getClass("java/lang/reflect/Proxy");
var1 = this.interfaces;
var2 = var1.length;
for(var3 = 0; var3 < var2; ++var3) {
var4 = var1[var3];
this.cp.getClass(dotToSlash(var4.getName()));
}
this.cp.setReadOnly();
ByteArrayOutputStream var13 = new ByteArrayOutputStream();
DataOutputStream var14 = new DataOutputStream(var13);
try {
var14.writeInt(-889275714);
var14.writeShort(0);
var14.writeShort(49);
this.cp.write(var14);
var14.writeShort(this.accessFlags);
var14.writeShort(this.cp.getClass(dotToSlash(this.className)));
var14.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));
var14.writeShort(this.interfaces.length);
Class[] var17 = this.interfaces;
int var18 = var17.length;
for(int var19 = 0; var19 < var18; ++var19) {
Class var22 = var17[var19];
var14.writeShort(this.cp.getClass(dotToSlash(var22.getName())));
}
var14.writeShort(this.fields.size());
var15 = this.fields.iterator();
while(var15.hasNext()) {
ProxyGenerator.FieldInfo var20 = (ProxyGenerator.FieldInfo)var15.next();
var20.write(var14);
}
var14.writeShort(this.methods.size());
var15 = this.methods.iterator();
while(var15.hasNext()) {
ProxyGenerator.MethodInfo var21 = (ProxyGenerator.MethodInfo)var15.next();
var21.write(var14);
}
var14.writeShort(0);
return var13.toByteArray();
} catch (IOException var9) {
throw new InternalError("unexpected I/O Exception", var9);
}
}
}
是怎样将Object方法添加到代理类容器的呢?下面我们看一看sun.misc.ProxyGenerator中的addProxyMethod方法
private void addProxyMethod(Method var1, Class<?> var2) {
String var3 = var1.getName(); // 方法名
Class[] var4 = var1.getParameterTypes(); // 方法参数类型
Class var5 = var1.getReturnType(); // 方法返回值类型
Class[] var6 = var1.getExceptionTypes(); // 方法异常类型
String var7 = var3 + getParameterDescriptors(var4); // 方法签名
Object var8 = (List)this.proxyMethods.get(var7); //根据方法签名获得方法对象
if (var8 != null) { // 处理多个代理接口中的重复方法
Iterator var9 = ((List)var8).iterator();
while(var9.hasNext()) {
ProxyGenerator.ProxyMethod var10 = (ProxyGenerator.ProxyMethod)var9.next();
if (var5 == var10.returnType) {
ArrayList var11 = new ArrayList();
collectCompatibleTypes(var6, var10.exceptionTypes, var11);
collectCompatibleTypes(var10.exceptionTypes, var6, var11);
var10.exceptionTypes = new Class[var11.size()];
var10.exceptionTypes = (Class[])var11.toArray(var10.exceptionTypes);
return;
}
}
} else {
var8 = new ArrayList(3);
this.proxyMethods.put(var7, var8);
}
((List)var8).add(new ProxyGenerator.ProxyMethod(var3, var4, var5, var6, var2, null));
}
通过下面这个设置,可以将代理类字节码文件保存到项目根目录下的com.sun.proxy文件夹中看到$Proxy.class
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
下面我们看一看$Proxy0.class
package com.sun.proxy;
import com.kristin.design_pattern.proxy.jdk_proxy.UserDao;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy implements UserDao {
private static Method m1;
private static Method m2;
private static Method m3;
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 add() throws {
try {
/**
* this: 当前生成的$Proxy0实例
* m3: 就是我们定义的接口方法
* (Object)null: 被传入的被调用的接口方法的实际参数
*
* 可以看到实际上这个方法中的3个参数Object proxy, Method method, Object[] args就是$Proxy0通过h.invoke(this, m3, new Object[]{var1});传递给我们的,
* 也就是说,我们实现的InvocationHandler的invoke()实际上是一个回调,也就是我们预先定义好的,然后JDK生成的类$Proxy0回过来调用的。
*/
super.h.invoke(this, m3, (Object[])null); //注意这里super.h其实就是InvocationHandler的对象h
} 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("com.kristin.design_pattern.proxy.jdk_proxy.UserDao").getMethod("add");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
IN ALL:
JDK动态代理的基本原理就是 我们定义好接口和默认实现,JDK根据通过生成class文件的方式”动态”的生成一个代理类,这个代理类实现了我们定义的接口,并在接口实现方法中回调了我们通过InvocationHandler定义的处理流程,这个处理流程中我们回去调用默认实现,并提供增强。
不足:
JDK动态代理只能代理实现了接口的类,而Cglib动态代理可以代理没有实现接口的类
JDK vs CGLIB:
JDK动态代理和CGLIB字节码生成的区别?
(1)JDK动态代理只能对实现了接口的类生成代理,而不能针对类
(2)CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法
因为是继承,所以该类或方法最好不要声明成final
结论
1.同样情况下,cglib两种实现方式,invokeSuper + setSuperClass 永远比 invoke + setInterfaces慢
2.cglib invoke + setInterfaces 在方法数量较少的时候,在函数平均调用的情况下 比jdkProxy快,随着函数增多,优势越来越不明显,到达某个数量级一定比jdk动态代理慢
3.cglib invoke + setInterfaces 在调用特定函数(在switch中靠后的case) 会比jdk动态代理慢
思考
cglib的瓶颈在于:
调用net.sf.cglib.reflect.FastClass#invoke(int, java.lang.Object, java.lang.Object[])时需要switch的case:
如果有n个函数,那么就要一个个比较,复杂度O(n)
这里如果有一个key -> behavior的映射就好了,目前并没有。
如果可以用asm在这里写成一个二分搜索,cglib就会快多了,变成O(log2 n),时间上就是一个飞跃,只不过这个fastclass就会看起来很丑。(目前最新3.2.5的版本也没有改动这一块)
CGLIB动态代理
1.定义
cglib是针对类来实现代理的,它的原理是对指定的目标类生成一个类,并覆盖其中方法实现增强
代理类将委托类作为自己的父类并为其中的非final委托方法创建两个方法,一个是与委托方法签名相同的方法,它在方法中会通过super调用委托方法;另一个是代理类独有的方法。在代理方法中,它会判断是否存在实现了MethodInterceptor接口的对象,若存在则将调用intercept方法对委托方法进行代理.
2.应用
UserDao.java
public interface UserDao {
void add();
}
UserDaoImpl.java
public class UserDaoImpl implements UserDao {
public void add() {
System.out.println("the method is running");
}
}
Interceptor.java
public class Interceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("-------------------before------------------");
proxy.invokeSuper(obj, args);
System.out.println("--------------------after-------------------");
return null;
}
}
Main.java
public class Main {
@org.junit.jupiter.api.Test
public void test() {
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "E:\\code");
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserDaoImpl.class);
enhancer.setCallback(new Interceptor());
UserDao userDao = (UserDao) enhancer.create();
userDao.add();
}
}
3.实现原理
代理类将委托类作为自己的父类并为其中的非final委托方法创建两个方法,一个是与委托方法签名相同的方法,它在方法中会通过super调用委托方法;另一个是代理类独有的方法。在代理方法中,它会判断是否存在实现了MethodInterceptor接口的对象,若存在则将调用intercept方法对委托方法进行代理
创建代理对象的几个步骤:
1.生成代理类的二进制字节码文件;
2.加载二进制字节码,生成Class对象( 例如使用Class.forName()方法 );
3.通过反射机制获得实例构造,并创建代理类对象
代理类Class文件反编译后的Java代码:
import java.lang.reflect.Method;
import net.sf.cglib.core.ReflectUtils;
import net.sf.cglib.core.Signature;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Factory;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class UserDaoImpl$$EnhancerByCGLIB$$3ef42b63 extends UserDaoImpl implements Factory {
private boolean CGLIB$BOUND;
public static Object CGLIB$FACTORY_DATA;
private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
private static final Callback[] CGLIB$STATIC_CALLBACKS;
private MethodInterceptor CGLIB$CALLBACK_0;
private static Object CGLIB$CALLBACK_FILTER;
private static final Method CGLIB$add$0$Method;
private static final MethodProxy CGLIB$add$0$Proxy;
private static final Object[] CGLIB$emptyArgs;
private static final Method CGLIB$equals$1$Method;
private static final MethodProxy CGLIB$equals$1$Proxy;
private static final Method CGLIB$toString$2$Method;
private static final MethodProxy CGLIB$toString$2$Proxy;
private static final Method CGLIB$hashCode$3$Method;
private static final MethodProxy CGLIB$hashCode$3$Proxy;
private static final Method CGLIB$clone$4$Method;
private static final MethodProxy CGLIB$clone$4$Proxy;
static void CGLIB$STATICHOOK1() {
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
Class var0 = Class.forName("com.kristin.design_pattern.proxy.cglib_proxy.UserDaoImpl$$EnhancerByCGLIB$$3ef42b63");
Class var1;
Method[] var10000 = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
CGLIB$equals$1$Method = var10000[0];
CGLIB$equals$1$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1");
CGLIB$toString$2$Method = var10000[1];
CGLIB$toString$2$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$2");
CGLIB$hashCode$3$Method = var10000[2];
CGLIB$hashCode$3$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$3");
CGLIB$clone$4$Method = var10000[3];
CGLIB$clone$4$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4");
CGLIB$add$0$Method = ReflectUtils.findMethods(new String[]{"add", "()V"}, (var1 = Class.forName("com.kristin.design_pattern.proxy.cglib_proxy.UserDaoImpl")).getDeclaredMethods())[0];
CGLIB$add$0$Proxy = MethodProxy.create(var1, var0, "()V", "add", "CGLIB$add$0");
}
final void CGLIB$add$0() {
super.add();
}
public final void add() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (this.CGLIB$CALLBACK_0 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
var10000.intercept(this, CGLIB$add$0$Method, CGLIB$emptyArgs, CGLIB$add$0$Proxy);
} else {
super.add();
}
}
final boolean CGLIB$equals$1(Object var1) {
return super.equals(var1);
}
public final boolean equals(Object var1) {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (this.CGLIB$CALLBACK_0 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
Object var2 = var10000.intercept(this, CGLIB$equals$1$Method, new Object[]{var1}, CGLIB$equals$1$Proxy);
return var2 == null ? false : (Boolean)var2;
} else {
return super.equals(var1);
}
}
final String CGLIB$toString$2() {
return super.toString();
}
public final String toString() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (this.CGLIB$CALLBACK_0 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
return var10000 != null ? (String)var10000.intercept(this, CGLIB$toString$2$Method, CGLIB$emptyArgs, CGLIB$toString$2$Proxy) : super.toString();
}
final int CGLIB$hashCode$3() {
return super.hashCode();
}
public final int hashCode() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (this.CGLIB$CALLBACK_0 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
Object var1 = var10000.intercept(this, CGLIB$hashCode$3$Method, CGLIB$emptyArgs, CGLIB$hashCode$3$Proxy);
return var1 == null ? 0 : ((Number)var1).intValue();
} else {
return super.hashCode();
}
}
final Object CGLIB$clone$4() throws CloneNotSupportedException {
return super.clone();
}
protected final Object clone() throws CloneNotSupportedException {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (this.CGLIB$CALLBACK_0 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
return var10000 != null ? var10000.intercept(this, CGLIB$clone$4$Method, CGLIB$emptyArgs, CGLIB$clone$4$Proxy) : super.clone();
}
public static MethodProxy CGLIB$findMethodProxy(Signature var0) {
String var10000 = var0.toString();
switch(var10000.hashCode()) {
case -1422568652:
if (var10000.equals("add()V")) {
return CGLIB$add$0$Proxy;
}
break;
case -508378822:
if (var10000.equals("clone()Ljava/lang/Object;")) {
return CGLIB$clone$4$Proxy;
}
break;
case 1826985398:
if (var10000.equals("equals(Ljava/lang/Object;)Z")) {
return CGLIB$equals$1$Proxy;
}
break;
case 1913648695:
if (var10000.equals("toString()Ljava/lang/String;")) {
return CGLIB$toString$2$Proxy;
}
break;
case 1984935277:
if (var10000.equals("hashCode()I")) {
return CGLIB$hashCode$3$Proxy;
}
}
return null;
}
public UserDaoImpl$$EnhancerByCGLIB$$3ef42b63() {
CGLIB$BIND_CALLBACKS(this);
}
public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] var0) {
CGLIB$THREAD_CALLBACKS.set(var0);
}
public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] var0) {
CGLIB$STATIC_CALLBACKS = var0;
}
private static final void CGLIB$BIND_CALLBACKS(Object var0) {
UserDaoImpl$$EnhancerByCGLIB$$3ef42b63 var1 = (UserDaoImpl$$EnhancerByCGLIB$$3ef42b63)var0;
if (!var1.CGLIB$BOUND) {
var1.CGLIB$BOUND = true;
Object var10000 = CGLIB$THREAD_CALLBACKS.get();
if (var10000 == null) {
var10000 = CGLIB$STATIC_CALLBACKS;
if (CGLIB$STATIC_CALLBACKS == null) {
return;
}
}
var1.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])var10000)[0];
}
}
public Object newInstance(Callback[] var1) {
CGLIB$SET_THREAD_CALLBACKS(var1);
UserDaoImpl$$EnhancerByCGLIB$$3ef42b63 var10000 = new UserDaoImpl$$EnhancerByCGLIB$$3ef42b63();
CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
return var10000;
}
public Object newInstance(Callback var1) {
CGLIB$SET_THREAD_CALLBACKS(new Callback[]{var1});
UserDaoImpl$$EnhancerByCGLIB$$3ef42b63 var10000 = new UserDaoImpl$$EnhancerByCGLIB$$3ef42b63();
CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
return var10000;
}
public Object newInstance(Class[] var1, Object[] var2, Callback[] var3) {
CGLIB$SET_THREAD_CALLBACKS(var3);
UserDaoImpl$$EnhancerByCGLIB$$3ef42b63 var10000 = new UserDaoImpl$$EnhancerByCGLIB$$3ef42b63;
switch(var1.length) {
case 0:
var10000.<init>();
CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
return var10000;
default:
throw new IllegalArgumentException("Constructor not found");
}
}
public Callback getCallback(int var1) {
CGLIB$BIND_CALLBACKS(this);
MethodInterceptor var10000;
switch(var1) {
case 0:
var10000 = this.CGLIB$CALLBACK_0;
break;
default:
var10000 = null;
}
return var10000;
}
public void setCallback(int var1, Callback var2) {
switch(var1) {
case 0:
this.CGLIB$CALLBACK_0 = (MethodInterceptor)var2;
default:
}
}
public Callback[] getCallbacks() {
CGLIB$BIND_CALLBACKS(this);
return new Callback[]{this.CGLIB$CALLBACK_0};
}
public void setCallbacks(Callback[] var1) {
this.CGLIB$CALLBACK_0 = (MethodInterceptor)var1[0];
}
static {
CGLIB$STATICHOOK1();
}
}
CGLIB在进行代理的时候都进行了哪些工作呢
1.生成的代理类UserDaoImpl$$EnhancerByCGLIB$$3ef42b63.class继承被代理类UserDaoImpl。在这里我们需要注意一点:如果委托类被final修饰,那么它不可被继承,即不可被代理;同样,如果委托类中存在final修饰的方法,那么该方法也不可被代理;
2.代理类会为委托方法生成两个方法,一个是重写的add方法,另一个是CGLIB$add$0方法,我们可以看到它是直接调用父类的add方法;
3.当执行代理对象的add方法时,会首先判断一下是否存在实现了MethodInterceptor接口的CGLIB$CALLBACK_0;,如果存在,则将调用MethodInterceptor中的intercept方法。
在intercept方法中,我们除了会调用委托方法,还会进行一些增强操作。在Spring AOP中,典型的应用场景就是在某些敏感方法执行前后进行操作日志记录。
调用委托方法是通过代理方法的MethodProxy对象调用invokeSuper方法来执行的,下面我们看看invokeSuper方法:
/**
* Invoke the original (super) method on the specified object.
* @param obj the enhanced object, must be the object passed as the first
* argument to the MethodInterceptor
* @param args the arguments passed to the intercepted method; you may substitute a different
* argument array as long as the types are compatible
* @see MethodInterceptor#intercept
* @throws Throwable the bare exceptions thrown by the called method are passed through
* without wrapping in an <code>InvocationTargetException</code>
*/
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
try {
init();
FastClassInfo fci = fastClassInfo;
return fci.f2.invoke(fci.i2, obj, args);
} catch (InvocationTargetException e) {
throw e.getTargetException();
}
}
private static class FastClassInfo
{
FastClass f1;
FastClass f2;
int i1;
int i2;
}
f1指向委托类对象
f2指向代理类对象
i1和i2分别代表着add方法以及CGLIB$add$0方法在对象信息数组中的下标。
总结
| 代理方式 | 实现 | 特点 | 优点 | 缺点 |
| 静态代理 | 代理类与委托类实现同一接口,并且在代理类中需要硬编码接口 | 好像没啥特点 | 实现简单,容易理解 | 代理类需要硬编码接口,在实际应用中可能会导致重复编码,浪费存储空间并且效率很低 |
| JDK动态代理 | 代理类与委托类实现同一接口,主要是通过代理类实现InvocationHandler并重写invoke方法来进行动态代理的,在invoke方法中将对方法进行增强处理 | 底层使用反射机制进行方法的调用 | 不需要硬编码接口,代码复用率高 | 只能够代理实现了接口的委托类 |
| CGLIB动态代理 | 代理类将委托类作为自己的父类并为其中的非final委托方法创建两个方法,一个是与委托方法签名相同的方法,它在方法中会通过super调用委托方法;另一个是代理类独有的方法。在代理方法中,它会判断是否存在实现了MethodInterceptor接口的对象,若存在则将调用intercept方法对委托方法进行代理 | 底层将方法全部存入一个数组中,通过数组索引直接进行方法调用 | 可以在运行时对类或者是接口进行增强操作,且委托类无需实现接口 | 不能对final类以及final方法进行代理 |
参考:
https://www.cnblogs.com/MOBIN/p/5597215.html
https://since1986.github.io/blog/bf178159.html
https://www.jianshu.com/p/1aaacf92e2cd
https://h2pl.github.io/2018/06/03/spring5/
https://www.cnblogs.com/cruze/p/3865180.html

浙公网安备 33010602011771号