首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件架构设计 >

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

2012-11-09 
别装了,难道你们不想把properties直接注入到object中去(spring-plugin)?[sizesmall]/** *作者:张荣华(ahu

别装了,难道你们不想把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"/>

这么一来这个xxxxxxx包下所有的使用@Service这个注释的对象都会自动的被spring管理。

虽然这样看上去很美好,但是却是不满足我们的需求的,因为我们的service中,或者其他被管理的bean中有时候需要一些配置,比如说String,Integer等等,而且这些配置的值一般都来自Properties文件,一般情况下我们会使用如下这段代码:
<bean id="propertyConfigurer" name="code">@Servicepublic 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()));本来我是通过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"name="code">@Componentpublic 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 {@autowiredprivate 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] )accounts=xxxuser=xxxpassword=xxx
然后实现一个BeanPostProcessor来根据这个约定来注入值。
这样就省了在Config.java文件写一堆的@Properties annotation,如果你觉得在某些情况下properties文件无法保证和field同名这个约定,你也可以再以@Properties annotation为优先设定。

请问有没有在这个设计基础上的成型代码?accounts=xxxuser=xxxpassword=xxx
然后实现一个BeanPostProcessor来根据这个约定来注入值。
这样就省了在Config.java文件写一堆的@Properties annotation,如果你觉得在某些情况下properties文件无法保证和field同名这个约定,你也可以再以@Properties annotation为优先设定。

请问有没有在这个设计基础上的成型代码?
等分散到package之后,或许有一天你又会想把它们都集中管理,放入某个xml里边,这个xml名字或许可以叫做package-class-mapping.xml 简称pcm跟hibernate的hbm对应,可以一个文件放一个vo设置,或者根据package放置多个vo设置,总之随你心意:) 29 楼 Jellee 2008-07-10   有一些正在用~
但有一些还真的没有尝试过~ 找空试一下...
又偷学到东西了... 嘻嘻~ 30 楼 leon1509 2008-10-23   按楼主的方法写完,值没注入进去! 31 楼 swantt 2008-11-10   为什么都要用annotation啊. 晕. 32 楼 swantt 2008-11-10   不太好吧. 33 楼 neptune 2009-01-13   用了,并自己修改了一下,非常好 34 楼 frenchleaf 2009-01-20   太帅了,正在用CXF+Spring,正需要这个 35 楼 frenchleaf 2009-01-20   能不能给个源码例子啊

热点排行