什么是 代理模式
代理模式:为其他对象提供一种代理以控制对这个对象的访问。
代理模式中有三种角色:Subject抽象主题角色、RealSubject真实主题角色、Proxy代理主题角色。Subject描述了业务行为,RealSubject执行具体的业务逻辑,Proxy代理会拦截对RealSubject对象方法的调用,并在方法调用前后做预处理以及一些善后工作。
代理模式可以很好地在不侵入原代码的情况下,拓展原来的功能。
下图为Proxy模式的静态类图:
下图为Proxy模式的调用关系:
原理
Java 动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
而 CGLIB 动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现 AOP
2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP
3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换
JDK动态代理和CGLIB字节码生成的区别?
(1)JDK动态代理只能对实现了接口的类生成代理,而不能针对类
(2)CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法
因为是继承,所以该类或方法最好不要声明成final
如何强制使用CGLIB实现AOP?
1)添加CGLIB库(aspectjrt-xxx. jar 、aspectjweaver-xxx.jar、cglib-nodep-xxx.jar)
2)在Spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class=”true”/>
CGlib比JDK快吗
1)使用CGLib实现动态代理,CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,
在jdk6之前比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,
因为CGLib原理是动态生成被代理类的子类。
2)在jdk6、jdk7、jdk8逐步对JDK动态代理优化之后,在调用次数较少的情况下,JDK代理效率高于CGLIB代理效率,
只有当进行大量调用的时候,jdk6和jdk7比CGLIB代理效率低一点,但是到jdk8的时候,jdk代理效率高于CGLIB代理,
总之,每一次jdk版本升级,jdk代理效率都得到提升,而CGLIB代理消息确有点跟不上步伐。
JDK动态代理实现
我们在JDK动态代理的测试类中发现代理类生成是通过Proxy类中的newProxyInstance来完成的,下面我们进入这个函数:
Proxy类中的newProxyInstance
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { //如果h为空将抛出异常 Objects.requireNonNull(h); final Class<?>[] intfs = interfaces.clone();//拷贝被代理类实现的一些接口,用于后面权限方面的一些检查 final SecurityManager sm = System.getSecurityManager(); if (sm != null) { //在这里对某些安全权限进行检查,确保我们有权限对预期的被代理类进行代理 checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } /* * 下面这个方法将产生代理类 */ Class<?> cl = getProxyClass0(loader, intfs); /* * 使用指定的调用处理程序获取代理类的构造函数对象 */ try { if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); } final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; //假如代理类的构造函数是private的,就使用反射来set accessible 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); } }
所以代理类其实是通过getProxyClass方法来生成的:
/** * 生成一个代理类,但是在调用本方法之前必须进行权限检查 */ 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); }
那么ProxyClassFactory是什么呢?
/**
* 里面有一个根据给定ClassLoader和Interface来创建代理类的工厂函数
*
*/ private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
// 代理类的名字的前缀统一为“$Proxy”
private static final String proxyClassNamePrefix = "$Proxy";
// 每个代理类前缀后面都会跟着一个唯一的编号,如$Proxy0、$Proxy1、$Proxy2
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) {
/*
* 验证类加载器加载接口得到对象是否与由apply函数参数传入的对象相同
*/ 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; // 声明代理类所在的package
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
/*
* 记录一个非公共代理接口的包,以便在同一个包中定义代理类。同时验证所有非公共
* 代理接口都在同一个包中
*/ 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) {
// 如果全是公共代理接口,那么生成的代理类就在com.sun.proxy package下
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
/*
* 为代理类生成一个name package name + 前缀+唯一编号
* 如 com.sun.proxy.$Proxy0.class
*/ long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
/*
* 生成指定代理类的字节码文件
*/ 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());
}
}
}
JDK代理字节码生成
由上方代码byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);可以发现,其实生成代理类字节码文件的工作是通过 ProxyGenerate类中的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() { 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), new String[0]); Files.createDirectories(var3, new FileAttribute[0]); var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class"); } else { var2 = Paths.get(var0 + ".class", new String[0]); } Files.write(var2, var4, new OpenOption[0]); return null; } catch (IOException var4x) { throw new InternalError("I/O exception saving generated file: " + var4x); } } }); } return var4; }
ASPECTJ 是什么
- AspectJ 是一个代码生成工具(Code Generator)。
- AspectJ 语法就是用来定义代码生成规则的语法。您如果使用过 Java Compiler Compiler (JavaCC),您会发现,两者的代码生成规则的理念惊人相似。
- AspectJ 有自己的语法编译工具,编译的结果是 Java Class 文件,运行的时候,classpath 需要包含 AspectJ 的一个 jar 文件(Runtime lib)。