spring源码学习之aop事务标签解析

在使用spring事务对数据操作的时候,经常使用到aop、tx等标签,spring使用aop面向切面编程,来实现对事务的控制,那么srping是怎么解析这些标签的呢?解析的入口在哪里呢?

spring版本:4.2.0.RELEASE

简单实例

一个简单的例子开始,对数据库进行事务操作,bean.xml的配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://www.springframework.org/schema/beans"
	   xmlns:tx="http://www.springframework.org/schema/tx"
	   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-3.0.xsd
	http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.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">

	<aop:aspectj-autoproxy proxy-target-class="true"/>

	<context:component-scan base-package="com.test" />

	<bean name="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init"
		  destroy-method="close" parent="dbCommProperty">
		<property name="url" value="jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&amp;characterEncoding=utf8&amp;allowMultiQueries=true"/>
		<property name="username" value="root"/>
		<property name="password" value="test"/>
		<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
	</bean>

	<!-- 配置 SqlSessionFactory -->
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<!-- 加载数据源 -->
		<property name="dataSource" ref="dataSource"/>
		<property name="mapperLocations" value="classpath*:mappers/*Mapper.xml"/>
	</bean>

	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<!-- 指定扫描的包,如果存在多个包使用(逗号,)分割 -->
		<property name="basePackage" value="com.test.bean"/>
		<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
	</bean>

	<!-- 配置事物管理器 TransactionManager -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"/>
	</bean>

	<!-- 配置通知 -->
	<tx:advice id="interceptorAdvice" transaction-manager="transactionManager">
		<tx:attributes>
			<!--REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。-->
			<tx:method name="delete*" propagation="REQUIRED" read-only="false" rollback-for="Exception"/>
			<tx:method name="insert*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Exception"/>
			<tx:method name="update*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Exception"/>
			<tx:method name="remove*" propagation="REQUIRED" read-only="false" rollback-for="Exception"/>
			<tx:method name="query*" propagation="SUPPORTS"/>
		</tx:attributes>
	</tx:advice>

	<!-- 配置切面 aop -->
	<aop:config>
		<aop:pointcut id="transactionPointcut" expression="execution(* com.test.service.*.*(..))"/>
		<aop:advisor pointcut-ref="transactionPointcut" advice-ref="interceptorAdvice"/>
	</aop:config>
</beans>

编写一段测试代码

public class Test {
    private static Logger logger = Logger.getLogger(Test.class);

    public static void main(String[] args){
        ApplicationContext ctx = new ClassPathXmlApplicationContext("bean.xml");
        IAuthUserService authUserServiceImpl = ctx.getBean(AuthUserServiceImpl.class);
        Map<String,Object> params = new HashMap<>();
        params.put("userAccount","user002");
        int ret = authUserServiceImpl.delete("user002");
        //logger.info("查询结果:"+ JSON.toJSONString(authUsers));
    }
}

aop标签的解析

在执行new ClassPathXmlApplicationContext("bean.xml");这段代码的时候,看看spring都做过哪些事情。spring在使用事物的过程中有几个关键的类AnnotationAwareAspectJAutoProxyCreator、AspectJAwareAdvisorAutoProxyCreator、AspectJExpressionPointcut、DefaultBeanFactoryPointcutAdvisor等是怎么创建的。

下面看看<aop:aspectj-autoproxy proxy-target-class="true"/>标签,在spring启动的过程中,标签是怎么被解析和注册的,

ClassPathXmlApplicationContext在解析bean.xml文件的时候创建了一个xml阅读器XmlBeanDefinitionReader,

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
        beanDefinitionReader.setEnvironment(this.getEnvironment());
        beanDefinitionReader.setResourceLoader(this);
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
        this.initBeanDefinitionReader(beanDefinitionReader);
        this.loadBeanDefinitions(beanDefinitionReader);
    }

这个reader负责加载bean.xml配置文件,并且使用xml文件创建一个Document 文档对象,resource对象就代表了bean.xml资源文件。

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
			throws BeanDefinitionStoreException {
		try {
			Document doc = doLoadDocument(inputSource, resource);
			return registerBeanDefinitions(doc, resource);
		}
		catch (BeanDefinitionStoreException ex) {
			throw ex;
		}
		//......
	}

从这个doc对象中就可以获取到beans 这个root根元素,

public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
		this.readerContext = readerContext;
		logger.debug("Loading bean definitions");
		Element root = doc.getDocumentElement();//获取到beans这个root根据元素
		doRegisterBeanDefinitions(root);
	}
