一、定義:Spring 是一個開源的控制反轉(Inversion of Control,IoC/DI)和面向切面(AOP)的容器框架,它的主要目的是簡化企業開發
二、實例化Spring容器:
方法一:在類路徑下尋找配置文件來實例化容器
ApplicationContext ctx =
new ClassPathXmlApplicationContext(new String[]{“beans.xml”});
方法二:在文件系統路徑下尋找配置文件來實例化容器
ApplicationContext ctx =
new FileSystemXmlApplicationContext(new String[]{“d:""beans.xml”});
注:文件可指定多個
可建立單元測試來測試Spring容器的初始化
三、編寫Spring 配置文件時,不能出現幫助信息的解決辦法—添加schema文件
由于Spring的schema文件位于網絡上,如果機器不能連接到網絡,那么在寫配置文件的時候就無法出現提示信息,解決辦法有兩種:
1. 讓機器上網,Eclipse會自動從網絡上下載schema文件并緩存在硬盤上
2.手動添加schema文件,方法如下:
windows->preferences->myeclipse->files and editors->xml->xmlcatalog
a. 我們可以選擇將schema文件放到User Specified Entries然后點擊‘add’,在出現的窗口中的Key Type中選擇URL, 在‘Location’中選擇‘File System’,然后在Spring解壓目錄的dist/resources目錄中選擇spring-beans-2.5.xsd,
b. 回到設置窗口時候不要急于關閉窗口,應把窗口中的Key Type改為schema location,
c. key改為:
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
四、Spring 配置文件一個小插曲
<bean id=”” name=””></bean>,在這里,id 和name都是為這個bean取的名字,其實這兩者意思相同,只是如果選擇使用的是id,那么id命名則不能有特殊字符,如果使用的是name則可以使用特殊字符作為name。如
<bean id=”beanId” /> && <bean name=”bean-name”/>
五、Spring容器如何管理bean
首先讀取beans.xml里的bean配置所有的id、class的值,作為String形式保存至對應一個Bean,形成bean列表
利用反射機制,將bean列表遍歷,生成對象,如下:
package junit.test;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.XPath;
import org.dom4j.io.SAXReader;
/**
*傳智傳客版容器
*
*/
publicclass ItcastClassPathXMLApplicationContext {
private List<BeanDefinition> beanDefines = new ArrayList<BeanDefinition>();
private Map<String, Object> sigletons = new HashMap<String, Object>();
public ItcastClassPathXMLApplicationContext(String filename){
this.readXML(filename);
this.instanceBeans();
}
/**
*完成bean的實例化
*/
privatevoid instanceBeans() {
for(BeanDefinition beanDefinition : beanDefines){
try {
if(beanDefinition.getClassName()!=null && !"".equals(beanDefinition.getClassName().trim()))
sigletons.put(beanDefinition.getId(), Class.forName(beanDefinition.getClassName()).newInstance());
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
*讀取xml配置文件
*@paramfilename
*/
privatevoid readXML(String filename) {
SAXReader saxReader = new SAXReader();
Document document = null;
try {
URL xmlpath = this.getClass().getClassLoader().getResource(filename);
document = saxReader.read(xmlpath);
Map<String, String> nsMap = new HashMap<String, String>();
nsMap.put("ns", "http://www.springframework.org/schema/beans");// 加入命名空間
XPath xsub = document.createXPath("http://ns:beans/ns:bean");// 創建beans/bean查詢路徑
xsub.setNamespaceURIs(nsMap);// 設置命名空間
List<Element> beans = xsub.selectNodes(document);// 獲取文檔下所有bean節點
for (Element element : beans) {
String id = element.attributeValue("id");// 獲取id屬性值
String clazz = element.attributeValue("class"); // 獲取class屬性值
BeanDefinition beanDefine = new BeanDefinition(id, clazz);
beanDefines.add(beanDefine);
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
*獲取bean實例
*@parambeanName
*@return
*/
public Object getBean(String beanName){
returnthis.sigletons.get(beanName);
}
}
六、Spring的三種實例化Bean方式
1.使用構造器實例化
<bean id="personService" class="cn.itcast.service.impl.PersonServiceBean"></bean>
這種就是利用默認的構造器進行的實例化
2. 靜態工廠方法實例化
<bean id="personService2" class="cn.itcast.service.impl.PersonServiceBeanFactory" factory-method="createPersonServiceBean"/>
3. 使用實例工廠方法實例化
<bean id="personServiceFactory" class="cn.itcast.service.impl.PersonServiceBeanFactory"/>
<bean id="personService3" factory-bean="personServiceFactory" factory-method="createPersonServiceBean2"/>
七、bean的作用域-屬性scope
singleton:(默認)在每個Spring IOC容器中一個bean定義只有一個對象實例,默認情況下會在容器啟動時初始化bean。但我們可以指定bean節點lazy-init=”true” 則表示不需要在容器初始化時候對bean進行初始化,只有在第一次getBean時候進行初始化,如果需要所有的bean都應用延遲初始化,可以在根節點<beans>設置default-lazy-init=”true”(不推薦,不利于觀察bean初始化情況)
prototype:每次從容器中獲取的bean都是新的對象
request
session
global session
七、Spring管理的bean的生命周期
默認情況下容器初始化的時候對bean進行實例化
如果scope為prototype時,在調用getBean方法時候對bean進行實例化
如果lazy-init為true時,容器初始化時候不會對bean進行實例化
綜上所述,Spring管理的bean初始化點根據設定的條件不同而不同
init-method:在bean被實例化的后即會執行的方法
destroy-method:在bean被銷毀的時候執行的方法(如果沒有手動的銷毀該bean,則只有在容器關閉的時候才會銷毀)
正常的關閉Spring容器
AbstractApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
ctx.close();
八、控制反轉IoC 定義:
Public class PersonServiceBean {
Private PersonDao personDao = new PersonDaoBean();
Public void save (Person person) {
personDao.save(person);
}
}
PersonDaoBean是在應用內部創建和維護的,所謂的控制反轉就是應用本身不負責依賴對象的創建和維護,依賴對象的創建及維護是由外部容器負責的,這樣控制權就由應用轉移到了外部容器,控制權的轉移就是所謂的反轉。
九、依賴注入(Dependency Injection)的定義:
當我們把依賴對象交給外部容器負責創建,那么PersonServiceBean類可以改成如下:
public class PersonServiceBean{
private PersonDao personDao;
//通過構造器參數,讓容器把創建好的依賴對象注入進PersonServiceBean,當然也可以使用setter方法進行注入
public PersonServiceBean(PersonDao personDao) {
this.personDao = personDao;
}
Public void save(Person person) {
personDao.save(person);
}
}
所謂的依賴注入就是指:在運行期,由外部容器動態地將依賴對象注入到組件中
十、依賴注入:
1.使用構造器注入
private PersonDao personDao;
private String name;
public PersonServiceBean(PersonDao personDao, String name) {
this.personDao = personDao;
this.name = name;
}
beans.xml配置:
<bean id="personService" class="cn.itcast.service.impl.PersonServiceBean">
<constructor-arg index="0" type="cn.itcast.dao.PersonDao" ref=""></constructor-arg>
<constructor-arg index="1" type="paramValue"></constructor-arg>
</bean>
2.使用setter方法注入
ref形式注入:
<bean id="personService" class="cn.itcast.service.impl.PersonServiceBean">
<property name="personDao" ref="personDaoBean" />
</bean>
可以采用內部bean方式進行注入,不同過ref方式的是,這種內部bean注入對于bean的重用性效果不好:
<bean id="personService" class="cn.itcast.service.impl.PersonServiceBean">
<property name="personDao">
<bean class="cn.itcast.dao.impl.PersonDaoBean" />
</property>
</bean>
bean基本類型的注入:
<bean id="personService" class="cn.itcast.service.impl.PersonServiceBean">
<property name="personDao">
<bean class="cn.itcast.dao.impl.PersonDaoBean" />
</property>
<property name="name" value="itcast" />
<property name="id" value="100" />
</bean>
集合類的注入:
<bean id="personService" class="cn.itcast.service.impl.PersonServiceBean" >
<property name="sets">
<set>
<value>第一個</value>
<value>第二個</value>
<value>第三個</value>
</set>
</property>
<property name="lists">
<list>
<value>第一個list元素</value>
<value>第二個list元素</value>
<value>第三個list元素</value>
</list>
</property>
<property name="properties">
<props>
<prop key="key1">value1</prop>
<prop key="key2">value2</prop>
<prop key="key3">value3</prop>
</props>
</property>
<property name="maps">
<map>
<entry key="key-1" value="value-1" />
<entry key="key-2" value="value-2" />
<entry key="key-3" value="value-3" />
</map>
</property>
</bean>
3.使用Field注入(用于注解方式)
注入依賴對象,可以采用手工裝配或者自動裝配,在實際應用中建議使用手工裝配,因為自動裝配會產生未知情況,開發人員無法預見最終裝配的結果
1. 手工裝配依賴對象
a) 在xml配置文件中,通過在bean節點下配置
b) 在java代碼中使用@Autowired或@Resource注解方式進行裝配。但我們需要在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
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
>
<!-- 相當于啟用解析注解的處理器 -->
<context:annotation-config/>
</beans>
這個配置隱式注冊了多個注釋進行解析處理的處理器:
注解本身不做任何事情,只是利用這些處理器來達到配置一樣的效果
AutowiredAnnotationBeanPostProcessor,
CommonAnnotationBeanPostProcesor,
PersistenceAnnotationBeanPostProcessor,
RquiredAnnotationBeanPostProcessor
注:@Resource注解在spring安裝目錄的lib/j2ee/common-annotations.jar
jar文件:
dist/spring.jar
lib/Jakarta-commons/commons-logging.jar
lib/aspectjaspectjweaver.jar、aspectjrt.jar//aop
lib/cglib/cglib-nodep-2.1_3.jar
lib/j2ee/common-annotations.jar
在java代碼中使用@Autowired或@Resource注解方式進行裝配,這兩個注解的區別是:
@Autowired默認按類型裝配,@Resource默認按照名稱裝配,當找不到與名稱匹配的bean才會匹配的bean才會按類型裝配
@Autowired
private PersonDao personDao; //用于字段上
@Autowired
public void setOrderDao(OrderDao orderDao) { //用于屬性的setter方法上
this.orderDao = orderDao;
}
@Autowired注解是按類型裝配依賴對象,默認情況下它要求依賴對象必須存在,如果允許null值,可以設置它required屬性為false(默認是true),如果我們想按名稱裝配,可以結合@Qualifier注解一起使用,如下:
@Autowired @Qualifier(“personDaoBean”)
private PersonDao personDao;
如上面的注解,則@Autowired本身是按照類型裝配,現在將會按名稱裝配
@Resource注解和@Actowired一樣,也可以標注在字段或者屬性的setter方法上,但他默認按照名稱裝配。名稱可以通過@Resource的name屬性指定,如果沒有指定name屬性,當注解標注在字段上,即默認取字段的名稱作為bean名稱尋找依賴對象,當注解標注在屬性的setter方法上,即默認取屬性名作為bean名稱尋找依賴對象。
@Resource(name=”personDaoBean”)
private PersonDao personDao;
注意:如果沒有指定name屬性,并且暗號默認的名稱仍然找不到依賴對象時,“Resource注解會回退到按類型裝配,但一旦指定了name屬性,就只能按名稱裝配了
2.自動裝配依賴對象
對于自動裝配,作為了解
byType:按類型裝配,可以根據屬性的類型,在容器中尋找跟該類型匹配的bean。如果發現多個,那么將會拋出異常。如果沒有找到,即屬性值為null。
byName:按名稱裝配,可以根據屬性的名稱在容器中尋找跟該屬性名相同的bean,如果沒有找到,即屬性值為null
constructor與byType的方式類似,不同之處在于它應用于構造器參數。如果在容器中沒有找到與構造器參數類型一致的bean,那么將會拋出異常
autodetect:通過bean類的自省機制(introspection)來決定是使用constructor還是byType方式進行自動裝配。如果發現默認的構造器,那么將使用byType方式
十一、依賴注入的原理
在Spring容器管理bean原理的實例中進行改造,該例子涉及到兩個實體類:BeanDefinition{id,class}、PropertyDefinition{name,ref}
package junit.test;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.XPath;
import org.dom4j.io.SAXReader;
/**
*傳智傳客版容器
*
*/
publicclass ItcastClassPathXMLApplicationContext {
private List<BeanDefinition> beanDefines = new ArrayList<BeanDefinition>();
private Map<String, Object> sigletons = new HashMap<String, Object>();
public ItcastClassPathXMLApplicationContext(String filename){
this.readXML(filename);
this.instanceBeans();
this.injectObject();
}
/**
*為bean對象的屬性注入值
*/
privatevoid injectObject() {
for(BeanDefinition beanDefinition : beanDefines){
Object bean = sigletons.get(beanDefinition.getId());
if(bean!=null){
try {
PropertyDescriptor[] ps = Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors();
for(PropertyDefinition propertyDefinition : beanDefinition.getPropertys()){
for(PropertyDescriptor properdesc : ps){
if(propertyDefinition.getName().equals(properdesc.getName())){
Method setter = properdesc.getWriteMethod();//獲取屬性的setter方法 ,private
if(setter!=null){
Object value = sigletons.get(propertyDefinition.getRef());
setter.setAccessible(true);
setter.invoke(bean, value);//把引用對象注入到屬性
}
break;
}
}
}
} catch (Exception e) {
}
}
}
}
/**
*完成bean的實例化
*/
privatevoid instanceBeans() {
for(BeanDefinition beanDefinition : beanDefines){
try {
if(beanDefinition.getClassName()!=null && !"".equals(beanDefinition.getClassName().trim()))
sigletons.put(beanDefinition.getId(), Class.forName(beanDefinition.getClassName()).newInstance());
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
*讀取xml配置文件
*@paramfilename
*/
privatevoid readXML(String filename) {
SAXReader saxReader = new SAXReader();
Document document=null;
try{
URL xmlpath = this.getClass().getClassLoader().getResource(filename);
document = saxReader.read(xmlpath);
Map<String,String> nsMap = new HashMap<String,String>();
nsMap.put("ns","http://www.springframework.org/schema/beans");//加入命名空間
XPath xsub = document.createXPath("http://ns:beans/ns:bean");//創建beans/bean查詢路徑
xsub.setNamespaceURIs(nsMap);//設置命名空間
List<Element> beans = xsub.selectNodes(document);//獲取文檔下所有bean節點
for(Element element: beans){
String id = element.attributeValue("id");//獲取id屬性值
String clazz = element.attributeValue("class"); //獲取class屬性值
BeanDefinition beanDefine = new BeanDefinition(id, clazz);
XPath propertysub = element.createXPath("ns:property");
propertysub.setNamespaceURIs(nsMap);//設置命名空間
List<Element> propertys = propertysub.selectNodes(element);
for(Element property : propertys){
String propertyName = property.attributeValue("name");
String propertyref = property.attributeValue("ref");
PropertyDefinition propertyDefinition = new PropertyDefinition(propertyName, propertyref);
beanDefine.getPropertys().add(propertyDefinition);
}
beanDefines.add(beanDefine);
}
}catch(Exception e){
e.printStackTrace();
}
}
/**
*獲取bean實例
*@parambeanName
*@return
*/
public Object getBean(String beanName){
returnthis.sigletons.get(beanName);
}
}
十二、Spring 開發的好處
a) 降低組件之間的耦合度,實現軟件各層之間的解耦
Controller -- Service -- Dao
b) 可是使用容器提供的眾多服務
可使用容器提供的眾多服務:事務管理服務(事務傳播,無需手工控制事務)、JMS服務、Spring core核心服務、持久化服務、其他……
c) 容器提供單例模式支持,開發人員不再需要自己編寫實現代碼
d) 容器提供了AOP技術,利用它狠容易實現如權限攔截,運行期監控等功能
e) 容器提供了眾多的輔助類,使用這些類,能夠加快應用的開發,如:Jdbc Template、Hibernate Template
f) Spring 對于主流的應用框架提供了集成支持,如:集成Hibernate、JPA、Struts等,這樣更便于應用的開發
十三、 Spring DI配置文件減肥(Spring2.0 –Spring2.5的升級)
1.@Resource注解
2.注解使Spring 自動掃描和管理bean
掃描路徑:<context:component-scan base-package=”cn.itcast” />,將會掃描該包下包括其子包的類
@Component:泛指組件、當組件不好歸類的時候,我們可以使用這個注解進行標注
@Service:用于標注業務層組件
@Controller:用于標注控制層組件(如struts中的action)
@Repository:用于標注數據訪問組件,即DAO組件
此時bean默認的名稱為類全名首寫字母小寫,也可指定bean名稱,如
@Service(“personService”)
下面有幾個例子:
service組件的注解
package cn.itcast.service.impl;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.springframework.stereotype.Service;
import cn.itcast.dao.PersonDao;
import cn.itcast.service.PersonService;
@Service("personService") @Scope("prototype") //這樣可以修改bean作用域
public class PersonServiceBean implements PersonService {
//@Autowired(required=false) @Qualifier("personDaoxxxx")
private PersonDao personDao;
//初始化bean時會執行該方法的注解(ejb3中同樣應用)
@PostConstruct
public void init(){
System.out.println("初始化");
}
//銷毀bean之前會執行該方法的注解(ejb3中同樣應用)
@PreDestroy
public void destory(){
System.out.println("開閉資源");
}
public void setPersonDao(PersonDao personDao) {
this.personDao = personDao;
}
public void save(){
personDao.add();
}
}
dao組件的注解
package cn.itcast.dao.impl;
import org.springframework.stereotype.Repository;
import cn.itcast.dao.PersonDao;
@Repository
public class PersonDaoBean implements PersonDao {
public void add(){
System.out.println("執行PersonDaoBean中的add()方法");
}
}
小結:@PostConstruct 和 @PreDestroy的補充
Spring 容器中的 Bean 是有生命周期的,Spring 允許在 Bean 在初始化完成后以及 Bean 銷毀前執行特定的操作,您既可以通過實現 InitializingBean/DisposableBean 接口來定制初始化之后 / 銷毀之前的操作方法,也可以通過 <bean> 元素的 init-method/destroy-method 屬性指定初始化之后 / 銷毀之前調用的操作方法
使用時只需要在方法前標注 @PostConstruct 或 @PreDestroy,這些方法就會在 Bean 初始化后或銷毀之前被 Spring容器執行了,我們知道,不管是通過實現 InitializingBean/DisposableBean 接口,還是通過 <bean> 元素的 init-method/destroy-method 屬性進行配置,都只能為 Bean 指定一個初始化 / 銷毀的方法。但是使用 @PostConstruct 和 @PreDestroy 注釋卻可以指定多個初始化 / 銷毀方法,那些被標注 @PostConstruct 或 @PreDestroy 注釋的方法都會在初始化 / 銷毀時被執行。