目录
1.前言
2.代理的实现
2.1 静态代理
2.2 动态代理
2.2.1 JDK实现动态代理
2.2.2 CGLIB实现动态代理
3. Spring中AOP的实现
1.前言
在上一遍文章Spring之面向切面编程(AOP)中主要是了解了AOP相关的知识,这篇文章中主要讲解AOP的实现原理。
2.代理的实现
上一篇文章中我们有讲到,代理可分为静态代理和动态代理,而动态代理又有jdk动态代理和cglib动态代理两种不同的实现,下面来讲解具体的实现。
2.1 静态代理
静态代理比较好理解,就是我们自己创建一个代理对象,去代替被代理的对象做事情,进而对被代理的对象进行增强。
我们先创建一个动物的接口,并定义了一个方法call
public interface Animal {
void call();
}
然后创建一个动物的实现类Dog,并实现了call方法,如下:
public class Dog implements Animal {
@Override
public void call() {
System.out.println("Dog call");
}
}
这个Dog就是被代理的对象,我们现在创建一个代理类AnimalProxy,它实现了Animal,如下:
public class AnimalProxy implements Animal{
private Animal animal;
//传入被代理的对象
public AnimalProxy(Animal animal) {
this.animal = animal;
}
@Override
public void call() {
System.out.println("调用业务方法之前执行");
//调用被代理对象的业务方法
animal.call();
System.out.println("调用业务方法之后执行");
}
}
然后我们看方法的调用
public static void main(String[] args) {
//1.静态代理
//创建被代理的对象
Dog dog = new Dog();
//创建代理对象,并把被代理的对象通过构造方法传入
Animal animal = new AnimalProxy(dog);
//调用代理类的方法
animal.call();
}
可以看到,我们先创建被代理的对象dog,然后把它传入到代理类AnimalProxy的构造方法去创建代理对象,最后调用代理类的call方法,进而调用了dog的call方法,当然在调用dog的call方法之前和之后,我们可以自定义的业务逻辑,比如做日志拦截、权限控制等。
但是静态代理存在一个问题,就是针对每一个接口,都需要手动去写一个代理类,有没有什么好的方式呢?答案是肯定有的,这就要说到动态代理了。
2.2 动态代理
2.2.1 JDK实现动态代理
JDK动态代理主要有用到两个类,InvacationHandler和Proxy
我们先看InvacationHandler,它是一个接口,其中只有一个invoke方法
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
我们可以通过实现该接口,并重写invoke方法,来把横切逻辑和业务逻辑编织在一起
而Proxy类是用于创建代理对象的,我们可以调用其newProxyInstance方法来创建代理对象
public class Proxy implements java.io.Serializable {
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class>[] interfaces,
InvocationHandler h) {
//省略代码
}
}
其中:
第一个参数为类加载器
第二个参数是要实现的接口,根据jdk动态代理的实现,这里应该是我们需要代理类的父接口,我们拿2.1中为例,则这里为Animal
第三个参数是一个InvacationHandler实例,其实就是我们自定义的InvacationHandler实现类,用于在业务方法调用之前进行拦截
接下来,我们首先自定义类JDKProxyHandler,并实现InvacationHandler
//实现InvocationHandler接口
public class JDKProxyHandler implements InvocationHandler {
//目标业务类
private Object target;
//传入目标业务类
public JDKProxyHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("jdk动态代理 调用业务方法之前");
//利用反射调用业务类的目标方法
Object reval = method.invoke(target, args);
System.out.println("jdk动态代理 调用业务方法之后");
return reval;
}
}
在我们自定义的JDKProxyHandler中,我们提供通过构造方法把目标业务类的对象传递进来,然后在invoke方法中,可以通过method.invoke(target, args)来调用业务类的目标方法,并且在这之前或者之后可以做增强逻辑。
我们再来看看调用端的使用
public static void main(String[] args) {
//2.jdk动态代理
//被代理的目标业务类
Animal jdkTarget = new Dog();
//将目标业务类和横切代码编织到一起
JDKProxyHandler jdkProxyHandler = new JDKProxyHandler(jdkTarget);
//创建代理对象
Animal jdkAnimal = (Animal) Proxy.newProxyInstance(jdkTarget.getClass().getClassLoader(), jdkTarget.getClass().getInterfaces(), jdkProxyHandler);
//调用代理类的方法
jdkAnimal.call();
}
我们运行起来可以发现,jdkTarget.getClass().getInterfaces()其实就是Animal,并且最后通过Proxy.newProxyInstance方法得到的其实是一个代理类对象
当我们调用了代理对象的call方法,则会进入到我们重写的invoke方法中,如下:
可以看到,proxy就是我们新创建出来的代理对象,method就是我们调用的call方法,target则是被代理的Dog对象,最后我们通过method.invoke(target,args)反射来调用业务类的方法call
现在这个图,是不是更好理解了?
2.2.2 CGLIB实现动态代理
如果我们被代理的类没有实现接口,比如下面的猫类Cat没有实现Animal,则不能使用jdk动态代理,而需要使用cglib动态代理的方式来实现
public class Cat {
public void call() {
System.out.println("Cat call");
}
}
cglib是采用底层的字节码技术,可以为我们被代理的类去创建一个子类,然后在子类中采用方法拦截的技术拦截所有父类方法的调用,并在其中去织入我们的横切逻辑。
主要是使用了MethodInterceptor接口,它实现了Callback接口,其中的intercept方法就为拦截方法
public interface MethodInterceptor extends Callback {
Object intercept(Object var1, Method var2, Object[] var3, MethodProxy var4) throws Throwable;
}
其中:
第一个参数Object var1是代理对象
第二个参数Method var2是被调用的方法
第三个参数Object[] var3是方法的参数
第四个参数MethodProxy var4是方法的代理,可通过该对象的invokeSuper方法调用其父类的方法
是不是跟InvocationHandler的invoke方法很像?
另外,springframework提供了一个Enhancer类用于去创建代理对象
下面我们来创建一个MethodInterceptor的实现类:
public class CglibInterceptor implements MethodInterceptor {
public Object getProxy(Class targetClass) {
Enhancer enhancer = new Enhancer();
//设置回调对象
enhancer.setCallback(this);
//设置父类为被代理的对象
enhancer.setSuperclass(targetClass);
//创建子类实例(代理对象)
return enhancer.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("cglib动态代理 调用业务方法之前");
//通过代理类调用父类中的方法
Object reval = methodProxy.invokeSuper(obj, args);
System.out.println("cglib动态代理 调用业务方法之后");
return reval;
}
}
首先,我们提供了一个getProxy方法,用于获取代理对象,参数为需要代理的类的class对象,这里为Cat的class对象
通过Enhancer的setCallback方法去设置回调的独享,这里就是本身,所以传的是this
通过Enhancer的setSuperclass方法去设置父类,这里就是传入的参数
最后调用Enhancer的create去创建一个代理对象
然后我们重写了intercept方法,通过methodProxy.invokeSuper(obj,args)调用代理对象父类的业务方法(Cat中的call方法),当然在这之前和之后可以实现横切逻辑。
接下来,我们看看调用端:
public static void main(String[] args) {
//3.cglib动态代理
//方法的拦截对象
CglibInterceptor cglibInterceptor = new CglibInterceptor();
Cat cat = (Cat) cglibInterceptor.getProxy(Cat.class);
//调用代理类的方法
cat.call();
}
我们运行起来,可以看到创建出来的代理对象其实是Cat的子类
我们再来回头看看CGLIB动态代理的实现,是不是很一目了然了呢?
3. Spring中AOP的实现
我们以MyController的test方法作为连接点看看Spring中AOP的实现逻辑
@RestController
@RequestMapping("/my")
public class MyController {
@GetMapping("/test")
public void test() {
System.out.println("test 业务方法");
}
}
然后我们定义一个切面MyAspect
@Aspect //告诉Spring 这是一个切面
@Component //告诉Spring容器需要管理该对象
public class MyAspect {
//通过规则确定哪些方法是需要增强的
@Pointcut("execution (public * com.yc.springboot.controller.MyController.*())")
public void controller() {
}
//前置通知
@Before("controller()")
public void before(JoinPoint joinPoint) {
System.out.println("before advice");
}
//返回通知
@AfterReturning(
pointcut = "controller()",
returning = "retVal"
)
public void afterReturning(JoinPoint joinPoint, Object retVal) {
System.out.println("after returning advice, 返回结果 retVal:" + retVal);
}
//异常通知
@AfterThrowing(
pointcut = "controller()",
throwing = "ex"
)
public void afterThrowing(JoinPoint joinPoint, Exception ex) {
System.out.println("after throwing advice, 异常 ex:" + ex.getMessage());
}
//后置通知
@After("controller()")
public void after(JoinPoint joinPoint) {
System.out.println("after advice");
}
//环绕通知
@Around("controller()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("around before advice");
//相当于是before advice
Object reval = null;
try {
reval = joinPoint.proceed();
} catch (Exception e) {
//相当于afterthrowing advice
System.out.println("around afterthrowing advice");
}
//相当于是after advice
System.out.println("around after advice");
return reval;
}
}
因为MyController没有实现任何接口,所以Spring是使用CGLIB动态代理来实现的,如果Spring提供了一个跟我们之前自定义的CglibInterceptor类似的类,是不是就可以实现了呢?
这就是DynamicAdvisedInterceptor,它实现了MethodInterceptor接口,并重写了intercept方法
private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {
private final AdvisedSupport advised;
public DynamicAdvisedInterceptor(AdvisedSupport advised) {
this.advised = advised;
}
@Override
@Nullable
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
Object target = null;
TargetSource targetSource = this.advised.getTargetSource();
try {
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// Get as late as possible to minimize the time we "own" the target, in case it comes from a pool...
target = targetSource.getTarget();
Class> targetClass = (target != null ? target.getClass() : null);
List
我们启动项目,通过http://localhost:8080/my/test访问后,就会进入intercept方法
可以看到代理对象proxy是MyController的一个使用CGLIB增强的子类,调用的方法为test
我们继续看DynamicAdvisedInterceptor中的intercept方法,首先要看
List
该方法获得了方法的所有增强,
然后通过chain去创建一个CglibMethodInvacation对象
new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy)
它继承了ReflectiveMethodInvocation类
private static class CglibMethodInvocation extends ReflectiveMethodInvocation
调用了其proceed方法,我们看看代码:
@Override
@Nullable
public Object proceed() throws Throwable {
try {
return super.proceed();
}
catch (RuntimeException ex) {
throw ex;
}
catch (Exception ex) {
if (ReflectionUtils.declaresException(getMethod(), ex.getClass())) {
throw ex;
}
else {
throw new UndeclaredThrowableException(ex);
}
}
}
只是调用了父类的proceed方法,其实就是ReflectiveMethodInvocation的proceed方法:
@Override
@Nullable
public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// evaluate dynamic method matcher here: static part will already have
// been evaluated and found to match.
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
Class> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {
// Dynamic matching failed.
// Skip this interceptor and invoke the next in the chain.
return proceed();
}
}
else {
// It's an interceptor, so we just invoke it: The pointcut will have
// been evaluated statically before this object was constructed.
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
currentInterceptorIndex是表示chain中当前拦截的序号,开始为-1,这里把currentInterceptorIndex自增1,然后作为下标从chain中获取值,到的则是ExposeInvocationInterceptor,然后调用它的invoke方法,我们来看看它的invoke方法
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
MethodInvocation oldInvocation = invocation.get();
invocation.set(mi);
try {
return mi.proceed();
}
finally {
invocation.set(oldInvocation);
}
}
直接调用了mi的proceed方法,而mi就是传入进来的参数,是CglibMethodInvacation对象,所以又回到了它的proceed方法,它会继续调用父类的proceed方法。
这个时候currentInterceptorIndex的值为0,自增后获取到的增强为AspectJAfterThrowingAdvice,看下它的invoke方法,如下:
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
try {
return mi.proceed();
}
catch (Throwable ex) {
if (shouldInvokeonThrowing(ex)) {
invokeAdviceMethod(getJoinPointMatch(), null, ex);
}
throw ex;
}
}
其实也是回到了CglibMethodInvacation对象,所以又回到了它的proceed方法
直到AspectJAroundAdvice,它的invoke方法比较特殊
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
if (!(mi instanceof ProxyMethodInvocation)) {
throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
}
ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi);
JoinPointMatch jpm = getJoinPointMatch(pmi);
return invokeAdviceMethod(pjp, jpm, null, null);
}
而在invokeAdviceMethod方法中,是一连串的调用,如下:
protected Object invokeAdviceMethod(
@Nullable JoinPointMatch jpMatch, @Nullable Object returnValue, @Nullable Throwable ex)
throws Throwable {
return invokeAdviceMethodWithGivenArgs(argBinding(getJoinPoint(), jpMatch, returnValue, ex));
}
protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable {
Object[] actualArgs = args;
if (this.aspectJAdviceMethod.getParameterCount() == 0) {
actualArgs = null;
}
try {
ReflectionUtils.makeAccessible(this.aspectJAdviceMethod);
// TODO AopUtils.invokeJoinpointUsingReflection
return this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs);
}
catch (IllegalArgumentException ex) {
throw new AopInvocationException("Mismatch on arguments to advice method [" +
this.aspectJAdviceMethod + "]; pointcut expression [" +
this.pointcut.getPointcutexpression() + "]", ex);
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
这里需要关注的是this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs);这里的aspectJAdviceMethod是MyAspect的around方法,其实就是我们的环绕增强的方法,它会利用发射直接调用around方法,我们再来看看我们自定义的环绕增强
//环绕通知
@Around("controller()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("around before advice");
//相当于是before advice
Object reval = null;
try {
reval = joinPoint.proceed();
} catch (Exception e) {
//相当于afterthrowing advice
System.out.println("around afterthrowing advice");
}
//相当于是after advice
System.out.println("around after advice");
return reval;
}
需要关注的是joinPoint.proceed方法,这里调用的是MethodInvocationProceedingJoinPoint的proceed方法
@Override
public Object proceed() throws Throwable {
return this.methodInvocation.invocableClone().proceed();
}
这里调用了MethodInvocation的proceed方法,而procced方法是CglibMethodInvocation来实现的,所以又回到了CglibMethodInvocation的procced方法,然后继续走ReflectiveMethodInvocation的proceed方法,从chain中取值,当获取到的值为MethodBeforeAdviceInterceptor时,它的invoke方法也值得看一看:
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
return mi.proceed();
}



