什么是AOP
AOP是不同于OOP的一种编程思想,AOP关注的是横向,OOP关注的是纵向
将系统中与业务无关的动作或服务(如:日志记录、事务处理、权限控制)设计为通用、不介入特定业务对象的、职责清楚的Aspect对象,这就是Aspect-oriented programming,即AOP编程
- Aspect:使用代理对象将记录等与业务无关的动作或任务提出来,设计为一个服务对象,这样的对象称为切面
- Advice:Aspect指的是一类服务,而Advice是这类服务当中具体的一个,如日志服务前面的DynamicProxy类 (Advice与Aspect是具体与抽象的关系)
- Pointcut(切入点):Pointcut是一个定义,可以由它定义一些织入时机(Joinpoint)
- Joinpoint(织入时机,连接点):在业务流程执行时织入Aspect对象的时机,如业务方法执行之前、之后、异常发生时
- Cross-cutting-concern:StudentService本身的职责添加学生,却必须插入日志记录这样的与业务无关的动作,这样的动作AOP中称为横切关切点(Cross-cutting-concern)
创建过程
创建代理之前需要,一些准备工作,设置拦截器,设置父类,设置回调
- 设置拦截器,这里通过切入表达式来匹配需要的Advisor,筛选完仅需拓扑排序就是成为了这个类所需要的Advisor,设置进拦截器链 (Aspect到Advisor解析过程 是在第一次调用findcandidateadvisor解析出来的,后面匹配过程直接使用,注意的是不同方法可能使用的拦截器链不同)
resolveBeforeInstantiation -> applyBeanPostProcessorsBeforeInstantiation ->postProcessBeforeInstantiation
判断逻辑
if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
if (this.advisedBeans.containsKey(cacheKey)) {
return null;
}
if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return null;
}
}
isInfrastructureClass:判断当前是否是spring自己需要的系统内部类(是否为Advice,Pointcut,Advisor…)
shouldSkip:判断当前类是否需要被被代理,判断就需要找到所有的advisor来判定,所以第一次调用这个方法就会把所有的advisor创建出来。是通过类型把容器及其父容器的advisorName取出来,下面就是取出来的Name结果
advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Advisor.class, true, false);
@Override
protected boolean shouldSkip(Class<?> beanClass, String beanName) {
// TODO: Consider optimization by caching the list of the aspect names
//获取advisor
List<Advisor> candidateAdvisors = findCandidateAdvisors();
//advisor和aspect无需代理
for (Advisor advisor : candidateAdvisors) {
if (advisor instanceof AspectJPointcutAdvisor &&
((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) {
return true;
}
}
return super.shouldSkip(beanClass, beanName);
}
AspectJPointcutAdvisor#0 虽然是相同的前缀,但是# 后数字不同执行的对象是不同的,比如#0指向的是AspectJAround
得到Name自然可以getBean来实例化
advisors.add(this.beanFactory.getBean(name, Advisor.class));
一般是调用无参构造来创建对象,后面属性填充来填充属性
但这些Advisor没有无参,调用的有参构造,所以需要把构造器中需要的对象提前创建好。
public AspectJAroundAdvice(
Method aspectJAroundAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {
super(aspectJAroundAdviceMethod, pointcut, aif);
}
所以创建过程会设计Method AspectJExpressionPointcut AspectInstanceFactory
创建advisor符合 isInfrastructureClass,不会被创建代理,所以被放入了advisedBeans
- 设置父类,setSuperclass很简单
- 设置回调,将七个回调传入,主要是DynamicAdviceInterceptor里面属性会设置拦截器链
准备完成之后就可以创建代理,之后调用函数就是调用代理对象的函数(因为一级缓存,缓存的是这个代理对象了)
下面拿了部分代码,不是存在与同一个方法中
//获取拦截器
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
//会把拦截器变为advisor
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
//设置父类
enhancer.setSuperclass(proxySuperClass);
//得到回调,并且getCallbacks会把advisor放入DynamicAdviceInterceptor中
Callback[] callbacks = getCallbacks(rootClass);
Class<?>[] types = new Class<?>[callbacks.length];
for (int x = 0; x < types.length; x++) {
types[x] = callbacks[x].getClass();
}
//设置回调
enhancer.setCallbackTypes(types);
调用过程
调用add方法,是调用代理的add方法,代理的add方法是调用回调里面DynamicAdviceInterceptor的interceptor方法
这个interceptor方法或获取这个方法对应的拦截器链,然后需要转换为MethodInterceptor,其中有几个本身是MethodInterceptor子类,另外的比如AspectJAfterReturningAdvice和AspectJMethodBeforeAdvice不是MethodInterceptor子类需要由adapter帮助转换
有了拦截器链,同对象,方法一起传入CglibMethodInvocation,调用proceed方法
new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed()
整个调用过程是CglibMethodInvocation和拦截器链配合调用,效果就和上面的图差不多,执行过程一句话说不清