Spring事务解析3-增强方法的获取

时间:2023-02-17 23:30:05

从InfrastructureAdvisorAutoProxyCreator的层次结构中可以看到,InfrastructureAdvisorAutoProxyCreator间接实现了SmartInstantiationAwareBeanPostProcessor,而SmartInstantiationAwareBeanPostProcessor又继承自InstantiationAwareBeanPostProcessor,也就是说在Spring中,所有bean实例化时Spring都会保证调用其postProcessAfterInitialization方法,其实现是在父类AbstractAutoProxyCreator类中实现。

    @Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean != null) {
//根据给定的bean的class和name构建出个key,beanClassName_beanName
       Object cacheKey = getCacheKey(bean.getClass(), beanName);
       //是否是由于避免循环依赖而创建的bean代理
if (!this.earlyProxyReferences.contains(cacheKey)) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}

这里实现的主要目的是对指定bean进行封装,当然首先要确定是否需要封装,检测及封装的工作都委托给了wrapIfNecessary函数进行。

    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
     //如果已经处理过
if (beanName != null && 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;
}

wrapIfNecessary函数功能实现起来很复杂,但是逻辑上理解起来还是相对简单的,在wrapIfNecessary函数中主要的工作如下。

(1)找出指定bean对应的增强器。

(2)根据找出的增强器创建代理。

获取对应class/method的增强器

获取指定bean对应的增强器,其中包含两个关键字:增强器与对应。也就是说在getAdvicesAndAdvisorsForBean函数中,不但要找出增强器,而且还需要判断增强器是否满足要求。

    @Override
protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource targetSource) {
List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
if (advisors.isEmpty()) {
return DO_NOT_PROXY;
}
return advisors.toArray();
} 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;
}

渐渐地体会到了 Spring 中代码的优秀,即使是一个很复杂的逻辑,在 Spring中也会被拆分成若干个小的逻辑,然后在每个函数中实现,使得每个函数的逻辑简单到我们能快速地理解,而不会像有些人开发的那样,将一大堆的逻辑都罗列在一个函数中,给后期维护人员造成巨大的困扰。

同样,通过上面的函数,Spring又将任务进行了拆分,分成了获取所有增强器与增强器是否匹配两个功能点。

寻找候选增强器

    protected List<Advisor> findCandidateAdvisors() {
return this.advisorRetrievalHelper.findAdvisorBeans();
}
public List<Advisor> findAdvisorBeans() {
String[] advisorNames = null;
synchronized (this) {
advisorNames = this.cachedAdvisorBeanNames;
if (advisorNames == null) {
advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Advisor.class, true, false);
this.cachedAdvisorBeanNames = advisorNames;
}
}
if (advisorNames.length == 0) {
return new LinkedList<Advisor>();
}
List<Advisor> advisors = new LinkedList<Advisor>();
for (String name : advisorNames) {
if (isEligibleBean(name)) {
if (this.beanFactory.isCurrentlyInCreation(name)) {
if (logger.isDebugEnabled()) {
logger.debug("Skipping currently created advisor '" + name + "'");
}
}
else {
try {
advisors.add(this.beanFactory.getBean(name, Advisor.class));
}
catch (BeanCreationException ex) {
Throwable rootCause = ex.getMostSpecificCause();
if (rootCause instanceof BeanCurrentlyInCreationException) {
BeanCreationException bce = (BeanCreationException) rootCause;
if (this.beanFactory.isCurrentlyInCreation(bce.getBeanName())) {
if (logger.isDebugEnabled()) {
logger.debug("Skipping advisor '" + name +
"' with dependency on currently created bean: " + ex.getMessage());
}
// Ignore: indicates a reference back to the bean we're trying to advise.
// We want to find advisors other than the currently created bean itself.
continue;
}
}
throw ex;
}
}
}
}
return advisors;
}

首先是通过BeanFactoryUtils类提供的工具方法获取所有对应Advisor.class的类,获取办法无非是使用ListableBeanFactory中提供的方法:

