無線&移動互聯網技術研發

          換位思考·····
          posts - 19, comments - 53, trackbacks - 0, articles - 283
            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

          Spring IOC/DI/注解-理論與實例并存

          Posted on 2010-05-27 00:04 Gavin.lee 閱讀(2482) 評論(0)  編輯  收藏 所屬分類: SSH2 --Spring

          一、定義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文件

          由于Springschema文件位于網絡上,如果機器不能連接到網絡,那么在寫配置文件的時候就無法出現提示信息,解決辦法有兩種:

          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配置所有的idclass的值,作為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進行實例化

          如果scopeprototype時,在調用getBean方法時候對bean進行實例化

          如果lazy-inittrue時,容器初始化時候不會對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.jaraspectjrt.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方法上,但他默認按照名稱裝配。名稱可以通過@Resourcename屬性指定,如果沒有指定name屬性,當注解標注在字段上,即默認取字段的名稱作為bean名稱尋找依賴對象,當注解標注在屬性的setter方法上,即默認取屬性名作為bean名稱尋找依賴對象。

          @Resource(name=”personDaoBean”)

          private PersonDao personDao;

          注意:如果沒有指定name屬性,并且暗號默認的名稱仍然找不到依賴對象時,“Resource注解會回退到按類型裝配,但一旦指定了name屬性,就只能按名稱裝配了

          2.自動裝配依賴對象

          對于自動裝配,作為了解

          byType:按類型裝配,可以根據屬性的類型,在容器中尋找跟該類型匹配的bean。如果發現多個,那么將會拋出異常。如果沒有找到,即屬性值為null

          byName:按名稱裝配,可以根據屬性的名稱在容器中尋找跟該屬性名相同的bean,如果沒有找到,即屬性值為null

          constructorbyType的方式類似,不同之處在于它應用于構造器參數。如果在容器中沒有找到與構造器參數類型一致的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 TemplateHibernate Template

          f) Spring 對于主流的應用框架提供了集成支持,如:集成HibernateJPAStruts等,這樣更便于應用的開發

          十三、 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 注釋的方法都會在初始化 / 銷毀時被執行。

          主站蜘蛛池模板: 绵竹市| 新和县| 大同县| 旅游| 皋兰县| 乐山市| 玉环县| 陕西省| 滁州市| 通道| 侯马市| 奉贤区| 灌阳县| 府谷县| 酒泉市| 台中县| 台中市| 呼图壁县| 延吉市| 文成县| 北碚区| 江北区| 铜鼓县| 铁力市| 宝清县| 贡觉县| 高州市| 兴安盟| 布拖县| 阿拉善右旗| 长宁县| 朔州市| 宁陵县| 曲麻莱县| 星子县| 大埔县| 札达县| 平泉县| 石城县| 万载县| 长葛市|