Spring中使用Configuration注入Bean

在Spring容器中使用applicationContext.xml中來給對應的類注入對應的屬性,來完成初始化,最典型的就是配置數據庫連接池了。

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
        init-method="init" destroy-method="close">
        <!-- 基本屬性 url、user、password -->
        <property name="url" value="${connection.url}" />
        <property name="username" value="${connection.username}" />
        <property name="password" value="${connection.password}" />
        <!-- 配置初始化大小、最小、最大 -->
        <property name="initialSize" value="${druid.initialSize}" />
        <property name="minIdle" value="${druid.minIdle}" />
        <property name="maxActive" value="${druid.maxActive}" />
        <!-- 配置獲取連接等待超時的時間 -->
        <property name="maxWait" value="${druid.maxWait}" />
        <!-- 配置間隔多久才進行一次檢測,檢測需要關閉的空閒連接,單位是毫秒 -->
        <property name="timeBetweenEvictionRunsMillis" value="${druid.timeBetweenEvictionRunsMillis}" />
        <!-- 配置一個連接在池中最小生存的時間,單位是毫秒 -->
        <property name="minEvictableIdleTimeMillis" value="${druid.minEvictableIdleTimeMillis}" />
        <property name="removeAbandoned" value="${druid.removeAbandoned}" />
        <!-- 超時時間;單位爲秒。180秒=3分鐘 -->
        <property name="removeAbandonedTimeout" value="${druid.removeAbandonedTimeoutSeconds}" />
        <property name="validationQuery" value="${druid.validationQuery}" />
        <property name="testWhileIdle" value="${druid.testWhileIdle}" />
        <property name="testOnBorrow" value="${druid.testOnBorrow}" />
        <property name="testOnReturn" value="${druid.testOnReturn}" />
        <!-- 打開PSCache,並且指定每個連接上PSCache的大小 如果用Oracle,則把poolPreparedStatements配置爲true,mysql可以配置爲false。 -->
        <property name="poolPreparedStatements" value="${druid.poolPreparedStatements}" />
        <property name="maxPoolPreparedStatementPerConnectionSize"
            value="${druid.maxPoolPreparedStatementPerConnectionSize}" />
        <!-- 配置監控統計攔截的filters -->
        <property name="filters" value="${druid.filters}" />
    </bean>

配置參數就不貼出。

使用@Configuration創建Bean

Configuration 是 Spring 3.X 後提供的註解,用於取代 XML 來配置 Spring,

@Configuration可理解爲用spring的時候xml裏面的<beans>標籤;

@Bean可理解爲用spring的時候xml裏面的<bean>標籤。

這樣就很好理解了。

需要注意的時配置spring 掃描的包 <context:component-scan base-package="com.xxx.xxx" /> 不然註解不起效果(springboot不需要設置)。

讀取json文件的屬性注入Bean

這次使用Json文件來配置bean;

首先寫出實體類,和需要配置的數據;

編寫實體類

package com.devframe.util;

import java.util.List;

/**
 * @author Zhang Kai
 * @version 1.0
 * @since <pre>2017/11/2 11:31</pre>
 */
public class PersonCfg {

    private String name;
    private int age;
    private String city;
    private List<Contact> contacts;

    @Override
    public String toString() {
        return "PersonCfg{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", city='" + city + '\'' +
                ", contacts=" + contacts +
                ", hobby=" + hobby +
                '}';
    }

    public List<Contact> getContacts() {
        return contacts;
    }

    public void setContacts(List<Contact> contacts) {
        this.contacts = contacts;
    }

    public List getHobby() {
        return hobby;
    }

    public void setHobby(List hobby) {
        this.hobby = hobby;
    }

    private List hobby;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

}

class Contact {

    private String phone;

    private String email;

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    @Override
    public String toString() {
        return "Contact{" +
                "phone='" + phone + '\'' +
                ", email='" + email + '\'' +
                '}';
    }
}

需要注入的數據

創建文件命名data.json,(注意屬性名對應):

{
  "name": "wuwii",
  "age": 23,
  "city": "WuHan",
  "hobby": ["騎行", "跑步","足球"],
  "contacts": [{
    "phone": "18772383543",
    "email": "[email protected]"
  },{
    "phone": "12345678912",
    "email": "[email protected]"
  }]
}

創建Beans

spring 容器初始化,自動掃描,去初始化Bean,加載進Environment,後面調用的直接自動裝配(Autowired);

package com.devframe.util;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.io.File;
import java.io.IOException;

/**
 * @author Zhang Kai
 * @version 1.0
 * @since <pre>2017/11/2 11:23</pre>
 */
@Configuration
public class Configs {
    @Value("classpath:data.json")
    protected File configFile;

    @Bean
    public PersonCfg readServerConfig() throws IOException {
        return new ObjectMapper().readValue(configFile, PersonCfg.class);
    }

}

@Bean 註解方法的返回值,將注入到容器中,可以使用自動裝配。

