Spring +0配置+0注解Autowire Bean对象
来源:互联网 发布:白葡萄酒淘宝 编辑:程序博客网 时间:2024/04/30 10:56
说明:我们知道Spring有一个<context:component-scan base-package="" />组件用于实现包搜索并加载bean到Spring容器中(参见:对受管组件的Classpath扫描)。但是这样一来还是要为每个bean对象标注相应的注解,如@Resource 和@Autowired等(参见:基于注解(Annotation-based)的配置)。
现在的问题是,已经有了一整套的程序,使用Spring-XML的方式配置所有bean,由于bean数量过多,导致配置文件的数量同样很多(超过50个,并在持续增加中),于是想改用component-scan的方式,来自动注册某个包下符合命名规则条件的所有bean,当然,重点是不想对原有代码进行任何修改。不想使用注解去对每一个bean进行标注,从而单纯的组件扫描方式是不可行的。
分析:于是想到了Struts2的Spring插件。
我们知道这个插件有一个奇妙的能力,对于Struts2 Action中引用的bean,只需要有对应的setter方法即可实现对该bean对象的自动注入(如果你使用@Autowired,你甚至无需写setter方法,只需一个私有变量即可),Spring容器透明的完成了这一点。当然,一切都是在下面一个拦截器中完成的。
Struts2采用Spring生成对象时,默认的对象工厂变成了StrutsSpringObjectFactory,这是一个对SpringObjectFactory进行了简单包装的对象工厂,主要实现还是基于SpringObjectFactory。对象的自动注入依靠的是ActionAutowiringInterceptor这个拦截器,Struts2-Spring-plugin配置文件中首次声明并引用了该拦截器。
1
Object bean = invocation.getAction();
2
factory.autoWireBean(bean);
1
public
Object autoWireBean(Object bean, AutowireCapableBeanFactory autoWiringFactory) {
2
if
(autoWiringFactory !=
null
) {
3
autoWiringFactory.autowireBeanProperties(bean,
4
autowireStrategy,
false
);
5
}
6
injectApplicationContext(bean);
7
injectInternalBeans(bean);
8
return
bean;
9
}
说了许多,还没有进入正题~
上面说到了Struts2的处理方式,但实际上这里用不上。在参考了“了解bean的一生”系列文章后明白了Spring初始、以及实例化bean的过程(流程图如下),推荐查看原文。
因此,基本得出了本文的一个解决方案。
实现:由于项目中良好的命名习惯,所有服务接口的名称均是以“Service”结尾,实现类则是“*ServiceImpl”,而所有对实现类的引用均是以服务名首字母小写的非限定类名的形式,即AbcService - AbcServiceImpl - abcService的对应关系。于是要把所有实现类注册为bean,要做的就很明确了。
1.首先需要一个BeanNameGenerator,并注册到组件扫描器中,以为bean类重命名。代码如下:
01
import
org.springframework.beans.factory.config.BeanDefinition;
02
import
org.springframework.beans.factory.support.BeanDefinitionRegistry;
03
import
org.springframework.beans.factory.support.BeanNameGenerator;
04
05
public
class
MyBeanNameGenerator
implements
BeanNameGenerator {
06
@Override
07
public
String generateBeanName(BeanDefinition bd, BeanDefinitionRegistry bdr) {
08
String classFullName = bd.getBeanClassName();
09
String beanName = classFullName.substring(classFullName.lastIndexOf(
"."
) +
1
);
10
beanName = String.valueOf(beanName.charAt(
0
)).toLowerCase() + beanName.substring(
1
);
11
int
end = beanName.lastIndexOf(
"Impl"
);
12
if
(end >
0
)
13
beanName = beanName.substring(
0
, end);
14
return
beanName;
15
}
16
}
2.按照上图的理解,你可以知道一个BeanPostProcessor的实现类在bean对象的实例化过程中有何作用。实际上就是一层接口,用于在Spring实例化bean的前后执行一些附加的自定义动作。简单到输出一行debug信息,复杂可以重定义整个bean,至于是前置方法还是后置方法区别不大。具体栗子如下:
01
public
class
MyBeanPostProcessor
implements
BeanPostProcessor, ApplicationContextAware {
02
03
private
ApplicationContext ac;
04
@Override
05
public
Object postProcessAfterInitialization(Object bean, String arg1)
throws
BeansException {
06
if
(
null
!= bean && bean.getClass().getName().endsWith(
"Impl"
)) {
07
try
{
08
BeanInfo bi = Introspector.getBeanInfo(bean.getClass());
09
for
(PropertyDescriptor pd : bi.getPropertyDescriptors()) {
10
String beanName = pd.getName();
11
Method m = pd.getWriteMethod();
12
if
((!
"class"
.equals(beanName)) &&
this
.ac.containsBean(beanName) &&
null
!= m
13
&& Modifier.isPublic(m.getModifiers()) && m.getParameterTypes().length ==
1
) {
14
try
{
15
Object param =
this
.ac.getBean(beanName);
16
if
(m.getParameterTypes()[
0
].isInstance(param))
17
m.invoke(bean, param);
18
}
catch
(Exception e) {
19
e.printStackTrace();
20
}
21
}
22
}
23
}
catch
(IntrospectionException e1) {
24
e1.printStackTrace();
25
}
26
ReflectionUtils.doWithFields(bean.getClass(),
new
FieldCallback() {
27
@Override
28
public
void
doWith(Field f)
throws
IllegalArgumentException, IllegalAccessException {
29
if
(!f.isAccessible())
30
f.setAccessible(
true
);
31
Object param = MyBeanPostProcessor.
this
.ac.getBean(f.getName());
32
if
(f.get(bean) ==
null
&& f.getType().isInstance(param))
33
f.set(bean, param);
34
}
35
},
new
FieldFilter() {
36
@Override
37
public
boolean
matches(Field f) {
38
// 只处理private(not static or final)参数不要求为interface
39
if
(Modifier.isPrivate(f.getModifiers()) && !Modifier.isFinal(f.getModifiers())
40
&& !Modifier.isStatic(f.getModifiers()) && ac.containsBean(f.getName()))
41
return
true
;
42
return
false
;
43
}
44
});
45
}
46
return
bean;
47
}
48
@Override
49
public
Object postProcessBeforeInitialization(
final
Object arg0, String arg1)
throws
BeansException {
50
return
arg0;
51
}
52
@Override
53
public
void
setApplicationContext(ApplicationContext ac)
throws
BeansException {
54
this
.ac = ac;
55
}
56
}
上面的代码实现了对bean中引用的其他bean对象的自动注入,根据项目中已经使用的规则,凡是有setter方法、且属性名可以在Spring容器中找到对应名字的bean的属性均会被自动注入(私有、非static及final的属性,属性类型可以是接口亦可以是普通类)。此外,更支持对私有字段(没有公共setter方法)的赋值注入(利用Java反射特性)。
3.最后,只需在Spring配置文件中加上简单的几行:
1
<
bean
class
=
"test.MyBeanPostProcessor"
/>
2
3
<
context:component-scan
base-package
=
"test.beans"
4
name-generator
=
"test.MyBeanNameGenerator"
5
use-default-filters
=
"false"
annotation-config
=
"false"
>
6
<
context:include-filter
type
=
"regex"
expression
=
".*Impl"
/>
7
</
context:component-scan
>
然后就可以把之前在配置文件中的绝大部分bean定义给删除了(除了部分需要特殊定义的、或是DataSource等bean对象)。上面的配置中,其中name-generator就是指定命名器的选项,另外,关于annotation-config这个参数,我们知道还有一个类似的配置项:<context:annotation-config />,这个参数则是指定可被扫描到的bean都可以使用annotation配置(即文首所说的autowire注解等),默认是为true,即默认启用。
4.what's more,设置annotation-config会同时引用进其他几个BeanPostProcessor,参见:Spring配置项<context:annotation-config/>解释说明。这就造成一个 BeanPostProcessor的执行顺序和默认覆盖问题。参考AbstractApplicationContext类的invokeBeanFactoryPostProcessors和registerBeanPostProcessors方法,其中有关于order顺序的特殊处理。因为系统定义的BeanPostProcessor实现类都有同时实现了order接口,因此会以一定的顺序执行(具体顺序以OrderComparator类的sort方法执行结果为准),对于没有实现order接口或自定义的BeanPostProcessor实现类,都将在最后执行,此外,也受到Spring配置文件中的配置顺序的影响。关于默认覆盖问题,则同样以配置文件为准。
至此,本文方休。
EOF ? 最后再总结一下本文讲了些什么,好吧,我不想把开头重复一遍,所以我讲一个小插曲:让我们回到MyBeanPostProcessor类中,有一行这样的代码使用JDK的标准bean定义解析器来解析bean对象的get/set方法,
1
BeanInfo bi = Introspector.getBeanInfo(bean.getClass());
2
for
(PropertyDescriptor pd : bi.getPropertyDescriptors()) {...
然而属性包装器(PropertyDescriptor)似乎是有一个bug(找了很多bean定义都没有关于此的特殊说明)。譬如,对于首字母是小写而次字母是大写的属性(如aBc),则其setter方法是setABc,对这个setter方法进行反向解析时得到的属性名确是ABc,即对应关系成了aBc - setAbc - ABc
-_- ~很奇妙吧~这样注入对象时就会在Spring容器中找不到对应名字的bean。
Finaly.本文还参考了以下链接中的内容:
Spring BeanPostProcessor类 (在Spring实例化bean的前后执行一些附加操作),
Spring开闭原则的表现-BeanPostProcessor扩展点系列文章,
Spring英文档中关于BeanPostProcessor的部分:http://sina.lt/d5b
- Spring +0配置+0注解Autowire Bean对象
- Spring - bean配置-Autowire
- Spring -- 注解配置Bean
- spring 注解配置bean
- Spring - 注解配置Bean
- spring annotation注解 autowire
- Spring Bean配置:注解配置
- 非注解方式获取spring bean对象以及配置值
- spring 注解方式配置Bean
- spring 注解扫描bean配置
- Spring基于注解配置Bean
- spring 注解方式配置Bean
- spring通过注解配置Bean
- spring之注解配置bean
- Spring通过注解配置bean
- Spring通过注解配置bean
- Spring通过注解配置bean
- Spring通过注解配置bean
- C#项目打开/保存文件夹/指定类型文件,获取路径
- vb.net datagridview中保存到xml
- html中position的一个小用法
- 2013 年度最新的 20 大热门开源软件
- jquery的css之--获取位置偏移函数position()
- Spring +0配置+0注解Autowire Bean对象
- 为什么java.util.concurrent 包里没有并发的ArrayList实现?
- 使用route(路由)模拟镜像数据包
- 使用 Osql 工具管理 SQL Server 桌面引擎 (MSDE 2000)应用介绍
- 关于JAVA中为什么每个编译单元内都只能有一个public类
- jquery和JSON结合
- java23设计模式思想
- HDU 2544 最短路 (SSSP & O(V^2)的Dijkstra算法)
- OSTaskCreateExt分析