Spring框架进阶Spring V2.0 AOP

Posted 烟锁迷城

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring框架进阶Spring V2.0 AOP相关的知识,希望对你有一定的参考价值。

1、源码方法

AOP是Spring框架最重要的功能之一,它负责减少代码的冗余,简化开发流程。AOP,就是面向切面,主要使用代理模式进行设计

在Spring中,其实现流程是

  1. getBean:获取Bean的方法
  2. ApplicationContext:不必赘述
  3. AdvisedSupport:完成对配置文件的解析,构建切面与切点的关系
  4. AopConfig:保存AOP配置信息
  5. Advice:完成切面方法的回调
  6. JDKDynamicAopProxy:生成代理类,此为JDK代理,还有CglibAopProxy完成Cglib的代理,Spring同时支持两种方式的代理,因为它们都继承AopProxy,通过DefaultAopProxyFactory的策略模式来进行选择。

2、实现思路

在实例化Bean的时候,查看是否需要进行代理,如果需要,就要将原本的类替换为代理对象,反之返回初始对象。

检测是否需要代理的条件是是否满足代理的过滤要求,需要指定对应的代理。

实际上,在Spring框架中,实际被代理的类是实现类,而非接口。

设置一个类,AopConfig,用来保存这些固定的配置。

public class MyAopConfig 

    private String pointCut;
    private String aspectClass;
    private String aspectBefore;
    private String aspectAfter;
    private String aspectAfterThrow;
    private String aspectAfterThrowingName;

    public String getPointCut() 
        return pointCut;
    

    public void setPointCut(String pointCut) 
        this.pointCut = pointCut;
    

    public String getAspectClass() 
        return aspectClass;
    

    public void setAspectClass(String aspectClass) 
        this.aspectClass = aspectClass;
    

    public String getAspectBefore() 
        return aspectBefore;
    

    public void setAspectBefore(String aspectBefore) 
        this.aspectBefore = aspectBefore;
    

    public String getAspectAfter() 
        return aspectAfter;
    

    public void setAspectAfter(String aspectAfter) 
        this.aspectAfter = aspectAfter;
    

    public String getAspectAfterThrow() 
        return aspectAfterThrow;
    

    public void setAspectAfterThrow(String aspectAfterThrow) 
        this.aspectAfterThrow = aspectAfterThrow;
    

    public String getAspectAfterThrowingName() 
        return aspectAfterThrowingName;
    

    public void setAspectAfterThrowingName(String aspectAfterThrowingName) 
        this.aspectAfterThrowingName = aspectAfterThrowingName;
    

 里面放置的条件依次为

  1. 符合匹配条件的类名
  2. 作为切面功能的类
  3. 前置方法
  4. 后置方法
  5. 抛错方法
  6. 抛错类型
private MyAdvisedSupport instantiateAopConfig() 
    MyAopConfig config = new MyAopConfig();
    config.setPointCut("public .* com.example.springwrite.demo.service..*ServiceImpl..*(.*)");
    config.setAspectClass("com.example.springwrite.demo.aspect.LogAspect");
    config.setAspectBefore("aspectBefore");
    config.setAspectAfter("aspectAfter");
    config.setAspectAfterThrow("aspectAfterThrow");
    config.setAspectAfterThrowingName("java.lang.Exception");
    return new MyAdvisedSupport(config);

 增加类AdvisedSupport,用来保存被代理的类和类实例

public class MyAdvisedSupport 

    private MyAopConfig config;

    //目标Class
    private Class targetClass;
    //目标对象
    private Object target;

    public MyAdvisedSupport(MyAopConfig config) 
        this.config = config;
    

如果符合代理筛选规则,就进行代理,反之就不需要

代理使用的是策略模式,用来自动选择使用JDK代理还是Cglib代理

    private Object instantiateBean(String beanName, MyBeanDefinition beanDefinition) 

        //如果是单例对象,直接返回在缓存中的对象
        if (beanDefinition.isSingleton() && this.factoryBeanObjectCache.containsKey(beanName)) 
            return this.factoryBeanObjectCache.get(beanName);
        

        String beanClassName = beanDefinition.getBeanClassName();
        Object instance = null;
        try 
            Class<?> aClass = Class.forName(beanClassName);
            instance = aClass.newInstance();

            //如果是代理对象,将触发AOP代理
            MyAdvisedSupport config = instantiateAopConfig();
            config.setTarget(instance);
            config.setTargetClass(aClass);

            //判断是否需要代理,需要就调用代理工厂生成代理类,然后放入三级缓存
            //如果不需要,就返回原生类
            if (config.pointCutMatch()) 
                instance = proxyFactory.createAopProxy(config).getProxy();
            
            this.factoryBeanObjectCache.put(beanName, instance);
            for (Class<?> anInterface : aClass.getInterfaces()) 
                this.factoryBeanObjectCache.put(anInterface.getName(), instance);
            
         catch (Exception e) 
            e.printStackTrace();
        
        return instance;
    

