这篇文章主要解决三个问题
什么是AOP
Spring 中怎么实现的AOP
AOP的应用场景
首先我们看下 到底什么是AOP
AOP的基本概念
AOP 官方定义
Aspect-Oriented Programming (AOP) complements Object-Oriented Programming (OOP) by providing another way of thinking about program structure.
1
( 面向方面的编程(AOP)是面向对象编程(OOP)的补充,它提供了另一种关于程序结构的思考方式)
1
这个解释听起来很抽象,如何理解呢?
我们知道面向对象编程关注的是对象和对象的行为,相同类似行为或属性的对象可以通过继承来实现,但是如果我们关注的是毫不相干的一组对象的特定方法 怎么办
如图,我们要关注的方法 散布在不同类中,如果想要在这些方法中加入相同的处理逻辑,过去我们采用的方法只能是硬编码,但是这样会造成大量的重复代码,不便于维护,AOP的出现就是解决针对这种问题的一种实现模式。
首先我们看下AOP中涉及到的几个重要的基本概念
Aspect:字面意思是切面,官方解释 a modularization of a concern that cuts across multiple classes ( 横切多个类的一个关注点模块),简单说就是对类具体行为的关注点集合,这样解释还是太抽象
上图中 可以看到 对多个类关注的方法 形成的一个切面,就是Aspect
Join point: a point during the execution of a program, such as the execution of a method or the handling of an exception. In Spring AOP, a join point always represents a method execution(程序执行过程中的里连接点,比如 方法调用或者异常处理。在spring aop中,一个连接点通常代表一个方法的调用,我们可以认为一个方法的执行就是一个连接点)
Advice: action taken by an aspect at a particular join point. Different types of advice include “around,” “before” and “after” advice. (Advice types are discussed below.)(通知:切面在特定连接点上产生的动作,包括多种不同类型的通知,比如 环绕通知 aroud,前置通知before和后置通知after等)
简单的说就是我们要在切面上做什么事情(动作-action)
Pointcut: a predicate that matches join points. Advice is associated with a pointcut expression and runs at any join point matched by the pointcut (for example, the execution of a method with a certain name).(切入点,匹配连接点的断言,通知与切入点表达式相关联,并且在任何匹配该断言的连接点上执行(比如一个特定名称的方法执行))
用来描述我们要在哪些地方执行,也可以说成是 用表达式匹配(正则断言)的切入点
Target object: object being advised by one or more aspects. Also referred to as the advised object(目标对象 ,被一个或多个切面通知的对象,通常被称为被通知对象)
Proxy Pattern(代理模式)
GOF 定义
What problems can the Proxy design pattern solve? (代理模式能解决什么问题)
The access to an object should be controlled.(控制被访问对象)
Additional functionality should be provided when accessing an
object.(给被访问对象提供额外功能)
What solution does the Proxy design pattern describe?(代理模式的解决方案是如何描述的)
Define a separate Proxy object that can be used as substitute for another object (Subject) and implements additional functionality to control the access to this subject.
(定义一个单独的代理类,用来提供对目标对象的代理访问 如下图所示 代理类会默认实现目标对象的接口,并在此基础上提供其他功能)
Spring AOP Revolution(进化史)
详细内容可以参考 这里不再详述
http://cxis.me/2017/04/10/Spring%E4%B8%ADAOP%E7%9A%84%E9%85%8D%E7%BD%AE%E4%BB%8E1.0%E5%88%B05.0%E7%9A%84%E6%BC%94%E8%BF%9B/
Spring如何实现AOP
我们重点使用最新最简洁的spring 5 自动配置模式来进行讲解
这里我们通过一个性能拦截器来演示spring aop的代码流程,这个拦截器可以打印服务层所有方法的执行时间
CODE
业务接口定义
/**
* 业务服务接口
*/
public interface IBusinessService {
//业务执行方法A
void executeBusinessA();
//业务执行方法B
void executeBusinessB();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
业务实现类
/**
* 业务实现类
*/
@Service("businessService")
public class BusinessServiceImpl implements IBusinessService {
private static final Logger logger = LogManager.getLogger(BusinessServiceImpl.class);
/**
* 常规业务方法(不超时)
*/
public void executeBusinessA() {
//模拟一个执行时间
try {
Thread.sleep(200);
} catch (InterruptedException e) {
System.out.println(e.getMessage());
}
logger.info("executing business in methodA");
}
/**
* 超时业务方法
*/
public void executeBusinessB() {
//模拟一个超时执行时间
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println(e.getMessage());
}
logger.info("executing business in methodB");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
性能分析器
@Component
@Aspect
public class PerformanceAnalysisInterceptor {
private static Logger logger = LogManager.getLogger(PerformanceAnalysisInterceptor.class);
private static final long DELAY_MINUTE = 1000;
public static final String POINTCUT = "execution (* aop.service.impl.BusinessServiceImpl.*(..))";
@Around(POINTCUT)
public Object analysisAround(ProceedingJoinPoint joinPoint) {
Object obj = null;
long startTime = System.currentTimeMillis();
try {
obj = joinPoint.proceed(joinPoint.getArgs());
} catch (Throwable e) {
logger.error(e.getMessage());
}
long endTime = System.currentTimeMillis();
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
String methodName = signature.getDeclaringTypeName() + "." + signature.getName();
long diffTime = endTime - startTime;
logger.info("-----" + methodName + "执行时间 :" + diffTime + " ms");
if(diffTime > DELAY_MINUTE)
delayWarning(methodName, diffTime-DELAY_MINUTE);
return obj;
}
private void delayWarning(String methodName,long delayTime) {
logger.warn("-----" + methodName + "超时 :" + delayTime + " ms");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
xml自动化配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="aop"/>
<aop:aspectj-autoproxy/>
</beans>
1
2
3
4
5
6
7
8
9
10
11
12
主函数调用执行
public class AopMain {
public static void main(String[] args) {
ClassPathXmlApplicationContext f = new ClassPathXmlApplicationContext("spring-aop.xml");
IBusinessService businessService = (IBusinessService) f.getBean("businessService");
businessService.executeBusinessA();
businessService.executeBusinessB();
}
}
1
2
3
4
5
6
7
8
9
10
11
concole打印结果
11:21:36.344 [main] INFO org.springframework.context.support.ClassPathXmlApplicationContext - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@7dc222ae: startup date [Sat Dec 09 11:21:36 CST 2017]; root of context hierarchy
11:21:36.393 [main] INFO org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loading XML bean definitions from class path resource [spring-aop.xml]
11:21:37.245 [main] INFO aop.service.impl.BusinessServiceImpl - executing business in methodA
11:21:37.247 [main] INFO aop.interceptor.PerformanceAnalysisInterceptor - -----aop.service.IBusinessService.executeBusinessA执行时间 :203 ms
11:21:38.250 [main] INFO aop.service.impl.BusinessServiceImpl - executing business in methodB
11:21:38.250 [main] INFO aop.interceptor.PerformanceAnalysisInterceptor - -----aop.service.IBusinessService.executeBusinessB执行时间 :1003 ms
11:21:38.250 [main] WARN aop.interceptor.PerformanceAnalysisInterceptor - -----aop.service.IBusinessService.executeBusinessB超时 :3 ms
Process finished with exit code 0
1
2
3
4
5
6
7
8
9
下边我们进行代码的详细分析
我们看到 xml配置文件里边只有两行配置信息,第一行配置是context自动扫描,作用是扫描制定范围的组件并注册到spring,第二行是注解Aspect自动发现
这样解释还是有点抽象,我们从两个问题入手(问题是揭开真相的火花)
第一个问题,spring怎么识别出需要代理的目标对象,也就是我们这里边实现了IBusinessService接口的BusinessServiceImpl对象
第二问题是 我们定义的这个切面(aspect注解的bean)怎么被发现并应用到目标对象(businessService)
1
2
3
第一个问题属于spring ioc的范围,看过ioc部分的同学基本应该能够理解,入手点就是对 context 这个标签的处理 我们全局搜索component-scan这个关键字,最后代码定位到 ContextNamespaceHandler类
spring加载完xml配置文件,会对配置文件中的标签进行解析,spring默认会加载自己的解析器,这些解析器散布在各个不同的jar包中
这些配置文件中的handlers就是spring对默认标签的处理器,我们看下context包下的spring.handlers文件
spring在解析标签时,通过实现NamespaceHandlerResolver接口的DefaultNamespaceHandlerResolver加载所有classpath下的spring.handlers文件中的映射,并在解析标签时,寻找这些标签对应的处理器,然后用这些处理器来处理标签。所以,当遇到component-scan标签,spring 容器就会使用ComponentScanBeanDefinitionParser标签进行解析,我们看下ComponentScanBeanDefinitionParser来的parse方法
public BeanDefinition parse(Element element, ParserContext parserContext) {
//取base-package属性
String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
//解析base-packet属性(通配符)
basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
//转换成数组(用,或者;分割的定义)
String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
// Actually scan for bean definitions and register them.
ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
//扫描basepacket下所有的bean
Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
//注册到spring中
registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
return null;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
进入扫描方法
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
for (String basePackage : basePackages) {
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
这里我们看到,spring会扫描base-package指定资源路径下所有的java文件,并依次解析成对应的内部映射BeanDefinition,最后注册到spring容器中,方便后续调用 具体分析不再展开
可以参考 : http://www.cnblogs.com/question-sky/p/6953315.html
OK,我们已经知道spring怎么发现bean,那么第二问题是 我们定义的这个切面(aspect注解的bean)怎么被发现并应用到目标对象(businessService),这也是我们
要分析的重点,这也是第二行标签的作用
我们看到 PerformanceAnalysisInterceptor类的注解有两个 一个是 @Component 一个是@Aspect Component标签刚才已经解释过,说明这是一个spring的组件,已经注册到spring中,那Aspect注解呢?根据刚才的分析 我们知道 spring对于不同的标签采用不同的处理器,同理,这里Aspect也有对应的标签处理器,进过全局搜索,我们发现在AopNamespaceHandler中
我们进入AspectJAutoProxyBeanDefinitionParser的parse方法
public BeanDefinition parse(Element element, ParserContext parserContext) {
//注册AspectJAnnotationAutoProxyCreator
AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
extendBeanDefinition(element, parserContext);
return null;
}
1
2
3
4
5
6
进入registerAspectJAutoProxyCreatorIfNecessary方法
public static void registerAspectJAutoProxyCreatorIfNecessary(
ParserContext parserContext, Element sourceElement) {
//把应用了注解@Aspect的bean注册成BeanDefiniton
BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAutoProxyCreatorIfNecessary(
parserContext.getRegistry(), parserContext.extractSource(sourceElement));
//处理proxy-target-class和expose-proxy属性
useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
//注册组件并通知监听器
registerComponentIfNecessary(beanDefinition, parserContext);
}
1
2
3
4
5
6
7
8
9
10
11
我们重点看下第一个流程 进入方法体
public static BeanDefinition registerAspectJAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry,
@Nullable Object source) {
return registerOrEscalateApcAsRequired(AspectJAwareAdvisorAutoProxyCreator.class, registry, source);
}
private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry,
@Nullable Object source) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
int requiredPriority = findPriorityForClass(cls);
if (currentPriority < requiredPriority) {
apcDefinition.setBeanClassName(cls.getName());
}
}
return null;
}
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
beanDefinition.setSource(source);
beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
return beanDefinition;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
这里我们看到,spring会注册一个内部的BeanDefiniton key是 org.springframework.aop.config.internalAutoProxyCreator
从AspectJAwareAdvisorAutoProxyCreator结构图,我们看到它实现了BeanPostProcessor接口,意味着spring在加载整个bean时,实例化前会调用
postProcessAfterInitialization方法,我们进入这个方法(在父类AbstractAutoProxyCreator中)
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.contains(cacheKey)) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
1
2
3
4
5
6
7
8
9
重点是方法wrapIfNecessary,这个方法的作用就是 如果目标bean能够被代理,则使用代理进行包裹
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
//如果已经处理过,直接返回
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
//无需增强
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
//如果是基础类比如 advice pointcut advisor 这些基础aop类则直接返回
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// Create proxy if we have advice.
//获取被代理类所有的拦截器
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
//创建代理
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
这里我们看到了AOP逻辑的核心 ,整个流程很清晰,首先获取这个bean对应的所有拦截器 如果拦截为空,直接返回bean,否则 创建对应的代理 然后返回代理bean
这里有两个重点分析部分 一个是拦截器的获取,一个是如何代理
我们先开拦截器的获取
protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
if (advisors.isEmpty()) {
return DO_NOT_PROXY;
}
return advisors.toArray();
}
1
2
3
4
5
6
7
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
//获取所有的拦截增强器
List<Advisor> candidateAdvisors = findCandidateAdvisors();
//看哪些拦截增强器可以应用到目标bean
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
1
2
3
4
5
6
7
8
9
10
11
protected List<Advisor> findCandidateAdvisors() {
// Add all the Spring advisors found according to superclass rules.
//通过父类来加载配置文件中的拦截器
List<Advisor> advisors = super.findCandidateAdvisors();
// Build Advisors for all AspectJ aspects in the bean factory.
if (this.aspectJAdvisorsBuilder != null) {
//获取AOP注解拦截器
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
}
return advisors;
}
1
2
3
4
5
6
7
8
9
10
11
从代码来看,拦截器分成两部分,一部分来源是配置文件,一部分是应用了注解的bean,在我们的演示案例中配置文件中没有对应的拦截器xml配置,只有注解的@Aspect,所以我们直接分析this.aspectJAdvisorsBuilder.buildAspectJAdvisors方法
public List<Advisor> buildAspectJAdvisors() {
List<String> aspectNames = this.aspectBeanNames;
if (aspectNames == null) {
synchronized (this) {
aspectNames = this.aspectBeanNames;
if (aspectNames == null) {
List<Advisor> advisors = new LinkedList<>();
aspectNames = new LinkedList<>();
//获取所有注册的beanName
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Object.class, true, false);
for (String beanName : beanNames) {
// 子类定义规则来过滤bean
if (!isEligibleBean(beanName)) {
continue;
}
// We must be careful not to instantiate beans eagerly as in this case they
// would be cached by the Spring container but would not have been weaved.
//获取bean类型
Class<?> beanType = this.beanFactory.getType(beanName);
if (beanType == null) {
continue;
}
//判断是否是Aspect注解 (重点)
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);
//解析Aspect注解中的拦截器方法(比如 @After @Before @Aroud等)
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
if (this.beanFactory.isSingleton(beanName)) {
this.advisorsCache.put(beanName, classAdvisors);
}
else {
this.aspectFactoryCache.put(beanName, factory);
}
advisors.addAll(classAdvisors);
}
else {
// Per target or per this.
if (this.beanFactory.isSingleton(beanName)) {
throw new IllegalArgumentException("Bean with name '" + beanName +
"' is a singleton, but aspect instantiation model is not singleton");
}
MetadataAwareAspectInstanceFactory factory =
new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
this.aspectFactoryCache.put(beanName, factory);
advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
}
}
this.aspectBeanNames = aspectNames;
return advisors;
}
}
}
if (aspectNames.isEmpty()) {
return Collections.emptyList();
}
//缓存
List<Advisor> advisors = new LinkedList<>();
for (String aspectName : aspectNames) {
List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
if (cachedAdvisors != null) {
advisors.addAll(cachedAdvisors);
}
else {
MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
}
return advisors;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
到这里其实已经找到了 我们的性能拦截器 PerformanceAnalysisInterceptor 因为它上边的@Aspect注解标识了它就是一个拦截器切面 ,下边就是解析这个切面中的规则(Pointcut定义)和拦截方法(Advice)
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
validate(aspectClass);
// We need to wrap the MetadataAwareAspectInstanceFactory with a decorator
// so that it will only instantiate once.
MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);
List<Advisor> advisors = new LinkedList<>();
// 获取切面拦截器的所有拦截方法
for (Method method : getAdvisorMethods(aspectClass)) {
//获取拦截器方法(增强)并包装成advisor
Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
if (advisor != null) {
advisors.add(advisor);
}
}
// If it's a per target aspect, emit the dummy instantiating aspect.
if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
advisors.add(0, instantiationAdvisor);
}
// Find introduction fields
//获取DeclareParents注解
for (Field field : aspectClass.getDeclaredFields()) {
Advisor advisor = getDeclareParentsAdvisor(field);
if (advisor != null) {
advisors.add(advisor);
}
}
return advisors;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
进入getAdvisor方法
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
int declarationOrderInAspect, String aspectName) {
validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());
//获取Pointcut表达式
AspectJExpressionPointcut expressionPointcut = getPointcut(
candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
if (expressionPointcut == null) {
return null;
}
//根据pointcut生成拦截器对象
return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
这里getPointcut方法就是如何解析 PerformanceAnalysisInterceptor中的@Around(POINTCUT)
具体看下实现
private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {
AspectJAnnotation<?> aspectJAnnotation =
AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
if (aspectJAnnotation == null) {
return null;
}
AspectJExpressionPointcut ajexp =
new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class<?>[0]);
ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
if (this.beanFactory != null) {
ajexp.setBeanFactory(this.beanFactory);
}
return ajexp;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//获取制定方法上的注解
protected static AspectJAnnotation<?> findAspectJAnnotationOnMethod(Method method) {
Class<?>[] classesToLookFor = new Class<?>[] {
Before.class, Around.class, After.class, AfterReturning.class, AfterThrowing.class, Pointcut.class};
for (Class<?> c : classesToLookFor) {
AspectJAnnotation<?> foundAnnotation = findAnnotation(method, (Class<Annotation>) c);
if (foundAnnotation != null) {
return foundAnnotation;
}
}
return null;
}
1
2
3
4
5
6
7
8
9
10
11
12
这里是从spring提供的所有候选注解类中寻找 当前方法上的注解 并封装成 AspectJAnnotation对象 对于我们定义的性能拦截器PerformanceAnalysisInterceptor
上边只有一个@Around注解,所以这里的Advisor也就只有一个(Advisor可以看成advice和pointcut的集合),下边就是这些找到的拦截器中哪些可以应用到目标对象的方法上
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
1
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
if (candidateAdvisors.isEmpty()) {
return candidateAdvisors;
}
List<Advisor> eligibleAdvisors = new LinkedList<>();
for (Advisor candidate : candidateAdvisors) {
if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
eligibleAdvisors.add(candidate);
}
}
boolean hasIntroductions = !eligibleAdvisors.isEmpty();
for (Advisor candidate : candidateAdvisors) {
if (candidate instanceof IntroductionAdvisor) {
// already processed
continue;
}
if (canApply(candidate, clazz, hasIntroductions)) {
eligibleAdvisors.add(candidate);
}
}
return eligibleAdvisors;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
if (advisor instanceof IntroductionAdvisor) {
return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
}
else if (advisor instanceof PointcutAdvisor) {
PointcutAdvisor pca = (PointcutAdvisor) advisor;
return canApply(pca.getPointcut(), targetClass, hasIntroductions);
}
else {
// It doesn't have a pointcut so we assume it applies.
return true;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
我们定义的是PointcutAdvisor
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
Assert.notNull(pc, "Pointcut must not be null");
if (!pc.getClassFilter().matches(targetClass)) {
return false;
}
MethodMatcher methodMatcher = pc.getMethodMatcher();
if (methodMatcher == MethodMatcher.TRUE) {
// No need to iterate the methods if we're matching any method anyway...
return true;
}
IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
}
Set<Class<?>> classes = new LinkedHashSet<>(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
classes.add(targetClass);
for (Class<?> clazz : classes) {
Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
for (Method method : methods) {
if ((introductionAwareMethodMatcher != null &&
introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) ||
methodMatcher.matches(method, targetClass)) {
return true;
}
}
}
return false;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
这里的大体逻辑是 用找到的切点断言匹配当前bean的所有方法,过滤 找到匹配的切点 到此为止我们已经看到spring是如何一步一步的解析我们声明的注解@Aspect的切面spring会把这些注解@Aspect的类当成拦截器 找到了目标bean(BusinessServiceImpl)的拦截器,下一步就是判断拦截器是否为空,为空说明不需要生成代理,直接返回bean,否则就需要创建一个目标bean的代理,这也就是我们常说的代理模式的应用,如图所示,所有对目标对象的访问都通过代理来实现,这样我们就可以把拦截器应用到目标对象上
下边我们重点分析第二步流程,spring中如何创建代理
Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
1
protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
@Nullable Object[] specificInterceptors, TargetSource targetSource) {
if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
}
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
if (!proxyFactory.isProxyTargetClass()) {
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
proxyFactory.addAdvisors(advisors);
proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
return proxyFactory.getProxy(getProxyClassLoader());
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
我们看到,spring把创建代理的责任交给了**ProxyFactory**,我们看下getPorxy主流程
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
从这里我们可以很清楚的看到,spring通过两种方式创建代理,分别是JdkDynamicAopProxy和ObjenesisCglibAopProxy,这两个就是我们常说的jdk动态代理和cglib代理(基于字节码)
spring怎么区分通过哪种代理是通过三个判断
isOptimeiz 对CGlib代理的创建优化
isProxyTargetClass <aop:aspectj-autoproxy proxy-target- class=“true”/> 表示使用CGLib进行代理
hasNoUserSuppliedProxyInterfaces 是否存在代理接口
1
2
3
4
5
三个条件之一后,还有两个判断 是否实现了接口或是否是代理类型,通过上述判断我们可以知道spring选择代理的策略
如果目标类实现了接口,默认采用jdk动态代理来实现AOP
如果目标类实现了接口,可以通过配置文件强行使用CGLib来实现AOP代理
如果目标类没有实现接口,只能使用CGlib来实现AOP代理
1
2
3
这说明CGlib可以为那些没有实现接口的类增强行为,原理是为目标类生成一个子类,并覆盖其中的方法
下边我们重点看下jdk动态代理部分的实现,如果采用jdk动态代理获取代理,关键的调用方法就是invoke调用
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MethodInvocation invocation;
Object oldProxy = null;
boolean setProxyContext = false;
TargetSource targetSource = this.advised.targetSource;
Object target = null;
try {
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
// The target does not implement the equals(Object) method itself.
return equals(args[0]);
}
else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
// The target does not implement the hashCode() method itself.
return hashCode();
}
else if (method.getDeclaringClass() == DecoratingProxy.class) {
// There is only getDecoratedClass() declared -> dispatch to proxy config.
return AopProxyUtils.ultimateTargetClass(this.advised);
}
else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
method.getDeclaringClass().isAssignableFrom(Advised.class)) {
// Service invocations on ProxyConfig with the proxy config...
return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
}
Object retVal;
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// Get as late as possible to minimize the time we "own" the target,
// in case it comes from a pool.
//获取目标对象和对象类型
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
// Get the interception chain for this method.
//获取目标对象上当前方法的的所有拦截器链(已经注入到代理类的属性中,直接可以获取)
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// Check whether we have any advice. If we don't, we can fallback on direct
// reflective invocation of the target, and avoid creating a MethodInvocation.
if (chain.isEmpty()) {
// We can skip creating a MethodInvocation: just invoke the target directly
// Note that the final invoker must be an InvokerInterceptor so we know it does
// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
//如果没有任何拦截器,则直接调用目标对象的切点方法
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
// We need to create a method invocation…
//执行拦截器的proceed方法
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
retVal = invocation.proceed();
}
// Massage return value if necessary.
Class<?> returnType = method.getReturnType();
if (retVal != null && retVal == target &&
returnType != Object.class && returnType.isInstance(proxy) &&
!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
// Special case: it returned "this" and the return type of the method
// is type-compatible. Note that we can't help if the target sets
// a reference to itself in another returned object.
retVal = proxy;
}
else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
throw new AopInvocationException(
"Null return value from advice does not match primitive return type for: " + method);
}
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
// Must have come from TargetSource.
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
进入拦截器的proceed方法
public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
//如果已经执行到最后一个拦截器,就执行切点上的方法
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
//获取下一个拦截器
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// Evaluate dynamic method matcher here: static part will already have
// been evaluated and found to match.
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
//匹配当前方法
if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {
// Dynamic matching failed.
// Skip this interceptor and invoke the next in the chain.
//不匹配直接跳过
return proceed();
}
}
else {
// It's an interceptor, so we just invoke it: The pointcut will have
// been evaluated statically before this object was constructed.
//普通拦截器直接调用
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
在这里我们看到spring会循环递归调用代理的invoke方法 这里从代码上看不够直观 下边我们用图形来进行分析 初始状态
这里 proxy bean就是上边的JdkDynamicAopProxy ,里边包含已经解析完的拦截器链和 目标对象 ,当调用目标对象的指定方法时,其实就是调用代理类的processed方法,processed方法先取出拦截器链中第一个拦截器,然后执行第一个拦截器的拦截方法
进入after拦截器后,会先调用目标对象的方法,现在对于目标对象的调用,都有代理对象来处理
这样会再次递归调用代理的invoke方法,下一个拦截器Around开始被调用
进入Around拦截器方法,会默认先执行around前置逻辑,然后在调用目标对象方法,最后执行后置逻辑
当执行Around拦截器的目标对象方法时,会再次递归调用代理的invoke方法,这时会执行最后一个拦截器Before
最后进入Before拦截器,会先执行before逻辑方法,最后调用目标对象方法
调用目标对象的方法会调用代理的invoke方法,这时候代理的所有拦截器都已经调用完毕,所以会直接执行目标对象的方法
执行完目标对象的方法后,会依次回归调用之前的方法
从上边的分析,我们可以知道 最后执行的顺序其实交给了每个要执行的拦截器,通过拦截器中的调用来决定拦截顺序,至此spring aop jdk代理就分析完了,有关CGLib代理方式,这里就不再展开分析,流程大同小异,有兴趣的可以自行研究
文字版
SPRING 通过注解实现AOP的过程的文字描述 - xml配置文件中声明自动发现注解(),spring默认启动时会注册注解解析器(AspectJAutoProxyBeanDefinitionParser),在注解解析器解析(parse)的过程中,会注册一个实现了BeanPostProcessor的后处理器(AspectJAwareAdvisorAutoProxyCreator),这个后处理器在目标对象实例化后进行拦截处理,拦截的流程是,先搜索所有已经注册的BeanDefiniton,从中找到标记了注解(@Aspect)的切面组成拦截器链,选择那些可以应用到目标对象的拦截器(过滤),如果拦截器链不为空,则为目标对象生成代理(JdkDynamicAopProxy或CglibAopProxy),当调用目标对象的指定拦截方法时,就会默认调用对应代理类的代理方法(invoke),这样就完成了AOP的整个流程,
PS 其实在使用过程中,我们都是基于接口进行编程,所以用的最多的也是jdk动态代理,但是当我们目标对象没有实现任何接口,但是我们还想给它增加新的行为,这时候我们就需要CGLib方式,所以具体选择哪种代理方法还要看具体的业务场景
spring AOP应用场景
企业开发里边用的最后的就是 事务拦截器,日志拦截器 性能拦截器 还有 权限拦截器 等等 后边我们分析spring mvc还会介绍spring 拦截器的使用 尽请关注
参考
Spring源码深度解析 https://book.douban.com/subject/25866350/
Spring Partten https://en.wikipedia.org/wiki/Proxy_pattern#cite_note-2
AOP Git source https://github.com/spring-projects/spring-framework
GOF Porxy wiki https://en.wikipedia.org/wiki/Aspect-oriented_programming