别装了,难道你们不想把properties直接注入到object中去(spring-plugin)?

[size=small]/**
*作者:张荣华(ahuaxuan)
*日期:2008-4-9
**/

1背景
Spring2.5支持使用annotation来配置我们的service,比如如下代码:

@Service("userService")
public class UserServiceImpl extends BaseServiceSupport implements UserService {
public void xxx() {
}
}

这样就表示这个service需要被spring管理,不过只是这样做是不够的,我们还需要在applicationcontext***.xml中加入这么一段:

<context:component-scan base-package="xxxxxxx"/>
  1. 这么一来这个xxxxxxx包下所有的使用@Service这个注释的对象都会自动的被spring管理。

虽然这样看上去很美好,但是却是不满足我们的需求的,因为我们的service中,或者其他被管理的bean中有时候需要一些配置,比如说String,Integer等等,而且这些配置的值一般都来自Properties文件,一般情况下我们会使用如下这段代码:

<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:jdbc.properties</value>
</list>
</property>
</bean>

这样我们就可以通过${}来引用到properties文件中的值。

不过使用了@service之后,我们就无法通过${}来得到properties中的值了。downpour是spring2.5使用的先行者,他很早就意识到这个问题,通过我们的讨论,确定了解决问题的方向。下面我把这个方案拿出来和大家共享。

2目标:
我们的目标是实现一个Annotation,代码如下:

@Service
public class ImageFileUpload implements Serializable {
@Properties(name="pic.address" )
private String picAddress;
@Properties(name="pic.url" )
private String picUrl;
private String picServerUrl;
}

pic.address和pic.url是properties文件中的两个属性

以上代码中的@Properties就是我们要实现的Annotation,通过name的值作为key去对应的properties中寻找对应的value,并且主动赋值给ImageFileUpload的对应属性。

3步骤:
我们知道,spring在初始化完bean之后我们可以对这些bean进行一定的操作,这里就是一个扩展点,我决定使用BeanPostProcessor这个接口,这个接口中有一个postProcessAfterInitialization方法就是用来做bean的后处理的,一旦一个bean被初始化完成之后,我们就可以对这个bean进行赋值了。

但是考虑到我们项目中不是所有的bean都使用Annotation来注册到spring中的,这些普通的,配置在xml文件中的bean也有用到${}的需求,所以我考虑扩展PropertyPlaceholderConfigurer这个类。我们来分析一下具体的代码。

首先建立一个Annotation,如下:

/**
* @author ahuaxuan(aaron zhang)
* @since 2008-4-7
* @version $Id: Properties.java 261 2008-04-07 07:03:41Z aaron $
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Properties {
// String bundle();
String name();
}

接着我们实现我们的扩展主类:

/**
* @author ahuaxuan(aaron zhang)
* @since 2008-4-7
* @version $Id: AnnotationBeanPostProcessor.java 260 2008-04-07 07:03:35Z aaron $
*/
public class AnnotationBeanPostProcessor extends PropertyPlaceholderConfigurer implements BeanPostProcessor, InitializingBean {
private static transient Log logger = LogFactory.getLog(AnnotationBeanPostProcessor.class);
private java.util.Properties pros;
@SuppressWarnings("unchecked")
private Class[] enableClassList = {String.class};
@SuppressWarnings("unchecked")
public void setEnableClassList(Class[] enableClassList) {
this.enableClassList = enableClassList;
}
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
Field [] fields = bean.getClass().getDeclaredFields();
for (Field field : fields) {
if (logger.isDebugEnabled()) {
StringBuilder sb = new StringBuilder();
sb.append(" ========= ")
.append(field.getType())
.append(" ============ ")
.append(field.getName())
.append(" ============ ")
.append(field.isAnnotationPresent(Properties.class));
logger.debug(sb.toString());
}
if (field.isAnnotationPresent(Properties.class)) {
if (filterType(field.getType().toString())) {
Properties p = field.getAnnotation(Properties.class);
try {
// StringBuilder sb = new StringBuilder();
// sb.append("set").append(StringUtils.upperCase(field.getName().substring(0, 1)))
// .append(field.getName().substring(1, field.getName().length()));
//
// Method method = bean.getClass().getMethod(sb.toString(), String.class);
// method.invoke(bean, pros.getProperty(p.name()));

  1. 本来我是通过set方法来把properties文件中的值注入到对应的属性上去的,后来downpour提供了更好的方案,就是下面这两行代码,虽然这样做破坏了private的功能,同时破坏了封装,但是确实节省了很多代码,建议大家在业务代码中不要这样做,如果做框架代码可以考虑一下。
ReflectionUtils.makeAccessible(field);
field.set(bean, pros.getProperty(p.name()));
} catch (Exception e) {
logger.error(" --- ", e);
}
}
}
}
return bean;
}
@SuppressWarnings("unchecked")
private boolean filterType(String type) {
if (type != null) {
for (Class c : enableClassList) {
if (c.toString().equals(type)) {
return true;
}
}
return false;
} else {
return true;
}
}
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
public void afterPropertiesSet() throws Exception {
pros = mergeProperties();
}
}

