在使用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&characterEncoding=utf8&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和属性集合将会在后面的章节中分析。