測試

直接使用spring-test 的JUnit4 單元測試;
直接裝配Bean ,來輸出它的屬性,查看是否裝配成功。

package com.devframe.util; 

import org.junit.Test; 
import org.junit.Before; 
import org.junit.After;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/** 
* Configs Tester. 
* 
* @author Zhang Kai 
* @since <pre>11/02/2017</pre> 
* @version 1.0 
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:spring/applicationContext-base.xml"})
public class ConfigsTest {
    @Autowired
    private PersonCfg personCfg;

@Before
public void before() throws Exception { 
} 

@After
public void after() throws Exception { 
} 

/** 
* 
* Method: 名字隨便起的,不規範。
* 
*/ 
@Test
public void testConfigBeans() throws Exception {
    System.out.printf("Use '@Configuration' autowired beans : %s%n", personCfg);
} 


} 

測試結果:

Use '@Configuration' autowired beans : PersonCfg{name='wuwii', age=23, city='WuHan', contacts=[Contact{phone='18772383543', email='[email protected]'}, Contact{phone='12345678912', email='[email protected]'}], hobby=[騎行, 跑步]}

讀取properties 文件的屬性注入Bean

上面的的方法中除了測試類的方法相同而已,爲了方便其餘都有改動;

首先實體類,通過構造方法傳入值

package com.devframe.util;

import java.util.List;

/**
 * @author Zhang Kai
 * @version 1.0
 * @since <pre>2017/11/2 11:31</pre>
 */
public class PersonCfg {

    private String name;
    private int age;
    private String city;
    private List<Contact> contacts;
    private List hobby;

    public PersonCfg() {
    }


    public PersonCfg(String name, int age, String city, List<Contact> contacts, List hobby) {
        this.name = name;
        this.age = age;
        this.city = city;
        this.contacts = contacts;
        this.hobby = hobby;
    }

    @Override
    public String toString() {
        return "PersonCfg{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", city='" + city + '\'' +
                ", contacts=" + contacts +
                ", hobby=" + hobby +
                '}';
    }

    public List<Contact> getContacts() {
        return contacts;
    }

    public void setContacts(List<Contact> contacts) {
        this.contacts = contacts;
    }

    public List getHobby() {
        return hobby;
    }

    public void setHobby(List hobby) {
        this.hobby = hobby;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

}

class Contact {

    private String phone;

    private String email;

    public Contact(String phone, String email) {
        this.phone = phone;
        this.email = email;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    @Override
    public String toString() {
        return "Contact{" +
                "phone='" + phone + '\'' +
                ", email='" + email + '\'' +
                '}';
    }
}

配置文件

由於properties 文件不能寫 只能寫那些單一屬性,數組和對象需要自己設置規則,去後臺解析出來使用。
創建person.properties 文件:

name=wuwii
age=23
city=WuHan
hobby=football,running
contacts=18772383543,[email protected];12345678912,1075199251@qq.com

創建Bean

通過@Configuration完成spring 初始化,設置@PropertySource,讀取配置文件:

package com.devframe.util;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author Zhang Kai
 * @version 1.0
 * @since <pre>2017/11/2 11:23</pre>
 */
@Configuration
@PropertySource("classpath:person.properties")
public class Configs {
    @Resource
    private Environment env;

    @Bean
    public PersonCfg getPersonFromProp() {
        return new PersonCfg(env.getProperty("name"), Integer.valueOf(env.getProperty("age")),
                env.getProperty("city"), string2contacts(env.getProperty("contacts")), string2list(env.getProperty("hobby")));
    }
    /**
     *  按照預先定義規則的列表字符串 轉換成列表
     * @param s 預先定義規則的列表字符串
     * @return java.util.List
     */
    private List string2list (String s) {
        return StringUtil.isNull(s) ? null : Arrays.asList(s.split(","));
    }

    /**
     *  <p>按照預先定義規則</p>
     *  <p>將配置文件 Contact 列表的字符串 轉換成 列表對象</p>
     * @param s 讀取配置文件 Contact 列表的字符串
     * @return java.util.List<com.devframe.util.Contact>
     */
    private List<Contact> string2contacts (String s) {
        if (StringUtil.isNull(s)) return null;
        List<String> list1 = Arrays.asList(s.split(";"));
        return list1.stream().map(this::contactStr2contact).collect(Collectors.toList());
    }

    /**
     * 按照預定義規則轉換成 contact對象
     * @param contactStr contact類的字符串
     * @return com.devframe.util.Contact
     */
    private Contact contactStr2contact (String contactStr) {
        String[] index = contactStr.split(",");
        // 傳入字段數,自己控制,有點蠢了
        if (index.length != 2) {
            return null;
        }
        return new Contact(index[0], index[1]);
    }
}

測試

最後JUnit4 測試類沒變,重新測試,打印出來結果:

Use '@Configuration' autowired beans : PersonCfg{name='wuwii', age=23, city='WuHan', contacts=[Contact{phone='18772383543', email='[email protected]'}, Contact{phone='12345678912', email='[email protected]'}], hobby=[football, running]}

成功。

補充

20171103 早上來看了下源碼:

