struts2中如何自动纠正如contextpath/contextpath/*.action这种路径

在上篇 struts2中如何根据请求路径定位到详细的访问action 中,主要了解了在struts2中是如何通过一种路径解析成一个action的。那么针对以下的路径
contextpath/contextpath/*.action的路径,在命名空间为""的情况下,struts2是如何进行纠正的呢。

其实也很简单,这里面可以说是struts2本身的一处处理策略,因为在整个解析过程中,除掉上下文之后,就剩下以下的一个路径了:

contextpath/actionName

这里面有一个/符号,并且从理论上来讲,并没有一个有效的actionConfig与之匹配,但是由于项目中设置了参数

struts.enable.SlashesInActionNames

这个值没有被设置,那么默认就是false,即不允许在actionName中存在着/符号,那么在struts2中就直接在actionName中去除了这个/符号,如下代码所示,类DefaultActionMapper的方法parseNameAndNamespace

if (!allowSlashesInActionNames && name != null) {
            int pos = name.lastIndexOf('/');
            if (pos > -1 && pos < name.length() - 1) {
                name = name.substring(pos + 1);
            }
        }

这样处理之后,剩下的actionName就成了我们所需要的actionName了,这样就间接的找到了正确的actionConfig,也可以说是碰巧正确了。

Spring中Autowired注解,Resource注解和xml default-autowire工作方式异同

前面说到了关于在xml中有提供default-autowire的配置信息,从spring 2.5开始,spring又提供了一个Autowired以及javaEE中标准的Resource注释,都好像可以实现类似的自动注入。那么是不是每个都实现同样的方式呢,这里面的几个配置到底有哪些异同点。哪个更全,哪个更优先,这些都需要对spring的内部原理有详细的了解才可以进行了解。
在以下文章时,首先有几个概念需要列出:
字段名称:即fieldName,这个即propertyDescriper的getPropertyName返回信息。
setter名称:即方法setter除set之外的名称,如setAbc,则名称为abc,这里的abc不一定和fieldName相同。
参数名称:即在参数中所定义的参数的名称,如setAbc(Abc a123)。这里的参数名称就是a123。
本文所使用spring版本为spring3.0.2。

处理类和处理顺序异同

default-autowire是在xml中进行配置的,而这个配置从spring初始就提供了。而Autowired注解,则是从2.5自支持以java1.5之后才出现的,这就必然导致对相应的处理以及逻辑是不同的。那么每个方式的处理顺序是怎样的呢,从我写的文章:Spring中获取一个bean的流程-2.也可以由下面的代码得出:

		if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||
				mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
			MutablePropertyValues newPvs = new MutablePropertyValues(pvs);

			// Add property values based on autowire by name if applicable.
			if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {
				autowireByName(beanName, mbd, bw, newPvs);
			}

			// Add property values based on autowire by type if applicable.
			if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
				autowireByType(beanName, mbd, bw, newPvs);
			}

			pvs = newPvs;
		}
......
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				if (bp instanceof InstantiationAwareBeanPostProcessor) {
					InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
					pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);

以上代码来源于类AbstractAutowireCapableBeanFactory的populateBean方法。从上可以看出,spring首先处理在bean定义上的autowire属性,然后再处理后面的InstantiationAwareBeanPostProcessor类。首先bean定义上的autowire属性,可以来自于<bean>定义时autowire属性,也可以来自于整个xml定义中<beans>节点中的default-autowire属性。

那么@Autowired注解和@Resource注解在哪儿处理呢,看上面的代码,有个InstantiationAwareBeanPostProcessor类,如果你仔细查看里面的实现,你可以发现里面即为处理相应注解类的实现。而这些注解类,只要在xml中启用了<context:annotation-config/>,即可以开启这些类了。而我们的Autowired注解,由AutowiredAnnotationBeanPostProcessor来进行处理,而Resource类,则由CommonAnnotationBeanPostProcessor进行处理。

继续阅读“Spring中Autowired注解,Resource注解和xml default-autowire工作方式异同”

Spring中获取一个bean的流程-2

上篇,进入第4节,实质性地创建一个Bean信息。

4 doCreateBean

4.1 首先创建一个原始对象的包装对象,即BeanWrapper,这通过对bean进行包装而来,如下代码所示:

instanceWrapper = createBeanInstance(beanName, mbd, args);

在这个createBeanInstance方法中,首先会创建原始对象,这就有几个策略。如下所示:
如果存在工厂方法,即在xml中定义了factory-method,就会调用工厂方法进行创建,代码如下所示:

if (mbd.getFactoryMethodName() != null) {
			return instantiateUsingFactoryMethod(beanName, mbd, args);
		}

如果已经确认或者解析了候选构造方法,则调用此方法进行创建

if (resolved) {
			if (autowireNecessary) {
				return autowireConstructor(beanName, mbd, null, null);
			}
			else {
				return instantiateBean(beanName, mbd);
			}
		}

如果存在SmartInstantiationAwareBeanPostProcessor并且对象解析出指定的bean Class有候选的构造方法,如在构造方法上追加@Autowired的话,会优化使用这个构造方法。
最后,采用默认的构造策略进行对象创建,如调用Class.newInstance()方法创建。

继续阅读“Spring中获取一个bean的流程-2”

Spring中获取一个bean的流程-1

本文从ClasspathXmlApplication进行入手,未考虑针对于Annotation的处理,仅从xml方向对getBean进行代码追踪,以形成一个完整的链条。以下代码从取得一个singleton对象入手进行分析。

1 获取Bean信息

1.1 首先这里会调用AbstractApplicationContext.getBean(String name),这个方法直接会转向1.2
1.2 进入方法AbstractBeanFactory.getBean(String name),这个方法会直接进入1.3
1.3 进入方法AbstractBeanFactory.doGetBean(final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)。这个方法是获取bean的主要入口。此方法的详细叙述在第2节

2 doGetBean信息

2.1 首先这里会将传入的beanName进行转化。代码如下所示:

		final String beanName = transformedBeanName(name);
protected String transformedBeanName(String name) {
		return canonicalName(BeanFactoryUtils.transformedBeanName(name));
	}

转化有两个步骤,首先处理beanName为&XXX的格式,这里表示要取指定name的factoryBean。在这里先把&符号取消,先获取bean再处理。然后,针对bean的alias机制,这里传入的参数可能是一个bean别名,那么我们先获取这个bean的主要id,只需要根据id值取bean就可以了。

继续阅读“Spring中获取一个bean的流程-1”

Spring中循环引用的处理-3

上文中对调用点A和调用点B的调用方法进行了陈述。接下来,针对创建bean的不同顺序对调用点和调用方法进行分析。

在正常的情况下,调用顺序如下:以下有无,表示是否持有对指定Bean的引用

  singletonFactories earlySingletonObjects singletonObjects
getSingleton(beanName, true)
doCreateBean(beanName,mdb,args)
getSingleton(beanName, true);
addSingleton(beanName, singletonObject)

但是出现循环引用之后呢,就会出现这种情况:

  singletonFactories earlySingletonObjects singletonObjects
getSingleton(A, true); A无B无 A无B无 A无B无
doCreateBean(A,mdb,args) A有B无 A无B无 A无B无
       
populateBean(A, mbd, instanceWrapper) 解析B……  
getSingleton(B, true) A有B无 A无B无 A无B无
doCreateBean(B,mdb,args) A有B有 A无B无 A无B无
populateBean(B, mbd, instanceWrapper)由B准备解析A……  
getSingleton(A, true) A无B有 A有B无 A无B无
完成populateBean(B, mbd, instanceWrapper)解析……  
addSingleton(B, singletonObject) A无B无 A有B无 A无B有
完成populateBean(A, mbd, instanceWrapper)  
       
A- = initializeBean(beanName, exposedObject, mbd)在initializeBean之后A变为A-  
getSingleton(A, false);验证  
addSingleton(A, singletonObject)    ……  

在上面这个过程中,在对A进行验证时,就会从earlySingletonObjects中取得一个A,但是这个A和后面的A-可能不是同一个对象,这是因为有了beanPostProcessor存在,它可以改变bean的最终值,比如对原始bean进行封装,代理等。在这个过程中,出现了3个对象A,A-,B,而B中所持有的A对象为原始的A。如果这里的A和A-不是同一个对象,即产生了beanA有了beanB的引用,但beanB并没有beanA的引用,而是另一个beanA的引用。这肯定不满足条件。

继续阅读“Spring中循环引用的处理-3”

Spring中循环引用的处理-2

上文中对涉及到循环引用的3个方法作了陈述。

在方法1中,对象信息对beanFactory的形式被放入singletonFactories中,这时earlySingletonObjects中肯定没有此对象(因为remove)。

在方法2中,在一定条件下(allowEarlyReference为true)的条件下,对象从singleFactories中的objectFactory中被取出来,同时remove掉,被放入earlySingletonObjects中。这时,earlySingletonObjects就持有对象信息了;当然,如果allowEarlyReference为false的情况下,且earlySingletonObjects本身就没有持有对象的情况下,肯定不会将对象从objectFactory中取出来的。这个很重要,因为后面将根据此信息进行循环引用处理。

在方法3中,对象被加入到singletonObjects中,同时singletonFactories和earlySingletonObjects中都remove掉持有的对象(不管持有与否),这就表示在之前的处理中,这只相当于一个临时容器,处理完毕之后都会remove掉。

继续阅读“Spring中循环引用的处理-2”