String[] getBeanNamesForType(Class type, boolean includeNonSingletons, boolean allowEagerInit);

而当我们知道增强器在容器中的beanName时,获取增强器已经不是问题了,在BeanFactory中提供了这样的方法,可以帮助我们快速定位对应的bean实例。

T getBean(String name, Class requiredType) throws BeansException;

自定义标签曾经注册了一个类型为BeanFactoryTransactionAttributeSourceAdvisor的bean,而在此bean中我们又注入了另外两个Bean,那么此时这个Bean就会被开始使用了。因为BeanFactoryTransactionAttributeSourceAdvisor同样也实现了Advisor接口,那么在获取所有增强器时自然也会将此bean提取出来,并随着其他增强器一起在后续的步骤中被织入代理。

候选增强器中寻找到匹配项

    protected List<Advisor> findAdvisorsThatCanApply(
List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) { ProxyCreationContext.setCurrentProxiedBeanName(beanName);
try {
return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
}
finally {
ProxyCreationContext.setCurrentProxiedBeanName(null);
}
}
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
if (candidateAdvisors.isEmpty()) {
return candidateAdvisors;
}
     //首先处理引介增强
List<Advisor> eligibleAdvisors = new LinkedList<Advisor>();
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) {
//引介增强已经处理
continue;
}
       //对于普通bean的处理
if (canApply(candidate, clazz, hasIntroductions)) {
eligibleAdvisors.add(candidate);
}
}
return eligibleAdvisors;
}
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) {
       //当前的advisor就是之前查找出来的类型为BeanFactoryTransactionAttributeSourceAdvisor的bean实例,
       //而通过类的层次结构我们又知道:BeanFactoryTransactionAttributeSourceAdvisor间接实现了PointcutAdvisor。
       //因此,在canApply函数中的第二个if判断if (advisor instanceof PointcutAdvisor)时就会通过判断,
       //会将BeanFactoryTransactionAttributeSourceAdvisor中的getPointcut()方法返回值作为参数继续调用canApply方法,
       //而getPoint()方法返回的是TransactionAttributeSourcePointcut类型的实例。见下面BeanFactoryTransactionAttributeSourceAdvisor.java
       //对于transactionAttributeSource这个属性是在解析自定义标签时注入进去的,见标签解析。
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;
}
     //此时的pc表示TransactionAttributeSourcePointcut,pc.getMethodMatcher()返回的正是自身(this). 
MethodMatcher methodMatcher = pc.getMethodMatcher();
IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
}
     //首先获取对应类的所有接口并连同类本身一起遍历
Set<Class<?>> classes = new LinkedHashSet<Class<?>>(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
classes.add(targetClass);
for (Class<?> clazz : classes) {
       //遍历过程中又对类中的方法再次遍历
Method[] methods = clazz.getMethods();
for (Method method : methods) {
          //一旦匹配成功便认为这个类适用于当前增强器。
if ((introductionAwareMethodMatcher != null &&
introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) ||
methodMatcher.matches(method, targetClass)) {
return true;
}
}
}
return false;
}

BeanFactoryTransactionAttributeSourceAdvisor.java
public class BeanFactoryTransactionAttributeSourceAdvisor extends AbstractBeanFactoryPointcutAdvisor {
  private TransactionAttributeSource transactionAttributeSource;
  private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut() {
    @Override
    protected TransactionAttributeSource getTransactionAttributeSource() {
      return transactionAttributeSource;
    }
  };
}

对于事务的配置不仅仅局限于在函数上配置,我们都知道,在类活接口上的配置可以延续到类中的每个函数,那么,如果针对每个函数进行检测,在类本身上配置的事务属性岂不是检测不到了吗?带着这个疑问,我们继续探求matcher方法。做匹配的时候methodMatcher.matches(method, targetClass)会使用TransactionAttributeSource Pointcut类的matches方法。

    @Override
public boolean matches(Method method, Class<?> targetClass) {
if (TransactionalProxy.class.isAssignableFrom(targetClass)) {
return false;
}
     //自定义标签解析时注入,AnnotationTransactionAttributeSource
TransactionAttributeSource tas = getTransactionAttributeSource();
return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
}
   //AnnotationTransactionAttributeSource.java
