在此之前我都是寫個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 這個兩個介面那麼剛剛提到的問題就可以解決了,下面是實現代碼
package com.test.annotation;
import java.lang.reflect.Method;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.util.ReflectionUtils;
public class AnnotationBeanPostProcessor extends PropertyPlaceholderConfigurer implements BeanPostProcessor, InitializingBean {
private java.util.Properties pros;
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
// TODO Auto-generated method stub
return bean;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
if(bean.getClass().getAnnotation(Property.class)!=null){
Method[] methods = bean.getClass().getDeclaredMethods();
for (Method method : methods) {
Property p = method.getAnnotation(Property.class);
if(p!=null){
// 這裡進行參數類型轉換
Object para=pros.getProperty(p.name());
if((method.getParameterTypes()[0]).getName().equals("java.lang.Integer")){
para= new Integer(para.toString());
}
ReflectionUtils.invokeMethod(method, bean, new Object[]{para});
}
}
}
return bean;
}
@Override
public void afterPropertiesSet() throws Exception {
pros = mergeProperties();
}
}
package com.test.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Property {
String name() default "";
}
package com.test;
import com.test.annotation.Property;
@Property
public class Bean {
private String name;
private Integer age;
private String address;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
@Property(name="com.test.Bean.address")
public void setAddress(String address) {
this.address = address;
}
public Integer getAge() {
return age;
}
@Property(name="com.test.Bean.age")
public void setAge(Integer age) {
this.age = age;
}
}
package com.test;
import com.test.annotation.Property;
@Property
public class JavaBean {
private String name;
private String address;
public String getName() {
return name;
}
@Property(name="com.test.JavaBean.name")
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
package com.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
/**
* @param args
*/
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("spring.xml");
System.out.println("載入設定檔結束");
System.out.println("--------------------------------------------");
JavaBean javaBean=(JavaBean)context.getBean("javaBean");
System.out.println(javaBean.getName());
System.out.println(javaBean.getAddress());
System.out.println("--------------------------------------------");
Bean bean=(Bean)context.getBean("bean");
System.out.println(bean.getName());
System.out.println(bean.getAddress());
System.out.println(bean.getAge());
System.out.println("--------------------------------------------");
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id=
attribute-value>"propertyConfigurer" class="com.test.annotation.AnnotationBeanPostProcessor">
<property name="locations">
<list>
<value>classpath*:system.properties</value>
</list>
</property>
</bean>
<bean id="javaBean" class="com.test.JavaBean">
<property name="address" value="${com.test.JavaBean.address}"></property>
</bean>
<bean id="bean" class="com.test.Bean">
<property name="name" value="${com.test.Bean.name}"></property>
</bean>
</beans>
ps:之所以要繼承PropertyPlaceholderConfigurer類,還有一個原因就是,原來通過${}注入值的方式還可以用
BeanPostProcessor有兩個方法,為什麼要寫在postProcessBeforeInitialization裡面,而不是postProcessAfterInitialization裡面,原因在於postProcessBeforeInitialization方法是在Bean的init方法之前執行,在init方法裡面可能會用到類的屬性,所以必須在init方法執行之前先賦值好.