打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
把properties直接注入到object中去(spring-plugin)
1背景 
Spring2.5支持使用annotation来配置我们的service,比如如下代码:
Java代码 
  1. @Service("userService")
  2. public class UserServiceImpl extends BaseServiceSupport implements UserService {
  3. public void xxx() {
  4. }
  5. }
@Service("userService")public class UserServiceImpl extends BaseServiceSupport implements UserService {	public void xxx() {	}}


这样就表示这个service需要被spring管理,不过只是这样做是不够的,我们还需要在applicationcontext***.xml中加入这么一段: 
Java代码 
  1. <context:component-scan base-package="xxxxxxx"/>
<context:component-scan base-package="xxxxxxx"/>

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

虽然这样看上去很美好,但是却是不满足我们的需求的,因为我们的service中,或者其他被管理的bean中有时候需要一些配置,比如说String,Integer等等,而且这些配置的值一般都来自Properties文件,一般情况下我们会使用如下这段代码: 
Java代码 
  1. <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
  2. <property name="locations">
  3. <list>
  4. <value>classpath:jdbc.properties</value>
  5. </list>
  6. </property>
  7. </bean>
<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,代码如下: 
Java代码 
  1. @Service
  2. public class ImageFileUpload implements Serializable {
  3. @Properties(name="pic.address" )
  4. private String picAddress;
  5. @Properties(name="pic.url" )
  6. private String picUrl;
  7. private String picServerUrl;
  8. }
@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,如下: 
Java代码 
  1. /**
  2. * @author ahuaxuan(aaron zhang)
  3. * @since 2008-4-7
  4. * @version $Id: Properties.java 261 2008-04-07 07:03:41Z aaron $
  5. */
  6. @Target(ElementType.FIELD)
  7. @Retention(RetentionPolicy.RUNTIME)
  8. public @interface Properties {
  9. // String bundle();
  10. String name();
  11. }
/** * @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();}


接着我们实现我们的扩展主类: 
Java代码 
  1. /**
  2. * @author ahuaxuan(aaron zhang)
  3. * @since 2008-4-7
  4. * @version $Id: AnnotationBeanPostProcessor.java 260 2008-04-07 07:03:35Z aaron $
  5. */
  6. public class AnnotationBeanPostProcessor extends PropertyPlaceholderConfigurer implements BeanPostProcessor, InitializingBean {
  7. private static transient Log logger = LogFactory.getLog(AnnotationBeanPostProcessor.class);
  8. private java.util.Properties pros;
  9. @SuppressWarnings("unchecked")
  10. private Class[] enableClassList = {String.class};
  11. @SuppressWarnings("unchecked")
  12. public void setEnableClassList(Class[] enableClassList) {
  13. this.enableClassList = enableClassList;
  14. }
  15. public Object postProcessAfterInitialization(Object bean, String beanName)
  16. throws BeansException {
  17. Field [] fields = bean.getClass().getDeclaredFields();
  18. for (Field field : fields) {
  19. if (logger.isDebugEnabled()) {
  20. StringBuilder sb = new StringBuilder();
  21. sb.append(" ========= ")
  22. .append(field.getType())
  23. .append(" ============ ")
  24. .append(field.getName())
  25. .append(" ============ ")
  26. .append(field.isAnnotationPresent(Properties.class));
  27. logger.debug(sb.toString());
  28. }
  29. if (field.isAnnotationPresent(Properties.class)) {
  30. if (filterType(field.getType().toString())) {
  31. Properties p = field.getAnnotation(Properties.class);
  32. try {
  33. // StringBuilder sb = new StringBuilder();
  34. // sb.append("set").append(StringUtils.upperCase(field.getName().substring(0, 1)))
  35. // .append(field.getName().substring(1, field.getName().length()));
  36. //
  37. // Method method = bean.getClass().getMethod(sb.toString(), String.class);
  38. // method.invoke(bean, pros.getProperty(p.name()));
  39. 本来我是通过set方法来把properties文件中的值注入到对应的属性上去的,后来downpour提供了更好的方案,就是下面这两行代码,虽然这样做破坏了private的功能,同时破坏了封装,但是确实节省了很多代码,建议大家在业务代码中不要这样做,如果做框架代码可以考虑一下。
  40. ReflectionUtils.makeAccessible(field);
  41. field.set(bean, pros.getProperty(p.name()));
  42. catch (Exception e) {
  43. logger.error(" --- ", e);
  44. }
  45. }
  46. }
  47. }
  48. return bean;
  49. }
  50. @SuppressWarnings("unchecked")
  51. private boolean filterType(String type) {
  52. if (type != null) {
  53. for (Class c : enableClassList) {
  54. if (c.toString().equals(type)) {
  55. return true;
  56. }
  57. }
  58. return false;
  59. else {
  60. return true;
  61. }
  62. }
  63. public Object postProcessBeforeInitialization(Object bean, String beanName)
  64. throws BeansException {
  65. return bean;
  66. }
  67. public void afterPropertiesSet() throws Exception {
  68. pros = mergeProperties();
  69. }
  70. }
/** * @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文件中配置一下: 
Java代码 
  1. <bean id="propertyConfigurer"
  2. class="xx.service.AnnotationBeanPostProcessor">
  3. <property name="locations">
  4. <list>
  5. <value>classpath:jdbc.properties</value>
  6. </list>
  7. </property>
  8. </bean>
<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,它代表一组配置: 
Java代码 
  1. @Component
  2. public class Config implements Serializable{
  3. /** */
  4. private static final long serialVersionUID = 8737228049639915113L;
  5. @Properties(name = " online.pay.accounts")
  6. private String accounts;
  7. @Properties(name = " online.pay.user")
  8. private String user;
  9. @Properties(name = " online.pay.password")
  10. private String password;
  11. @Properties(name = " online.transurl")
  12. private String transUrl;
  13. @Properties(name = " online.refundurl")
  14. private String refundUrl;
  15. @Properties(name = " online.query")
  16. private String queryUrl;
  17. ```setter and getter method
  18. }
@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的地方比如: 
Java代码 
  1. @Service(“userService”)
  2. public class UserServiceImpl implements UserService {
  3. @autowired
  4. private Config config;
  5. public void setConfig(Config config) {
  6. This.config = config;
  7. }
  8. }
@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文件中配置的总行数大大下降。 
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
Spring @Value 属性注入使用总结一
5.1.2 不用模式的解决方案
容器扩展点之PropertyPlaceholderConfigurer【整理】
SpringBoot自动装配原理,这一篇就够了!
SpringBoot非官方教程 | 第二篇:Spring Boot配置文件详解
Spring Boot读取配置文件的几种方式
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服