AOP 代理的两种实现:
- jdk是代理接口,私有方法必然不会存在在接口里,所以就不会被拦截到;
- cglib是子类,private的方法照样不会出现在子类里,也不能被拦截。
JDK 动态代理。
具体有如下四步骤:
- 通过实现 InvocationHandler 接口创建自己的调用处理器;
- 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
- 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
- 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。
示例
创建业务接口
public interface UserService {
/**
* 需要增强的方法
*/
void add();
}
创建业务接口 实现类
public class UserServiceImpl implements UserService {
@Override
public void add() {
System.out.println("-------------业务逻辑方法 add ------------");
}
}
创建自定义的 InvocationHandler,用于对接口提供的方法进行增强
public class MyInvocationHandler implements InvocationHandler {
/**
* 需要增强的对象
*/
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
/**
* 执行目标对象
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("-------------方法执行前的增强逻辑 ------------");
Object result = method.invoke(target, args);
System.out.println("-------------方法执行后的增强逻辑 ------------");
return result;
}
public Object getProxy() {
return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), target.getClass().getInterfaces(), this);
}
}
测试类
public class ProxyTest {
@Test
public void contextTest() {
// 实例化目标对象
UserService userService = new UserServiceImpl();
// 实例化 InvocationHandler
MyInvocationHandler invocationHandler = new MyInvocationHandler(userService);
// 生成代理对象
UserService proxy = (UserService) invocationHandler.getProxy();
// 输出代理类的class
ProxySourceClassUtil.writeClassToDisk("C:\Users\yuhao.wang3\Desktop\" + proxy.getClass().getSimpleName() + ".class", proxy.getClass().getSimpleName(), UserService.class);
// 调用代理对象的方法
proxy.add();
}
}
输出结果:
Connected to the target VM, address: '127.0.0.1:60738', transport: 'socket'
-------------方法执行前的增强逻辑 ------------
-------------业务逻辑方法 add ------------
-------------方法执行后的增强逻辑 ------------
Disconnected from the target VM, address: '127.0.0.1:60738', transport: 'socket'
用起来很简单,其实’这基本上就 AOP 的一个简单实现了,在目标对象的方法执行之前和 执行之后进行了增强。 Spring的AOP实现其实也是用了 Proxy 和 InvocationHandler。在整个创建过程中InvocationHandler的创建最为核心,在自定义的InvocationHandler中需要重新3个函数:
- 构造函数,将代理的对象传入。
- invoke方法,此方法中实现了AOP增强的所有逻辑。
- getProxy方法,此方千篇一律,但是必不可少。
反编译代理类结果
package com.xiaolyuh.aop.jdk;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
// 代理类会去实现我们的接口
public final class Proxy4 extends Proxy implements UserService {
// 解析所有的方法
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public Proxy4() throws {
super(paramInvocationHandler);
}
public final boolean equals() throws {
try {
return ((Boolean) this.h.invoke(this, m1, new Object[]{paramObject})).booleanValue();
} catch (RuntimeException localRuntimeException) {
throw localRuntimeException;
} catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
}
public final String toString() throws {
try {
return ((String) this.h.invoke(this, m2, null));
} catch (RuntimeException localRuntimeException) {
throw localRuntimeException;
} catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void add() throws {
try {
// 通过InvocationHandler的invoke方法去执行增强和原有方法逻辑
this.h.invoke(this, m3, null);
return;
} catch (RuntimeException localRuntimeException) {
throw localRuntimeException;
} catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
}
public final int hashCode() throws {
try {
return ((Integer) this.h.invoke(this, m0, null)).intValue();
} catch (RuntimeException localRuntimeException) {
throw localRuntimeException;
} catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
}
static {
try {
// 解析所有的method出来
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m3 = Class.forName("com.xiaolyuh.aop.jdk.UserService").getMethod("add", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
return;
} catch (NoSuchMethodException localNoSuchMethodException) {
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
} catch (ClassNotFoundException localClassNotFoundException) {
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
}
从结果我们可以看出,代理类会实现我们的接口,并会实现我们接口中的方法。然后通过InvocationHandler的invoke方法去执行增强逻辑和原有方法逻辑。在invoke中我们可以使用反射去执行原有逻辑,并在方法执行前后加上自己的增强逻辑。
JdkDynamicAopProxy
JdkDynamicAopProxy是Spring中JDK动态代理的实现,它也实现了InvocationHandler接口。核心方法:
- getProxy:获取代理对象,这个和上面的示例几乎是一样的
- invoke:实现了AOP的核心逻辑
源码:
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
...
/** Config used to configure this proxy(对增强器的支持) */
private final AdvisedSupport advised;
...
public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {
Assert.notNull(config, "AdvisedSupport must not be null");
if (config.getAdvisors().length == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
throw new AopConfigException("No advisors and no TargetSource specified");
}
this.advised = config;
}
@Override
public Object getProxy() {
return getProxy(ClassUtils.getDefaultClassLoader());
}
@Override
public Object getProxy(ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
}
// 获取目标类接口
Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
// 返回代理对象
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
...
/**
* Implementation of {@code InvocationHandler.invoke}.
* <p>Callers will see exactly the exception thrown by the target,
* unless a hook method throws an exception.
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MethodInvocation invocation;
Object oldProxy = null;
boolean setProxyContext = false;
TargetSource targetSource = this.advised.targetSource;
Class<?> targetClass = null;
Object target = null;
try {
// equals 方法处理
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
// The target does not implement the equals(Object) method itself.
return equals(args[0]);
}
// hashCode 方法处理
else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
// The target does not implement the hashCode() method itself.
return hashCode();
}
else if (method.getDeclaringClass() == DecoratingProxy.class) {
// There is only getDecoratedClass() declared -> dispatch to proxy config.
return AopProxyUtils.ultimateTargetClass(this.advised);
}
else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
method.getDeclaringClass().isAssignableFrom(Advised.class)) {
// Service invocations on ProxyConfig with the proxy config...
return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
}
Object retVal;
// 有时候目标对象内部的自我调用无法实施切面中的增强则需要通过此属性暴露代理
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// May be null. Get as late as possible to minimize the time we "own" the target,
// in case it comes from a pool.
target = targetSource.getTarget();
if (target != null) {
targetClass = target.getClass();
}
// 获取当前方法的拦截链
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
if (chain.isEmpty()) {
// 如果没有拦截链那么直接调用切点方法
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
// 将拦截器封装在ReflectiveMethodInvocation中,以便使用proceed方法执行拦截链
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// 执行拦截链
retVal = invocation.proceed();
}
// Massage return value if necessary.
Class<?> returnType = method.getReturnType();
if (retVal != null && retVal == target &&
returnType != Object.class && returnType.isInstance(proxy) &&
!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
// Special case: it returned "this" and the return type of the method
// is type-compatible. Note that we can't help if the target sets
// a reference to itself in another returned object.
retVal = proxy;
}
else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
throw new AopInvocationException(
"Null return value from advice does not match primitive return type for: " + method);
}
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
// Must have come from TargetSource.
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
...
}
GCLIB代理
cglib(Code Generation Library)是一个强大的,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java接口。
- cglib封装了asm,可以在运行期动态生成新的class(子类)。
- cglib用于AOP,jdk中的proxy必须基于接口,cglib却没有这个限制。
示例
需要代理的类
public class EnhancerDemo {
public void test() {
System.out.println("-------------业务逻辑方法 test ------------");
}
}
MethodInterceptor的实现类(增强器)
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class MethodInterceptorImpl implements MethodInterceptor {
@Override
public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("-------------方法执行前的增强逻辑 ------------" + method);
Object result = methodProxy.invokeSuper(object, args);
System.out.println("-------------方法执行后的增强逻辑 ------------" + method);
return result;
}
}
测试类:
public class EnhancerTest {
@Test
public void contextTest() {
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\class");
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(EnhancerDemo.class);
enhancer.setCallback(new MethodInterceptorImpl());
EnhancerDemo proxy = (EnhancerDemo) enhancer.create();
proxy.test();
System.out.println(proxy);
}
}
执行结果
Connected to the target VM, address: '127.0.0.1:61217', transport: 'socket'
-------------方法执行前的增强逻辑 ------------public void com.xiaolyuh.aop.cglib.EnhancerDemo.test()
-------------业务逻辑方法 test ------------
-------------方法执行后的增强逻辑 ------------public void com.xiaolyuh.aop.cglib.EnhancerDemo.test()
-------------方法执行前的增强逻辑 ------------public java.lang.String java.lang.Object.toString()
-------------方法执行前的增强逻辑 ------------public native int java.lang.Object.hashCode()
-------------方法执行后的增强逻辑 ------------public native int java.lang.Object.hashCode()
-------------方法执行后的增强逻辑 ------------public java.lang.String java.lang.Object.toString()
com.xiaolyuh.aop.cglib.EnhancerDemo$$EnhancerByCGLIB$$da6c48f9@7a187f14
Disconnected from the target VM, address: '127.0.0.1:61217', transport: 'socket'
可以看到 System.out.println(porxy); 首先调用了 toString() 方法,然后又调用了 hashCode() ,生成的对象为 EnhancerDemo$$EnhancerByCGLIB$$da6c48f9@7a187f14
的实例,这个类是运行时由 CGLIB 产生。
反编译代理结果
在测试类的第一行加上下面语句,那么在cglib中生成的class文件将会持久化到硬盘。https://www.cnblogs.com/cruze/p/3847968.html
// 该设置用于输出cglib动态代理产生的类
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\class");
// 该设置用于输出jdk动态代理产生的类
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
反编译结果:
package com.xiaolyuh.aop.cglib;
import java.lang.reflect.Method;
import org.springframework.cglib.core.ReflectUtils;
import org.springframework.cglib.core.Signature;
import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.Factory;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
public class da6c48f9 extends EnhancerDemo
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$test$0$Method;
private static final MethodProxy CGLIB$test$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()
{
Class localClass2;
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
Class localClass1 = Class.forName("com.xiaolyuh.aop.cglib.EnhancerDemo$$EnhancerByCGLIB$$da6c48f9");
Method[] tmp83_80 = ReflectUtils.findMethods(new String[] { "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;" }, (localClass2 = Class.forName("java.lang.Object")).getDeclaredMethods());
CGLIB$equals$1$Method = tmp83_80[0];
CGLIB$equals$1$Proxy = MethodProxy.create(localClass2, localClass1, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1");
Method[] tmp103_83 = tmp83_80;
CGLIB$toString$2$Method = tmp103_83[1];
CGLIB$toString$2$Proxy = MethodProxy.create(localClass2, localClass1, "()Ljava/lang/String;", "toString", "CGLIB$toString$2");
Method[] tmp123_103 = tmp103_83;
CGLIB$hashCode$3$Method = tmp123_103[2];
CGLIB$hashCode$3$Proxy = MethodProxy.create(localClass2, localClass1, "()I", "hashCode", "CGLIB$hashCode$3");
Method[] tmp143_123 = tmp123_103;
CGLIB$clone$4$Method = tmp143_123[3];
CGLIB$clone$4$Proxy = MethodProxy.create(localClass2, localClass1, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4");
tmp143_123;
Method[] tmp191_188 = ReflectUtils.findMethods(new String[] { "test", "()Ljava/lang/String;" }, (localClass2 = Class.forName("com.xiaolyuh.aop.cglib.EnhancerDemo")).getDeclaredMethods());
CGLIB$test$0$Method = tmp191_188[0];
CGLIB$test$0$Proxy = MethodProxy.create(localClass2, localClass1, "()Ljava/lang/String;", "test", "CGLIB$test$0");
tmp191_188;
}
final String CGLIB$test$0()
{
return super.test();
}
public final String test()
{
MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
if (tmp4_1 == null)
{
tmp4_1;
CGLIB$BIND_CALLBACKS(this);
}
MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;
if (tmp17_14 != null)
return ((String)tmp17_14.intercept(this, CGLIB$test$0$Method, CGLIB$emptyArgs, CGLIB$test$0$Proxy));
return super.test();
}
final boolean CGLIB$equals$1()
{
return super.equals(paramObject);
}
public final boolean equals()
{
MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
if (tmp4_1 == null)
{
tmp4_1;
CGLIB$BIND_CALLBACKS(this);
}
MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;
if (tmp17_14 == null)
break label57;
label50: label57: if (tmp17_14.intercept(this, CGLIB$equals$1$Method, new Object[] { paramObject }, CGLIB$equals$1$Proxy) != null)
break label50;
}
final String CGLIB$toString$2()
{
return super.toString();
}
public final String toString()
{
MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
if (tmp4_1 == null)
{
tmp4_1;
CGLIB$BIND_CALLBACKS(this);
}
MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;
if (tmp17_14 != null)
return ((String)tmp17_14.intercept(this, CGLIB$toString$2$Method, CGLIB$emptyArgs, CGLIB$toString$2$Proxy));
return super.toString();
}
final int CGLIB$hashCode$3()
{
return super.hashCode();
}
public final int hashCode()
{
MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
if (tmp4_1 == null)
{
tmp4_1;
CGLIB$BIND_CALLBACKS(this);
}
MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;
if (tmp17_14 == null)
break label52;
label45: label52: if (tmp17_14.intercept(this, CGLIB$hashCode$3$Method, CGLIB$emptyArgs, CGLIB$hashCode$3$Proxy) != null)
break label45;
}
final Object CGLIB$clone$4()
throws CloneNotSupportedException
{
return super.clone();
}
protected final Object clone()
throws CloneNotSupportedException
{
MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
if (tmp4_1 == null)
{
tmp4_1;
CGLIB$BIND_CALLBACKS(this);
}
MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;
if (tmp17_14 != null)
return tmp17_14.intercept(this, CGLIB$clone$4$Method, CGLIB$emptyArgs, CGLIB$clone$4$Proxy);
return super.clone();
}
public static MethodProxy CGLIB$findMethodProxy(Signature paramSignature)
{
String tmp4_1 = paramSignature.toString();
switch (tmp4_1.hashCode())
{
case -