- 什么是aop(官方的讲)
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
- aop的基本概念
AOP 是一个概念,一个规范,本身并没有设定具体语言的实现,这实际上提供了非常广阔的发展的空间。AspectJ是AOP的一个很悠久的实现,它能够和 Java 配合起来使用。
这里只是重温 AspectJ 中几个必须要了解的概念:
Aspect: Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。
Joint point:表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point。
Pointcut:表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。
Advice:Advice 定义了在 pointcut 里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码。
- aop的使用
//配置类
@Configuration
@EnableAspectJAutoProxy
@ComponentScan(basePackages = {"com.example.springdemo.testaop"})
//@EnableAspectJAutoProxy(proxyTargetClass = true) 强制使用CGLIB动态代理
//@EnableAspectJAutoProxy(exposeProxy = true) 提前发布代理对象
public class MainConfig {
@Bean
public TestAspectJ testAspectJ() {
return new TestAspectJ();
}
}
//切面类
@Aspect
public class TestAspectJ {
@Value("1234")
private String name;
@Pointcut("execution(* com.example.springdemo.testaop.TestLog.*(..))")
public void pointCut() {
}
@Before("pointCut()")
public void methodBefore() {
System.out.println("进入methodBefore方法");
}
@After("pointCut()")
public void methodAfter() {
System.out.println("methodAfter");
}
@AfterReturning("pointCut()")
public void methodAfterReturning() {
System.out.println("methodAfterReturning");
}
@AfterThrowing("pointCut()")
public void methodAfterThrowing() {
System.out.println("methodAfterThrowing");
}
}
源码解析
首先从配置类入手,发现使用了注解@EnableAspectJAutoProxy,spring习惯使用@Enable开头的注解表明使用哪项功能。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
//.....省略代码
}
//AspectJAutoProxyRegistrar 实现了ImportBeanDefinitionRegistrar接口,表明是为spring容器注入组件了。逻辑很简单,为我们容器中注入了AnnotationAwareAspectJAutoProxyCreator组件。继承关系图如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dbRTpXaY-1626935283670)(https://leanote.com/api/file/getImage?fileId=60e55d2bab64411035000511)]
在spring bean的生命周期中,doCreateBean()方法执行之前,有这样一行代码肯定不会陌生:
try {
// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
//给bean的后置处理器一个机会取返回目标对象的代理而不是bean的实例本身,在这里因为我们现在还只是beanDefinition还么有具体的Bean,所以resolveBeforeInstantiation返回的一直为null
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
return bean;
}
}
catch (Throwable ex) {
throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
"BeanPostProcessor before instantiation of bean failed", ex);
}
//resolveBeforeInstantiation方法的实现
protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
Object bean = null;
if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
// Make sure bean class is actually resolved at this point.
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
Class<?> targetType = determineTargetType(beanName, mbd);
if (targetType != null) {
//调用系统中的后置处理器的brfore方法
bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
if (bean != null) {
bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
}
}
}
mbd.beforeInstantiationResolved = (bean != null);
}
return bean;
}
//applyBeanPostProcessorsBeforeInstantiation
//找到InstantiationAwareBeanPostProcessor类型的实现类,并调用postProcessBeforeInstantiation方法
@Nullable
protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
if (result != null) {
return result;
}
}
}
return null;
}
通过类的继承关系图。我们知道AnnotationAwareAspectJAutoProxyCreator是实现了InstantiationAwareBeanPostProcessor接口的,所以我们直接进入到AnnotationAwareAspectJAutoProxyCreator的postProcessBeforeInstantiation方法去看具体的实现,但是我们发现,这个类本身并没有具体的实现,spring将postProcessBeforeInstantiation方法的实现放到了它的父类AbstractAutoProxyCreator中
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
//cacheKey
Object cacheKey = getCacheKey(beanClass, beanName);
if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
if (this.advisedBeans.containsKey(cacheKey)) {
return null;
}
//isInfrastructureClass(beanClass) 判断是否为一些基本类型(Advice/Pointcut/Advisor)
//shouldSkip找到Advisor的实现类和Object中带有AspectJ注解的作为候选人,并加入到缓存中去
if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return null;
}
}
//....
}
//shouldSkip
@Override
protected boolean shouldSkip(Class<?> beanClass, String beanName) {
// TODO: Consider optimization by caching the list of the aspect names
List<Advisor> candidateAdvisors = findCandidateAdvisors();
for (Advisor advisor : candidateAdvisors) {
if (advisor instanceof AspectJPointcutAdvisor &&
((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) {
return true;
}
}
return super.shouldSkip(beanClass, beanName);
}
//findCandidateAdvisors 注意要跟踪到子类中的实现中
@Override
protected List<Advisor> findCandidateAdvisors() {
// Add all the Spring advisors found according to superclass rules.
//获取Advisor接口的所有实现类(事务相关?)
List<Advisor> advisors = super.findCandidateAdvisors();
// Build Advisors for all AspectJ aspects in the bean factory.
if (this.aspectJAdvisorsBuilder != null) {
//buildAspectJAdvisor (切面相关)
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
}
return advisors;
}
//2.判断BeanType是否标注了AspectJ注解,找到目标(也就是用户自定义的切面类),然后会获取到切面类中的除去@PointCut()方法之外的其余方法作为增强的方法,连同PointCut方法组装成InstantiationModelAwarePointcutAdvisorImpl对象返回,添加到集合中去作为候选人。如果beanName对应的bean是单例的。还会将beanName->advisors作为key->Value添加到缓存中去
public List<Advisor> buildAspectJAdvisors() {
//....省略其余代码
//获取所有Object类型的beanName
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Object.class, true, false);
//根据beanName获取BeanType
Class<?> beanType = this.beanFactory.getType(beanName, false);
if (beanType == null) {
continue;
}
//判断是否标注了@AspectJ注解
if (this.advisorFactory.isAspect(beanType)) {
aspectNames.add(beanName);
AspectMetadata amd = new AspectMetadata(beanType, beanName);
if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
MetadataAwareAspectInstanceFactory factory =
new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
//核心在这里,主要看下是如何获取增强器的
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
if (this.beanFactory.isSingleton(beanName)) {
//加缓存的操作
this.advisorsCache.put(beanName, classAdvisors);
}
else {
//暴露factory
this.aspectFactoryCache.put(beanName, factory);
}
advisors.addAll(classAdvisors);
}
}
//getAdvisors
//获取增强器
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
//省略部分代码....
//获取切面类上的所有advisorMethods,也就是获取所有的 除去@PointCut之外的增强方法。
for (Method method : getAdvisorMethods(aspectClass)) {
Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName);
if (advisor != null) {
advisors.add(advisor);
}
}
//省略部分代码....
}
//getAdvisor
//具体获取增强器的方法,可以看出返回的是一个InstantiationModelAwarePointcutAdvisorImpl对象。实现了Advisor接口的
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
int declarationOrderInAspect, String aspectName) {
validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());
AspectJExpressionPointcut expressionPointcut = getPointcut(
candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
if (expressionPointcut == null) {
return null;
}
return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
}
//对象的构造方法
public InstantiationModelAwarePointcutAdvisorImpl(AspectJExpressionPointcut declaredPointcut,
//保存切点表达式,后面需要用到切点表达式的匹配规则去判断当前bean是否match
//....
this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);
//....
}
private Advice instantiateAdvice(AspectJExpressionPointcut pointcut) {
Advice advice = this.aspectJAdvisorFactory.getAdvice(this.aspectJAdviceMethod, pointcut,
this.aspectInstanceFactory, this.declarationOrder, this.aspectName);
return (advice != null ? advice : EMPTY_ADVICE);
}
//根据method方法注解类型生成对应的AbstractAspectJAdvice对象
public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,
MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
//.....
switch (aspectJAnnotation.getAnnotationType()) {
case AtPointcut:
if (logger.isDebugEnabled()) {
logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
}
return null;
case AtAround:
springAdvice = new AspectJAroundAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
case AtBefore:
springAdvice = new AspectJMethodBeforeAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
case AtAfter:
springAdvice = new AspectJAfterAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
case AtAfterReturning:
springAdvice = new AspectJAfterReturningAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterReturningAnnotation.returning())) {
springAdvice.setReturningName(afterReturningAnnotation.returning());
}
break;
case AtAfterThrowing:
springAdvice = new AspectJAfterThrowingAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
}
break;
default:
throw new UnsupportedOperationException(
"Unsupported advice type on method: " + candidateAdviceMethod);
}
// Now to configure the advice...
springAdvice.setAspectName(aspectName);
springAdvice.setDeclarationOrder(declarationOrder);
String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
if (argNames != null) {
springAdvice.setArgumentNamesFromStringArray(argNames);
}
springAdvice.calculateArgumentBindings();
return springAdvice;
}
至此 查询切面并缓存的操作已经完成了,接下来让我看看是如何获取的并切面并生成代理对象的