使用默认代理工厂,如果有接口,就使用JDK代理,如果没有就使用Cglib代理

public class MyDefaultAopProxyFactory 

    public MyAopProxy createAopProxy(MyAdvisedSupport config) 
        if (config.getTargetClass().getInterfaces().length > 0) 
            return new MyJdkDynamicAopProxy(config);
        
        return new MyCglibAopProxy(config);
    
MyCglibAopProxy和MyJdkDynamicAopProxy都继承于MyAopProxy 
public interface MyAopProxy 

    Object getProxy();

    Object getProxy(ClassLoader classLoader);

现在实现判断是否需要代理的方法,这里我简化了一下,只过滤指定的包下的类。

public class MyAdvisedSupport 

    private MyAopConfig config;

    private Map<Method, List<MyMethodInterceptor>> methodCache = new HashMap<Method, List<MyMethodInterceptor>>();

    private Pattern pointCutClassPattern;

    //目标Class
    private Class targetClass;
    //目标对象
    private Object target;

    public MyAdvisedSupport(MyAopConfig config) 
        this.config = config;
    

    public MyAopConfig getConfig() 
        return config;
    

    public Class getTargetClass() 
        return targetClass;
    

    public void setTargetClass(Class targetClass) 
        this.targetClass = targetClass;
    

    public Object getTarget() 
        return target;
    

    public void setTarget(Object target) 
        this.target = target;
    

    public boolean pointCutMatch() 
        boolean matches = this.targetClass.toString().contains("com.example.springwrite.demo.service");
        return matches;
    

 在Spring框架中,将方法与代理方法列表的关系保存起来,并且将所有代理切面方法放进这个列表中,形成链条。

private Map<Method, List<MyMethodInterceptor>> methodCache = new HashMap<Method, List<MyMethodInterceptor>>();

 List<MyMethodInterceptor>是方法拦截,可以根据目前的代理增强分为三种,前置,后置和错误后置。

基础方法,只有调用增强

public interface MyMethodInterceptor 

    Object invoke(MyMethodInvocation invocation) throws Throwable;

 这个抽象类的目的在于,让三个切面能有公共的调用抽取,就是invokeAdviceMethod

invokeAdviceMethod具有三个参数,切点,返回值和抛出错误,其内部是用反射实现的,反射将调用aspect切面类的advicemethod方法。有没有参数作为调用区分,如果无参,则直接反射调用即可,如果为有参,则需要根据参数类型判断该参数在列表中的位置,放入后完成反射调用

public abstract class MyAbstractAspectJAdvice implements MyAdvice 

    private Object aspect;
    private Method adviceMethod;
    private String throwName;

    public MyAbstractAspectJAdvice(Object aspect, Method adviceMethod) 
        this.aspect = aspect;
        this.adviceMethod = adviceMethod;
    

    protected Object invokeAdviceMethod(MyJoinPoint joinPoint, Object returnValue, Throwable ex) throws Throwable 
        Class<?>[] parameterTypes = this.adviceMethod.getParameterTypes();
        if (null == parameterTypes || parameterTypes.length == 0) 
            return this.adviceMethod.invoke(aspect);
         else 
            Object[] args = new Object[parameterTypes.length];
            for (int i = 0; i < parameterTypes.length; i++) 
                if (parameterTypes[i] == MyJoinPoint.class) 
                    args[i] = joinPoint;
                 else if (parameterTypes[i] == Throwable.class) 
                    args[i] = ex;
                 else if (parameterTypes[i] == Object.class) 
                    args[i] = returnValue;
                
            
            return this.adviceMethod.invoke(aspect, args);
        
    

切点接口包含一些参数,包括代理的类,参数列表,代理的方法,存放键值对的UserAttribute方法

public interface MyJoinPoint 

    Object getThis();

    Object[] getArguments();

    Method getMethod();

    void setUserAttribute(String key, Object value);

    Object getUserAttribute(String key);


 其对应的实现为MethodInvocation,这里面实现了关键的proceed方法,用来实现代理的反复调用。

如果代理链条List<MyMethodInterceptor> chain是空的,就代表没有任何代理,直接调用本来的方法即可。

如果不为空,就从第一个开始进行匹配,如果匹配结果是MethodInterceptor的继承类,就执行代理,反之就进行递归调用。

但是可以发现的是,这里的方法并不区分前置,后置和抛错,那么AOP是如何确保执行顺序的呢?

