首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

利用 Java 註解特性載入屬性檔值到 Java 類別

     編輯:Java 相關知識

前言

過去我都寫 PropertyUtil 類別載入設定檔,再透過 get 方法取值。Spring 提供 PropertyPlaceholderConfigurer 讀取設定檔,在 Spring 設定檔用 ${hibernate.dialect} 方式注入 JavaBean,但程式碼中取值不便。接觸 Java 註解技術後發現其強大 (Hibernate 映射、WebService 皆可用註解完成),便思考能否透過註解載入 properties 值到 Java 類別。

實現方案

大體方案:

  1. 定義一個註解類別
  2. 在需載入屬性的 JavaBean setter 方法上標註註解,參數為 key
  3. Spring 啟動時讀取屬性檔,將值賦予 JavaBean

根據 Spring 中 JavaBean 初始化順序,實作 BeanPostProcessor 接口的 Bean 會在其他 Bean 初始化後處理。可寫一個 Bean 實作 BeanPostProcessor,屬性指向屬性檔路徑,初始化時讀取內容,在 postProcessBeforeInitialization 方法中對標註註解的 Bean 賦值。但 Spring 已有 PropertyPlaceholderConfigurer 載入屬性檔,另寫一類不夠統一,可能造成多個屬性檔。解決辦法是擴展 PropertyPlaceholderConfigurer:繼承該類並實作 BeanPostProcessor、InitializingBean 介面。在 PropertyPlaceholderConfigurer 初始化完成後取得載入的屬性內容,在 postProcessBeforeInitialization 中對標註註解的類別屬性賦值。

如何確定 PropertyPlaceholderConfigurer 載入完成?根據初始化順序,實作 InitializingBean 接口的 Bean,容器會在初始化後呼叫 afterPropertiesSet 方法。故寫一類繼承 PropertyPlaceholderConfigurer 並實作 BeanPostProcessor、InitializingBean 即可解決。

程式碼實現

註解定義

package com.test.annotation;
import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Property {
    String name() default "";
}

擴展 PropertyPlaceholderConfigurer

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 {
        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;
import com.test.annotation.Property;

@Property
public class Bean {
    private String name;
    private Integer age;
    private String address;
    // ... getter
    public void setName(String name) { this.name = name; }
    @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; }
}

@Property
public class JavaBean {
    private String name;
    private String address;
    // ... getter
    @Property(name="com.test.JavaBean.name")
    public void setName(String name) { this.name = name; }
    public void setAddress(String address) { this.address = address; }
}

Spring 設定檔

<?xml version="1.0" encoding="UTF-8"?>
<beans>
    <bean id="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}"/>
    </bean>
    <bean id="bean" class="com.test.Bean">
        <property name="name" value="${com.test.Bean.name}"/>
    </bean>
</beans>

測試類別

package com.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
        System.out.println("載入設定檔結束");
        JavaBean javaBean = (JavaBean) context.getBean("javaBean");
        System.out.println(javaBean.getName());
        System.out.println(javaBean.getAddress());
        Bean bean = (Bean) context.getBean("bean");
        System.out.println(bean.getName());
        System.out.println(bean.getAddress());
        System.out.println(bean.getAge());
    }
}

補充說明

繼承 PropertyPlaceholderConfigurer 另一原因是保留原有 ${} 注入方式。BeanPostProcessor 有兩個方法,為何寫在 postProcessBeforeInitialization 而非 postProcessAfterInitialization?因為前者在 Bean 的 init 方法之前執行,init 方法中可能用到類別屬性,故必須在 init 執行前先賦值完成。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved