通过前3篇,大家应该可以清楚的知道:AOP代理原理有3元素
-
BeanPostProcessor,作为代理对象初始入口
-
Advisor&Pointcut&MethodMatcher完成匹配
-
Advice的声明及链式结构维护
三个问题在前面的讲解中已经讲解了。其中:
Advice的链式结构,是通过ProxyFactory统一维护的管理的,在《编程式实现AOP》中已说明;
匹配在前面系列2及系列3中也做了说明;
代理对象初始入口也在系列第3讲中进行了讲解。
本文,主要从这3各方面入手,对常用的aop schema做一个全方位的剖析。主要内容
-
使用aop schema方式做一个简单的演示demo
-
确认并剖析相关的BeanPostProcessor
-
确认并剖析相关的Advisor,PointCut
1.使用aop schema方式做一个简单的演示demo
例子代码来源于http://www.tutorialspoint.com/spring/schema_based_aop_appoach.htm
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
|
//1 切面类
package
com.tutorialspoint;
public
class
Logging {
/**
* This is the method which I would like to execute
* before a selected method execution.
*/
public
void
beforeAdvice(){
System.out.println(
"Going to setup student profile."
);
}
/**
* This is the method which I would like to execute
* after a selected method execution.
*/
public
void
afterAdvice(){
System.out.println(
"Student profile has been setup."
);
}
/**
* This is the method which I would like to execute
* when any method returns.
*/
public
void
afterReturningAdvice(Object retVal){
System.out.println(
"Returning:"
+ retVal.toString() );
}
/**
* This is the method which I would like to execute
* if there is an exception raised.
*/
public
void
AfterThrowingAdvice(IllegalArgumentException ex){
System.out.println(
"There has been an exception: "
+ ex.toString());
}
}
//2 业务模拟类
package
com.tutorialspoint;
public
class
Student {
private
Integer age;
private
String name;
public
void
setAge(Integer age) {
this
.age = age;
}
public
Integer getAge() {
System.out.println(
"Age : "
+ age );
return
age;
}
public
void
setName(String name) {
this
.name = name;
}
public
String getName() {
System.out.println(
"Name : "
+ name );
return
name;
}
public
void
printThrowException(){
System.out.println(
"Exception raised"
);
throw
new
IllegalArgumentException();
}
}
|
XML(Beans.xml)
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
|
<?
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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">
<
aop:config
>
<
aop:aspect
id
=
"log"
ref
=
"logging"
>
<
aop:pointcut
id
=
"selectAll"
expression
=
"execution(* com.tutorialspoint.*.*(..))"
/>
<
aop:before
pointcut-ref
=
"selectAll"
method
=
"beforeAdvice"
/>
<
aop:after
pointcut-ref
=
"selectAll"
method
=
"afterAdvice"
/>
<
aop:after-returning
pointcut-ref
=
"selectAll"
returning
=
"retVal"
method
=
"afterReturningAdvice"
/>
<
aop:after-throwing
pointcut-ref
=
"selectAll"
throwing
=
"ex"
method
=
"AfterThrowingAdvice"
/>
</
aop:aspect
>
</
aop:config
>
<!-- Definition for student bean -->
<
bean
id
=
"student"
class
=
"com.tutorialspoint.Student"
>
<
property
name
=
"name"
value
=
"Zara"
/>
<
property
name
=
"age"
value
=
"11"
/>
</
bean
>
<!-- Definition for logging aspect -->
<
bean
id
=
"logging"
class
=
"com.tutorialspoint.Logging"
/>
</
beans
>
|
Main
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public class MainApp {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("com/tutorialspoint/Beans.xml");
Student student = (Student) context.getBean("student");
student.getName();
student.getAge();
// student.printThrowException();
}
}
|
执行结果
Going to setup student profile.
Name : Zara
Student profile has been setup.
Returning:Zara
Going to setup student profile.
Age : 11
Student profile has been setup.
Returning:11
通过查看日志,可以捕获到以下信息,为咱们剖析源码找到了分析点。
...
DEBUG: org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator - Creating implicit proxy for bean 'student' with 0 common interceptors and 5 specific interceptors
DEBUG: org.springframework.aop.framework.CglibAopProxy - Creating CGLIB proxy: target source is SingletonTargetSource for target object [com.tutorialspoint.Student@6fc2862b]
DEBUG: org.springframework.aop.framework.CglibAopProxy - Unable to apply any optimisations to advised method: public java.lang.String com.tutorialspoint.Student.getName()
2.找寻aop schema对应的BeanPostProcessor
aop的schema 对应的spring beanPostProcessor,是Spring自动指派的,对我们是透明的,这一点和以前讲到的BeanNameAutoProxyCreator和DefaultAdvisorAutoProxyCreator不同,咱们没有声明。
首先,aop是客制化的标签(不是bean标签,都是客制化标签),要找到这个位置。熟悉spring自定义标签的朋友应该都知道,需要找到aop标签*.handler文件.位置在$base/spring-aop\src\main\resources\META-INF\spring.handlers.
内容如下
http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler
2.1 分析AopNamespaceHandler
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
|
public
class
AopNamespaceHandler
extends
NamespaceHandlerSupport {
/**
完成一些解析器
* Register the {@link BeanDefinitionParser BeanDefinitionParsers} for the
*
*/
public
void
init() {
// In 2.0 XSD as well as in 2.1 XSD.
//负责解析<aop:config>
registerBeanDefinitionParser(
"config"
,
new
ConfigBeanDefinitionParser());
//负责即系<aspectj-autoproxy>
registerBeanDefinitionParser(
"aspectj-autoproxy"
,
new
AspectJAutoProxyBeanDefinitionParser());
registerBeanDefinitionDecorator(
"scoped-proxy"
,
new
ScopedProxyBeanDefinitionDecorator());
// Only in 2.0 XSD: moved to context namespace as of 2.1
registerBeanDefinitionParser(
"spring-configured"
,
new
SpringConfiguredBeanDefinitionParser());
}
}
//接下来关注的重点是ConfigBeanDefinitionParser
class
ConfigBeanDefinitionParser
implements
BeanDefinitionParser {
public
BeanDefinition parse(Element element, ParserContext parserContext) {
CompositeComponentDefinition compositeDef =
new
CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
parserContext.pushContainingComponent(compositeDef);
//配置the auto proxy creator
configureAutoProxyCreator(parserContext, element);
//接下来解析xml节点元素
List<Element> childElts = DomUtils.getChildElements(element);
for
(Element elt: childElts) {
String localName = parserContext.getDelegate().getLocalName(elt);
if
(POINTCUT.equals(localName)) {
parsePointcut(elt, parserContext);
}
else
if
(ADVISOR.equals(localName)) {
parseAdvisor(elt, parserContext);
}
else
if
(ASPECT.equals(localName)) {
parseAspect(elt, parserContext);
}
}
parserContext.popAndRegisterContainingComponent();
return
null
;
}
private
void
configureAutoProxyCreator(ParserContext parserContext, Element element) {
AopNamespaceUtils.registerAspectJAutoProxyCreatorIfNecessary(parserContext, element);
}
}
--------------------
public
abstract
class
AopNamespaceUtils {
public
static
void
registerAspectJAutoProxyCreatorIfNecessary(
ParserContext parserContext, Element sourceElement) {
BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAutoProxyCreatorIfNecessary(
parserContext.getRegistry(), parserContext.extractSource(sourceElement));
useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
registerComponentIfNecessary(beanDefinition, parserContext);
}
}
--------------------
public
abstract
class
AopConfigUtils {
public
static
final
String AUTO_PROXY_CREATOR_BEAN_NAME =
"org.springframework.aop.config.internalAutoProxyCreator"
;
//AspectJAwareAdvisorAutoProxyCreator注册到spring容器
public
static
BeanDefinition registerAspectJAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
return
registerOrEscalateApcAsRequired(AspectJAwareAdvisorAutoProxyCreator.
class
, registry, source);
}
}
|
到这儿,任务也算完成了。AspectJAwareAdvisorAutoProxyCreator就是我们苦苦寻找的BeanPostProcessor.
真巧它和DefaultAdvisorAutoProxyCreator是兄弟。接下来,就是剖析AspectJAwareAdvisorAutoProxyCreator。
结合《死磕Spring AOP系列3》,可以将getAdvicesAndAdvisorsForBean作为分析的起点,以前说过该方法是AbstractAutoProxyCreator的抽象方法,由子类实现。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
public
abstract
class
AbstractAdvisorAutoProxyCreator
extends
AbstractAutoProxyCreator {
...
@Override
protected
Object[] getAdvicesAndAdvisorsForBean(Class beanClass, String beanName, TargetSource targetSource) {
List advisors = findEligibleAdvisors(beanClass, beanName);
if
(advisors.isEmpty()) {
return
DO_NOT_PROXY;
}
return
advisors.toArray();
}
//查找适合的Advisors
protected
List<Advisor> findEligibleAdvisors(Class beanClass, String beanName) {
List<Advisor> candidateAdvisors = findCandidateAdvisors();
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
//交给子类实现扩展
if
(!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return
eligibleAdvisors;
}
}
|
2.3剖析AspectJAwareAdvisorAutoProxyCreator.extendAdvisors方法
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
|
public
class
AspectJAwareAdvisorAutoProxyCreator
extends
AbstractAdvisorAutoProxyCreator {
//添加 ExposeInvocationInterceptor to the beginning of the advice chain
protected
void
extendAdvisors(List<Advisor> candidateAdvisors) {
AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary(candidateAdvisors);
}
}
public
abstract
class
AspectJProxyUtils {
public
static
boolean
makeAdvisorChainAspectJCapableIfNecessary(List<Advisor> advisors) {
// Don't add advisors to an empty list; may indicate that proxying is just not required
if
(!advisors.isEmpty()) {
boolean
foundAspectJAdvice =
false
;
for
(Advisor advisor : advisors) {
// Be careful not to get the Advice without a guard, as
// this might eagerly instantiate a non-singleton AspectJ aspect
if
(isAspectJAdvice(advisor)) {
foundAspectJAdvice =
true
;
}
}
if
(foundAspectJAdvice && !advisors.contains(ExposeInvocationInterceptor.ADVISOR)) {
advisors.add(
0
, ExposeInvocationInterceptor.ADVISOR);
//添加到链
return
true
;
}
}
return
false
;
}
/**
*判断是不是AspectJAdvice
* Determine whether the given Advisor contains an AspectJ advice.
* @param advisor the Advisor to check
*/
private
static
boolean
isAspectJAdvice(Advisor advisor) {
return
(advisor
instanceof
InstantiationModelAwarePointcutAdvisor ||
advisor.getAdvice()
instanceof
AbstractAspectJAdvice ||
(advisor
instanceof
PointcutAdvisor &&
((PointcutAdvisor) advisor).getPointcut()
instanceof
AspectJExpressionPointcut));
}
}
//就做一件事,对外曝光MethodInvocation,放到ThreadLocal中
public
class
ExposeInvocationInterceptor
implements
MethodInterceptor, Ordered, Serializable {
/** Singleton instance of this class */
public
static
final
ExposeInvocationInterceptor INSTANCE =
new
ExposeInvocationInterceptor();
private
static
final
ThreadLocal<MethodInvocation> invocation =
new
NamedThreadLocal<MethodInvocation>(
"Current AOP method invocation"
);
private
ExposeInvocationInterceptor() {
}
public
Object invoke(MethodInvocation mi)
throws
Throwable {
MethodInvocation oldInvocation = invocation.get();
invocation.set(mi);
try
{
return
mi.proceed();
}
finally
{
invocation.set(oldInvocation);
}
}
public
int
getOrder() {
return
Ordered.HIGHEST_PRECEDENCE +
1
;
}
}
|
3.剖析Spring pointcut匹配逻辑
如图
重点关注AspectJExpressionPointcut.该类同时实现了MethodMatcher和ClassFilter两个接口。
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
|
public
class
AspectJExpressionPointcut
extends
AbstractExpressionPointcut
implements
ClassFilter, IntroductionAwareMethodMatcher, BeanFactoryAware {
//ClassFilter实现
public
boolean
matches(Class targetClass) {
checkReadyToMatch();
try
{
return
this
.pointcutExpression.couldMatchJoinPointsInType(targetClass);
}
catch
(ReflectionWorldException e) {
logger.debug(
"PointcutExpression matching rejected target class"
, e);
}
}
//MethodMatcher实现
public
boolean
matches(Method method, Class targetClass,
boolean
beanHasIntroductions) {
checkReadyToMatch();
Method targetMethod = AopUtils.getMostSpecificMethod(method, targetClass);
ShadowMatch shadowMatch = getShadowMatch(targetMethod, method);
// Special handling for this, target, @this, @target, @annotation
// in Spring - we can optimize since we know we have exactly this class,
// and there will never be matching subclass at runtime.
if
(shadowMatch.alwaysMatches()) {
return
true
;
}
else
if
(shadowMatch.neverMatches()) {
return
false
;
}
else
{
// the maybe case
return
(beanHasIntroductions || matchesIgnoringSubtypes(shadowMatch) || matchesTarget(shadowMatch, targetClass));
}
}
}
public
class
PointcutExpressionImpl
implements
PointcutExpression {
//AspectJExpressionPointcut 实现ClassFilter接口时候调用。
public
boolean
couldMatchJoinPointsInType(Class aClass) {
ResolvedType matchType = world.resolve(aClass.getName());
ReflectionFastMatchInfo info =
new
ReflectionFastMatchInfo(matchType,
null
,
this
.matchContext, world);
boolean
couldMatch = pointcut.fastMatch(info).maybeTrue();
if
(MATCH_INFO) {
System.out.println(
"MATCHINFO: fast match for '"
+
this
.expression +
"' against '"
+ aClass.getName() +
"': "
+ couldMatch);
}
return
couldMatch;
}}
|
4序列图
5.总结
截止到现在已经讲解了BeanNameAutoProxyCreator,DefaultAdvisorAutoProxyCreator及今天的AspectJAwareAdvisorAutoProxyCreator。虽然复杂度有所区别,但底层设计本质是一样的。Spring 在AbstractAutoProxyCreator进行了抽象处理,实现了扩展性。
本文转自 randy_shandong 51CTO博客,原文链接:http://blog.51cto.com/dba10g/1786117,如需转载请自行联系原作者