Spring入門 --- IOC
配置環境
我們在開發中,會寫很多的類,而有些類之間不可避免的產生依賴關係,這種依賴關係稱之為耦合。有些依賴關係是必須的,有些依賴關係可以通過優化程式碼來解除的。請看下面的示例程式碼:
1 建立maven工程;
2 匯入必要的依賴
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</ dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId >
</dependency>
</dependencies>
2.1 引入log4j的配置檔案
### direct log messages to stdout ### log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.Target=System.out log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n ### direct messages to file mylog.log ### log4j.appender.file=org.apache.log4j.FileAppender log4j.appender.file.File=c:/mylog.log log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n ### set log levels - for more verbose logging change 'info' to 'debug' ### log4j.rootLogger=debug, stdout
3 建立UserDao介面
public interface UserDao {
void save();
}
4 建立UserDao介面的實現類UserDaoImpl
public class UserDaoImpl implements UserDao {
@Override
public void save() {
System.out.println("持久層:使用者儲存...");
}
}
5 建立 applicationContext.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
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userDao" class="cn.cosco.impl.UserDaoImpl"></bean>
</beans>
6 建立測試類
@Test
public void test1(){
//建立Spring工廠(建立IOC容器)
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");//1
UserDao userDao = (UserDao) ac.getBean("userDao");//2
userDao.save();
}
測試結果:
控制檯輸出如下結果:
持久層:使用者儲存...
測試發現:我們可以從spring容器中獲取物件,但是,這個物件是如何創建出來的呢?
這其實是利用了 Spring IOC 方法。
Spring IOC
1 什麼是 Spring IOC
IOC,它是Inverse of Control的縮寫,中文含義是控制反轉,表示將物件的建立權力反轉給Spring框架!!
IOC解決的問題:使用IOC可以解決的程式耦合性高的問題!!
2 Spring中的工廠
在spring中提供了兩個工廠介面:
1、ApplicationContext
2、BeanFactory
2.1 ApplicationContext介面
使用該介面可以獲取到具體的Bean物件
該介面下有兩個具體的實現類
ClassPathXmlApplicationContext – 載入類路徑下的Spring配置檔案 FileSystemXmlApplicationContext – 載入本地磁碟下的Spring配置檔案
下面演示FileSystemXmlApplicationContext的用法:把src下的applicationContext.xml拷貝到你電腦的某個目錄,例如:c:/spring,可以通過FileSystemXmlApplicationContext載入本地磁碟下的spring配置檔案
public class TestIOC {
@Test
public void test1(){
//建立Spring工廠(建立IOC容器)
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = (UserDao) ac.getBean("userDao");
userDao.save();
}
@Test
public void test2(){
//建立Spring工廠(建立IOC容器)
ApplicationContext ac = new FileSystemXmlApplicationContext("C:/spring/applicationContext.xml");
UserDao userDao = (UserDao) ac.getBean("userDao");
userDao.save();
}
}
2.2 BeanFactory工廠 ---- 已過時
BeanFactory是Spring框架早期的建立Bean物件的工廠介面。在TestIOC中建立test3測試方法:
/**
* 早期版本的spring工廠:BeanFactory
*/
@Test
public void test3() {
//此方法已經過時
BeanFactory factory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
UserDao userDao = (UserDao) factory.getBean("userDao");
userDao.save();
}
2.3 BeanFactory和ApplicationContext的區別 :
BeanFactory – BeanFactory採取延遲載入,第一次getBean時才會初始化Bean
ApplicationContext – 在載入applicationContext.xml時候就會建立具體的Bean物件的例項
修改UserDaoImpl,增加一個無參構造方法,執行test1和test3兩個單元測試方法,驗證ApplicationContext和BeanFactory這兩個工廠到底什麼時候建立bean物件??
public class UserDaoImpl implements UserDao {
public UserDaoImpl() {
System.out.println("呼叫了無參構造方法...");
}
@Override
public void save() {
System.out.println("持久層:使用者儲存...");
}
}
修改test1單元測試方法:
/**
* 建立ioc容器時就已經把物件建立好了
*/
@Test
public void test1(){
//建立Spring工廠(建立IOC容器)
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
System.out.println("===============");
UserDao userDao = (UserDao) ac.getBean("userDao");
userDao.save();
}
執行結果:
呼叫了無參構造方法...
===============
執行test1方法,發現:ApplicationContext是在建立ioc容器時就已經把物件建立好了
修改test3單元測試方法:
/**
* 早期版本的spring工廠:BeanFactory
* 建立ioc容器時並沒有建立物件,而是在第一次getBean時再建立物件
*/
@Test
public void test3() {
BeanFactory factory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
System.out.println("===============");
UserDao userDao = (UserDao) factory.getBean("userDao");
userDao.save();
}
執行結果:
===============
呼叫了無參構造方法...
執行test3方法,發現:BeanFactory是建立ioc容器時並沒有建立物件,而是在第一次getBean時再建立物件
2.4 Spring配置檔案 解析 ----- applicationContext.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
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userDao" class="cn.cosco.impl.UserDaoImpl"></bean>
</beans>
2.4.1 id屬性
id屬性是bean的唯一標識
2.4.2 class屬 性
javaBean 的全路徑名
其實,除了 id 屬性和 class 屬性外,還可以配置如下屬性:
2.4.3 scope屬性
scope屬性代表Bean的作用範圍
singleton: 單例(預設值)
prototype: 多例,在Spring框架整合Struts2框架的時候,Action 類也需要交給 Spring 做管理,配置把 Action 類配置成多例!!
request: 應用在 web 工程中,將建立的物件存入到 request 域中。
session: 應用在 web 工程中,將建立的物件存入到 session 域中。
globalsession: 應用在 porlet 環境下使用。將建立的物件存入到全域性的 session 中。
後三種比較少見。
在TestIOC中建立單元測試方法test4:
@Test
public void test4() {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao1 = (UserDao) ac.getBean("userDao");
UserDao userDao2 = (UserDao) ac.getBean("userDao");
System.out.println(userDao1==userDao2);
}
執行結果:
呼叫了無參構造方法...
true
測試發現:當scope為 singleton 時,無論例項化幾次 javaBean 物件,都只會建立一次。
修改 applicationContext.xml,把 UserDaoImpl 的作用域改為 prototype:
<?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
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userDao" class="cn.cosco.dao.impl.UserDaoImpl" scope="prototype"></bean>
</beans>
執行結果:
呼叫了無參構造方法...
呼叫了無參構造方法...
false
測試發現:當scope為prototype時,每次獲取bean,都會重新例項化
2.4.4 init-method屬性
當bean被載入到容器的時候呼叫init-method屬性指定的方法
修改 UserDaoImpl ,在其中提供 init 方法
public class UserDaoImpl implements UserDao {
public UserDaoImpl() {
System.out.println("呼叫了無參構造方法...");
}
public void init(){
System.out.println("呼叫了init方法...");
}
@Override
public void save() {
System.out.println("持久層:使用者儲存...");
}
}
修改 applicationContext.xml,為 UserDaoImpl 指定初始化方法
<?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
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userDao" class="cn.cosco.dao.impl.UserDaoImpl" init-method="init"></bean>
</beans>
執行 test1 單元測試方法,測試結果:
呼叫了無參構造方法...
呼叫了init方法...
===============
持久層:使用者儲存...
2.4.5 destroy-method屬性
當bean從容器中刪除的時候呼叫destroy-method屬性指定的方法
想檢視destroy-method的效果,有如下條件:
scope= singleton 有效
web容器中會自動呼叫,但是 main 函式或測試用例需要手動呼叫(需要使用 ClassPathXmlApplicationContext 的close() 方法)
在applicationContext.xml中為UserDaoImpl指定銷燬方法
<?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
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userDao" class="cn.cosco.dao.impl.UserDaoImpl" init-method="init" destroy-method="destroy"></bean>
</beans>
修改UserDaoImpl,在其中提供destroy方法
public class UserDaoImpl implements UserDao {
public UserDaoImpl() {
System.out.println("呼叫了無參構造方法...");
}
public void init(){
System.out.println("呼叫了init方法...");
}
@Override
public void save() {
System.out.println("持久層:使用者儲存...");
}
public void destroy(){
System.out.println("呼叫了銷燬方法...");
}
}
修改test1單元測試方法,顯示關閉ioc容器。測試結果如下:
呼叫了無參構造方法...
呼叫了init方法...
===============
持久層:使用者儲存...
呼叫了銷燬方法...
3 Spring 生成bean的三種方式
3.1 無參構造方法
預設呼叫無參構造方法例項化bean。在此之前,都是呼叫無參構造來例項化的。
3.2 靜態工廠例項化方式
通過呼叫工廠類的靜態方法來生成bean
編寫DeptDao介面:
public Interface DeptDaoImpl {
public void save();
}
編寫DeptDaoImpl實現類:
public class DeptDaoImpl implements DeptDao{
@Override
public void save() {
System.out.println("持久層:部門儲存...");
}
}
編寫工廠類,在其中建立靜態工廠方法
public class Factory {
/**
* 靜態工廠方法
*/
public static DeptDao create(){
System.out.println("呼叫了靜態工廠方法");
return new DeptDaoImpl();
}
}
編寫applicationContext.xml配置檔案,採用靜態工廠方式配置DeptDaoImpl類
<?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
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userDao" class="cn.cosco.dao.impl.UserDaoImpl" init-method="init" destroy-method="destroy"></bean>
<bean id="deptDao" class="cn.cosco.factory.Factory" factory-method="create"></bean>
</beans>
在配置DeptDaoImpl這個bean時,class屬性寫的不是DeptDaoImpl的全路徑名,而是工廠類的全路徑名;
factory-method:指定工廠類中靜態方法的名字
在TestIOC類中編寫測試方法test5:
@Test
public void test5(){
//建立Spring工廠(建立IOC容器)
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
DeptDao deptDao = (DeptDao) ac.getBean("deptDao");
deptDao.save();
}
測試結果如下:
呼叫了靜態工廠方法
持久層:部門儲存...
3.3 例項工廠例項化方式
修改Factory工廠類,建立例項工廠方法:
public class Factory {
/**
* 靜態工廠方法
*/
// public static DeptDao create(){
// System.out.println("呼叫了靜態工廠方法");
// return new DeptDaoImpl();
// }
/**
* 例項工廠方法
* @return
*/
public DeptDao create(){
System.out.println("呼叫了例項工廠方法");
return new DeptDaoImpl();
}
}
編寫applicationContext.xml,採用例項工廠方式重寫配置DeptDaoImpl
<?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
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userDao" class="cn.cosco.dao.impl.UserDaoImpl" init-method="init" destroy-method="destroy"></bean>
<!-- 例項工廠方法來例項化 -->
<bean id="factory" class="cn.cosco.factory.Factory"></bean>
<bean id="deptDao" factory-bean="factory" factory-method="create"></bean>
</beans>
factory-bean:指定工廠bean的id;
Factory-method:指定工廠bean的例項工廠方法的名字
執行test5測試方法,測試結果如下:
呼叫了例項工廠方法
持久層:部門儲存...
4 注入依賴
4.1 什麼是注入依賴
IOC和DI的概念: * IOC – Inverse of Control,控制反轉,將物件的建立權反轉給Spring!! * DI – Dependency Injection,依賴注入,在Spring框架負責建立Bean物件時,動態的將依賴物件注入到Bean元件中!!
如果UserServiceImpl的實現類中有一個屬性,那麼使用Spring框架的IOC功能時,可以通過依賴注入把該屬性的值傳入進來!!
4.2 構造方法注入
什麼是構造方法注入?構造方法注入就是利用bean的構造方法完成對bean中屬性的賦值。
建立Car實體類,提供有參構造方法
public class Car implements Serializable{
private String name;
private Double price;
public Car(String name, Double price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
@Override
public String toString() {
return "Car{" +
"name='" + name + '\'' +
", price=" + price +
'}';
}
}
建立applicationContext2.xml,在applicationContext2.xml配置Car這個bean
<?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
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 構造方法注入 -->
<bean id="car" class="cn.cosco.domain.Car">
<constructor-arg name="name" value="蘭博基尼"></constructor-arg>
<constructor-arg name="price" value="3000000.0"></constructor-arg>
</bean>
</beans>
建立單元測試類TestDI,在其中建立單元測試方法test1測試,注意此處建立IOC容器時,應該載入applicationContext2.xml
public class TestDI {
@Test
public void test1(){
//建立Spring工廠(建立IOC容器)
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext2.xml");
Car car = (Car) ac.getBean("car");
System.out.println(car);
}
}
執行結果:
Car{name='蘭博基尼', price='3000000.0'}
4.3 set方法注入
什麼是set方法注入?set方法注入就是利用bean中屬性的set方法對屬性賦值。
建立People實體類,提供屬性的set方法,不需要提供有參構造方法。
public class People implements Serializable{
private String name;//要提供屬性所對應的set方法
private String address;
private Car car;//物件屬性
public void setName(String name) {
this.name = name;
}
public void