Spring之面向切面编程(AOP)的实现

Spring之面向切面编程(AOP)的实现,第1张

Spring之面向切面编程(AOP)的实现

目录

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 chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
				Object retVal;
				// Check whether we only have one InvokerInterceptor: that is,
				// no real advice, but just reflective invocation of the target.
				if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
					// We can skip creating a MethodInvocation: just invoke the target directly.
					// Note that the final invoker must be an InvokerInterceptor, so we know
					// it does nothing but a reflective operation on the target, and no hot
					// swapping or fancy proxying.
					Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
					retVal = methodProxy.invoke(target, argsToUse);
				}
				else {
					// We need to create a method invocation...
					retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
				}
				retVal = processReturnType(proxy, target, method, retVal);
				return retVal;
			}
			finally {
				if (target != null && !targetSource.isStatic()) {
					targetSource.releaseTarget(target);
				}
				if (setProxyContext) {
					// Restore old proxy.
					AopContext.setCurrentProxy(oldProxy);
				}
			}
		}

	} 

我们启动项目,通过http://localhost:8080/my/test访问后,就会进入intercept方法

 可以看到代理对象proxy是MyController的一个使用CGLIB增强的子类,调用的方法为test

我们继续看DynamicAdvisedInterceptor中的intercept方法,首先要看

List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); 

该方法获得了方法的所有增强, 

然后通过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();
	}

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5076114.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-11-16
下一篇 2022-11-16

发表评论

登录后才能评论

评论列表(0条)