在此之前我都是寫個PropertyUtil類來加載配置文件,然後通過get方法,把key對應的值取出來.
Spring提供一個PropertyPlaceholderConfigurer類,可以讀取配置文件,然後在Spring配置文件通過${hibernate.dialect}這種方式注入到JavaBean中,有個不好的地方就是,要在代碼中取的時候不是很方便.
然後在接觸到Java註解特註解技術以後,感覺這個東東很好,hibernate映射,WebService都可以通過註解來完成,方便的很多,然後就在想能不能通過Java註解特性加載屬性文件(properties)的值到Java類裏面呢?
其實上面一篇寫在Spring中JavaBean的初始化順序就是爲現在寫這個做準備的,要實現現在說的這個功能,大體方案有如下:
1.定義一個註解類
2.在需要加載屬性的JavaBean的屬性set方法上寫註解,註解的參數就是key
3.在Spring啓動的時候,去讀取屬性文件,然後把值賦給JavaBean
我們在上一篇寫在Spring中JavaBean的初始化順序提到了,如果一個JavaBean實現了BeanPostProcessor接口,那麼其他Bean初始化以後都會交給這個Bean來處理.這樣我們就可以寫一個JavaBean實現BeanPostProcessor接口,這個Bean有個屬性,它指向屬性文件路徑,在這個Bean初始化的時候讀取屬性文件內容,然後在postProcessBeforeInitialization方法裏面對寫了註解的Bean進行賦值.這樣一個實現思路似乎很順其自然,都是自己覺得不是很好,Spring通過PropertyPlaceholderConfigurer這樣類來加載屬性文件,而我們有寫了另外一個類,這樣一是不夠統一,二是不夠可能會造成多個屬性文件.解決辦法就是擴展PropertyPlaceholderConfigurer類.寫一個類繼承PropertyPlaceholderConfigurer類.然後在PropertyPlaceholderConfigurer初始化完成以後,獲取加載的屬性文件內容,在postProcessBeforeInitialization裏面把寫過註解的類屬性進行賦值.那麼怎麼確定什麼時候PropertyPlaceholderConfigurer加載完成呢,根據Spring中JavaBean的初始化順序,我們知道一個JavaBean如果實現了InitializingBean接口,那麼Spring容器會在這個Bean初始化以後調用afterPropertiesSet方法,現在我寫個類繼承PropertyPlaceholderConfigurer類,實現BeanPostProcessor, InitializingBean 這個兩個接口那麼剛剛提到的問題就可以解決了,下面是實現代碼
- packagecom.test.annotation;
- importjava.lang.reflect.Method;
- importorg.springframework.beans.BeansException;
- importorg.springframework.beans.factory.InitializingBean;
- importorg.springframework.beans.factory.config.BeanPostProcessor;
- importorg.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
- importorg.springframework.util.ReflectionUtils;
- publicclassAnnotationBeanPostProcessorextendsPropertyPlaceholderConfigurerimplementsBeanPostProcessor,InitializingBean{
- privatejava.util.Propertiespros;
- @Override
- publicObjectpostProcessAfterInitialization(Objectbean,StringbeanName)
- throwsBeansException{
- //TODOAuto-generatedmethodstub
- returnbean;
- }
- @Override
- publicObjectpostProcessBeforeInitialization(Objectbean,StringbeanName)
- throwsBeansException{
- if(bean.getClass().getAnnotation(Property.class)!=null){
- Method[]methods=bean.getClass().getDeclaredMethods();
- for(Methodmethod:methods){
- Propertyp=method.getAnnotation(Property.class);
- if(p!=null){
- //這裏進行參數類型轉換
- Objectpara=pros.getProperty(p.name());
- if((method.getParameterTypes()[0]).getName().equals("java.lang.Integer")){
- para=newInteger(para.toString());
- }
- ReflectionUtils.invokeMethod(method,bean,newObject[]{para});
- }
- }
- }
- returnbean;
- }
- @Override
- publicvoidafterPropertiesSet()throwsException{
- pros=mergeProperties();
- }
- }
- packagecom.test.annotation;
- importjava.lang.annotation.Retention;
- importjava.lang.annotation.RetentionPolicy;
- importjava.lang.annotation.Target;
- importjava.lang.annotation.ElementType;
- @Retention(RetentionPolicy.RUNTIME)
- @Target({ElementType.TYPE,ElementType.METHOD})
- public@interfaceProperty{
- Stringname()default"";
- }
- packagecom.test;
- importcom.test.annotation.Property;
- @Property
- publicclassBean{
- privateStringname;
- privateIntegerage;
- privateStringaddress;
- publicStringgetName(){
- returnname;
- }
- publicvoidsetName(Stringname){
- this.name=name;
- }
- publicStringgetAddress(){
- returnaddress;
- }
- @Property(name="com.test.Bean.address")
- publicvoidsetAddress(Stringaddress){
- this.address=address;
- }
- publicIntegergetAge(){
- returnage;
- }
- @Property(name="com.test.Bean.age")
- publicvoidsetAge(Integerage){
- this.age=age;
- }
- }
- packagecom.test;
- importcom.test.annotation.Property;
- @Property
- publicclassJavaBean{
- privateStringname;
- privateStringaddress;
- publicStringgetName(){
- returnname;
- }
- @Property(name="com.test.JavaBean.name")
- publicvoidsetName(Stringname){
- this.name=name;
- }
- publicStringgetAddress(){
- returnaddress;
- }
- publicvoidsetAddress(Stringaddress){
- this.address=address;
- }
- }
- packagecom.test;
- importorg.springframework.context.ApplicationContext;
- importorg.springframework.context.support.ClassPathXmlApplicationContext;
- publicclassTest{
- /**
- *@paramargs
- */
- publicstaticvoidmain(String[]args){
- ApplicationContextcontext=
- newClassPathXmlApplicationContext("spring.xml");
- System.out.println("加載配置文件結束");
- System.out.println("--------------------------------------------");
- JavaBeanjavaBean=(JavaBean)context.getBean("javaBean");
- System.out.println(javaBean.getName());
- System.out.println(javaBean.getAddress());
- System.out.println("--------------------------------------------");
- Beanbean=(Bean)context.getBean("bean");
- System.out.println(bean.getName());
- System.out.println(bean.getAddress());
- System.out.println(bean.getAge());
- System.out.println("--------------------------------------------");
- }
- }
- <?xmlversion="1.0"encoding="UTF-8"?>
- <!DOCTYPEbeansPUBLIC"-//SPRING//DTDBEAN//EN""http://www.springframework.org/dtd/spring-beans.dtd">
- <beans>
- <beanid="propertyConfigurer"class="com.test.annotation.AnnotationBeanPostProcessor">
- <propertyname="locations">
- <list>
- <value>classpath*:system.properties</value>
- </list>
- </property>
- </bean>
- <beanid="javaBean"class="com.test.JavaBean">
- <propertyname="address"value="${com.test.JavaBean.address}"></property>
- </bean>
- <beanid="bean"class="com.test.Bean">
- <propertyname="name"value="${com.test.Bean.name}"></property>
- </bean>
- </beans>
ps:之所以要繼承PropertyPlaceholderConfigurer類,還有一個原因就是,原來通過${}注入值的方式還可以用
BeanPostProcessor有兩個方法,爲什麼要寫在postProcessBeforeInitialization裏面,而不是postProcessAfterInitialization裏面,原因在於postProcessBeforeInitialization方法是在Bean的init方法之前執行,在init方法裏面可能會用到類的屬性,所以必須在init方法執行之前先賦值好.