@Override
public TransactionAttribute getTransactionAttribute(Method method, Class<?> targetClass) {
// First, see if we have a cached value.
Object cacheKey = getCacheKey(method, targetClass);
Object cached = this.attributeCache.get(cacheKey);
if (cached != null) {
// Value will either be canonical value indicating there is no transaction attribute,
// or an actual transaction attribute.
if (cached == NULL_TRANSACTION_ATTRIBUTE) {
return null;
}
else {
return (TransactionAttribute) cached;
}
}
else {
// We need to work it out.
TransactionAttribute txAtt = computeTransactionAttribute(method, targetClass);
// Put it in the cache.
if (txAtt == null) {
this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);
}
else {
if (logger.isDebugEnabled()) {
Class<?> classToLog = (targetClass != null ? targetClass : method.getDeclaringClass());
logger.debug("Adding transactional method '" + classToLog.getSimpleName() + "." +
method.getName() + "' with attribute: " + txAtt);
}
this.attributeCache.put(cacheKey, txAtt);
}
return txAtt;
}
}

很遗憾,在getTransactionAttribute函数中并没有找到我们想要的代码,没有找到在类活接口上的配置可以延续到类中的每个函数的逻辑,这里是指常规的一贯的套路。尝试从缓存加载,如果对应信息没有被缓存的话,工作又委托给了computeTransactionAttribute函数,在computeTransactionAttribute函数中终于的我们看到了事务标签的提取过程。

    protected TransactionAttribute computeTransactionAttribute(Method method, Class<?> targetClass) {
// Don't allow no-public methods as required.
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
return null;
}
// Ignore CGLIB subclasses - introspect the actual user class.
Class<?> userClass = ClassUtils.getUserClass(targetClass);
//method代表接口中的方法,specificMethod代表实现类中的方法
Method specificMethod = ClassUtils.getMostSpecificMethod(method, userClass);
// If we are dealing with method with generic parameters, find the original method.
specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
     //查看实现方法中是否存在事务声明
TransactionAttribute txAtt = findTransactionAttribute(specificMethod);
if (txAtt != null) {
return txAtt;
}
//查看方法所在类中是否存在事务声明
txAtt = findTransactionAttribute(specificMethod.getDeclaringClass());
if (txAtt != null) {
return txAtt;
}
     //如果存在接口,则到接口中去寻找
if (specificMethod != method) {
//查找接口方法是否存在事务声明
txAtt = findTransactionAttribute(method);
if (txAtt != null) {
return txAtt;
}
//查看接口所在类上面是否存在事务声明
return findTransactionAttribute(method.getDeclaringClass());
}
return null;
}

如果方法中存在事务属性,则使用方法上的属性,否则使用方法所在的类上的属性,如果方法所在类的属性上还是没有搜寻到对应的事务属性,那么再搜寻接口中的方法,再没有的话,最后尝试搜寻接口的类上面的声明。对于函数computeTransactionAttribute中的逻辑与我们所认识的规则并无差别,但是上面函数中并没有真正的去做搜寻事务属性的逻辑,而是搭建了个执行框架,将搜寻事务属性的任务委托给了findTransactionAttribute方法去执行。

    @Override
