电竞比分网-中国电竞赛事及体育赛事平台

分享

把properties直接注入到object中去(spring-plugin)

 CevenCheng 2012-06-10
1背景 
Spring2.5支持使用annotation來(lái)配置我們的service,比如如下代碼:
Java代碼 復(fù)制代碼
  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() {

	}
}


這樣就表示這個(gè)service需要被spring管理,不過(guò)只是這樣做是不夠的,我們還需要在applicationcontext***.xml中加入這么一段: 
Java代碼 復(fù)制代碼
  1. <context:component-scan base-package="xxxxxxx"/>
<context:component-scan base-package="xxxxxxx"/>

這么一來(lái)這個(gè)xxxxxxx包下所有的使用@Service這個(gè)注釋的對(duì)象都會(huì)自動(dòng)的被spring管理。 

雖然這樣看上去很美好,但是卻是不滿足我們的需求的,因?yàn)槲覀兊膕ervice中,或者其他被管理的bean中有時(shí)候需要一些配置,比如說(shuō)String,Integer等等,而且這些配置的值一般都來(lái)自Properties文件,一般情況下我們會(huì)使用如下這段代碼: 
Java代碼 復(fù)制代碼
  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>	

這樣我們就可以通過(guò)${}來(lái)引用到properties文件中的值。 

不過(guò)使用了@service之后,我們就無(wú)法通過(guò)${}來(lái)得到properties中的值了。downpour是spring2.5使用的先行者,他很早就意識(shí)到這個(gè)問(wèn)題,通過(guò)我們的討論,確定了解決問(wèn)題的方向。下面我把這個(gè)方案拿出來(lái)和大家共享。 

2目標(biāo): 
我們的目標(biāo)是實(shí)現(xiàn)一個(gè)Annotation,代碼如下: 
Java代碼 復(fù)制代碼
  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. }
@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文件中的兩個(gè)屬性 

以上代碼中的@Properties就是我們要實(shí)現(xiàn)的Annotation,通過(guò)name的值作為key去對(duì)應(yīng)的properties中尋找對(duì)應(yīng)的value,并且主動(dòng)賦值給ImageFileUpload的對(duì)應(yīng)屬性。 

3步驟: 
我們知道,spring在初始化完bean之后我們可以對(duì)這些bean進(jìn)行一定的操作,這里就是一個(gè)擴(kuò)展點(diǎn),我決定使用BeanPostProcessor這個(gè)接口,這個(gè)接口中有一個(gè)postProcessAfterInitialization方法就是用來(lái)做bean的后處理的,一旦一個(gè)bean被初始化完成之后,我們就可以對(duì)這個(gè)bean進(jìn)行賦值了。 

但是考慮到我們項(xiàng)目中不是所有的bean都使用Annotation來(lái)注冊(cè)到spring中的,這些普通的,配置在xml文件中的bean也有用到${}的需求,所以我考慮擴(kuò)展PropertyPlaceholderConfigurer這個(gè)類。我們來(lái)分析一下具體的代碼。 

首先建立一個(gè)Annotation,如下: 
Java代碼 復(fù)制代碼
  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();
}


接著我們實(shí)現(xiàn)我們的擴(kuò)展主類: 
Java代碼 復(fù)制代碼
  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. 本來(lái)我是通過(guò)set方法來(lái)把properties文件中的值注入到對(duì)應(yīng)的屬性上去的,后來(lái)downpour提供了更好的方案,就是下面這兩行代碼,雖然這樣做破壞了private的功能,同時(shí)破壞了封裝,但是確實(shí)節(jié)省了很多代碼,建議大家在業(yè)務(wù)代碼中不要這樣做,如果做框架代碼可以考慮一下。
  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()));
本來(lái)我是通過(guò)set方法來(lái)把properties文件中的值注入到對(duì)應(yīng)的屬性上去的,后來(lái)downpour提供了更好的方案,就是下面這兩行代碼,雖然這樣做破壞了private的功能,同時(shí)破壞了封裝,但是確實(shí)節(jié)省了很多代碼,建議大家在業(yè)務(wù)代碼中不要這樣做,如果做框架代碼可以考慮一下。

						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代碼 復(fù)制代碼
  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>


這樣任何一個(gè)bean,不管它是使用annotation注冊(cè)的,還是直接配置在xml文件中的都可以使用這種方式來(lái)注入properties中的值。 

下面看一下我在項(xiàng)目中的一個(gè)真實(shí)的例子,這個(gè)類是一個(gè)value object,它代表一組配置: 
Java代碼 復(fù)制代碼
  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. }
@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的地方比如: 
Java代碼 復(fù)制代碼
  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;
	}
}

就這么多內(nèi)容就ok了,如果按照原來(lái)的辦法,我們就需要在xml配置以上兩個(gè)bean,然后在里面寫一堆又一堆的${},肯定能讓你看了之后崩潰,至少我差點(diǎn)崩潰,因?yàn)樗瓷先?shí)在是太丑陋了。而現(xiàn)在,我的心情好多了,因?yàn)槲矣眠@個(gè)@Properties(name = "")用的很爽,呵呵,而且即使有些bean是配置在xml文件中的,比如datasource等等,我們還是可以通過(guò)${}來(lái)進(jìn)行設(shè)值,也就是說(shuō)這個(gè)方案既支持annotation,也支持${},很好,很強(qiáng)大。 

結(jié)語(yǔ): 
很顯然,在spring2.5的時(shí)代,以上這個(gè)需求是非常平常的,我相信在spring3.0中一定會(huì)提供這樣的功能,而且我覺(jué)得spring2.5應(yīng)該是一個(gè)過(guò)渡版本,雖然上面的方案中代碼行數(shù)并不多,但是我覺(jué)得很有價(jià)值,應(yīng)該很有市場(chǎng)才對(duì),也許我們可以把這個(gè)東東叫做spring-properties2object-plugin。 

題外話: 
說(shuō)點(diǎn)題外話吧,目前在我的項(xiàng)目里,我使用了struts2.0+spring2.5+hibernate3.2,使用struts2.0的時(shí)候我使用struts2.0的zero configuration和codebehind,基本上實(shí)現(xiàn)了真正意義零配置,剩下的都是一些common的配置,而且很少,不超過(guò)150行。在使用spring的時(shí)候,我也基本上是使用annotation來(lái)注冊(cè)我的bean,同時(shí)使用上面的方案來(lái)作為補(bǔ)充,所以applicationContext-xxx.xml中的也是一些common的配置,也是非常少,應(yīng)該只有200行左右。而hibernate我是使用annotation來(lái)配置我的PO,基本沒(méi)有配置文件。所以整個(gè)項(xiàng)目的xml文件中配置的總行數(shù)大大下降。 

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多