//在DefaultBeanDefinitionDocumentReader类中对所有的标签进行遍历
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
		if (delegate.isDefaultNamespace(root)) {
            //遍历根beans元素下的所有子节点
			NodeList nl = root.getChildNodes();
			for (int i = 0; i < nl.getLength(); i++) {
				Node node = nl.item(i);
				if (node instanceof Element) {
					Element ele = (Element) node;
					if (delegate.isDefaultNamespace(ele)) {
						parseDefaultElement(ele, delegate);
					}
					else {
						delegate.parseCustomElement(ele);
					}
				}
			}
		}
		else {
			delegate.parseCustomElement(root);
		}
	}

遍历到[aop:aspectj-autoproxy:]元素的时候,交给delegate去解析,

public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
		String namespaceUri = getNamespaceURI(ele);
		NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
		if (handler == null) {
			error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
			return null;
		}
		return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
	}

这个Element ele目前代表的元素就是aop:aspectj-autoproxy标签,getNamespaceURI(ele)就是解析这个标签的命名空间,namespaceUri的值就是我们在bean.xml文档开头所声明的命名空间xmlns:aop="http://www.springframework.org/schema/aop"的值http://www.springframework.org/schema/aop,这个地方就可以看到通过aop标签,就可以找到命名空间,那么通过这个命名空间是怎么解析出来的NamespaceHandler的呢,看看方法resolve(namespaceUri)做了什么。

public NamespaceHandler resolve(String namespaceUri) {
		Map<String, Object> handlerMappings = getHandlerMappings();
		Object handlerOrClassName = handlerMappings.get(namespaceUri);
		if (handlerOrClassName == null) {
			return null;
		}
		else if (handlerOrClassName instanceof NamespaceHandler) {
			return (NamespaceHandler) handlerOrClassName;
		}
		else {
			String className = (String) handlerOrClassName;
			try {
				Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
				if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
					throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
							"] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
				}
				NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
				namespaceHandler.init();
				handlerMappings.put(namespaceUri, namespaceHandler);
				return namespaceHandler;
			}
//....
		}
	}

这个getHandlerMappings方法解析出了所有命名空间和handler的映射,这样我们就可以拿到这个aop标签对应的handler处理器,并且调用处理器的init()方法完成处理器的初始化工作。那这个getHandlerMappings方法是怎么解析的呢。

private Map<String, Object> getHandlerMappings() {
		if (this.handlerMappings == null) {
			synchronized (this) {
				if (this.handlerMappings == null) {
					try {
//这个this.handlerMappingsLocation的值就是META-INF/spring.handlers
						Properties mappings =
								PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
						Map<String, Object> handlerMappings = new ConcurrentHashMap<String, Object>(mappings.size());
						CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
						this.handlerMappings = handlerMappings;
					}//....
				}
			}
		}
		return this.handlerMappings;
	}

从上面getHandlerMappings方法的源码中可以看得出来,映射关系是从Properties对象中取出来的,把这个Properties转换成了map对象,那这个Properties对象怎么加载的,还需要看看loadAllProperties方法。

public static Properties loadAllProperties(String resourceName, ClassLoader classLoader) throws IOException {
        Assert.notNull(resourceName, "Resource name must not be null");
        ClassLoader classLoaderToUse = classLoader;
        if(classLoader == null) {
            classLoaderToUse = ClassUtils.getDefaultClassLoader();
        }

        Enumeration<URL> urls = classLoaderToUse != null?classLoaderToUse.getResources(resourceName):ClassLoader.getSystemResources(resourceName);
        Properties props = new Properties();

        while(urls.hasMoreElements()) {
            URL url = (URL)urls.nextElement();
            URLConnection con = url.openConnection();
            ResourceUtils.useCachesIfNecessary(con);
            InputStream is = con.getInputStream();

            try {
                if(resourceName != null && resourceName.endsWith(".xml")) {
                    props.loadFromXML(is);
                } else {
                    props.load(is);
                }
            } finally {
                is.close();
            }
        }

        return props;
    }

loadAllProperties方法参数resourceName的值就是META-INF/spring.handlers,这是一个相对路径,根据这个路径中去加载文件中的内容,这个文件的位置就在spring-aop的jar包下面。

文件的内容就是一行key value键值对,这就可以清晰看到aop标签所对应的handler处理器是怎么解析出来的。

http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler

在回到上面resolve()方法handler被初始化的地方,handler类的全路径被解析出来,就可以被实例化出对象,在调用

namespaceHandler.init(),在AopNamespaceHandler处理器中看看init方法做了什么事情。

public void init() {
		registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
		registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
		registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
		registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
	}

初始化方法就完成了aop标签对应的这些元素的注册功能。每一个元素还有一个对应的解析器,

protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {
		this.parsers.put(elementName, parser);
	}

注册的结果就是将元素和解析器放在一个map变量里,Map<String, BeanDefinitionParser> parsers = new HashMap<String, BeanDefinitionParser>();所有的解析器都是BeanDefinitionParser接口的子类。

aop元素解析

回到上面解析handler的地方