最后我们需要在xml文件中配置一下:

<bean id="propertyConfigurer"
class="xx.service.AnnotationBeanPostProcessor">
<property name="locations">
<list>
<value>classpath:jdbc.properties</value>
</list>
</property>
</bean>

这样任何一个bean,不管它是使用annotation注册的,还是直接配置在xml文件中的都可以使用这种方式来注入properties中的值。

下面看一下我在项目中的一个真实的例子,这个类是一个value object,它代表一组配置:

@Component
public class Config implements Serializable{
/** */
private static final long serialVersionUID = 8737228049639915113L;
@Properties(name = " online.pay.accounts")
private String accounts;
@Properties(name = " online.pay.user")
private String user;
@Properties(name = " online.pay.password")
private String password;
@Properties(name = " online.transurl")
private String transUrl;
@Properties(name = " online.refundurl")
private String refundUrl;
@Properties(name = " online.query")
private String queryUrl;
```setter and getter method
}

那么在需要用到该vo的地方比如:

@Service(“userService”)
public class UserServiceImpl implements UserService {
@autowired
private Config config;
public void setConfig(Config config) {
This.config = config;
}
}

就这么多内容就ok了,如果按照原来的办法,我们就需要在xml配置以上两个bean,然后在里面写一堆又一堆的${},肯定能让你看了之后崩溃,至少我差点崩溃,因为它看上去实在是太丑陋了。而现在,我的心情好多了,因为我用这个@Properties(name = "")用的很爽,呵呵,而且即使有些bean是配置在xml文件中的,比如datasource等等,我们还是可以通过${}来进行设值,也就是说这个方案既支持annotation,也支持${},很好,很强大。

结语:
很显然,在spring2.5的时代,以上这个需求是非常平常的,我相信在spring3.0中一定会提供这样的功能,而且我觉得spring2.5应该是一个过渡版本,虽然上面的方案中代码行数并不多,但是我觉得很有价值,应该很有市场才对,也许我们可以把这个东东叫做spring-properties2object-plugin。

题外话:
说点题外话吧,目前在我的项目里,我使用了struts2.0+spring2.5+hibernate3.2,使用struts2.0的时候我使用struts2.0的zero configuration和codebehind,基本上实现了真正意义零配置,剩下的都是一些common的配置,而且很少,不超过150行。在使用spring的时候,我也基本上是使用annotation来注册我的bean,同时使用上面的方案来作为补充,所以applicationContext-xxx.xml中的也是一些common的配置,也是非常少,应该只有200行左右。而hibernate我是使用annotation来配置我的PO,基本没有配置文件。所以整个项目的xml文件中配置的总行数大大下降。

[/size]

时间: 2025-01-14 01:35:32

别装了,难道你们不想把properties直接注入到object中去(spring-plugin)?的相关文章

小米投资互联网家装公司“爱空间”,想低调都不行

摘要: 小米投资互联网家装公司爱空间,想低调都不行,699元/平米的定价,让朋友圈都刷屏了.热闹的场面不仅由于此,有住网更早一步杀入家装市场,定价多少?599元/平米!而海尔地产云 小米投资互联网家装公司"爱空间",想低调都不行,699元/平米的定价,让朋友圈都刷屏了.热闹的场面不仅由于此,有住网更早一步杀入家装市场,定价多少?599元/平米!而海尔地产云菜网也与有住网合作签约.巨头们杀入家装的意图是什么? 599元如何炼成 近日雷军小米旗下的顺为资本领投.总共6000万元的投资砸向&

想引用System.Web,发现System中没有Web了,怎么办啊??

问题描述 想引用System.Web,发现System中没有Web了,怎么办啊??这是哪的毛病啊??? 解决方案 解决方案二:项目添加引用.net选system.web.dllwinform下默认上没有的解决方案三:我记得以前直接装上就有了呀??怎么这回没有??

ou-如果我仅仅想查询某个AD账号在域中是否存在?怎么做?

问题描述 如果我仅仅想查询某个AD账号在域中是否存在?怎么做? 我用一下代码获取所有的AD账号,但是提示"指定的域不存在,或无法联系. ": DirectorySearcher mySearcher = new DirectorySearcher();//想搜索出所有,此处可省参数 mySearcher.Filter = ("(objectClass=user)"); //user表示用户,group表示组 mySearcher.PageSize = 10000;/

各位高手们,我想抓取新闻信息到数据库中,求指教

问题描述 各位高手们,我想抓取新闻信息到数据库中,求指教 各位高手们,我想抓取新闻信息到数据库中,求指教,麻烦加我QQ交流一下 解决方案 最好是能用asp能写出来 解决方案二: 可以跟你说说我的思路,后边你可以自己完成: 1.先获取新闻页面的html 代码 2. 通过正则表达式 获取你想要的内容 解决方案三: 百度火车头采集,很好用的采集软件 自己写要分析新闻页面内容,前后截取了,asp下载网页DEMO:asp xmlHttp用法举例 解决方案四: js等抓去html页面内容. 分析DOM类容

我想每隔一天将SQL2000数据库中的dattime型数据更改为当天日期?如何实现?急!

问题描述 我想每隔一天将SQL2000数据库中的dattime型数据更改为当天日期?即使不开机也能定时更新的,如何实现?急! 解决方案 解决方案二:关注中--解决方案三:即使不开机也能定时更新的,呵呵你到数据库里上个电池就行了SQL2000数据库中的dattime型数据更改为当天日期用触发器应该能在连接数据库时更新.或者自己写个电脑的程序,每次电脑开启时更新.解决方案四:job作业解决方案五:只要BIOS里面还有电就行,这个字段就设置getDate()为好了,只要查看或者修改就自动修改了解决方案

初学者的忧伤-我想在非ie浏览器的页面中执行一个js,然后打开一个指定的ie页面

问题描述 我想在非ie浏览器的页面中执行一个js,然后打开一个指定的ie页面 ActiveX只有在ie中才能运行,我现在想在谷歌或者火狐浏览器网页中自动打开一个ie浏览器的指定页面,请问直接用js可以实现吗? 解决方案 [原创]在winform程序中实现在IE浏览器中打开一个新的页面,全屏化并屏蔽IE窗口的工具栏和地址栏 解决方案二: 没有这种办法.页面在哪个浏览器打开就会在哪个浏览器呈现,页面不属于任何一个浏览器专有.你要想支持activeX,就只能在IE浏览器中打开.

想做SQL语句注入实验,可以在JSP页面直接嵌入sql语句查询吗?

问题描述 想做SQL语句注入实验,可以在JSP页面直接嵌入sql语句查询吗? 我是名大三的学生,想请假各位大神,如果想做SQL语句注入实验,可以在JSP页面直接嵌入sql语句查询吗?还是使用ssh框架反应机制?还是其它的,有大神可以指教一下小弟吗?该课程是网络安全,主要就是想做sql语句注入检测网页漏洞的? 解决方案 直接建个环境做测试啊,网上这方面的课程很多. 解决方案二: 只要是页面有访问数据库操作的 都可以拿来做 SQL语句注入 解决方案三: 最简单的就是在页面上允许输入查询条件,在jsp

JAVA+Mysql程序,想在Mysql的一张表中让数据库自动生成一个字段的Icq号

问题描述 JAVA+Mysql程序,想在Mysql的一张表中让数据库自动生成一个字段的Icq号 我想做个聊天系统,想在Mysql的一张注册表中,加入注册信息后,让数据库自动生成一个Icq号.好像我们的QQ账号似的.简单的只要能实现就行.我新手. 解决方案 添加一个字段,设置为int或bigint类型,在设置主键自增长(AUTO_INCREMENT ). 解决方案二: 添加一个字段,设置为int 主键 自动增长就可以了. 解决方案三: 数据库增加一列,设置为标识列,有种子和增量. 解决方案四: 可

想哭(WannaCry)勒索病毒中招后,该怎么恢复数据?(非解密)

本文讲的是想哭(WannaCry)勒索病毒中招后,该怎么恢复数据?(非解密), 一.前言 受WannaCry勒索病毒影响,许多遭受攻击的电脑中的大部分文件被加密而被勒索要求支付比特币以进行解密文件.当前没有完美的解密工具或者方案,但根据对病毒的分析,我们发现病毒采用加密原文件后再删除原文件的方式,于是针对被删除的文件就存在一定恢复的可能性,我们只要恢复出删除的原文件即可. 我们在13号嘶吼的采访中提到,可使用数据恢复软件通过恢复被删除的加密前的文件,能恢复部分文件,一定程度上挽回用户损失.应用户