在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佔位符 注入了。
總結
- 還可以讀取xml文件進行裝配,當然也不使用配置文件,直接在Beans的
@Value
註解上寫出需要註解的值,但是那樣後期部署修改起來麻煩。 - 常用的應該時這麼兩個 比較好,properties 可能用的多點吧;因爲平時使用這個外部需要修改的參數 的基本都是一些常量,不會存在這麼多轉換,這個只是我的測試的代碼,所以有一些鬼轉換。
- 還有我使用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();