public class MyMethodInvocation implements MyJoinPoint 

    protected final Object proxy;

    protected final Object target;

    protected final Method method;

    protected Object[] arguments = new Object[0];

    private final Class<?> targetClass;

    private Map<String, Object> userAttributes = new HashMap<String, Object>();

    protected final List<?> interceptorsAndDynamicMethodMatchers;

    private Map<String, Object> userAttribute = new HashMap<String, Object>();

    private int currentInterceptorIndex = -1;

    public MyMethodInvocation(Object proxy, Object target, Method method, Object[] args, Class targetClass, List<MyMethodInterceptor> chain) 
        this.proxy = proxy;
        this.target = target;
        this.method = method;
        this.arguments = args;
        this.targetClass = targetClass;
        this.interceptorsAndDynamicMethodMatchers = chain;
    

    public Object proceed() throws Throwable 
        //如果代理链条是空的,就证明没有被代理,直接执行方法本身
        if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) 
            return this.method.invoke(target, this.arguments);
        
        Object interceptorsAndDynamic = this.interceptorsAndDynamicMethodMatchers.get(++currentInterceptorIndex);
        //如果符合匹配要求,就执行对应代理
        if (interceptorsAndDynamic instanceof MyMethodInterceptor) 
            MyMethodInterceptor mi = (MyMethodInterceptor) interceptorsAndDynamic;
            return mi.invoke(this);
         else 
            //否则递归调用
            return proceed();
        
    

    public Method getMethod() 
        return this.method;
    

    @Override
    public void setUserAttribute(String key, Object value) 
        this.userAttribute.put(key, value);
    

    @Override
    public Object getUserAttribute(String key) 
        return this.userAttribute.get(key);
    

    public Object[] getArguments() 
        return this.arguments;
    

    public Object getThis() 
        return this.target;
    

在MethodBeforeAdviceInterceptor类中,可以看到,是先执行前置增强,再进行递归调用

public class MyMethodBeforeAdviceInterceptor extends MyAbstractAspectJAdvice implements MyMethodInterceptor 

    private MyJoinPoint joinPoint;

    public MyMethodBeforeAdviceInterceptor(Object newInstance, Method method) 
        super(newInstance, method);
    

    @Override
    public Object invoke(MyMethodInvocation mi) throws Throwable 
        joinPoint = mi;
        this.before(mi.getMethod(), mi.getArguments(), mi.getThis());
        return mi.proceed();
    

    private void before(Object method, Object arguments, Object aThis) throws Throwable 
        invokeAdviceMethod(joinPoint, null, null);
    

 在MyMethodAfterReturningAdviceInterceptor中,先执行递归调用,再进行增强方法调用

public class MyMethodAfterReturningAdviceInterceptor extends MyAbstractAspectJAdvice implements MyMethodInterceptor 

    private MyJoinPoint joinPoint;

    public MyMethodAfterReturningAdviceInterceptor(Object newInstance, Method method) 
        super(newInstance, method);
    

    @Override
    public Object invoke(MyMethodInvocation mi) throws Throwable 
        joinPoint = mi;
        Object proceed = mi.proceed();
        this.after(proceed, mi.getMethod(), mi.getArguments(), mi.getThis());
        return proceed;
    

    private void after(Object proceed, Method method, Object[] arguments, Object aThis) throws Throwable 
        this.invokeAdviceMethod(joinPoint, proceed, null);
    

在MyAspectAfterThrowingAdvice 中,只有抛出错误,才会执行

public class MyAspectAfterThrowingAdvice extends MyAbstractAspectJAdvice implements MyMethodInterceptor 

    private String throwingName;

    public MyAspectAfterThrowingAdvice(Object newInstance, Method method) 
        super(newInstance, method);
    

    @Override
    public Object invoke(MyMethodInvocation mi) throws Throwable 
        try 
            return mi.proceed();
         catch (Throwable ex) 
            this.invokeAdviceMethod(mi, null, ex);
            throw ex;
        
    

    public void setThrowName(String aspectAfterThrowingName) 
        this.throwingName = aspectAfterThrowingName;
    

这样通过递归调用和增强调用的顺序更换,就保证了增强顺序的执行不会有错误。

那么接下来就是将代理方法装载到对应的代理链条中

这个方法就是通过获取到被代理类的全部方法,将这些方法和代理增强方法进行绑定。