    /**
     * Return the property value associated with the given key, or {@code null}
     * if the key cannot be resolved.
     * @param key the property name to resolve
     * @see #getProperty(String, String)
     * @see #getProperty(String, Class)
     * @see #getRequiredProperty(String)
     */
    String getProperty(String key);

    /**
     * Return the property value associated with the given key, or
     * {@code defaultValue} if the key cannot be resolved.
     * @param key the property name to resolve
     * @param defaultValue the default value to return if no value is found
     * @see #getRequiredProperty(String)
     * @see #getProperty(String, Class)
     */
    String getProperty(String key, String defaultValue);

    /**
     * Return the property value associated with the given key, or {@code null}
     * if the key cannot be resolved.
     * @param key the property name to resolve
     * @param targetType the expected type of the property value
     * @see #getRequiredProperty(String, Class)
     */
    <T> T getProperty(String key, Class<T> targetType);

    /**
     * Return the property value associated with the given key, or
     * {@code defaultValue} if the key cannot be resolved.
     * @param key the property name to resolve
     * @param targetType the expected type of the property value
     * @param defaultValue the default value to return if no value is found
     * @see #getRequiredProperty(String, Class)
     */
    <T> T getProperty(String key, Class<T> targetType, T defaultValue);

PropertyResolver接口中發現:

<T> T getProperty(String key, Class<T> targetType);

這個方法可以直接讀取文件內容轉換成我們的需要類型,雖然說很好,調試了半天代碼不知道properties文件怎麼寫對象來讓它轉換,這個以後再看,list列表很好轉,將上面的方法加載hobby屬性改成這個:

env.getProperty(("age"), Integer.class)
env.getProperty(("hobby"), List.class)

person文件中hobby屬性爲:

hobby=running,football

執行結果:

Use '@Configuration' autowired beans : PersonCfg{name='wuwii', age=23, city='WuHan', contacts=[Contact{phone='18772383543', email='[email protected]'}, Contact{phone='12345678912', email='[email protected]'}], hobby=[running, football]}

沒問題

直接使用@Value佔位符注入

方法一

使用@Component 方式注入,需要再applicationContext.xml中引入properties文件:

    <!-- 參數佔位符 -->
    <bean
        class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
        lazy-init="true">
        <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
        <property name="ignoreResourceNotFound" value="false" />
        <property name="locations">
            <list>
                <value>classpath:spring/database.properties</value>
                <value>classpath:person.properties</value>
            </list>
        </property>
    </bean>

改下實體類,直接在屬性上注入@Value,佔位符符號${ }

package com.devframe.util;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * @author Zhang Kai
 * @version 1.0
 * @since <pre>2017/11/2 11:31</pre>
 */
@Component
public class PersonCfg {

    @Value("${name}")
    private String name;
    @Value("${age}")
    private int age;
    @Value("${city}")
    private String city;
    //這個不會,對象屬性不會寫
    //@Value("${contacts1}")
    private List<Contact> contacts;
    @Value("${hobby}")
    private List hobby;

//省略代碼

}

測試結果:

Use '@Configuration' autowired beans : PersonCfg{name='wuwii', age=23, city='WuHan', contacts=null, hobby=[running,football]}

發現數組列表也能直接注入。

方法二

在配置類中設置引入配置文件,還需引入佔位符,等價於XML中的<context:property-placeholder/>配置。

@Configuration
@PropertySource("classpath:person.properties")
public class Configs {
    private static final Logger LOGGER = LoggerFactory.getLogger(Configs.class);
    @Autowired
    private Environment env;

    @Bean
    public PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
        return new PropertySourcesPlaceholderConfigurer();
    }
    //省略
}

就可以在類中的屬性上使用@Value佔位符 注入了。

總結

  1. 還可以讀取xml文件進行裝配,當然也不使用配置文件,直接在Beans的@Value註解上寫出需要註解的值,但是那樣後期部署修改起來麻煩。
  2. 常用的應該時這麼兩個 比較好,properties 可能用的多點吧;因爲平時使用這個外部需要修改的參數 的基本都是一些常量,不會存在這麼多轉換,這個只是我的測試的代碼,所以有一些鬼轉換。
  3. 還有我使用properties 中爲什麼沒使用中文,因爲亂碼了。尷尬。這是需要注意的地方,因爲電腦默認編碼是gbk,但是讀的時候,又沒有設置編碼。解決辦法:在讀取properties文件的工具類上,加上指定編碼格式utf-8:
URL url = PropertyUtil.class.getResource("/config.properties");
FileInputStream in = new FileInputStream(url.getPath());
//這段代碼不是 以前的  PROP.load(in);
PROP.load(new InputStreamReader(in, "utf-8"));
in.close();
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章