作者: 一字马胡
转载标志 【2018-01-02】
更新日志
日期 | 更新内容 | 备注 |
---|---|---|
2018-01-02 | 创建分析文档 | Spring源码分析系列文章(三) |
前言
本文是Spring源码分析系列的第三篇文章,前两篇文章分别分析总结了Spring bean的解析以及bean的加载过程,可以参考下面的链接来了解详细内容:
本文涉及Spring源码中关于AOP相关的内容,AOP(Aspect-OrientedProgramming,面向方面编程)是一种编程思想,是一种面向切面的编程方法,也就是说,AOP是一种横向的编程技术,与面向对象的编程思想不一样,面向对象的编程思想是一种纵向的编程技术,面向对象需要我们做抽象封装,而AOP则希望对应用进行横向扩展,类似记录方法调用日志、方法调用耗时等这样的需求,使用面向对象的思想就不太容易解决,而AOP就是用来解决这类具有“横向”关系的问题的。
在进行AOP源码分析之前,需要了解几个Spring AOP的关键概念:
-
切面(Aspect):一个关注点的模块化,这个关注点实现可能另外横切多个对象。
-
连接点(Joinpoint): 程序执行过程中明确的点,如方法的调用或特定的异常被抛出。
-
通知(Advice): 在特定的连接点,AOP框架执行的动作。各种类型的通知包括“around”、“before”和“throws”通知。
-
切入点(Pointcut): 指定一个通知将被引发的一系列连接点的集合。AOP框架必须允许开发者指定切入点:例如,使用正则表达式。 Spring定义了Pointcut接口,用来组合MethodMatcher和ClassFilter,可以通过名字很清楚的理解, MethodMatcher是用来检查目标类的方法是否可以被应用此通知,而ClassFilter是用来检查Pointcut是否应该应用到目标类上
-
目标对象(Target Object): 包含连接点的对象。也被称作被通知或被代理对象。
-
AOP代理(AOP Proxy): AOP框架创建的对象,包含通知。 在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。
-
织入(Weaving): 组装方面来创建一个被通知对象。这可以在编译时完成(例如使用AspectJ编译器),也可以在运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入。
在开始进行源码分析之前,先来看一下怎么使用Spring AOP来做一些事情,仅介绍基于注解方式的使用方法,xml配置方式的暂不做介绍。在xml配置文件中需要把aop功能打开,添加下面的配置:
<aop:aspectj-autoproxy/>
然后就可以在项目中使用Spring AOP了,下面是一个简单的小例子:
package com.hujian.aop;
import com.hujian.star.log.impl.SampleLocalLogServiceImpl;
import com.hujian.star.log.utils.LogUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import java.io.Serializable;
/**
* Created by hujian on 2017/5/11.
*/
@Aspect
public class ServiceAop implements Serializable {
@Autowired
private SampleLocalLogServiceImpl sampleLocalLogHandler = null;
/**
* the star
*/
@Pointcut("execution(* com.hujian.*.services.*..*(..))")
public void aspect(){
}
/**
* before calling the services
* for the methods call-time statistic info.
*/
@Before("aspect()")
public void BeforeCallServices(JoinPoint joinPoint){
String dec = joinPoint.getSignature().getDeclaringType().getName();
String method = joinPoint.getSignature().getName();
String args = "";
for( Object arg : joinPoint.getArgs() ){
args += arg.toString() + " ";
}
//do the log,then we can read the record from log to do some statistics
//such as the call count of the methods
String log = "[" + LogUtils.getTime() + "]\t" + method + "\t" + args + "\t" + dec + "\n";
sampleLocalLogHandler.writeLog(log,"aop_info");
}
}
需要使用@Aspect注解来告诉Spring这是一个AOP类,里面有几个注解需要注意,@Pointcut定义了一个切点,也就是在什么地方切入,@Before注解说明需要在切点之前触发,还有一些其他的注解可以使用,在下面的分析中逐渐说明。
Spring AOP源码分析
在编写了一个简单的Spring AOP应用之后,现在开始来分析Spring AOP的源码实现,本系列文章不会涉及太过细节的内容,只会将主干内容分析总结出来,细节内容将在未来逐步补充学习。另外一点需要说明的是,细节是很重要的,但是又是很花费时间的,在没有理清整体框架思路之前分析细节是很危险的,因为很容易掉进去出不来,特别是对于复杂的框架,如果太过注意细节的话,首先是需要花费大量的时间再抠细节,其次会因为有些细节没有及时搞懂而耽误进度。一个合理的做法是首先将主干打通,然后再根据实际中碰到的问题进行细节分析整体,这样的学习效果才是最好的,效率也是最高的。
本文的分析基于注解,而xml配置相关的内容将不再本文的分析范围之内。上面的例子中已经提到,我们需要关注的是@Aspect这个注解。现在开始来进行AOP相关的源码分析。
在上面的例子中,xml中配置了 <aop:aspectj-autoproxy/>来开启aop功能,而 <aop:aspectj-autoproxy/>似乎是一个自定义标签,不想bean等默认标签,而在Spring中,每一个自定义标签都会对应一个handler来进行处理,全局搜索 aspectj-autoproxy就会找到这个handler,也就是AopNamespaceHandler,在init方法内部,找到aspectj-autoproxy对应的parser是AspectJAutoProxyBeanDefinitionParser,所以,本文对Spring AOP源码的分析从AspectJAutoProxyBeanDefinitionParser这个类开始。关于自定义标签的解析,可以参考文章:浅析Spring自定义标签的使用。
AspectJAutoProxyBeanDefinitionParser类的parse方法是标签解析的入口,主要关注AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element)这行代码,跟踪进去可以看到下面的代码:
public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
ParserContext parserContext, Element sourceElement) {
BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
parserContext.getRegistry(), parserContext.extractSource(sourceElement));
useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
registerComponentIfNecessary(beanDefinition, parserContext);
}
主要关注registerAspectJAnnotationAutoProxyCreatorIfNecessary这个方法调用,进去之后发现了一个关键的类AnnotationAwareAspectJAutoProxyCreator,这个类也是本文源码分析的重点。下面首先展示了AnnotationAwareAspectJAutoProxyCreator类的类图:
可以看到AnnotationAwareAspectJAutoProxyCreator 继承了BeanPostProcessor,所以可以知道,Spring在加载该bean的时候,在实例化前会调用postProcessAfterInitialization方法,所以分析的入口是AnnotationAwareAspectJAutoProxyCreator的postProcessAfterInitialization方法。
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;
}
主要关注wrapIfNecessary这个方法的调用。
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;
}
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;
}
在真正开始进行流程内容之前会做一些判断,比如是否是一个基础设施bean,或者是否被子类设定可以skip等等,关键的代码是对方法getAdvicesAndAdvisorsForBean的调用,调用getAdvicesAndAdvisorsForBean方法可以获取到增强方法,获取到增强方法之后就可以进行代理了。现在先来分析getAdvicesAndAdvisorsForBean方法的具体实现过程。跟踪该方法几步之后可以发现下面的关键方法:
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;
}
findCandidateAdvisors方法的功能是获取到所有的增强,findAdvisorsThatCanApply方法的功能是获取可以适用于该bean的增强。下面先来分析findCandidateAdvisors的具体流程。因为分析是基于注解分析的,所以会走到下面的方法中:
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) {
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
}
return advisors;
}
super方法的调用是为了获取xml中的aop配置,而该方法中需要关注的内容时this.aspectJAdvisorsBuilder.buildAspectJAdvisors这一行代码。该方法实现的功能为下面几个个:
- 获取所有的beanName,所有在BeanFactory中注册的bean都会被扫描到
- 遍历所有的beanName,然后将那些声明为Aspect注解的beanName挑选出来
- 开始解析这些挑选出来的bean,提取增强内容
- 将提取的内容缓存起来
下面分别分析每一个步骤是怎么实现的,首先,获取所有的beanName,可以通过下面的代码实现:
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Object.class, true, false);
获取到了所有的bean之后,遍历这些bean,然后逐个进行判断,首先获取bean的type,然后判断是不是声明为Aspect注解的bean,如果是的话继续解析,不是的话就跳过,下面来看一下是如何判断一个bean是不是声明为Aspect注解的beanName的:
public boolean isAspect(Class<?> clazz) {
return (hasAspectAnnotation(clazz) && !compiledByAjc(clazz));
}
private boolean hasAspectAnnotation(Class<?> clazz) {
return (AnnotationUtils.findAnnotation(clazz, Aspect.class) != null);
}
private static <A extends Annotation> A findAnnotation(
Class<?> clazz, @Nullable Class<A> annotationType, boolean synthesize) {
Assert.notNull(clazz, "Class must not be null");
if (annotationType == null) {
return null;
}
AnnotationCacheKey cacheKey = new AnnotationCacheKey(clazz, annotationType);
A result = (A) findAnnotationCache.get(cacheKey);
if (result == null) {
result = findAnnotation(clazz, annotationType, new HashSet<>());
if (result != null && synthesize) {
result = synthesizeAnnotation(result, clazz);
findAnnotationCache.put(cacheKey, result);
}
}
return result;
}
接着开始最为关键的一步,就是提取增强器,相关的方法是this.advisorFactory.getAdvisors,下面来分析一下是如何做到提取增强器的。
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 = 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.
for (Field field : aspectClass.getDeclaredFields()) {
Advisor advisor = getDeclareParentsAdvisor(field);
if (advisor != null) {
advisors.add(advisor);
}
}
return advisors;
}
主要关注其中的getAdvisor方法调用:
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);
}
然后关注切点信息的获取方法getPointcut:
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;
}
首先调用findAspectJAnnotationOnMethod方法获取了所有aop感兴趣的注解信息,可以看下面的具体的代码:
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;
}
这里面的findAnnotation方法是一个需要关注的方法:
private static <A extends Annotation> AspectJAnnotation<A> findAnnotation(Method method, Class<A> toLookFor) {
A result = AnnotationUtils.findAnnotation(method, toLookFor);
if (result != null) {
return new AspectJAnnotation<>(result);
}
else {
return null;
}
}
重点在于return new AspectJAnnotation<>(result)这一行代码:
public AspectJAnnotation(A annotation) {
this.annotation = annotation;
this.annotationType = determineAnnotationType(annotation);
// We know these methods exist with the same name on each object,
// but need to invoke them reflectively as there isn't a common interface.
try {
this.pointcutExpression = resolveExpression(annotation);
this.argumentNames = (String) annotation.getClass().getMethod("argNames").invoke(annotation);
}
catch (Exception ex) {
throw new IllegalArgumentException(annotation + " cannot be an AspectJ annotation", ex);
}
}
private String resolveExpression(A annotation) throws Exception {
for (String methodName : EXPRESSION_PROPERTIES) {
Method method;
try {
method = annotation.getClass().getDeclaredMethod(methodName);
}
catch (NoSuchMethodException ex) {
method = null;
}
if (method != null) {
String candidate = (String) method.invoke(annotation);
if (StringUtils.hasText(candidate)) {
return candidate;
}
}
}
throw new IllegalStateException("Failed to resolve expression: " + annotation);
}
到此为止,所有aop相关的注解都可以获取到了,下面回过头来看getPointcut方法接下来的内容,可以看到new出来了一个AspectJExpressionPointcut对象,并且将其expression属性设置为了上面解析好的PointcutExpression,以及设置BeanFactory并且返回。现在可以回到getAdvisor方法,来看接下来的内容,在getAdvisor方法中,调用getPointcut方法获取到信息之后,getAdvisor方法会返回一个InstantiationModelAwarePointcutAdvisorImpl对象,下面的图片展示了InstantiationModelAwarePointcutAdvisorImpl类的构造函数的细节,看起来就是将一些属性保存起来,然后最后有一个非常关键的方法调用:instantiateAdvice。
下面来分析instantiateAdvice方法的具体实现细节。
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);
}
继续跟踪下去,就会走到ReflectiveAspectJAdvisorFactory类的getAdvice方法中去,该方法中会根据aspectJAnnotation的类型来返回相应的Advice,细节入下图所示:
比如对于@Before注解,会生成一个AspectJMethodBeforeAdvice类型的Advice,下图展示了AspectJMethodBeforeAdvice类的实现细节。
主要关注before方法,在before方法中调用了invokeAdviceMethod方法,下面来分析一下invokeAdviceMethod方法的实现细节:
protected Object invokeAdviceMethod(
@Nullable JoinPointMatch jpMatch, @Nullable Object returnValue, @Nullable Throwable ex)
throws Throwable {
return invokeAdviceMethodWithGivenArgs(argBinding(getJoinPoint(), jpMatch, returnValue, ex));
}
接着又调用了invokeAdviceMethodWithGivenArgs方法,下面是invokeAdviceMethodWithGivenArgs方法的实现内容:
protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable {
Object[] actualArgs = args;
if (this.aspectJAdviceMethod.getParameterCount() == 0) {
actualArgs = null;
}
try {
ReflectionUtils.makeAccessible(this.aspectJAdviceMethod);
// TODO AopUtils.invokeJoinpointUsingReflection
return this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs);
}
catch (IllegalArgumentException ex) {
throw new AopInvocationException("Mismatch on arguments to advice method [" +
this.aspectJAdviceMethod + "]; pointcut expression [" +
this.pointcut.getPointcutExpression() + "]", ex);
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
在该方法中,触发了实际设置的增强方法。这里面有一个细节需要我们分析一下,这里面的args是怎么来的?这也就牵扯到Spring是如何解析aop表达式的问题,下面来分析一下Spring解析aop的流程。在getAdvice方法的末尾,有一个关键的方法调用:springAdvice.calculateArgumentBindings(),下面来分析一下这个方法的具体实现细节:
public synchronized final void calculateArgumentBindings() {
// The simple case... nothing to bind.
if (this.argumentsIntrospected || this.parameterTypes.length == 0) {
return;
}
int numUnboundArgs = this.parameterTypes.length;
Class<?>[] parameterTypes = this.aspectJAdviceMethod.getParameterTypes();
if (maybeBindJoinPoint(parameterTypes[0]) || maybeBindProceedingJoinPoint(parameterTypes[0])) {
numUnboundArgs--;
}
else if (maybeBindJoinPointStaticPart(parameterTypes[0])) {
numUnboundArgs--;
}
if (numUnboundArgs > 0) {
// need to bind arguments by name as returned from the pointcut match
bindArgumentsByName(numUnboundArgs);
}
this.argumentsIntrospected = true;
}
这其中有一个关键的方法调用:bindArgumentsByName(numUnboundArgs):
private void bindArgumentsByName(int numArgumentsExpectingToBind) {
if (this.argumentNames == null) {
this.argumentNames = createParameterNameDiscoverer().getParameterNames(this.aspectJAdviceMethod);
}
if (this.argumentNames != null) {
// We have been able to determine the arg names.
bindExplicitArguments(numArgumentsExpectingToBind);
}
else {
throw new IllegalStateException("Advice method [" + this.aspectJAdviceMethod.getName() + "] " +
"requires " + numArgumentsExpectingToBind + " arguments to be bound by name, but " +
"the argument names were not specified and could not be discovered.");
}
}
然后主要看getParameterNames这个方法调用,具体的实现细节可以参考AspectJAdviceParameterNameDiscoverer类中的getParameterNames的具体实现,详细的细节就不再次分析了。
在解析完所有的增强之后,就需要为当前bean挑选合适的增强器了,因为所有的增强器不一定都能匹配到当前bean,而这个挑选匹配的增强器的工作由findAdvisorsThatCanApply方法完成,下面来分析一下这个方法的具体内容:
protected List<Advisor> findAdvisorsThatCanApply(
List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {
ProxyCreationContext.setCurrentProxiedBeanName(beanName);
try {
return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
}
finally {
ProxyCreationContext.setCurrentProxiedBeanName(null);
}
}
主要关注 AopUtils.findAdvisorsThatCanApply这一句:
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;
}
主要看canApply这个方法的实现:
public static boolean canApply(Advisor advisor, Class<?> targetClass) {
return canApply(advisor, targetClass, false);
}
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;
}
}
具体的匹配判断过程可以看下面的代码:
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;
}
到此为止,我们获取了所有bean对应的增强器,下面就可以开始创建代理了,而创建代理则通过createProxy方法来进行,下面来开始分析一下createProxy方法的实现流程。
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());
}
首先new了一个ProxyFactory对象,然后调用了ProxyFactory对象的copyFrom方法,该方法实现的功能是从当前对象中获取一些属性,具体实现细节如下:
public void copyFrom(ProxyConfig other) {
Assert.notNull(other, "Other ProxyConfig object must not be null");
this.proxyTargetClass = other.proxyTargetClass;
this.optimize = other.optimize;
this.exposeProxy = other.exposeProxy;
this.frozen = other.frozen;
this.opaque = other.opaque;
}
接着,判断对于给定的bean是否应该使用targetClass而不是它的接口代理,其中,shouldProxyTargetClass方法用于判断是否应该使用targetClass类而不是接口来进行代理,下面来看具体的实现细节:
protected boolean shouldProxyTargetClass(Class<?> beanClass, @Nullable String beanName) {
return (this.beanFactory instanceof ConfigurableListableBeanFactory &&
AutoProxyUtils.shouldProxyTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName));
}
public static boolean shouldProxyTargetClass(ConfigurableListableBeanFactory beanFactory, @Nullable String beanName) {
if (beanName != null && beanFactory.containsBeanDefinition(beanName)) {
BeanDefinition bd = beanFactory.getBeanDefinition(beanName);
return Boolean.TRUE.equals(bd.getAttribute(PRESERVE_TARGET_CLASS_ATTRIBUTE));
}
return false;
}
如果使用接口代理的话,会执行evaluateProxyInterfaces方法,具体细节如下:
protected void evaluateProxyInterfaces(Class<?> beanClass, ProxyFactory proxyFactory) {
Class<?>[] targetInterfaces = ClassUtils.getAllInterfacesForClass(beanClass, getProxyClassLoader());
boolean hasReasonableProxyInterface = false;
for (Class<?> ifc : targetInterfaces) {
if (!isConfigurationCallbackInterface(ifc) && !isInternalLanguageInterface(ifc) &&
ifc.getMethods().length > 0) {
hasReasonableProxyInterface = true;
break;
}
}
if (hasReasonableProxyInterface) {
// Must allow for introductions; can't just set interfaces to the target's interfaces only.
for (Class<?> ifc : targetInterfaces) {
proxyFactory.addInterface(ifc);
}
}
else {
proxyFactory.setProxyTargetClass(true);
}
}
接着回到createProxy方法中,看剩下部分的流程,接着是一个重要的方法buildAdvisors,这个方法的功能是将该bean的所有Advisor以及拦截器统一封装成Advisor,并设置到proxyFactory中。具体的包装细节可以参考下面的代码:
public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
if (adviceObject instanceof Advisor) {
return (Advisor) adviceObject;
}
if (!(adviceObject instanceof Advice)) {
throw new UnknownAdviceTypeException(adviceObject);
}
Advice advice = (Advice) adviceObject;
if (advice instanceof MethodInterceptor) {
// So well-known it doesn't even need an adapter.
return new DefaultPointcutAdvisor(advice);
}
for (AdvisorAdapter adapter : this.adapters) {
// Check that it is supported.
if (adapter.supportsAdvice(advice)) {
return new DefaultPointcutAdvisor(advice);
}
}
throw new UnknownAdviceTypeException(advice);
}
接着关键部分是调用proxyFactory.getProxy来获取代理对象。
public Object getProxy(@Nullable ClassLoader classLoader) {
return createAopProxy().getProxy(classLoader);
}
public Object getProxy() {
return createAopProxy().getProxy();
}
而createAopProxy方法的细节如下:
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);
}
}
实现代理有两种可选的方式,jdk动态代理和cglib代理。具体的选择过程策略如下:
- 如果目标对象实现了接口,则默认选择使用jdk动态代理
- 如果目标对象没有实现接口,那么必须使用cglib来实现代理
下面先来看jdk动态代理的实现过程。
public Object getProxy(@Nullable ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
}
Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
关于jdk动态代理的内容,可以参考Java静态代理和动态代理。主要看Proxy.newProxyInstance的第三个参数,它是InvocationHandler的实现类,主要看InvocationHandler类的invoke方法。主要看下面的代码:
主要工作就是创建了一个拦截器链条,然后逐个调用,关键代码如下:
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);
}
}
需要调用完所有的拦截器才会执行切点方法,这里面还是有很多细节,待理清楚了再补充说明。关于使用cglib进行代理的实现可以参考CglibAopProxy#getProxy的具体实现,本文不再进行分析总结。
至此,本文的分析内容就结束了,分析的内容非常粗略,但是可以从中看出Spring AOP实现的原理,以及一些细节,这其中还有大量的细节需要学习补充,本文作为Spring源码分析的第三篇文章,主要内容时分析Spring AOP的实现,关于如何使用Spring AOP以及更为细节的一些内容将在未来分析总结,然后再进行补充。
网友评论