private void parse() 
    //修饰符 返回值 包名 类名 方法名(参数列表)
    String pointCut = config.getPointCut()
            .replaceAll("\\\\.", "\\\\\\\\.")
            .replaceAll("\\\\\\\\.\\\\*", ".*")
            .replaceAll("\\\\(", "\\\\\\\\(")
            .replaceAll("\\\\)", "\\\\\\\\)");

    methodCache = new HashMap<Method, List<MyMethodInterceptor>>();
    Map<String, Method> aspectMethods = new HashMap<String, Method>();
    try 
        Class aspectClass = Class.forName(this.config.getAspectClass());
        for (Method method : aspectClass.getMethods()) 
            aspectMethods.put(method.getName(), method);
        

        for (Method method : this.targetClass.getMethods()) 
            String methodString = method.toString();
            if (methodString.contains("throw")) 
                methodString = methodString.substring(0, methodString.lastIndexOf("throws")).trim();
            
            Matcher matcher = Pattern.compile(pointCut).matcher(methodString);
            if (matcher.matches()) 
                List<MyMethodInterceptor> advices = new LinkedList<MyMethodInterceptor>();
                if (null != this.config.getAspectBefore() && !"".equals(this.config.getAspectBefore())) 
                    advices.add(new MyMethodBeforeAdviceInterceptor(aspectClass.newInstance(), aspectMethods.get(this.config.getAspectBefore())));
                
                if (null != this.config.getAspectAfter() && !"".equals(this.config.getAspectAfter())) 
                    advices.add(new MyMethodAfterReturningAdviceInterceptor(aspectClass.newInstance(), aspectMethods.get(this.config.getAspectAfter())));
                
                if (null != this.config.getAspectAfterThrow() && !"".equals(this.config.getAspectAfterThrow())) 
                    MyAspectAfterThrowingAdvice myAspectAfterThrowingAdvice = new MyAspectAfterThrowingAdvice(aspectClass.newInstance(), aspectMethods.get(this.config.getAspectAfterThrow()));
                    myAspectAfterThrowingAdvice.setThrowName(this.config.getAspectAfterThrowingName());
                    advices.add(myAspectAfterThrowingAdvice);
                
                this.methodCache.put(method, advices);
            
        
     catch (Exception e) 
        e.printStackTrace();
    

绑定完毕,就需要进行调用了,调用时通过JDK代理进行,即JDKDynamicAopProxy,继承InvocationHandler 之后,重写invoke方法,在这里获取到对应的代理链条等数据。

在这里有一个方法,getInterceptorsAndDynamicInterceptionAdvice是用来获取代理链条的,但是直接从绑定关系中获取不就可以了,为什么还要一个单独的方法?

public class MyJdkDynamicAopProxy implements MyAopProxy, InvocationHandler 

    private MyAdvisedSupport advised;

    public MyJdkDynamicAopProxy(MyAdvisedSupport advised) 
        this.advised = advised;
    

    @Override
    public Object getProxy() 
        return getProxy(this.getClass().getClassLoader());
    

    @Override
    public Object getProxy(ClassLoader classLoader) 
        return Proxy.newProxyInstance(classLoader, this.advised.getTargetClass().getInterfaces(), this);
    

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 
        List<MyMethodInterceptor> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, this.advised.getTargetClass());
        MyMethodInvocation mi = new MyMethodInvocation(proxy, this.advised.getTarget(), method, args, this.advised.getTargetClass(), chain);
        return mi.proceed();
    

因为方法是有可能没有获取到的,也许是因为接口继承等缘故,所以需要根据方法名和参数名到被代理类中重新寻找锁定,将正确的结果重新绑定并返回,是一个保险作用。

public List<MyMethodInterceptor> getInterceptorsAndDynamicInterceptionAdvice(Method method, Class targetClass) 
    List<MyMethodInterceptor> stringMyAdviceMap = this.methodCache.get(method);
    if (null == stringMyAdviceMap) 
        Method m = null;
        try 
            m = targetClass.getMethod(method.getName(), method.getParameterTypes());
         catch (NoSuchMethodException e) 
            e.printStackTrace();
        
        stringMyAdviceMap = methodCache.get(m);
        methodCache.put(method, stringMyAdviceMap);
    
    return stringMyAdviceMap;

3、最终总结

AOP代理的本质是调用代理模式,从实例化Bean开始切入,借助三级缓存来进行实现,获取对应的切面配置,用AdviceSupport完成代理方法和切点链条的匹配,用MethodInvocation保存切点信息,用策略来选择使用JdkDynamicAopProxy还是MyCglibAopProxy,因为这两个都继承了AopProxy类,用MethodInterecptor进行拦截,Adice进行切点回调,通过递归调用proceed方法来实现代理,并且通过调整增强方法和proceed方法的顺序来保证前置后置等切面方法的顺序执行,是非常精妙的设计

以上是关于Spring框架进阶Spring V2.0 AOP的主要内容,如果未能解决你的问题,请参考以下文章

Spring框架进阶Spring V2.0 IOC与DI

Spring框架进阶Spring V2.0 IOC与DI

Spring框架进阶Spring V2.0 循环依赖

Spring框架进阶Spring V2.0 循环依赖

Spring框架进阶Spring V3.0 AOP源码分析流程

用30个类手写Spring V2.0版本之AOP实现与总结