protected TransactionAttribute findTransactionAttribute(Class<?> clazz) {
return determineTransactionAttribute(clazz);
}
protected TransactionAttribute determineTransactionAttribute(AnnotatedElement ae) {
if (ae.getAnnotations().length > 0) {
//this.annotationParsers是在当前类AnnotationTransactionAttributeSource初始化的时候初始化的,
       //其中的值被加入了SpringTransactionAnnotationParser,
//也就是当进行属性获取的时候其实是使用SpringTransactionAnnotationParser类的parseTransactionAnnotation方法进行解析的。
for (TransactionAnnotationParser annotationParser : this.annotationParsers) {
TransactionAttribute attr = annotationParser.parseTransactionAnnotation(ae);
if (attr != null) {
return attr;
}
}
}
return null;
}
//首先会判断当前的类是否含有Transactional注解,这是事务属性的基础,当然如果有的话会继续调用parseTransactionAnnotation方法解析详细的属性。
@Override
public TransactionAttribute parseTransactionAnnotation(AnnotatedElement ae) {
AnnotationAttributes attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(ae, Transactional.class);
if (attributes != null) {
return parseTransactionAnnotation(attributes);
}
else {
return null;
}
}
protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) {
RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();
Propagation propagation = attributes.getEnum("propagation");
//解析propagation
rbta.setPropagationBehavior(propagation.value());
Isolation isolation = attributes.getEnum("isolation");
//解析isolation
rbta.setIsolationLevel(isolation.value());
//解析timeout
rbta.setTimeout(attributes.getNumber("timeout").intValue());
//解析readOnly
rbta.setReadOnly(attributes.getBoolean("readOnly"));
//解析value
rbta.setQualifier(attributes.getString("value"));
//解析rollbackFor
ArrayList<RollbackRuleAttribute> rollBackRules = new ArrayList<RollbackRuleAttribute>();
Class<?>[] rbf = attributes.getClassArray("rollbackFor");
for (Class<?> rbRule : rbf) {
RollbackRuleAttribute rule = new RollbackRuleAttribute(rbRule);
rollBackRules.add(rule);
}
//解析rollbackForClassName
String[] rbfc = attributes.getStringArray("rollbackForClassName");
for (String rbRule : rbfc) {
RollbackRuleAttribute rule = new RollbackRuleAttribute(rbRule);
rollBackRules.add(rule);
}
//解析noRollbackFor
Class<?>[] nrbf = attributes.getClassArray("noRollbackFor");
for (Class<?> rbRule : nrbf) {
NoRollbackRuleAttribute rule = new NoRollbackRuleAttribute(rbRule);
rollBackRules.add(rule);
}
//解析noRollbackForClassName
String[] nrbfc = attributes.getStringArray("noRollbackForClassName");
for (String rbRule : nrbfc) {
NoRollbackRuleAttribute rule = new NoRollbackRuleAttribute(rbRule);
rollBackRules.add(rule);
}
rbta.getRollbackRules().addAll(rollBackRules);
return rbta;
}

上面方法中实现了对对应类或者方法的事务属性解析,你会在这个类中看到任何你常用或者不常用的属性提取。

找出某个增强器是否适合于对应的类,而是否匹配的关键则在于是否从指定的类或类中的方法中找到对应的事务属性,这个任务至此是完成了。在,我们以UserServiceImpl为例,已经在它的接口UserService中找到了事务属性,所以,它是与事务增强器匹配的,也就是它会被事务功能修饰。

当判断某个bean适用于事务增强时,也就是适用于增强器BeanFactoryTransactionAttributeSourceAdvisor,然后执行最上面的return advisors.toArray();返回,至此完成了Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);增强器的获取。

