SpringAoP动态代理以及拦截器链

Aop面向切面编程思想

我们都知道Java是一种面向对象的编程语言,强调用类和对象来实现一些功能,强调对象之间的组装思想,强调类与类之间封装、多态和继承的关系。
而在一些主业务逻辑之外,还有一些各类通用的业务代码,并不专属于某个类的功能,因此可能散落在工程的各个角落。这些功能可以做成切面,在任何需要业务增强的时候,插入到业务代码的前后,即可。

那么切面的插入是怎么实现的

切面的插入是说,要在某个对象执行某个方法的时候,在特定的点上插入增强功能,像是改变了这个方法,但实际上并没有改变原来的方法,这就是通过代理来实现的。

静态代理和动态代理

一般静态代理和被代理的普通类实现了同样的接口,但是静态代理可以添加额外的功能代码。但是静态代理是编译之前就写好的类。编译之后不能再变,要再添加业务代码需要重新编译。
动态代理也是为了实现业务代码的增强,但是,是在运行过程中代理一个具体类的实例,通过反射的思想,获取其class字节码,获取构造器,构造一个代理对象,并使用代理对象的方法,具体类的方法往往被代理类的方法增强。

生成动态代理的三个必要条件

看一下动态代理的生成方法

public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)//调用器自己实现invoke
//newProxyInstance返回一个具体实例的代理类。
//具体过程:先获得接口的二进制字节码文件
Class<?> cl = getProxyClass0(loader, intfs);
//然后获取代理类的构造器
final Constructor<?> cons = cl.getConstructor(constructorParams);
//然后返回一个代理实例
return cons.newInstance(new Object[]{h});

第一个必要条件是classLoader,第二个必要条件是接口class,第三个条件是InvocationHandler。
其中,InvocationHandler接口只有一个方法,就是invoke。实现了这个这个接口的类就可以作为某个对象的handler,当那个对象执行某个方法的时候,将被handler接手。

生成自己的动态代理

//首先得有一个Handler
//说明在对象执行invoke方法的时候,将会有哪些增强操作。
public class Handler implements InvocationHandler {
    private Object target;
    Handler(Object o){
        super();
        this.target=o;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("准备执行操作");
        Object o=method.invoke(target,args);
        System.out.println("操作执行完毕");
        return o;
    }
}

//然后需要有一个接口和具体实现类,用于被代理和增强
public interface Pser {
    void show();
}

public class PserImpl implements Pser{
    @Override
    public void show(){
        System.out.println("展示任务执行过程");
    }
}

//最后写一个test类
public class Test {
    public static void main(String[] args) {
        Pser person=(Pser) Proxy.newProxyInstance(
                ClassLoader.getSystemClassLoader(),
                new Class[]{Pser.class},
                new Handler(new PserImpl()));
        person.show();
        //person是一个代理实例
        //这里的person已经不是普通对象Person了,而是代理后的对象person。
    }
}
//看一下执行结果
准备执行操作
展示任务执行过程
操作执行完毕

为什么JdkDynamicAopProxy只能代理接口

我们可以看到,通过Proxy类的newProxyInstance方法生成的代理对象都是Proxy类的子类,根据Java单继承的原则,只能继承一个类,而实现多个接口,因此这里实现要代理的接口,对接口方法进行增强。
在这里插入图片描述

AOP的拦截器链

我们知道常用的增强方法有@After后置增强@Before前置增强@Around环绕增强。
为什么使用Before注解就能自动帮我们加到切点的前面呢?
这里因为AOP内部会获取一条拦截器链。(拦截器就是我们增强方法的实例化)

List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
if (chain.isEmpty()) {
    Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
    retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
} else {
	//这里会依次执行拦截器链的各个拦截器的方法。
    MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
    retVal = invocation.proceed();
}

看一下拦截器的执行过程

public Object proceed() throws Throwable {
    //如果拦截器已经执行到最后一个,直接执行InvokeJoinPoint
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        return this.invokeJoinpoint();
        //底层就是method.invoke,直接使用反射调用方法
    } else {
        //说明还有拦截器等待执行
        //拦截器链的机制,保证通知方法与目标方法的执行顺序;
        Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
        if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
            InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher)interceptorOrInterceptionAdvice;
            Class<?> targetClass = this.targetClass != null ? this.targetClass : this.method.getDeclaringClass();
            //这一句选择执行拦截器的方法或者是执行下一个方法。
            return dm.methodMatcher.matches(this.method, targetClass, this.arguments) ? dm.interceptor.invoke(this) : this.proceed();
            
        } else {
            return ((MethodInterceptor)interceptorOrInterceptionAdvice).invoke(this);
        }
    }
}

在看一下Before和After对应的类的invoke方法的实现,就能理解为什么一个在方法之前执行,一个在方法之后执行。

//AspectJAfterAdvice
public Object invoke(MethodInvocation mi) throws Throwable {
    Object var2;
    try {
        //After是先执行业务方法
        var2 = mi.proceed();
    } finally {
        //再执行advice方法。
        this.invokeAdviceMethod(this.getJoinPointMatch(), (Object)null, (Throwable)null);
    }

    return var2;
}
//MethodBeforeAdviceInterceptor
public Object invoke(MethodInvocation mi) throws Throwable {
		//先执行advice 的before回调
		this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
		//再执行方法。
		return mi.proceed();
	}

最后说一下反射把

反射是再运行时获取对象的class、构造器的技术,一般在要求灵活性和扩展性较高的框架里面会用到反射技术,如果是普通Java项目,使用反射不如直接new来的快,但是new了的说明编译过了,不能变的。
利用反射获取对象的方法是:
获取class字节码、获取构造器、构造实例

String className = "charactor.Hero";
 //类
Class pClass=Class.forName(className);
//构造器
Constructor c= pClass.getConstructor();
//通过构造器实例化
Hero h2= (Hero) c.newInstance();
h2.name="gareen";
System.out.println(h2);

使用反射调用类的方法

扫描二维码关注公众号,回复: 11167895 查看本文章
//使用反射对一个对象调用某个方法。
Method m = h.getClass().getMethod("setName", String.class);
// 对h对象,调用这个方法
m.invoke(h, "盖伦");

分享完啦~

原创文章 64 获赞 27 访问量 9408

猜你喜欢

转载自blog.csdn.net/weixin_44893585/article/details/105300941