1、源码方法
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<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方法的顺序来保证前置后置等切面方法的顺序执行,是非常精妙的设计