Spring事务解析3-增强方法的获取的更多相关文章

  1. AOP动态代理解析3-增强方法的获取

    对于指定bean的增强方法的获取一定是包含两个步骤的: 获取所有的增强 寻找所有增强中使用于bean的增强并应用 那么findCandidateAdvisors与findAdvisorsThatCan ...

  2. Spring事务解析4-切面织入

    BeanFactoryTransactionAttributeSourceAdvisor作为Advisor的实现类,自然要遵从Advisor的处理方式,当代理被调用时会调用这个类的增强方法,也就是此b ...

  3. Spring事务解析1-使用介绍

    spring的事务控制让我们从复杂的事务处理中得到解脱,是我们再也不需要去处理获得连接,关闭连接,事务提交和回滚等操作,再也不需要在事务相关的方法中处理大量的try..catch...finally代 ...

  4. spring事务解析

    1 初步理解 理解事务之前,先讲一个你日常生活中最常干的事:取钱. 比如你去ATM机取1000块钱,大体有两个步骤:首先输入密码金额,银行卡扣掉1000元钱:然后ATM出1000元钱.这两个步骤必须是 ...

  5. Spring事务调用类自己方法失效解决办法和原因

    问题 正常情况下,我们都是在controller里调用service里的方法,这个方法如果需要加事务,就在方法上加上@Transactional,这样是没问题的,事务会生效. 可是如果像下面这样,绕以 ...

  6. SpringBoot CGLIB AOP解决Spring事务&comma;对象调用自己方法事务失效&period;

    对于像我这种喜欢滥用AOP的程序员,遇到坑也是习惯了,不仅仅是事务,其实只要脱离了Spring容器管理的所有对象,对于SpringAOP的注解都会失效,因为他们不是Spring容器的代理类,Sprin ...

  7. Spring事务解析2-标签解析

    根据自定义标签的使用规则,可以知道会执行AnnotationDrivenBeanDefinitionParser的parse @Override public BeanDefinition parse ...

  8. Spring配置文件解析--集合注入方法

    <bean id="moreComplexObject" class="example.ComplexObject"> <property n ...

  9. Mybaits 源码解析 (十二)----- Mybatis的事务如何被Spring管理?Mybatis和Spring事务中用的Connection是同一个吗?

    不知道一些同学有没有这种疑问,为什么Mybtis中要配置dataSource,Spring的事务中也要配置dataSource?那么Mybatis和Spring事务中用的Connection是同一个吗 ...

随机推荐

  1. JavaScript内置对象&lpar;字符串&comma;数组&comma;日期的处理&rpar;

    Date 日期对象 日期对象可以储存任意一个日期,并且可以精确到毫秒数(1/1000 秒). 定义一个时间对象 : var Udate=new Date(); 注意:使用关键字new,Date()的首 ...

  2. 剑指Offer40 和为s的连续正数序列

    /************************************************************************* > File Name: 40_Contin ...

  3. docker&colon; &quot&semi;build&quot&semi; requires 1 argument&period; See &&num;39&semi;docker build --help&&num;39&semi;&period;

    http://bbs.csdn.net/topics/391040030 docker build  --tag="ouruser/sinatra:v3" -<Dockerf ...

  4. Thinking In Java 学习笔记 1-5 章

    第1章 对象导论 本章主要讲OOP的思想及一些OOP基本概念 1.抽象过程:万物都是对象,对象具有状态.行为和标识.对象拥有属性和方法,以及在内存中的唯一地址. 2.每个对象都有一个接口:通过接口给对 ...

  5. Python学习笔记6-Python中re&lpar;正则表达式&rpar;模块学习

    今天学习了Python中有关正则表达式的知识.关于正则表达式的语法,不作过多解释,网上有许多学习的资料.这里主要介绍Python中常用的正则表达式处理函数. re.match re.match 尝试从 ...

  6. centos-mysql 安装

    初学者自编文档,如有错误,请指出,具体命令就不阐述了,不明白 度娘吧! nginx我是编译安装在服务器上 和其他安装应该会有区别 安装路径路径:/usr/local/ 安装包存放位置:/home/ap ...

  7. continous integration environment &lpar;Jenkins and bitbucket configuration&rpar;

    ================================================================================ continous integrati ...

  8. 第一章 Linux系统概述

    linux是真正的多用户.多任务操作系统,他继承了UNIX系统的主要特征,具有强大的信息处理功能,特别在Internet和Intranet的应用中占有明显优势. 1.1计算机基础知识 计算机分为硬件和 ...

  9. aircrack-ng后台跑包&comma; 成功后自动发送邮件通知

    我的思路是直接用nohup守护进程输出结构到指定文件, 然后判断文件中"KEY FOUND"的字段. 启动aircrack-ng方式 nohup aircrack-ng -w /o ...

  10. Cisco无线AP上联口为trunk时无法注册故障处理

    网络环境:WLC5520  AP:1832i-h-k9 客户实施过程中电话告知其将AP和交换机互联的端口类型设置为TRUNK口后,AP无法注册到WLC上,后经过查看配置发现客户用于AP管理的VLAN为 ...