spring3.0版本configuration注解对象的启动过程分析

2016/08/03 15:43:41 No Comments

spring 3.0版本的一个很重要的特性在于支持注解式配置.之前在xml配置中的所有信息均可以通过注解进行配置了.本篇即从实现的角度理解spring是通过怎么样的机制来实现的.为了避免重复发明轮子,尽量采用之前已有的技术来进行实现才是最好的处理方式.

使用Configuration对象

整个的支持首先即是通过configuration这个对象来完成的.

第一种方式是通过声明AnnotationConfigApplicationContext上下文,然后通过调用其register方式来完成.如下参考所示:

        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(AppConfig.class);
        context.register(App2Config.class);

第二种方式,则是通过在一个统一的xml中声明<context:annotation-config/>,然后再通过声明<context:component-scan base-package="com.iflym"/>,将自己的@configuration对象放到相应的包下面,让spring容器自行进行解析即可.因为@Configuration注解上有声明@Component注解,因此也可以认为这也是注解一个bean的方式.

这两种方式均是通过将configBean放到spring容器当中,我们可以认为就是手动地注册相应的bean到容器当中.只不过这里的bean是特殊的configBean.

ConfigurationClassPostProcessor处理器

在整个spring容器处理周期当中,存在一个特殊的处理周期,即invokeBeanFactoryPostProcessors.这里即在容器完成初始解析之后,再进行特定的处理,以添加更多的bean或者对容器内的bean进行修改.这里分别通过接口BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor来完成.后者表示会对容器进行修改,会添加新的bean定义信息.

针对configBean的处理即是通过类ConfigurationClassPostProcessor来完成,其实现了BeanDefinitionRegistryPostProcessor接口,同时实现了BeanFactoryPostProcessor接口.(因为definitionRegistry接口本身即是继承了beanFactoryPost接口). 

ConfigurationClassPostProcessor类是通过静态方法AnnotationConfigUtils.registerAnnotationConfigProcessors注册到spring容器当中.而此静态方法,又是通过<context:annotation-config/>或者是AnnotationConfigApplicationContext声明自动完成此操作.

(more…)

spring 多placeHolder问题的解决方案

2015/04/15 17:13:05 No Comments

当前的spring版本为: 4.0.6.RELEASE
问题官方地址:https://jira.spring.io/browse/SPR-9989

问题重现

@Component
public class Tb {
    @Value("${tb.username:abcd}")
    private String username;

    public String getUsername() {
        return username;
    }
}

以上为定义bean,其中属性表示需要去获取属性为tb.username的属性定义,默认值为abcd。然后在spring分别如下配置

<context:property-placeholder location="classpath:springa.properties" ignore-unresolvable="true" ignore-resource-not-found="true"/>
<context:property-placeholder location="classpath:springb.properties" ignore-unresolvable="true" ignore-resource-not-found="true"/>

配置文件的值如下所示:
配置文件一:tb.username1=spring1
配置文件二:tb.username=spring2

因为在配置文件2中,有相应的配置定义,因此我们期望其返回username的值为 spring2。但是在实际运行中,此值即是abcd,如下输出所示:

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        Tb tb = context.getBean(Tb.class);
        System.out.println("->" + tb.getUsername());
    }

//输出值
->abcd

本文即通过更换相应的属性解析器,用于解决此问题,以让spring能够正常的解析并输出我们需要的值。

(more…)

使用线程安全的spring类型转换器ConversionService VS TypeConverter

2015/02/10 16:29:56 No Comments

此文翻译源:http://www.theserverside.com/tip/Spring-Converters-and-Formatters。部分无用的部分有删除

1 类型转换接口

在spring3.0之前,如果要自己实现一个从字符串到其它对象的转换,那么就需要实现PropertyEditor接口。PropertyEditor是遵循javaBean规范的属性处理器,其通过set方法设置属性值,通过get方法获取属性值,相应的转换逻辑就隐藏于其中。这个接口惟一的问题在于,他们不是线程安全的.并且只能实现从字符串到其它对象的转换。

在spring3.0,提供了一个更简单的类型转换器接口。在spring mvc中,可以使用内置的或者自己实现的接口来实现从请求参数到对象之间的转换。其相应的接口定义如下:

public interface Converter<S,T> {
    public T convert(S source);
}

即通过一个S类型的对象,将其转换为T类型的对象。其中S类型并不局限于字符串,可以是任何类型。这是与PropertyEditor的主要区别。

(more…)

spring LTW agent方式在tomcat 8下无效

2014/12/07 19:13:55 No Comments

在常规的spring ltw中,我们对tomcat使用ltw,一般是以下两种方式。

//1 在启动项中添加javaagent选项
-javaagent:e:/spring-instrument-4.1.1.RELEASE.jar

//2 使用自定义的启动器,在META-INF/context.xml中增加以下内容
<?xml version="1.0" encoding="UTF-8" ?>
<Context>
 <Loader loaderClass="org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader" /> 
</Context>

这两种方式在tomcat6以及tomcat7均可以正常工作。但对于tomcat8,第1种方式已经不能再工作,仅能使用第二种方式。原因就在于tomcat8的webClassLoader已经提供了InstrumentableClassLoader接口。此接口将导致spring直接将aspectj的AspectJClassBypassingClassFileTransformer 直接添加到tomcat的classLoader中。但由于不是很正确的实现方式,导致aspect在使用tomcat8提供的classLoader时,并不能有效地对自己的advice进行weaver,导致报以下的错误信息: 

java.lang.NoSuchMethodError: XXXAdvice.aspectOf()LXXXAdvice;
 atXXX.index(AbcController.java:30)
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
 at java.lang.reflect.Method.invoke(Method.java:606)
 at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:215)
 at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132)
 at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)

这个错误的产生在于aspectJ的初始化过程和classLoader之间的交互行为,以及tomcat8中不准确的缓存行为。

(more…)

aspectj中call和execute在spring架构中的重要区别

2013/05/13 22:40:05 No Comments

在使用aspectj进行定义pointcut时,经常碰到的问题就是该使用call还是execute。当然,在spring中,只支持execute,而不支持call,但使用aspectj如ltw时就可以使用,但两者究竟有什么区别,最大的区别在哪儿,适用点又在哪儿。这就要从定义出来,来了解相关信息了。

在文档《aspectj_in_action_second_editon.pdf》中,针对二者,主要如下面所述:

a call is on the caller side, whereas execution happens on the receiver side—they’re two completely different places. 

即call工作在调用端,而execute工作在被调用端。
如我们将在pointcut打印一句helloworld。那么针对helloAction调用helloService而言。针对call调用,这句打印将会在helloAction中调用,而execute调用,而是在helloService中调用。

以上只是表面上的区别,在具体使用时其实并不是太大的差别,但如果有以下一句话,那么区别就可能完全不一样了:

aspectj是通过修改字节码来完成相应的功能,通过在相应的pointcut定义指定的advice来完成功能。

具体点说,就是aspectj首先需要能够匹配到相应的pointcut,然后才能执行相应的advice方法,如果不能匹配到pointcut,那么就不能执行相应的方法。

(more…)

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

2012/11/07 17:25:28 1 Comment

前面说到了关于在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进行处理。

(more…)