Spring源码核心篇整体栏目
内容 | 链接地址 |
---|---|
【一】Spring的bean的生命周期 | https://zhenghuisheng.blog.csdn.net/article/details/143441012 |
【二】深入理解spring的依赖注入和属性填充 | https://zhenghuisheng.blog.csdn.net/article/details/143854482 |
【三】精通spring的aop的底层原理和源码实现 | https://zhenghuisheng.blog.csdn.net/article/details/144012934 |
深入理解spring的依赖注入和属性填充
如需转载,请附上链接:https://blog.csdn.net/zhenghuishengq/article/details/144012934
springaop_12">一,精通spring的aop的底层原理
前面两篇分析了spring的bean的生命周期,以及属性填充的底层实现,接下来这篇主要讲解的是Spring的另一个核心特性 AOP ,在了解aop之前,需要先了解动态代理的具体实现cglib和jdk动态代理。
1,proxyFactory获取代理对象
在spring源码中,实现动态代理的方式主要是通过这个proxyFactory工厂实现,如下面这段代码,先创建一个动态代理的工厂,然后从工厂中获取到对应的target
ProxyFactory proxyFactory = new ProxyFactory();
UserService proxy = (UserService)proxyFactory.getProxy();
直接进入这个getProxy中可以发现会先创建一个Aop的动态代理,再通过调用对应的代理方法拿到具体的实现类
public Object getProxy() {return createAopProxy().getProxy();
}
首先先看这个 createAopProxy 方法,判断是创建cglib动态代理还是创建jdk的动态代理
- config指的就是上面的userService对应的 proxy 对象,因此第一步就是对proxy 对象属性进行判断
- 随后拿到target代理类,判断是否接口,以及判断proxy是否是jdk代理,否则则使用jdk动态代理
- 如果不是接口或者不是使用的jdk动态代理,那么就是使用cglib代理,cglib底层也是通过实现父类实现
这个 getProxy 方法的AopProxy接口中,主要有两种方式实现动态代理,一种是基于cglib的 cglibAopProxy ,一种是基于jdk动态代理的 JdkDynamicAopProxy
使用jdk动态代理的实现如下,直接诶通过调用这个 newProxyInstance 实现,从而拿到代理对象
@Override
public Object getProxy(@Nullable ClassLoader classLoader) {Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
使用这个cglib的动态代理如下,从而拿到代理对象
@Override
public Object getProxy(@Nullable ClassLoader classLoader) {createProxyClassAndInstance(enhancer, callbacks)
}
2,invoke获取代理对象的某个方法
在上面已经通过proxyFactory获取到了一个代理对象,在有了对象之后,就需要获取到需要代理的方法,如在jdk动态代理中,主要是通过这个invoke方法来获取代理对象的方法
- 首先不会执行代理对象的equal方法,hashcode方法
- 若执行的class对象是DecoratingProxy 则不会对其应用切面进行方法的增强
- 如果目标对象实现的Advised接口,则不会对其应用切面进行方法的增强。 直接执行方法
接下来继续往这个方法下面走,会有一个提前暴露的操作,如果设置为true,那么就将这个代理存入到一个ThreadLocal里面,后续只需要去这个ThreadLocal中获取即可,如事务失效的情况,就可以在这个ThreadLocal中获取
if (this.advised.exposeProxy) {//把我们的代理对象暴露到线程变量中oldProxy = AopContext.setCurrentProxy(proxy);setProxyContext = true;
}
继续看这个类中的方法,会调用一个 getInterceptorsAndDynamicInterceptionAdvice 方法,把aop的advisor 全部转化为拦截器, 通过责任链模式依次调用,通过该类筛选出符合的 advisor
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
通过筛选出来的数组进行值判断,对有代理和没有代理,会有两种形式去获取方法
- 如果筛选的链路为空,代表不需要代理,那么就会直接的通过反射的方式进行获取对象和方法
- 如果筛选后发现链路不为空,那么就需要将代理类,目标类,目标方法,参数,class以及链路传入到 ReflectiveMethodInvocation 方法进行调用,然后返回一个重要的方法MethodInvocation对象,最终调用这个 proceed 方法进行责任链方法的执行
2.1,pointcutAdvisor匹配
接下来回到上面的那个 getInterceptorsAndDynamicInterceptionAdvice 筛选责任链的方法,在spring中,每一个advice都会被封装成一个advisor,每一个advisor会包括一个advice和一个pointcut切入点。
因此在这个筛选方法中,会去遍历全部的advisor,pointcut主要是作为匹配使用,因此需要将这个advisor先转换成这个pointcutAdvisor,然后继续执行的流程如下,通过以下流程确定对应的类和方法。可能会匹配到多个链路,因此最终返回一个一个list数组,包含 MethodInterceptor 类型的对象
- pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass) 先通过这个方法进行类的匹配
- MethodMatchers.matches(mm, method, actualClass, hasIntroductions) 再通过这个方法进行方法匹配
2.2,IntroductionAdvisor匹配
上面这种切入点匹配直接是通过类方法进行比较进行匹配,而下面的这种 IntroductionAdvisor 方式是通过一些适配器匹配的方式进行匹配
其核心是下面这个getInterceptors方法,其匹配的流程如下,先判断是否是他的MethodInterceptor实现类 ,然后判断是否 AdvisorAdapter 适配器匹配模式
@Override
public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {List<MethodInterceptor> interceptors = new ArrayList<>(3);Advice advice = advisor.getAdvice();if (advice instanceof MethodInterceptor) {interceptors.add((MethodInterceptor) advice);}for (AdvisorAdapter adapter : this.adapters) {if (adapter.supportsAdvice(advice)) {interceptors.add(adapter.getInterceptor(advisor));}}if (interceptors.isEmpty()) {throw new UnknownAdviceTypeException(advisor.getAdvice());}return interceptors.toArray(new MethodInterceptor[0]);
}
核心就是看这个适配器匹配模式,在构造这个 DefaultAdvisorAdapterRegistry 类时,会有三个具体的适配器模式,分别是 MethodBeforeAdviceAdapter,AfterReturningAdviceAdapter,ThrowsAdviceAdapter ,为什么没有around这种模式,其实很简单,这三种模式的总集合就是around模式
public DefaultAdvisorAdapterRegistry() {registerAdvisorAdapter(new MethodBeforeAdviceAdapter());registerAdvisorAdapter(new AfterReturningAdviceAdapter());registerAdvisorAdapter(new ThrowsAdviceAdapter());
}
随便查看一个 MethodBeforeAdviceAdapter 的实现如下,最左会返回一个 MethodInterceptor 对象,说明肯定是可以匹配成功的
3,proceed责任链调用
在spring的aop中,其底层主要是通过责任链模式实现,其底层主要是通过调用这个 proceed 方法实现,在上面匹配方法时,就已经进行了这个方法的调用
invocation.proceed();
直接进入这个 ReflectiveMethodInvocation 类中的proceed方法中,假设有三个调用链,那么第一步会先记录调用链的个数,随后每调用一个会加1,知道累加的个数等于调用链的个数时则会直接停止
接下来看这段执行完毕的方法,当执行的责任链路次数达到链路的个数时,那么就会执行这个 invokeJoinpoint 方法
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1){return invokeJoinpoint();
}
接下来进入这个 invokeJoinpoint 方法,发现只有一行代码,就是调用这个 invokeJoinpointUsingReflection
@Nullable
protected Object invokeJoinpoint() throws Throwable {return AopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments);
}
最后查看这个 invokeJoinpointUsingReflection 方法,发现最终就是执行上面的invoke方法,这里就是执行代理对象的那个方法
也就是说再匹配完所有的链路之后,才会最终的执行这个代理对象所对应的方法
springaop_198">4,spring中aop后置处理器
接下来又得回到spring的生命周期中,直接看这个 initializeBean 初始化方法,里面会有一个 applyBeanPostProcessorsAfterInitialization 调用bean的后置处理器的方法,主要是解析aop的
在这个 applyBeanPostProcessorsAfterInitialization 方法中,最主要的就是这个 postProcessAfterInitialization 核心方法
@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)throws BeansException {Object result = existingBean;//获取我们容器中的所有的bean的后置处理器for (BeanPostProcessor processor : getBeanPostProcessors()) {Object current = processor.postProcessAfterInitialization(result, beanName);//若只有有一个返回null 那么直接返回原始的if (current == null) {return result;}result = current;}return result;
}
最后进入这个 postProcessAfterInitialization 核心方法,并且进入里面的实现类 AbstractAutoProxyCreator
进入该类后看到的方法就是 postProcessAfterInitialization ,最后进入这个重要的 wrapIfNecessary 方法
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException {if (bean != null) {//获取缓存keyObject cacheKey = getCacheKey(bean.getClass(), beanName);// 之前循环依赖创建的动态代理 如果是现在的bean 就不再创建,,并且移除if (this.earlyProxyReferences.remove(cacheKey) != bean) {// 该方法将会返回动态代理实例return wrapIfNecessary(bean, beanName, cacheKey);}}return bean;
}
wrapIfNecessary方法的详细流程如下:
- 已经被处理过,就是自己实现创建动态代理逻辑,那么可以直接返回
- 不需要增加的bean,也直接返回
- 是不是基础的bean,是不是需要跳过的,重复判断
- 最后就是来到了重点,根据当前bean找到匹配的advisor,最后加入到缓存中
在spring中创建真正的代理对象的方法如下,首先创建一个 ProxyFactory 代理工厂,然后判断是cglib的动态代理还是cglib的动态代理,然后构建Advisors数组,随后将数组加入到代理工厂中,最后调用这个 getProxy 方法获取真正的代理对象,这里的getProxy方法,又回到了最上面使用cglib还是jdk动态代理的方法
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);//为proxyFactory设置创建jdk代理还是cglib代理// 如果设置了 <aop:aspectj-autoproxy proxy-target-class="true"/>不会进if,说明强制使用cglibif (!proxyFactory.isProxyTargetClass()) {// 内部设置的 , 配置类就会设置这个属性if (shouldProxyTargetClass(beanClass, beanName)) {proxyFactory.setProxyTargetClass(true);}else {// 检查有没有接口evaluateProxyInterfaces(beanClass, proxyFactory);}}//把我们的specificInterceptors数组中的Advisor转化为数组形式的Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);//为我们的代理工厂加入通知器,proxyFactory.addAdvisors(advisors);//设置targetSource对象proxyFactory.setTargetSource(targetSource);customizeProxyFactory(proxyFactory);proxyFactory.setFrozen(this.freezeProxy);// 代表之前是否筛选advise.// 因为继承了AbstractAdvisorAutoProxyCreator , 并且之前调用了findEligibleAdvisors进行筛选, 所以是trueif (advisorsPreFiltered()) {proxyFactory.setPreFiltered(true);}//真正的创建代理对象return proxyFactory.getProxy(getProxyClassLoader());
}
再来看一下这个 getAdvicesAndAdvisorsForBean 方法,如何根据bean找到匹配的advisor,进入这个 AbstractAdvisorAutoProxyCreator 实现类,最后查看这个方法里面的 findEligibleAdvisors 找advisor的方法
List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
找到合适的Advisor的具体实现如下,先拿到接口方式的aop,再判断通知能否作用到类上,最后进行一个排序
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {// 拿到接口方式的AOP (这次是从缓存中拿了)List<Advisor> candidateAdvisors = findCandidateAdvisors();//判断我们的通知能不能作用到当前的类上(切点是否命中当前Bean)List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);extendAdvisors(eligibleAdvisors);//对我们的advisor进行排序if (!eligibleAdvisors.isEmpty()) {eligibleAdvisors = sortAdvisors(eligibleAdvisors);}return eligibleAdvisors;
}
接下来就是查看这个 AnnotationAwareAspectJAutoProxyCreator 类中获取advisor的方法,可以发现有两种方式去找这个advisor对象,一种是直接通过xml配置或者实现原生Aop的Advisor的接口,另一种是加了@AspectJ注解的类
AspectJadvisor_332">5,@AspectJ注解获取advisor
在spring中通过@AspectJ使用aop时,@Before对应的就是advice注解,execution内部那一串就是pointcut
@Before("execution(* com.example.service..*.*(..))")
再来查看一下这个 @Aspect 这个注解是如何获取到封装的Advisor对象的,接下来直接看这个 buildAspectJAdvisors 方法,其内部核心步骤实现如下,就是遍历IOC容器中的全部bean,判断bean上面是否带有这个@AspectJ注解,找到这个注解之后,再去对应的类中解析一些 @Before,@After等注解,会将这些注解全部的封装成对应的Advisor
接下来在这个方法中,查看下面这段代码,真正的去获取我们的通知对象
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
接下来进入这个 getAdvisors 方法,看@AspectJ是如何去解析这些切面通知的
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {//获取我们的标记为Aspect的类Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();//获取我们的切面类的名称String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();//校验我们的切面类validate(aspectClass);//我们使用的是包装模式来包装我们的MetadataAwareAspectInstanceFactory 构建为MetadataAwareAspectInstanceFactoryMetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);List<Advisor> advisors = new ArrayList<>();//获取到切面类中的所有方法,但是该方法不会解析标注了@PointCut注解的方法for (Method method : getAdvisorMethods(aspectClass)) {//挨个去解析我们切面中的方法Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);if (advisor != null) {advisors.add(advisor);}}
}
再查看这个 getAdvisorMethods 方法,首先会过滤掉没有pointcut的方法,随后进行一个sort的排序
private List<Method> getAdvisorMethods(Class<?> aspectClass) {final List<Method> methods = new ArrayList<>();ReflectionUtils.doWithMethods(aspectClass, method -> {// Exclude pointcutsif (AnnotationUtils.getAnnotation(method, Pointcut.class) == null) {methods.add(method);}});methods.sort(METHOD_COMPARATOR);return methods;
}
在 ReflectiveAspectJAdvisorFactory 类的静态代码块中,有一段排序的接口,其先执行的的优先级如下:Around —> Before —> After —> AfterReturning ,如果有多个相同的切面,如有两个After的切面,那么会根据方法名字符串的ascII码进行比较,谁小谁先执行
static {Comparator<Method> adviceKindComparator = new ConvertingComparator<>(new InstanceComparator<>(Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class),(Converter<Method, Annotation>) method -> {AspectJAnnotation<?> annotation =AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(method);return (annotation != null ? annotation.getAnnotation() : null);});Comparator<Method> methodNameComparator = new ConvertingComparator<>(Method::getName);METHOD_COMPARATOR = adviceKindComparator.thenComparing(methodNameComparator);
}
在查看getAdvisors 方法中的 getAdvisor 方法,上面的getAdvisorMethods方法是过滤掉没有pointcut匹配的方法,那么这个方法就是用于解析这些方法,封装成对应的advisor
advisor_416">6,实现Advisor类获取advisor
在整个aop的动态代理思路中,有一步核心就是找到全部的advisor,这里讲解的第一种方式就是通过实现一些advisor类来获取。如举个例子,直接使用aop的原生注解的实现类来实现aop的动态增强
public class AopAdvisorTest implements MethodBeforeAdvice {@Overridepublic void before(Method method, Object[] args, Object target) throws Throwable {if (method.getName().equals("test")){System.out.println("test方法前置增加");}}
}
那么来通过源码来查看底层是如何实现的,直接进入这个 getAdvisor 方法,内部的核心方法就是调用了 InstantiationModelAwarePointcutAdvisorImpl 方法去封装成Advisor对象
通过外部传进来的参数生成一个Advisor对象
在这个对象的核心方法就是 getAdvice 方法,后序会调用这个 instantiateAdvice 方法,后面再次调用getadvice方法
在这个方法中,首先会获取切面类,然后获取方法上面的注解
后面会将获取到的注解进行匹配,通过不同的类型进行对应的封装,最后生成一个advice对象返回,5个注解对应5个advice
以before来看,内部就会提供一个before的实现方法,不同的advice内部会有对应的advice方法
advice不会以单独的方式存在,在 DefaultAdvisorAdapterRegistry 的适配器类中,最终通过这种责任链的调用模式,会将这些单独存在advice封装成一个个的advisor
advisor_466">7,匹配advisor,创建动态代理
接下来还是得回到4里面的 findEligibleAdvisors 方法,在5和6中主要都是在 findCandidateAdvisors 执行这个方法,就是如何在程序启动之后去获取这些advisor,主要有两种方法:一种是直接通过xml配置或者实现原生Aop的Advisor的接口,另一种是加了@AspectJ注解的类 ,这两种方法在找到对应的bean实例之后,最后都会封装成对应的advisor
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {// 拿到接口方式的AOP (这次是从缓存中拿了)List<Advisor> candidateAdvisors = findCandidateAdvisors();//判断我们的通知能不能作用到当前的类上(切点是否命中当前Bean)List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);extendAdvisors(eligibleAdvisors);//对我们的advisor进行排序if (!eligibleAdvisors.isEmpty()) {eligibleAdvisors = sortAdvisors(eligibleAdvisors);}return eligibleAdvisors;
}
接下来就是查看这个 findAdvisorsThatCanApply 方法,里面的核心方法就是 findAdvisorsThatCanApply 方法,从候选的通知器中找到当前bean关联的advisors对象
findAdvisorsThatCanApply 方法核心如下,
接下来查看第一个canApply方法,又到了熟悉的环节,上面也讲过,起流程如下:
- 先判断是否IntroductionAdvisor的实现类,是的话则根据类进行过滤判断
- 再判断是否是PointcutAdvisor的实现类,是的话再次调用这个canApply方法再次进行过滤
平常时开发用的最多的也是实现pointcut的实现类,因此主要看这个里面 canApply 中的过滤方法
其核心代码如下,首对class类进行过滤,匹配出代理类class对象,找到全部符合的对象之后,再去遍历这些class类对象,对里面的方法进行匹配,先通过反射获取类中的所有方法,在判断类上面是否有@AspectJ注解,方法上面的pointcut是否匹配等,如果都匹配的话,那么就会认为这了类上面的这个方法需要进行动态代理,后续就会去创建一个动态代理对象
//创建一个集合用于保存targetClass 的class对象
Set<Class<?>> classes = new LinkedHashSet<>();
//判断当前class是不是代理的class对象
if (!Proxy.isProxyClass(targetClass)) {//加入到集合中去classes.add(ClassUtils.getUserClass(targetClass));
}
//获取到targetClass所实现的接口的class对象,然后加入到集合中
classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
//循环所有的class对象
for (Class<?> clazz : classes) {//通过class获取到所有的方法Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);//循环我们的方法for (Method method : methods) {//通过methodMatcher.matches来匹配我们的方法if (introductionAwareMethodMatcher != null ?// 通过切点表达式进行匹配 AspectJ方式introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :// 通过方法匹配器进行匹配 内置aop接口方式methodMatcher.matches(method, targetClass)) {// 只要有1个方法匹配上了就创建代理return true;}}
}
aop_538">8,aop总结
- aop主要是用于切面,在原来的代码逻辑上,通过无侵入的方式实现代码的动态增强,内部主要通过代理方式cglib代理和jdk动态代理实现。
- aop主要是在bean初始化之后,通过bean的后置处理器实现,会将需要进行aop的方法找出全部封装成advisor,生成advisor的方式主要有两种,一种是直接实现advice接口,另一种是通过@AspectJ的方式实现。
- 每一个advisor包含一个advice和一个pointcut,advice主要用于增强使用,pointcut主要由于后续的寻找bean的匹配。
- 当把全部的advisor全部找到之后,会进行一个匹配的操作,将全部的获取到的advisor和进行aop的bean进行匹配,先通过class类和对应的method进行匹配,匹配成功之后再通过pointcut切入点进行匹配,当全部匹配成功之后,那么就会创建一个动态代理。