Spring依賴注入
常的java開發中,程序員在某個類中需要依賴其它類的方法,則通常是new一個依賴類再調用類實例的方法,這種開發存在的問題是new的類實例不好統一管理,spring提出了依賴注入的思想,即依賴類不由程序員實例化,而是通過spring容器幫我們new指定實例並且將實例注入到需要該對象的類中。依賴注入的另一種說法是“控制反轉”,通俗的理解是:平常我們new一個實例,這個實例的控制權是我們程序員,而控制反轉是指new實例工作不由我們程序員來做而是交給spring容器來做。
構造函數注入
在bean標籤的內部使用constructor-arg標籤就可以進行構造函數注入了。
constructor-arg標籤的屬性:
- type:用於指定要注入的數據的數據類型,該數據類型也是構造函數中某個或某些參數的類型
- index:用於指定要注入的數據給構造函數中指定索引位置的參數賦值,索引的位置從0開始
- name:用於給指定構造函數中指定名稱的參數賦值
- value:用於提供基本類型和String類型的數據
- ref:用於指定其他的bean類型數據,就是在IOC容器中出現過的bean對象
bean.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="accountService" class="com.sks.service.imp.AccountServiceImpl">
<constructor-arg type="java.lang.String" value="張三"/>
<constructor-arg index="1" value="20"/>
<constructor-arg name="birthday" ref="birthday"/>
</bean>
<bean id="birthday" class="java.util.Date"/>
</beans>
AccountServiceImpl 類
public class AccountServiceImpl implements AccountService {
private String name;
private Integer age;
private Date birthday;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "AccountServiceImpl{" +
"name='" + name + '\'' +
", age=" + age +
", birthday=" + birthday +
'}';
}
public AccountServiceImpl(String name, Integer age, Date birthday) {
System.out.println("含參的構造方法被調用了");
this.name = name;
this.age = age;
this.birthday = birthday;
}
public AccountServiceImpl() {
System.out.println("構造方法調用");
}
@Override
public int addMoney(int money) {
System.out.println("向賬戶中加錢:" + money);
return 0;
}
@Override
public void saveAccount(Account account) {
System.out.println("saveAccount方法執行了");
}
}
測試
/**
* 測試構造函數注入
*/
@Test
public void test8() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:bean.xml");;
AccountService accountService = (AccountService) applicationContext.getBean("accountService");
System.out.println(accountService.toString());
}
運行測試以後,可以在控制檯看到以下內容:
優點:在獲取bean對象時,注入數據是必須的操作,否則對象無法創建成功。
缺點:改變了bean對象的實例化方式,使我們在創建對象時,如果用不到這些數據也必須提供。
setter方法注入
在bean標籤內部使用property標籤進行配置。
property標籤的屬性:
- name:用於指定注入時所調用的set方法名稱
- value:用於提供基本類型和String類型的數據
- ref:用於指定其他的bean類型數據
這裏面我們注入了基本類型、包裝類型、日期類型數據。
AccountServiceImpl 類
public class AccountServiceImpl implements AccountService {
private String name;
private Integer age;
private Date birthday;
public String getName() {
return name;
}
public void setName(String name) {
System.out.println("給name設置值");
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
System.out.println("給age設置值");
this.age = age;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
System.out.println("給birthday設置值");
this.birthday = birthday;
}
@Override
public String toString() {
return "AccountServiceImpl{" +
"name='" + name + '\'' +
", age=" + age +
", birthday=" + birthday +
'}';
}
public AccountServiceImpl(String name, Integer age, Date birthday) {
System.out.println("含參的構造方法被調用了");
this.name = name;
this.age = age;
this.birthday = birthday;
}
public AccountServiceImpl() {
System.out.println("構造方法調用");
}
@Override
public int addMoney(int money) {
System.out.println("向賬戶中加錢:" + money);
return 0;
}
@Override
public void saveAccount(Account account) {
System.out.println("saveAccount方法執行了");
}
}
bean.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="accountService" class="com.sks.service.imp.AccountServiceImpl">
<!--注入基本類型、包裝類型、日期類型數據-->
<property name="age" value="22"/>
<property name="name" value="李四"/>
<property name="birthday" ref="birthday"/>
</bean>
<bean id="birthday" class="java.util.Date"/>
</beans>
測試
/**
* 測試setter方法注入
*/
@Test
public void test9() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:bean.xml");;
AccountService accountService = (AccountService) applicationContext.getBean("accountService");
System.out.println(accountService.toString());
}
運行測試以後,可以在控制檯看到以下內容:
優勢:創建對象時沒有明確的限制,可以直接使用默認構造函數。
缺點:如果又某個成員必須有值,則獲取對象有可能是set方法沒有執行。
對集合類型數據進行注入
AccountService2Impl 類
public class AccountService2Impl implements AccountService2 {
private String[] myStrs;
private List<String> myList;
private Set<String> mySet;
private Map<String, String> myMap;
private Properties myProps;
public String[] getMyStrs() {
return myStrs;
}
public void setMyStrs(String[] myStrs) {
this.myStrs = myStrs;
}
public List<String> getMyList() {
return myList;
}
public void setMyList(List<String> myList) {
this.myList = myList;
}
public Set<String> getMySet() {
return mySet;
}
public void setMySet(Set<String> mySet) {
this.mySet = mySet;
}
public Map<String, String> getMyMap() {
return myMap;
}
public void setMyMap(Map<String, String> myMap) {
this.myMap = myMap;
}
public Properties getMyProps() {
return myProps;
}
public void setMyProps(Properties myProps) {
this.myProps = myProps;
}
@Override
public String toString() {
return "AccountService2Impl{" +
"myStrs=" + Arrays.toString(myStrs) +
", myList=" + myList +
", mySet=" + mySet +
", myMap=" + myMap +
", myProps=" + myProps +
'}';
}
}
bean.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="accountService2" class="com.sks.service.imp.AccountService2Impl">
<property name="myStrs">
<array>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</array>
</property>
<property name="myList">
<list>
<value>list1</value>
<value>list2</value>
<value>list3</value>
</list>
</property>
<property name="mySet">
<set>
<value>set1</value>
<value>set2</value>
<value>set3</value>
</set>
</property>
<property name="myProps">
<props>
<prop key="name">柯森</prop>
<prop key="age">23</prop>
</props>
</property>
<property name="myMap">
<map>
<entry key="key1" value="value1"/>
<entry key="key2" value="value2"/>
<entry key="key3">
<value>value3</value>
</entry>
</map>
</property>
</bean>
</beans>
測試
/**
* 測試注入複雜類型/集合數據
*/
@Test
public void test10() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:bean.xml");
AccountService2 accountService2 = (AccountService2) applicationContext.getBean("accountService2");
System.out.println(accountService2.toString());
}
運行測試以後,可以看到在控制檯打印輸出了以下內容:
這說明我們注入集合類型數據成功了。
註解注入
用於注入數據的註解
bean.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--創建bean時要掃描的包-->
<context:component-scan base-package="com.sks"/>
</beans>
AccountService4Impl 類
@Component
public class AccountService4Impl implements AccountService3 {
@Autowired
private AccountDao accountDao;
@Override
public void addMoney(int money) {
System.out.println("向賬戶中加錢....AccountService3Impl");
}
}
假設此時只有一個AccountDao的實現類,並且這個類也加上了@Repository註解,那麼我們這樣注入是可以成功的,但是如果容器中存在多個AccountDao的實現類,此時僅僅使用AccountDao是不能完成數據注入的,需要配合@Qualifier註解使用注入數據。
假設現有如下兩個實現類,那我們應該怎麼寫才能成功注入數據?
@Component
public class AccountService4Impl implements AccountService3 {
//錯誤寫法,默認會去容器中查找名稱爲accountDao的bean
//@Autowired
//private AccountDao accountDao;
//正確寫法
//@Autowired
//private AccountDao accountDao1
//正確寫法
//@Autowired
//private AccountDao accountDao1;
//正確寫法
@Autowired
@Qualifier("accountDao1")
private AccountDao accountDao;
@Override
public void addMoney(int money) {
System.out.println("向賬戶中加錢....AccountService3Impl");
}
}
測試
@Test
public void test2() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:bean.xml");
AccountService4Impl accountService4 = (AccountService4Impl) applicationContext.getBean("accountService4Impl");
System.out.println("accountService4:" + accountService4);
}
@Value註解的基本使用
在使用@Value注入基本類型和String類型的數據時使用"#“號;使用@Value讀取配置文件的值時需要使用”$"符號,同時使用@PropertySource註解指定配置文件的位置。
@Component
@PropertySource("classpath:db.properties")
public class AccountService4Impl implements AccountService3 {
@Autowired
@Qualifier("accountDao1")
private AccountDao accountDao;
//使用SPEL表達式只注入值
@Value("#{19 - 9}")
private int age;
@Value("zhangsan")
private String name;
//讀取操作系統的名稱
@Value("#{systemProperties['os.name']}")
private String osname;
//讀取數據庫配置文件中的值
@Value("${password}")
private String password;
@Override
public void addMoney(int money) {
System.out.println("向賬戶中加錢....AccountService3Impl");
}
}
測試
@Test
public void test2() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:bean.xml");
AccountService4Impl accountService4 = (AccountService4Impl) applicationContext.getBean("accountService4Impl");
System.out.println("accountService4:" + accountService4 + " " + accountService4.getName() + " " + accountService4.getAge());
}
斷點調試可以看到如下結果,說明我們使用@Value注入數據成功。
最後
歡迎關注公衆號:前程有光,領取一線大廠Java面試題總結+各知識點學習思維導+一份300頁pdf文檔的Java核心知識點總結!