public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
		String namespaceUri = getNamespaceURI(ele);
		NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
		if (handler == null) {
			error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
			return null;
		}
		return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
	}

解析handler的过程完成了两件事情,一件是解析出handler,一件就是调用hanlder的init方法,完成初始化。初始化完成之后,就会调用handler的parse方法,进行aop元素的解析。

spring首先解析到的元素是aspectj-autoproxy,从handler的init方法可以知道,registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());

这个元素对应的解析器就是AspectJAutoProxyBeanDefinitionParser,下面就看看这个解析器干了什么事情。

public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
		return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
	}
private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, Object source) {
		Assert.notNull(registry, "BeanDefinitionRegistry must not be 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;
	}

这个解析器做主要的事情就是干了3件事情,
第一使用AnnotationAwareAspectJAutoProxyCreator类作为beanclass创建RootBeanDefinition,
第二将这个RootBeanDefinition注册到BeanFactory,这个registry的引用的对象就是DefaultListableBeanFactory工厂类的实例,以org.springframework.aop.config.internalAutoProxyCreator作为key,将RootBeanDefinition存储在BeanFactory的私有变量Map<String, BeanDefinition> beanDefinitionMap中。


第三将标签<aop:aspectj-autoproxy proxy-target-class="true"/>中参数proxy-target-class封装在RootBeanDefinition中,这个参数会影响到spring在创建代理类的时候,是使用cglib还是jdk的动态代理。

这样就完成了标签的解析,此处以<aop:aspectj-autoproxy>标签为例进行分析,其他的标签<aop:config>、<tx:advice>解析大致相似。

通过以上的解析过程,完成<aop:config>、<tx:advice>、<tx:attributes>、<aop:pointcut>、<aop:advisor>、<tx:method>这几个标签的解析,这几个标签都和数据库事物相关。每一个标签都会一个RootBeanDefinition beanDefinition对象与之对应,并完成beanDefinition在BeanFactory类的注册。而每一个RootBeanDefinition又对应有一个classbean对象。

<aop:aspectj-autoproxy>标签对应的classbean对象是AnnotationAwareAspectJAutoProxyCreator,

<aop:config>标签对应的classbean对象是AspectJAwareAdvisorAutoProxyCreator,

<aop:pointcut>标签对应的classbean对象是AspectJExpressionPointcut,

<aop:advisor>标签对应的classbean对象是DefaultBeanFactoryPointcutAdvisor,

<tx:attributes>标签对应的classbean对象是NameMatchTransactionAttributeSource,

<tx:advice>标签对应的classbean对象是TransactionInterceptor,

<tx:method>标签的属性被封装到了RuleBasedTransactionAttribute对象中,存储在<tx:attributes>标签对应的RootBeanDefinition属性集合中。

private RootBeanDefinition parseAttributeSource(Element attrEle, ParserContext parserContext) {
        List<Element> methods = DomUtils.getChildElementsByTagName(attrEle, "method");
        ManagedMap<TypedStringValue, RuleBasedTransactionAttribute> transactionAttributeMap = new ManagedMap(methods.size());
        transactionAttributeMap.setSource(parserContext.extractSource(attrEle));
        Iterator var5 = methods.iterator();

        while(var5.hasNext()) {
            Element methodEle = (Element)var5.next();
            String name = methodEle.getAttribute("name");
            TypedStringValue nameHolder = new TypedStringValue(name);
            nameHolder.setSource(parserContext.extractSource(methodEle));
            RuleBasedTransactionAttribute attribute = new RuleBasedTransactionAttribute();
            String propagation = methodEle.getAttribute("propagation");
            String isolation = methodEle.getAttribute("isolation");
            String timeout = methodEle.getAttribute("timeout");
            String readOnly = methodEle.getAttribute("read-only");
            if(StringUtils.hasText(propagation)) {
                attribute.setPropagationBehaviorName("PROPAGATION_" + propagation);
            }
            if(StringUtils.hasText(isolation)) {
                attribute.setIsolationLevelName("ISOLATION_" + isolation);
            }
            if(StringUtils.hasText(timeout)) {
                attribute.setTimeout(Integer.parseInt(timeout));
            }
            if(StringUtils.hasText(readOnly)) {
                attribute.setReadOnly(Boolean.valueOf(methodEle.getAttribute("read-only")).booleanValue());
            }
//......
            attribute.setRollbackRules(rollbackRules);
            transactionAttributeMap.put(nameHolder, attribute);
        }
        RootBeanDefinition attributeSourceDefinition = new RootBeanDefinition(NameMatchTransactionAttributeSource.class);
        attributeSourceDefinition.setSource(parserContext.extractSource(attrEle));
        attributeSourceDefinition.getPropertyValues().add("nameMap", transactionAttributeMap);
        return attributeSourceDefinition;
    }

这些标签对应的classbean和属性集合将会在后面的章节中分析。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值