AOP是Spring框架最重要的功能之一,它负责减少代码的冗余,简化开发流程。AOP,就是面向切面,主要使用代理模式进行设计
在Spring中,其实现流程是
getBean:获取Bean的方法ApplicationContext:不必赘述AdvisedSupport:完成对配置文件的解析,构建切面与切点的关系AopConfig:保存AOP配置信息Advice:完成切面方法的回调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; }}
里面放置的条件依次为
符合匹配条件的类名作为切面功能的类前置方法后置方法抛错方法抛错类型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
在Spring框架中,将方法与代理方法列表的关系保存起来,并且将所有代理切面方法放进这个列表中,形成链条。
private Map
List
基础方法,只有调用增强
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
如果不为空,就从第一个开始进行匹配,如果匹配结果是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
在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
绑定完毕,就需要进行调用了,调用时通过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
因为方法是有可能没有获取到的,也许是因为接口继承等缘故,所以需要根据方法名和参数名到被代理类中重新寻找锁定,将正确的结果重新绑定并返回,是一个保险作用。
public List
AOP代理的本质是调用代理模式,从实例化Bean开始切入,借助三级缓存来进行实现,获取对应的切面配置,用AdviceSupport完成代理方法和切点链条的匹配,用MethodInvocation保存切点信息,用策略来选择使用JdkDynamicAopProxy还是MyCglibAopProxy,因为这两个都继承了AopProxy类,用MethodInterecptor进行拦截,Adice进行切点回调,通过递归调用proceed方法来实现代理,并且通过调整增强方法和proceed方法的顺序来保证前置后置等切面方法的顺序执行,是非常精妙的设计