前面介紹了spring環境的搭建,在搭建spring環境的時候分為java環境和javaWeb環境,在javaWeb環境下通常會結合springMVC使用,在java項目中只需要把spring的包導入工程中,一般初學者會把所有的包全部導入,然後就可以通過獲得applicationContext,把類的實例化交給spring管理,然後從spring容器中獲得類的實例。
spring中有控制反轉(Ioc)和依賴注入(DI)兩個概念,Ioc和DI是spring的核心概念,同時也是一種新的編程思想。
控制反轉(Ioc)
控制反轉,顧名思義就是控制權的改變,在沒有接觸spring之前,我們要使用一個類的實例,必須使用new的方式生成一個對象,這個過程的主動權掌握在程序員亦自己寫的程序中,但是使用了spring之後,實例的創建不再由程序員手動實現,而是由spring容器來完成,實現了控制的反轉,即主動權交給了spring的IOC容器。
依賴注入(DI)
一個類包括屬性和方法,在創建了實例對象之後,或者在創建實例對象的同時,需要初始化成員變量(屬性),在spring之前成員變量的初始化可以通過構造方法或setXXX方法;在spring出現之後,由於實例的創建交給了spring的IOC容器,那麼成員變量的初始化也依賴於IOC容器,由容器去注入成員變量的值。
上面介紹了IOC和DI兩個概念,可以得出IOC和DI其實是在做一件事,就是spring的IOC容器創建實例對象,一切對象的實例化都交給了spring容器,在程序中不必手動使用new的方式實例化變量。
spring提供兩種不同的方式來配置spring,一種是配置文件(XML),另一種是基於注解(Annotation)。下面針對IOC和DI使用兩種方式一一做介紹
在介紹之前必須了解如何在java項目中獲得spring容器,也即ApplicationContext。
ClassPathXmlApplicationContext cpac=new ClassPathXmlApplicationContext("classpath:applications.xml");
獲取ApplicationContext的方式有三種,這裡使用的ClassPathXmlApplicationContext,ClassPathApplicationContext是ApplicationContext的子類,這裡的cpac就是spring的IOC容器,通過它的getBean()方法可以獲得容器中已經初始化的實例。
配置文件(XML)
假如我們有下面的學生類(student)
package com.cn.study.day1; public class Student { private String id; private String name; private String age; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAge() { return age; } public void setAge(String age) { this.age = age; } @Override public String toString() { return "Student [id=" + id + ", name=" + name + ", age=" + age + "]"; } //在有了有參的構造函數,必須要提供一個無參構造 public Student(String id, String name) { this.id = id; this.name = name; } //無參構造 public Student() { } public void init(){ System.out.println("我正在初始化!"); } public void destroy(){ System.out.println("我將要被銷毀了,。再見!"); } }
此類有三個屬性id、name、age,下面通過配置文件的方式,在spring的配置文件中配置,
<bean id="student" class="com.cn.study.day1.Student"> <property name="id" value="1"></property> <property name="name" value="測試"></property> <property name="age" value="23"></property> </bean>
在spring的配置文件中配置<bean>標簽,id屬性在配置文件中必須唯一,class屬性指定類的權限類名,下面有<property>標簽,指定Student的三個屬性,且指定了屬性值,測試結果如下,
Student [id=1, name=測試, age=23]
這裡可以看到輸出了配置的值,這種方式是使用屬性注入的方式,要求必須有setXXX方法,還有另外一種方式,通過構造方法的方式注入,
<!--構造方法注入--> <bean id="student2" class="com.cn.study.day1.Student" init-method="init" destroy-method="destroy"> <constructor-arg name="id" value="2"></constructor-arg> <constructor-arg name="name" value="test2"></constructor-arg> </bean>
既然是通過構造方法的方式注入,那麼在Student類中必須要有這個構造方法,在上面的Student類中已經有了有參的構造方法,我們知道在自定義了有參的構造方法之後,系統不會再默認的提供無參構造,我們還必須添加無參構造,下面看測試結果,
Student [id=2, name=test2, age=null]
由於我們只注入了id、name兩個屬性,這裡可以打印出,age由於未賦值且其類型為String,則默認為null。
上面是通過配置文件的方式,配置了根據屬性、構造方法注入的方式,這個例子是比較簡單的,下面看一個復雜的例子,在程序中經常會采用分層、面向接口編程的思緒,
DAO接口
package com.cn.study.day1.inter; public interface dbDAO { //一個簡單的dao層的保存接口 public void save(String str); }
service接口
package com.cn.study.day1.inter; public interface ServiceInter { /** * service層的保存方法 */ public void save(String str); }
DAO實現類
package com.cn.study.day1.inter.impl; import com.cn.study.day1.inter.dbDAO; public class DbDAOImpl implements dbDAO { @Override public void save(String str) { // TODO Auto-generated method stub System.out.println(str+",我會執行數據插入操作!你會嗎?"); } }
service實現類
package com.cn.study.day1.inter.impl; import com.cn.study.day1.inter.ServiceInter; import com.cn.study.day1.inter.dbDAO; public class ServiceImpl implements ServiceInter { //DAO接口對象 private dbDAO dbDAO; //setXXX方法 public void setDbDAO(dbDAO dbDAO) { this.dbDAO = dbDAO; } @Override public void save(String str) { // TODO Auto-generated method stub if(str!=null&&!"".equals(str)){ dbDAO.save(str); System.out.println("執行完了DAO層的方法,我是service層的方法!"); } } }
在service的實現類中有一個DAO層的接口對象,我們下面看這種是如何根據屬性注入的,
<!--下面是一個復雜的屬性注入的例子--> <bean id="dbDao" class="com.cn.study.day1.inter.impl.DbDAOImpl"></bean> <!--下面的這個類,有一個dbDAO的屬性,需要進行屬性注入,必須提供一個set方法--> <bean id="service" class="com.cn.study.day1.inter.impl.ServiceImpl"> <property name="dbDAO" ref="dbDao"></property> </bean>
首先,配置DAO層的bean,這裡的class屬性必須配置其實現類,然後配置service的實現類,在ServiceImpl中有一個dbDAO類型的屬性,使用ref屬性引用上面的bean。測試結果如下,
我是測試方法,我會執行數據插入操作!你會嗎? 執行完了DAO層的方法,我是service層的方法!
ref屬性可以引用spring配置文件中的bean,使用id的值。
通過上面的介紹,可以知道使用配置文件這種方式可以很方便的配置,但是當需要配置的類很多,且依賴很多的情況下,這種方式會很繁瑣,那麼另外一種方式便派上了用場。
注解(Annotation)
spring提供@Component、@Repository、@Controller、@Service四種注解,其實,只使用@Component一個便可以做到,@Repository、@Controller、@Service只是針對不同的層設置的,可以更加明顯,@Repository對應DAO層,@Controller對應控制層,@Service對應服務層,使用這三個注解可以明顯的分層,使系統容易理解,也可以只使用@Component。
使用了注解需要開啟組件自動掃描機制,在spring的配置文件中開啟組件自動掃描機制,需要context命名空間的支持,下面是一個配置文件的例子,
<?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:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd" > <!--spring 自動檢測--> <context:component-scan base-package="com.cn.study.day1" /> </beans>
在spring配置文件的頭部添加context命名空間的支持,然後使用<context:component-scan base-package="">標簽開啟組件掃描,base-package屬性指定掃描的基包,此包及此包下的子包都會進行掃描。開啟了組件掃描之後,還需要自動注入,網上有說需要:<context:annotation-config/>,其實前面的組件掃描已經包含了此標簽的作用,為此不需要配置此標簽了,自動注入默認根據類型(byType)進行注入,還有根據名字注入(byName)即bean中id的值,還有根據構造方法注入(constructor)。自動注入需要@Aotuwired注解,此注解可以放在屬性上,也可以放在setXXX方法上,放在屬性上則可以省略setXXX方法,下面是具體的例子
package com.cn.study.day1; import org.springframework.stereotype.Component; @Component("su") public class StudentAnnotation { public void print(){ System.out.println("我是使用注解生成的類!"); } }
在類上使用@Component注解,且指定了實例的名為su,如果不指定則默認為類名稱首字母小寫,即studentAnnotation。
下面是一個自動掃描和自動注入的例子,
package com.cn.study.day1.inter.impl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.cn.study.day1.inter.ServiceInter; import com.cn.study.day1.inter.dbDAO; @Service public class ServiceImpl implements ServiceInter { //DAO接口對象 @Autowired private dbDAO dbDAO; //setXXX方法 public void setDbDAO(dbDAO dbDAO) { this.dbDAO = dbDAO; } @Override public void save(String str) { // TODO Auto-generated method stub if(str!=null&&!"".equals(str)){ dbDAO.save(str); System.out.println("執行完了DAO層的方法,我是service層的方法!"); } } }
類上使用了@Service注解,屬性上使用了@Autowired注解,這時setDbDAO()方法可以不要。@Autowired注解就是為了省略setXXX方法的,@Autowired注解默認使用的按照類型注入,如果存在多個相同類型的實例,這裡自動注入會失敗,為了可以正確注入,引入另外一個注解@Qualifier(value=""),此注解的value屬性可以指定一個bean的id值,做到自動注入。
@Qualifier可以用在屬性或者setXXX方法上。
綜上,通過配置文件和注解兩種方式介紹了依賴注入。
有不正之處歡迎指出,謝謝。