在使用spring事物对数据操作的时候,经常使用到aop、tx等标签,spring使用aop面向切面编程,来实现对事物的控制,那么srping是怎么解析这些标签的呢?解析的入口在哪里呢?
spring版本:4.2.0.RELEASE
一个简单的例子开始,对数据库进行事物操作,bean.xml的配置如下:
编写一段测试代码
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 params = new HashMap<>();
params.put("userAccount","user002");
int ret = authUserServiceImpl.delete("user002");
//logger.info("查询结果:"+ JSON.toJSONString(authUsers));
}
}
在执行new ClassPathXmlApplicationContext("bean.xml");这段代码的时候,看看spring都做过哪些事情。spring在使用事物的过程中有几个关键的类AnnotationAwareAspectJAutoProxyCreator、AspectJAwareAdvisorAutoProxyCreator、AspectJExpressionPointcut、DefaultBeanFactoryPointcutAdvisor等是怎么创建的。
下面看看
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 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 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 handlerMappings = new ConcurrentHashMap(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 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
回到上面解析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
第三将标签
这样就完成了标签的解析,此处以
通过以上的解析过程,完成
private RootBeanDefinition parseAttributeSource(Element attrEle, ParserContext parserContext) {
List methods = DomUtils.getChildElementsByTagName(attrEle, "method");
ManagedMap 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和属性集合将会在后面的章节中分析。