spring基礎

--------------------------Spring 2.5 理論介紹--------------------------

Spring是一個開源的控制反轉(Inversion ofControl,IOC)和麪向切面(AOP)的容器框架,它的主要目的是簡化企業開發

1.    IOC控制反轉

先看一段代碼:

publicclass PersonServiceBean{

    private PersonDaopersonDao=new PersonDaoBean();

    public void save(Personperson){

        personDao.save(person);

    }

}

在這段代碼中,PersonDaoBean類的對象是在PersonServiceBean類中創建的,所以PersonDaoBean類對象的創建依賴於PersonServiceBean

2.    依賴注入(DependencyInjection)

當我們把依賴對象交給外部容器負責創建,那麼PersonServiceBean類可以改成:

publicclass PersonServiceBean{

    private PersonDaopersonDao;

    publicPersonServiceBean(PersonDao personDao){

        this.personDao=personDao;

//通過構造器參數,讓容器把創建好的依賴對象注入進PersonServiceBean,當然也可以使用setter方法進行注入

}

publicvoid save(Person person){

    personDao.save(person);

}

}

所謂依賴注入就是指:在運行期,由外部容器動態地將依賴對象注入到組件中

3.    爲什麼要使用Spring呢

在項目中引入Spring立即可以帶來下面的一些好處:

(1)  降低組件之間的耦合度,實現軟件各層之間的解耦

Controller—-Service—-DAO

(2)  可以使用容器提供的衆多服務,例如:事務管理服務,消息服務等等,當我們使用容器管理事務時,開發人員就不再需要手工控制事務,也不需要處理複雜的事務傳播

(3)  容器提供單例模式支持,開發人員不再需要自己編寫實現代碼

(4)  容器提供了AOP技術,利用它很容易實現如權限攔截、運行期監控等功能

(5)  容器提供的衆多輔佐類,使用這些類能夠加快應用的開發,如:JdbcTemplate、HibernateTemplate

(6)  Spring對於主流的應用框架提供了集成支持,如:集成Hibernate,JPA,Struts等,這樣更便於應用的開發

4.    輕量級與重量級概念的劃分

劃分一個應用是否屬於輕量級還是重量級,主要看它使用了多少服務,使用的服務越多,容器要爲普通Java對象做的工作就越多,必然會影響到應用的發佈時間或者是運行性能

對於Spirng容器,它提供了很多服務,但這些服務並不是預設爲應用打開的,應用需要某種服務,就需要指明使用該服務。如果應用使用的服務很少,比如只使用了Spring的核心服務,那麼我們可以認爲此時應用屬於輕量級的。如果應用使用了Spring提供的大部分功能,,這時應用就屬於重量級。

目前EJB容器就因爲它默認爲應用提供了EJB規範中的所有功能,所以它屬於重量級

 

 

---------------------搭建與測試Spring 2.5的開發環境---------------------

下載Spring的jar包,從Spring的官方網站下載

http://www.springsource.org/download可以找到Spring的不同版本

 

1.   Spring經常使用到的jar檔:

dist/spring.jar

如果使用日誌,需要下面這個jar

lib/jakarta-commons/commons-logging.jar

如果使用AOP,需要下面3jar

lib/aspect/aspectjweaver.jaraspectjrt.jar

lib/cglib/cglib-nodep-2.1_3.jar

如果使用JSR-250中的註解,例如:@Resource/@PostConstruct/@PreDestroy,還需要下列jar

lib/j2ee/common-annotations.jar

如果使用Spring內部的Junit測試,還需要下面4jar

 dist/modules/spring-test.jar

  lib/junit/junit-4.4.jar

lib/easymock/easymock.jareasymockclassextension.jar

 

2.   搭建環境

(1)  導入jar包

(2)  創建Spring的配置文件,配置文件的名稱可以任意,檔可以存放在任何目錄下,但考慮到通用性,一般放在類路徑下

配置文件的範本:

<?xmlversion="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-2.5.xsd>

</beans>

3.   創建一個需要使用Spring容器功能的接口及實現類

先創建一個Person類,並含有一個Hello方法

publicclass Person{

 

    publicvoid Notify(){

       System.out.println("HelloWorld");

    }

}

對該類點擊右鍵,選擇Refactor—ExtractInterface 提取該類成一個接口

界面如下:

publicinterface PersonService {

 

    publicabstractvoid Notify();

   

}

4.    修改配置文件Bean.xml實例化一個bean

在配置文件中的<beans>中添加

<bean id="personService" class="com.bean.Person"></bean>

5.   測試

創建一個java類進行測試

publicclass Test {

 

    publicstaticvoid main(String[] args) {

       ApplicationContext ctx = new ClassPathXmlApplicationContext("Bean.xml");

       PersonService person=(PersonService) ctx.getBean("personService");

       person.Notify();

    }

}

如果成功會打印出來:Hello Spring

 

 

--------------------利用MyEclipse加載配置文件的說明信息-----------------

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

1.讓機器上網,MyEclipse會自動從網絡上下載schema文件並緩存在硬盤上。

2.手動添加schema檔,方法如下:

以” spring-beans-2.5.xsd”爲例子

windwos->preferences->myeclipse->filesand editors->xml->xmlcatalog

點"add",在出現的窗口中的Key Type中選擇URI,在location中選"Filesystem",然後在spring解壓目錄的dist/resources目錄中選擇spring-beans-2.5.xsd,回到設置窗口的時候不要急着關閉窗口,應把窗口中的Key Type改爲Schemalocation,Key改爲http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

 

Spring所包含的xsd不止這一個,還有spring-aop-2.5.xsd等,所以當遇到非語法的配置文件報錯時,就很有可能是沒有配置其中所用到的xsd檔

 

 

--------------------------實例化Spring容器---------------------------

1.   實例化Spring容器有兩種方式:

方法一:

在類路徑下尋找配置文件來實例化容器

ApplicationContextctx = new ClassPathXmlApplicationContext(new String[]{"beans.xml"});

方法二:

在文件系統路徑下尋找配置文件來實例化容器

ApplicationContextctx = new FileSystemXmlApplicationContext(new String[]{“d://beans.xml“});

該方式不太推薦使用,因爲每個操作系統的文件系統都是不同的,所以他的通用性不強,例如這個路徑“d://beans.xml“是在window下的,而在linux就可能不是這個路徑了

2.   Spring的配置文件可以指定多個,可以通過String數組傳入。

3.   當spring容器啓動後,因爲spring容器可以管理bean對象的創建,銷燬等生命週期,所以我們只需從容器直接獲取Bean對象就行,而不用編寫一句代碼來創建bean對象。從容器獲取bean對象的代碼如下:

OrderServiceservice = (OrderService)ctx.getBean("personService");

這裏的OrderService表示一個界面

personService表示獲取配置文件中<bean>id屬性所對應的類的實例

 

 

-------------------------Spring管理Bean的原理-------------------------

利用Java讀取XML文檔的技術來模擬實現一個容器,從而利用該容器獲取配置文件中bean的實例

需要導入dom4j及相關一整套的jar包

1.   先創建一個JavaBean,用來保存配置文件中<bean>的id和class

publicclass BeanDefinition {

    private String id;

    private String className;

   

    public BeanDefinition(String id,String className) {

       this.id = id;

       this.className = className;

    }

    public String getId() {

       returnid;

    }

    publicvoid setId(String id) {

       this.id = id;

    }

    public String getClassName() {

       returnclassName;

    }

    publicvoid setClassName(String className) {

       this.className = className;

    }

}

 

2.   通過spring提供的jar中的某些特殊類的功能,例如:SAXReader、Document和XPath等,來模擬實現

publicclass ItcastClassPathXMLApplicationContext {

    private List<BeanDefinition> beanDefines = newArrayList<BeanDefinition>();

    private Map<String, Object> sigletons = new HashMap<String,Object>();

   

    publicItcastClassPathXMLApplicationContext(String filename){

       this.readXML(filename);

       this.instanceBeans();

    }

    /**

     *讀取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("//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屬性值       

                BeanDefinitionbeanDefine = new BeanDefinition(id, clazz);

                beanDefines.add(beanDefine);

             }  

            }catch(Exception e){  

                e.printStackTrace();

            }

    }

    /**

     *完成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();

           }

       }

      

    }

    /**

     *獲取bean實例

     *@parambeanName

     *@return

     */

    public Object getBean(StringbeanName){

       returnthis.sigletons.get(beanName);

    }

}

3.   最後進行測試

 

publicstaticvoid main(String[] args) {

ItcastClassPathXMLApplicationContext ctx = new ItcastClassPathXMLApplicationContext("Bean.xml");

       PersonService person=(PersonService) ctx.getBean("personService");

       person.Notify();

    }

 

 

-------------------------三種實例化bean的方式--------------------------

1.使用類構造器實例化

<bean

id="personService" class="com.bean.service.impl.Person">

</bean>

這個是使用類構造器來實例化,也是我們最初見到的最簡單的一種單實例,無論這個personService構造多少個,實質上也只有一個實例而已

這種方式實例化的bean,是一種

2. 使用靜態工廠方法實例化

publicclass PersonStaticFactory {

 

    publicstatic Person staticCreatePerson(){

       returnnew Person();

    }

}

工廠類裏邊提供一個靜態方法,專門用於創建bean對象

修改配置文件,實例化該bean

<bean

id="personService2"class="com.bean.service.impl.PersonStaticFactory"factory-method="staticCreatePerson"/>

這種配置方法表示利用PersonStaticFactory的createPerson方法來實例化

測試:

PersonServiceperson2=(PersonService) ctx.getBean("personService2");

System.out.print("使用靜態工廠方法實例化:");

person.Notify();

3. 使用實例工廠方法實例化:

publicclass PersonStaticFactory {

 

    public Person createPerson(){

       returnnew Person();

    }

}

修改配置文件,實例化該bean

<bean

id="personFactory" class="com.bean.service.impl.PersonStaticFactory"/>

<bean

id="personService3"

factory-bean="personFactory"

factory-method="createPerson"/>

先創建工廠類的一個實例bean--"personFactory"

再使用這個工廠bean—"personFactory"來調用"createPerson"方法創建Person類bean的實例

測試:

PersonServiceperson3=(PersonService) ctx.getBean("personService3");

System.out.print("使用實例工廠方法實例化:");

person.Notify();

 

 

-----------------------------bean的作用域-----------------------------

在每個spring IoC容器中默認一個bean定義只有一個對象實例。我們可以設置scope屬性的屬性範圍讓一個bean初始化多個對象實例

<bean

id="personService"

class="com.bean.service.impl.Person"

scope="prototype"/>

這樣每次從容器獲取bean都是新的對象

scope中還有其他參數:

Singleton:唯一值,在IOC的容器中,只能出現一個對象,類似類中的靜態方法,當spring的容器加載的時候,自動對singleton類型的的bean進行實例化

Prototype:每次產生一個新對象

Request:爲每一個http的請求創建一個全新的bean

Session:bean對象放置在session會話中,不同HTTPSession使用不同的Bean。該作用域僅適用於WebApplicationContext環境。

globalSession:同一個全局Session共享一個Bean,一般用於Protlet應用環境。該作用域僅適用於WebApplicationContext環境。

 

 

------------------------- bean的生命週期-------------------------

1.    預設情況下Spring會在容器啓動時初始化bean的實例。

我們可以指定Bean節點的lazy-init=”true”屬性來延遲初始化bean

這時候,只有第一次獲取bean纔會初始化bean

例如:

<bean

id="personService"

class="com.bean.service.impl.Person"

lazy-init="true"/>

lazy-init="false"時,表示在容器啓動時對bean進行實例化

lazy-init="true",表示進行延遲初始化,在ctx.getBean時對bean進行實例化

如果想對所有bean都應用延遲初始化,可以在根節點bean設置

default-lazy-init=”true”,例如:

<beans default-lazy-init="true"

</beans>

 

2.    在設置scope="prototype"屬性時,是在getBean("personService")時才初始化bean的實例,而不設置時,就在容器啓動時對bean進行實例化

 

3.   初始化方法

在bean添加一個init方法

publicvoid init(){

    System.out.println("init方法被調用");

}

在<bean>中設置init-method="init"屬性,再實例化bean後,會啓動該初始化方法

爲了更好地看到效果,添加Person類的構造方法,告訴我們bean何時被實例化

publicPerson(){

    System.out.println("bean已被實例化");

}

當執行下列這段代碼時

AbstractApplicationContextctx

=new ClassPathXmlApplicationContext("Bean.xml");

讀取xml配置文件,初始化Spring容器

先構造bean,再調用init方法

4.   銷燬方法

在bean添加一個destory方法

publicvoid destory(){

    System.out.println("關閉Spring容器,destory方法被調用");

}

在<bean>中設置destroy-method="destory"屬性,在關閉Spring容器前會啓動該銷燬方法

那麼又如何來關閉Spring容器呢?

首先,Spring容器讀取xml配置文件不能使用ApplicationContext,而要使用抽象類AbstractApplicationContext

AbstractApplicationContext ctx

= new ClassPathXmlApplicationContext("Bean.xml");

ctx.close();

AbstractApplicationContext也是被ApplicationContext所繼承的

close方法來正常關閉Spring容器

 

 

-----------------------Spring依賴注入之ref屬性------------------------

所謂依賴注入就是指:在運行期,由外部容器動態地將依賴對象注入到組件中

這個時候控制權發生了轉移,這就是所謂的控制反轉

依賴注入一共有兩種方法:

一個是使用property元素的ref屬性,這樣構造的bean可以被任意個其他bean使用

另一個是使用內部bean,後邊將會介紹,但是他只能爲一個bean所服務

例如:我要在Person類中由Spring容器在外部創建PersonDao類的實例,並注入進來

1.   創建一個PersonDao類,添加一個add方法

publicclass PersonDao implements PersonDaoService {

 

    publicvoid add(){

       System.out.println("這裏是PersonDao實例調用了add方法");

    }

}

2.   抽取接口 右鍵單擊該類,選擇Refactor—Extract Interface

publicinterface PersonService {

 

    publicabstractvoid Notify();

}

3.   在Person類中添加一個PersonDao類的對象引用(屬性),設置該對象的set方法,在notify方法中調用PersonDao對象的add方法

 

publicclass Person implements PersonService {

   

    public PersonDao personDao;

publicvoid Notify(){

       System.out.println("HelloSpring");

       personDao.add();

    }

publicvoid setPersonDao(PersonDao personDao) {

       this.personDao = personDao;

    }

}

4.   修改bean.xml配置文件

<bean id="personDao" class="com.bean.service.impl.PersonDao"></bean>

<bean id="personService" class="com.bean.service.impl.Person">

    <property name="personDao"ref="personDao"></property>

</bean>

創建PersonDao類的bean實例,該類要被注入進Person類中

在personService實例中添加<property>屬性,也就是通過property屬性隱式執行一個setter方法

其中:

name元素表示爲Person類中的personDao屬性注入值,且該元素必須與setXXX方法的XXX部分的名稱相同,且首字母小寫,與struts中的formbean利用反射機制獲取值的方式基本一致

ref元素表示要注入的bean的名稱,也就是要注入的那個bean的id名稱

5.   測試

依然調用person.Notify()方法,就會經過注入bean,調用PersonDao類的add方法

 

 

------------------------Spring依賴注入之內部bean-----------------------

內部bean只能被一個bean使用,不能被其他bean使用

與上個例子的步驟基本相同,只是xml配置文件的設置不一樣

修改xml配置文件

<bean id="personService" class="com.bean.service.impl.Person">

    <property name="personDao">

       <bean class="com.bean.service.impl.PersonDao"></bean>

    </property>

</bean>

 

 

------------------------Spring依賴注入之基本屬性-----------------------

Spring容器不僅能注入依賴對象,還可以注入基本類型的屬性

例如:我有一個id和name,要注入進Person類

1.   在Person類定義id和name屬性,創建他們的set方法

publicclass Person implements PersonService {

   

    publicintid;

    public String name;

   

    publicvoid Notify(){

       System.out.println("Hello Spring");

       System.out.println("id="+id+",name="+name);

    }

    publicvoid setId(int id) {

       this.id = id;

    }

publicvoid setName(String name) {

       this.name = name;

    }

}

2.   修改xml配置文件

<bean id="personService" class="com.bean.service.impl.Person">

    <property name="id"value="1"></property>

    <property name="name"value="Shiki"></property>

</bean>

3.   測試

依然調用person.Notify()方法,就會注入idname屬性,通過Notify方法打印出idname的值

 

 

----------------------Spring依賴注入之集合類型屬性----------------------

1.    定義4種集合類型的屬性,生成相應的get/set方法,將其get方法添加進接口中,因爲測試時要由接口對象來調用該方法

publicclass Person implements PersonService {

private Set<Person> set=newHashSet<Person>();

private List<Person> list = new ArrayList<Person>();

private Properties prop=new Properties();

private Map<String,String> map=newHashMap<String,String>();

public Set<Person> getSet() {

       returnset;

    }

    publicvoid setSet(Set<Person> set) {

       this.set = set;

    }

    public List<Person> getList() {

       returnlist;

    }

    publicvoid setList(List<Person> list) {

       this.list = list;

    }

    public Properties getProp() {

       returnprop;

    }

    publicvoid setProp(Properties prop) {

       this.prop = prop;

    }

    public Map<String, String>getMap() {

       returnmap;

    }

    publicvoid setMap(Map<String, String> map) {

       this.map = map;

    }

}

2.    抽取界面

publicinterface PersonService {

 

    publicabstractvoid Notify();

    publicabstract Set<Person> getSet();

    publicabstract List<Person> getList();

    publicabstract Properties getProp();

 

    publicabstract Map<String, String>getMap();

}

3.    修改xml配置文件

<bean id="personService" class="com.bean.service.impl.Person">

<property name="set">

        <set>

                 <value>第一個</value>

                 <value>第二個</value>

                 <value>第三個</value>

        </set>

       //value卷標保存值,ref卷標保存對象實例,<ref bean="person"/>

        </property>

        <property name="list">

             <list>

                 <value>第一個list元素</value>

                 <value>第二個list元素</value>

                 <value>第三個list元素</value>

             </list>

        </property>

        <property name="prop">

             <props>

                 <prop key="key1">value1</prop>

                 <prop key="key2">value2</prop>

                 <prop key="key3">value3</prop>

             </props>

        </property>

        <property name="map">

             <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>

4.    測試

AbstractApplicationContext ctx

= new ClassPathXmlApplicationContext("Bean.xml");

PersonService person=(PersonService)ctx.getBean("personService");

System.out.println("========set===========");

       for(String value : person.getSet()){

           System.out.println(value);

       }

       System.out.println("========list===========");

       for(String value : person.getList()){

           System.out.println(value);

       }

       System.out.println("========properties===========");

       for(Object key : person.getProp().keySet()){

           System.out.println(key+"="+ person.getProp().getProperty((String)key));

       }

       System.out.println("========map===========");

       for(String key : person.getMap().keySet()){

           System.out.println(key+"="+ person.getMap().get(key));

       }

 

 

-----------------------Spring依賴注入之bean構造器----------------------

使用bean構造器可以注入依賴對象或基本類型屬性

Person類中定義兩個屬性,並生成帶有參數的構造器

publicclass Person implements PersonService {

   

    public PersonDao personDao;

    public String name;

public Person(PersonDaopersonDao,String name){

       System.out.println("bean已被實例化");

       System.out.println("使用構造器注入:");

       this.personDao=personDao;

       this.name=name;

      

    }

}

修改xml配置文件

<bean id="personDao" class="com.bean.service.impl.PersonDao"/>

<bean id="personService" class="com.bean.service.impl.Person">

<constructor-arg index="0"type="com.bean.service.impl.PersonDao"ref="personDao"/>

    <constructor-arg index="1" type="java.lang.String" value="Shiki"/>

</bean>

index屬性表示構造器中參數的索引,type是參數的類型,refvalue指定要注入的對象或基本類型的值

 

 

---------------------Spring依賴注入之使用註解注入屬性--------------------

如果我們不希望配置文件所配置的屬性非常多非常複雜,我們可以使用註解的方式對依賴對象進行注入

1.    如果想使用註解的方式,則我們需要在xml配置文件中添加以下額外的信息:

<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.0.xsd"

http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-2.5.xsd">

    <context:annotation-config/>

</beans>

這個配置隱式註冊了多個處理器,用於對註釋進行解析

這些處理器包括:

AutowiredAnnotationBeanPostProcessor:用來解析@Autowired註解

CommonAnnotationBeanPostProcessor:用來解析@Resource註解

PersistenceAnnotationBeanPostProcessor:用來處理持久化

RequiredAnnotationBeanProcessor

註解本身和xml的作用一樣,也是用於配置,而不能自己幹活,之所以在這裏能夠幹活是因爲背後有這些處理器進行處理

<context:annotation-config/>就針對註解所使用的處理器注入到了Spring容器中

注,不配置處理器的另一個方法是:

@Autowired 起作用必須事先在 Spring 容器中聲明AutowiredAnnotationBeanPostProcessor Bean,該 BeanPostProcessor 將自動對標註 @Autowired 的 Bean 進行注入

<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>

2.    導入註解所需要的jar包

lib/j2ee/common-annotations.jar

3.    java代碼中使用@Autowired@Resource註解方式進行裝配。

(1)  這兩個註解的區別是:@Autowired默認按類型裝配,@Resource默認按名稱裝配,當找不到與名稱匹配的bean纔會按類型匹配

(2)  @Autowired默認情況下要求依賴對象必須存在,如果允許null值,可以設置required屬性爲false;如果我們想使用名稱匹配,可以結合@Qualifier註解一起使用

(3)  @Resource註解和@Autowired註解某些方面一樣,也可以標註在字段或屬性的set方法上,但他默認按名稱匹配,名稱可以通過@Resourcename屬性指定,如果沒有指定name屬性,當註解標註在字段上,即默認取字段的名稱作爲bean名稱尋找依賴對象,當註解標註在屬性的set方法上,即默認取屬性名作爲bean名稱尋找依賴對象

(4)  如果沒有指定name屬性,並且按照默認的名稱仍然找不到依賴對象時,@Resource註解會回退到按類型匹配。一旦指定了name屬性,就只能按名稱匹配了

 

xml配置文件中配置Person和要注入的bean

<bean id="personDao" class="com.bean.service.impl.PersonDao"/>

<bean id="personService" class="com.bean.service.impl.Person">

Person類中添加註解

publicclass Personimplements PersonService {

    @Autowired

    public PersonDao personDao;

   

    publicvoid Notify(){

       System.out.println("HelloSpring");

      

       personDao.add();

    }

}

@Autowired是按照類型匹配,結合@Qualifier註解一起使用就可以使用名稱匹配

@Autowired(required=false)required=false屬性表示不存在匹配的bean的時候,不報錯

@Resource @Qualifier("personDao")

@Qualifier(“XXX”)括號中的內容表示要注入的bean的名稱

當然,最好是直接使用按名稱匹配的註解@Resource,如果按照名稱找不到相應的bean,則會回退到按類型匹配,這樣做最保險

 

 

-----------------------Spring依賴注入之自動裝配-----------------------

注入依賴對象可以採用手工裝配和自動裝配

手工裝配包括了:使用構造器注入,set方法注入,Field注入(註解方式)

以上講解的裝配方式都屬於手動裝配,在實際應用中建議使用手工裝配,因爲自動裝配會產生未知情況,開發人員無法預見最終的裝配結果

下面來講解如何進行自動裝配:

在xml配置文件中

<bean id="personDao" class="com.bean.service.impl.PersonDao"/>

<bean

id="personService"

class="com.bean.service.impl.Person"

autowire="xxx"

這裏的autowire屬性的取值有多種,可以設定按類型,名稱,構造器和自省機制來自動匹配要注入的bean。

就這個程序而言,如果要進行名稱匹配,在Person類中搜索屬性的名稱與該xml配置文件中的其他bean的名稱相比較,如果存在,就將這個bean注入到Person類的屬性之中

Autowire屬性的取值如下

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

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

constructor與byType的方式類似,不同之處在於它應用於構造器函數。如果在容器中沒有找到與構造器參數類型一致的bean,那麼將會拋出異常

autodetect:通過bean類的自省機制(introspection)來決定是使用constructor還是byType方式進行自動匹配。如果發現默認的構造器,那麼將使用byType方式

 

Person類中不需要使用任何註解,但一定要注意!使用autowire與那兩種註解方式不同,在person類中必須含有該依賴對象的set方法!否則就無法給依賴對象賦值了

publicclass Person implements PersonService {

    public PersonDao personDao;

publicvoid Notify(){

       System.out.println("HelloSpring");

       System.out.println("id="+id+",name="+name);

       personDao.add();

    }

    publicvoid setPersonDao(PersonDao personDao) {

       this.personDao = personDao;

    }

}

測試:

依然調用person.Notify()方法,根據autowire所指定的匹配方式來尋找bean,如果成功,打印出注入beanadd方法中的信息

 

-----------------------Spring自動掃描和管理Bean------------------------

前面的例子我們都是使用XML的bean定義來配置組件。在一個稍大的項目中,通常會有上百個組件,如果這些組件都採用XML的bean定義來配置,顯然會增加配置文件的體積,即使使用註解的方式,也只能會減少<bean>中的屬性,而<bean>元素的數量依舊會很大,查找及維護起來也不太方便。

由此,Spring2.5版本爲我們引入了組件自動掃描機制,他可以在類路徑底下尋找標註了@Component、@Service、@Controller、@Repository註解的類,並把這些類納入進Spring容器中管理。它的作用和在xml檔中使用<bean>節點配置組件是一樣的

要使用自動掃描機制,我們需要打開一下配置信息:(添加自動掃描的處理器)

<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/contexthttp://www.springframework.org/schema/context/spring-context-2.5.xsd">

<context:component-scanbase-package="cn.itcast"/>

</beans>

其中base-package屬性爲需要掃描的包(含子包,也就是以cn.itcast開頭的包都會被掃描到)

@Service用於標註業務層組件

@Controller用於標註控制層組件(如Struts中的Action)

@Repository用於標註數據訪問組件,即DAO組件。

@Component泛指組件,即當組件不好歸類的時候,我們可以使用這個註解進行標註

 

在類中添加這些註解,Spring容器就會根據指定的包路徑自動掃描到這些類,並生成相應的bean實例,例如:

@Service

publicclass Person implements PersonService {}

如果沒有指定名稱,那麼bean的名稱就是類的簡單名稱,也就是把類名首字母小寫的名稱。在使用時,依然使用getBean(“xXX”)來獲取的bean實例,這裏邊的xXX的名稱就是那個類的簡單名稱

當然,也可以通過註解來給bean提供一個名稱

@Repository("abc")

publicclass PersonDao implements PersonDaoService {}

getBean(“XXX”)獲取時就使用abc這個提供的名稱了

測試:

AbstractApplicationContext ctx

= new ClassPathXmlApplicationContext("Bean.xml");

PersonServiceperson=(PersonService) ctx.getBean("person");

PersonDaoService personDao=(PersonDaoService)ctx.getBean("abc");

System.out.println("person是否存在?"+person);

System.out.println("personDao是否存在?"+personDao);

結合@Scope(“XXX”)註解一起使用,可以改變該bean的作用域範圍

@Service@Scope("prototype")

publicclass Person implements PersonService {}

prototype在之前講解過,每次從容器獲取bean都是新的對象,而默認獲取的bean的物件是單例的

 

利用註解來標註初始化和銷燬方法

@PostConstruct

    publicvoid init(){

    System.out.println("完成Spring容器初始化,init方法被調用");

}

@PreDestroy

    publicvoid destory(){

    System.out.println("Spring容器即將關閉,destory方法被調用");

}

 

 

-------------------------------靜態代理-------------------------------

例如:要在輸出“HelloWorld”前打印一個字符串“Welcome”

1.   定義一個接口類

 

  package ttitfly.proxy;   

    

  public interface HelloWorld {  

     public void print();   

//  public void say();  

}

2.   定義一個該接口的實現類

  package ttitfly.proxy;   

    

 public class HelloWorldImpl implements HelloWorld{  

    

     public void print(){   

         System.out.println("HelloWorld");  

      }   

  //  public void say(){  

 //      System.out.println("Say Hello!");  

  //  }   

  }

3.   定義一個靜態代理類

  package ttitfly.proxy;   

    

  public class StaticProxy implements HelloWorld{  

    

     public HelloWorld helloWorld ;  

     public StaticProxy(HelloWorld helloWorld){  

         this.helloWorld = helloWorld;  

      }   

         

     public void print(){   

          System.out.println("Welcome");  

         //相當於回調   

         helloWorld.print();  

      }   

         

  //  public void say(){  

  //      //相當於回調   

 //      helloWorld.say();   

  //  }   

  }

4.   測試類

  package ttitfly.proxy;   

    

  public class TestStaticProxy {  

    

     public static void main(String[] args){  

         HelloWorld helloWorld = new HelloWorldImpl();  

         StaticProxy staticProxy = new StaticProxy(helloWorld);  

         staticProxy.print();  

            

 //      staticProxy.say();   

      }   

  }

可以看出靜態代理類有一個很不好的缺點:如果當接口加一個方法(把上面所有的代碼的註釋給去掉),所有的實現類和代理類裏都需要做個實現。這就增加了代碼的複雜度。動態代理就可以避免這個缺點。

 

 

-----------------------Proxy技術實現AOP(動態代理)----------------------

AOP應用於做權限系統,我們可能需要粗粒度的權限控制,或者細粒度的權限控制,細粒度的權限控制我們一般對方法進行攔截,攔截方法後判斷用戶是否具有權限,有權限就允許用戶執行被攔截的方法

用Proxy技術來模擬一下實際的業務需求,在不使用AOP框架的時候,實現其功能

但Proxy有侷限性,必須當目標類實現了接口,纔可以使用jdk的Proxy來生成代理對象。

 

1.   創建需要代理的

PersonServiceBean.java

publicclass PersonServiceBean{

    private String user = null;

   

    public String getUser() {

       returnuser;

    }

    public PersonServiceBean(){}

    public PersonServiceBean(Stringuser){

       this.user = user;

    }

 

    public String getPersonName(Integerpersonid) {

       System.out.println("我是getPersonName()方法");

       return"xxx";

    }

    publicvoid save(String name) {

       System.out.println("我是save()方法");

    }

    publicvoid update(String name, Integer personid) {

       System.out.println("我是update()方法");

    }

}

2.   抽取接口,抽取後,會自動將該類實現該接口

PersonService.java

publicinterface PersonService {

    publicvoid save(String name);

    publicvoid update(String name, Integer personid);

    public String getPersonName(Integer personid);

}

3.   代理工廠(也就是一個代理的工具類,用來創建代理對象並攔截其方法)

JDKProxyFactory.java

publicclass JDKProxyFactoryimplements InvocationHandler{

   

    private Object targetObject;

    public ObjectcreateProxyIntance(Object targetObject){

       this.targetObject = targetObject;

       return Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(),

              this.targetObject.getClass().getInterfaces(),this);

       //三個參數爲代理對象的類加載器,實現的接口,以及InvocationHandler類的實例

       /* InvocationHandler是代理實例的調用處理程序實現的接口。

        每個代碼實例都具有一個關聯的調用處理程序。

        對代理實例調用方法時,將對方法調用進行編碼並將其指派到它的調用處理程序的 invoke 方法。

        通過創建代理對象後,由其所調用的方法都將被InvocationHandler界面的invoke方法所攔截,並根據需求來決定如何執行

        */

    }

 

    public Object invoke(Object proxy,Method method, Object[] args)

           throws Throwable {//環繞通知

       PersonServiceBean bean = (PersonServiceBean) this.targetObject;

       Object result = null;

       if(bean.getUser()!=null){

           //..... advice()-->前置通知

           try {

              result = method.invoke(targetObject, args);

              // afteradvice() -->後置通知

           } catch (RuntimeException e) {

              //exceptionadvice()--> 例外通知

           }finally{

              //finallyadvice(); -->最終通知

           }

       }

       return result;

    }

}

4.   測試類

publicclass AOPTest {

 

       publicstaticvoid main(String[] args) {

      

       JDKProxyFactory factory = new JDKProxyFactory();

       PersonService service = (PersonService) factory.createProxyIntance(new PersonServiceBean("xxx"));

       //創建一個代理對象

       service.save("888");

       //經過攔截,由invoke方法來調用

    }

}

-----------------------------CGLIB技術實現----------------------------

如果這個類沒有提供接口,那麼我們就無法來創建Proxy代理對象,那麼我們用一個第三方的創建代理對象的框架—cglib

spring中包含了它的jar檔  lib/cglib/cglib-nodep-2.1_3.jar

cglib可以生成目標類的子類,並重寫父類非final修飾符的方法。

通過cglib,我們可以通過沒有實現接口的類就可以創建代理對象

1.   繼續使用上邊的PersonServiceBean.java類作爲目標類

2.   創建cglib代理工廠

publicclass CGlibProxyFactory implements MethodInterceptor{

    private Object targetObject;

   

    public Object createProxyIntance(Object targetObject){

       this.targetObject = targetObject;

       Enhancer enhancer = new Enhancer();//創建代理對象

       enhancer.setSuperclass(this.targetObject.getClass());//final

       /*這個參數要填寫一個父類,這個父類也就是目標類,就是要代理的那個類

       當把目標類設置爲代理對象父類的時候,代理技術會產生一個目標類的子類,在這個子類裏邊,它可以覆蓋目標類中所有非final修飾的方法

       實際上,cglib創建的代理對象是繼承了目標類,對目標類的所有非final方法進行覆蓋,在這些方法中可以添加一些自身的方法

       */

       enhancer.setCallback(this);

       //回調方法,與Proxy的原理相同,也必須要實現一個接口,當代理對象的業務方法被調用的時候,會回調這個方法,

      

       returnenhancer.create();

    }

    public Object intercept(Object proxy,Method method, Object[] args,

           MethodProxy  methodProxy) throws Throwable {

       PersonServiceBean bean = (PersonServiceBean) this.targetObject;

        Object result = null;

       if(bean.getUser()!=null){

           result = methodProxy.invoke(targetObject, args);

       }

       return result;

    }

}

3.   測試類

publicclass AOPTest {

   

    publicstaticvoid main(String[] args) {

       //cglib

       CGlibProxyFactory factory = new CGlibProxyFactory();

       PersonServiceBean service =(PersonServiceBean) factory.createProxyIntance(new PersonServiceBean("xxx"));

       service.save("999");

    }

}

 

 

-----------------------------AOP中的概念-----------------------------

Aspect(切面):指橫切性關注點的抽象即爲切面,它與類相似,只是兩者的關注點不一樣,類是對物體特徵的抽象,而切面橫切性關注點的抽象.

joinpoint(連接點):所謂連接點是指那些被攔截到的點。在spring中,這些點指的是方法,因爲spring只支持方法類型的連接點,實際上joinpoint還可以是field或類構造器)

Pointcut(切入點):所謂切入點是指我們要對那些joinpoint進行攔截的定義.

Advice(通知):所謂通知是指攔截到joinpoint之後所要做的事情就是通知.通知分爲前置通知,後置通知,異常通知,最終通知,環繞通知

Target(目標對象):代理的目標對象

Weave(織入):指將aspects應用到target對象並導致proxy對象創建的過程稱爲織入.

Introduction(引入):在不修改類代碼的前提下, Introduction可以在運行期爲類動態地添加一些方法或Field.

 

 

---------------------使用Spring進行面向切面(AOP)程序設計--------------------

AOP,主要是用於攔截方法

要進行AOP程序設計,首先我們要在spring的配置文件中引入aop命名空間:

<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"

       xsi:schemaLocation="http://www.springframework.org/schema/beans

           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

http://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-2.5.xsd">

</beans>

Spring提供了兩種切面聲明方式,實際工作中我們可以選用其中一種:

基於XML配置方式聲明切面。

基於註解方式聲明切面。

 

基於註解方式

1.    首先啓動對@AspectJ註解的支持(藍色部分爲預設的,紅色部分是aop所用到的)

<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"

       xsi:schemaLocation="http://www.springframework.org/schema/beans

          http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

http://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-2.5.xsd">

<aop:aspectj-autoproxy/>

<bean id="PersonServiceBean" class="com.aop.PersonServiceBean"/>

<bean id="myInterceptor" class="com.aop.MyInterceptor"/>

</beans>

<aop:aspectj-autoproxy/>表示打開配置項,爲我們這個註解提供瞭解析的功能

註解本身不能自己幹活,之所以在這裏能夠幹活是因爲背後有這些處理器進行處理

<aop:aspectj-autoproxyproxy-target-class="true"/>

參數proxy-target-class=true表示使用cglib來生成代理對象,默認false是使用JDKProxy的方法創建,但是要依據情況而定,如果提供的目標類沒有實現接口就使用cglib,實現接口的使用JDKProxy

這裏要注意

2.    創建界面及實現類

publicinterface PersonService {

    publicvoid save(String name);

    publicvoid update(String name, Integer id);

    public String getPersonName(Integerid);

}

 

publicclass PersonServiceBean implements PersonService {

 

    public String getPersonName(Integerid) {

       System.out.println("我是getPersonName()方法");

       return"xxx";

    }

 

    publicvoid save(String name) {

       thrownew RuntimeException("例外,拋出異常");

       //System.out.println("我是save()方法");

    }

 

    publicvoid update(String name, Integer id) {

       System.out.println("我是update()方法");

    }

}

3.    創建切面類

利用aop技術進行方法攔截,對類和方法用不同的註解進行標記,完成不同的功能

@Aspect//表示採用註解的方式聲明切面

publicclass MyInterceptor {

    /*Pointcut 切入點,用來定義我們需要攔截的方法,後邊引用了一些aop表達是語言

                  1           2       3     4  5  6

    @Pointcut("execution (* com.source..*.*(..))")

   

    1號位置 execution表示執行,執行該方法的時候進行攔截

    2號位置表示返回值類型,*號通配符,表示任意類型

    3號位置表示包名,對哪個包下的類進行攔截

    4號位置兩個點表示對其包下的子包裏邊的內容也要進行攔截,如果不加點的話就表示只包含當前包裏邊的內容

    5號位置第一個*號表示類,第二個*號表示方法,*.*表示對任意個類的所有方法進行攔截

    6號位置表示方法的參數,..表示任意參數,有無都可以,1個,2個或者多個

    */

   

    /*

    通知的種類

    @Before  前置通知

    @AfterReturning  後置通知

    @After  最終通知

    @AfterThrowing  例外通知

    @Around  環繞通知

    */

   

    /*

    通知執行的順序(環繞通知包括進入方法和退出方法)

    前置通知:Shiki

    進入方法(環繞通知進入方法緊跟在方法前執行)

    我是save()方法

    後置通知:null(緊跟在方法後執行)

    最終通知(finally中執行)

    退出方法(環繞通知退出方法當方法執行完畢後執行)

    */

   

    @Pointcut("execution(* com.aop.PersonServiceBean.*(..))")

    privatevoid anyMethod() {}//利用@Pointcut註釋聲明一個切入點,名稱爲anyMethod

    //aop表達是語言表示對com.aop.PersonServiceBean類下的所有方法進行攔截

   

    @Before("anyMethod()&& args(name)")//括號裏邊填寫的是切入點的名稱和方法參數名

    //該參數名必須與該通知定義的方法的參數名同名,否則會報異常

    publicvoid doAccessCheck(String name) {

       System.out.println("前置通知:"+ name);

    }

    @AfterReturning(pointcut="anyMethod()",returning="result")

    publicvoid doAfterReturning(String result) {

       System.out.println("後置通知:"+ result);

    }

    @After("anyMethod()")

    publicvoid doAfter() {

       System.out.println("最終通知");

    }

    @AfterThrowing(pointcut="anyMethod()",throwing="e")

    publicvoid doAfterThrowing(Exception e) {

       System.out.println("例外通知:"+ e);

    }

   

    @Around("anyMethod()")

    public ObjectdoBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {

       //if(){//判斷用戶是否在權限這個環繞通知很適合於做權限驗證如果用戶存在,然後執行被攔截的方法

       System.out.println("進入方法");

       Object result = pjp.proceed();//表示被攔截的方法

       System.out.println("退出方法");

       //}

       return result;

    }

}

4.   修改配置文件,實例化該bean

<bean id="myInterceptor" class="com.aop.MyInterceptor"></bean>

<bean id="PersonServiceBean" class="com.aop.PersonServiceBean"></bean>

5.    測試類

publicclass Test {

 

    publicstaticvoid main(String[] args) {

       AbstractApplicationContext ctx = new ClassPathXmlApplicationContext("Bean.xml");

       PersonService personService=(PersonService) ctx.getBean("PersonServiceBean");

       personService.save("Shiki");

       System.out.println();

       personService.getPersonName(2);

    }

}

 

基於XML配置方式

1.   與前一個基於註解的方式所使用的文件基本相同(接口和抽象類保持不變),只是在切面類中不再寫入註解了,是一個只有各種通知方法的類

publicclass MyInterceptor_XML {

 

    privatevoid anyMethod() {}

   

    publicvoid doAccessCheck(String name) {

       System.out.println("前置通知:"+ name);

    }

   

    publicvoid doAfterReturning(String result) {

       System.out.println("後置通知:"+ result);

    }

   

    publicvoid doAfter() {

       System.out.println("最終通知");

    }

   

    publicvoid doAfterThrowing(Exception e) {

       System.out.println("例外通知:"+ e);

    }

   

    public ObjectdoBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {

       //if(){//判斷用戶是否在權限這個環繞通知很適合於做權限驗證如果用戶存在,然後執行被攔截的方法

       System.out.println("進入方法");

       Object result = pjp.proceed();//表示被攔截的方法

       System.out.println("退出方法");

       //}

       return result;

    }

}

2.   在Bean.xml文件中寫入配置信息

<bean id="personServiceBean" class="com.aop.PersonServiceBean"></bean>

    <bean id="aspectBean"class="com.aop.MyInterceptor_XML"></bean>

    <aop:config>

    <aop:aspect id="myaop" ref="aspectBean">

        <aop:pointcut

id="mycut" expression="execution(* com.aop..*.*(..))"/>

           <aop:before pointcut-ref="mycut" method="doAccessCheck"/>

           <aop:after-returning

pointcut-ref="mycut" method="doAfterReturning"/>

           <aop:after pointcut-ref="mycut" method="doAfter"/>

           <aop:after-throwing

pointcut-ref="mycut" method="doAfterThrowing"/>

           <aop:around

pointcut-ref="mycut" method="doBasicProfiling"/>

    </aop:aspect>

    </aop:config>

 

 

----------------------Springjdbc整合(配置數據源)---------------------

1.    配置數據源共有兩種方法

 

Bean.xml配置文件中配置數據源

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"destroy-method="close">

    <property name="driverClassName"value="org.gjt.mm.mysql.Driver"/>

<property name="url" value="jdbc:mysql://localhost:3306/hibernate?useUnicode=true&amp;characterEncoding=UTF-8"/>

    <property name="username"value="root"/>

    <property name="password"value="root"/>

   <property name="initialSize" value="1"/>

    <!-- 連接池啓動時的初始值 -->

    <property name="maxActive"value="500"/>

    <!-- 連接池的最大值 -->

    <property name="maxIdle"value="2"/>

    <!-- 最大空閒值.當經過一個高峯時間後,連接池可以慢慢將已經用不到的連接慢慢釋放一部分,一直減少到maxIdle爲止 -->

    <property name="minIdle"value="1"/>

    <!--  最小空閒值.當空閒的連接數少於閥值時,連接池就會預申請去一些連接,以免洪峯來時來不及申請 -->

</bean>

 

使用屬性佔位符方式配置數據源

使用<context:property-placeholderlocation=“jdbc.properties”/>屬性佔位符

將屬性文件的信息引入配置文件中

<context:property-placeholder location="jdbc.properties"/>

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"destroy-method="close">

   <property name="driverClassName"value="${driverClassName}"/>

    <property name="url" value="${url}"/>

    <property name="username"value="${username}"/>

    <property name="password"value="${password}"/>

     <!-- 連接池啓動時的初始值 -->

     <property name="initialSize"value="${initialSize}"/>

     <!-- 連接池的最大值 -->

     <property name="maxActive"value="${maxActive}"/>

     <!-- 最大空閒值.當經過一個高峯時間後,連接池可以慢慢將已經用不到的連接慢慢釋放一部分,一直減少到maxIdle爲止 -->

     <property name="maxIdle"value="${maxIdle}"/>

     <!--  最小空閒值.當空閒的連接數少於閥值時,連接池就會預申請去一些連接,以免洪峯來時來不及申請 -->

     <property name="minIdle"value="${minIdle}"/>

  </bean>

 

2.   創建數據表

CREATETABLE `person` (

  `id` int(11) NOT NULL auto_increment,

  `name` varchar(20) NOT NULL,

  PRIMARY KEY (`id`)

)ENGINE=InnoDB DEFAULT CHARSET=gbk;

3.   創建javabean

publicclass Person {

    private Integer id;

    private String name;

   

    public Person(){}

   

    public Person(String name) {

       this.name = name;

    }

    public Integer getId() {

       returnid;

    }

    publicvoid setId(Integer id) {

       this.id = id;

    }

    public String getName() {

       returnname;

    }

    publicvoid setName(String name) {

       this.name = name;

    }

}

4.   創建界面及實現類

publicinterface PersonService {

    /**

     *保存person

     *@paramperson

     */

    publicvoid save(Person person);

    /**

     *更新person

     *@paramperson

     */

    publicvoid update(Person person);

    /**

     *獲取person

     *@parampersonid

     *@return

     */

    public Person getPerson(Integerpersonid);

    /**

     *獲取所有person

     *@return

     */

    public List<Person>getPersons();

    /**

     *刪除指定idperson

     *@parampersonid

     */

    publicvoid delete(Integer personid);

}

 

@Transactional

publicclass PersonServiceBean implements PersonService {

    private JdbcTemplate jdbcTemplate;

   

    publicvoid setDataSource(DataSource dataSource) {

       this.jdbcTemplate = new JdbcTemplate(dataSource);

    }

 

    publicvoid delete(Integer personid) {

       jdbcTemplate.update("delete from person where id=?", new Object[]{personid},newint[]{java.sql.Types.INTEGER});

       //所有方法的參數基本類似,第一個爲sql語句,第二個是給?傳遞的參數,第三個是?參數的類型碼

    }

 

    public Person getPerson(Integerpersonid) {     

       return (Person)jdbcTemplate.queryForObject("select * from person where id=?", new Object[]{personid}, newint[]{java.sql.Types.INTEGER}, new PersonRowMapper());

    }

 

    @SuppressWarnings("unchecked")

    public List<Person> getPersons() {

       return(List<Person>)jdbcTemplate.query("select* from person", newPersonRowMapper());

       //由於sql語句中沒有參數,所以也就不存在參數類型碼的問題了

    }

 

    publicvoid save(Person person) {

       jdbcTemplate.update("insert into person(name) values(?)", new Object[]{person.getName()},

              newint[]{java.sql.Types.VARCHAR});

    }

 

    publicvoid update(Person person) {

       jdbcTemplate.update("update person set name=? where id=?", new Object[]{person.getName(), person.getId()},

              newint[]{java.sql.Types.VARCHAR, java.sql.Types.INTEGER});

    }

}

5.   創建查詢操作中所使用到的PersonRowMapper類

publicclass PersonRowMapper implements RowMapper {

 

    public Object mapRow(ResultSet rs, int index) throws SQLException {

       Person person = new Person(rs.getString("name"));

       person.setId(rs.getInt("id"));

       return person;

    }

}

6.   創建Junit測試類

publicclass PersonServiceTest {

    privatestatic PersonService personService;

 

    @BeforeClass

    publicstaticvoid setUpBeforeClass() throws Exception {

       try {

           ApplicationContext cxt = new ClassPathXmlApplicationContext("jdbcTemplate_properties_Bean.xml");

           personService = (PersonService) cxt.getBean("personService");

       } catch (RuntimeException e) {

           e.printStackTrace();

       }

    }

 

    @Testpublicvoid save(){

       for(int i=0; i<5; i++)

           personService.save(new Person("傳智播客"+ i));

    }

   

    @Testpublicvoid getPerson(){

       Person person = personService.getPerson(1);

       System.out.println(person.getName());

    }

   

    @Testpublicvoid update(){

       Person person = personService.getPerson(1);

       person.setName("xx");

       personService.update(person);

    }

   

    @Testpublicvoid delete(){

       personService.delete(1);

    }

   

    @Testpublicvoid getBeans(){

       for(Person person : personService.getPersons()){

           System.out.println(person.getName());

       }

    }

}

7.    對Spring的事務管理進行測試,查看當拋出不同異常時是否進行回滾操作(以刪除方法爲例)

1)拋出一個運行期例外,

publicvoid delete(Integer personid) {

       jdbcTemplate.update("delete from person where id=?", new Object[]{personid},

              newint[]{java.sql.Types.INTEGER});

       thrownew RuntimeException("運行期例外");

}

程序回滾

2)拋出一個Check這種例外(就是普通的Exception)

publicvoid delete(Integer personid) throws Exception{

       jdbcTemplate.update("delete from person where id=?", new Object[]{personid},

              newint[]{java.sql.Types.INTEGER});

       thrownew Exception("異常");

}

程序不回滾,雖然最後拋出異常,但是之前方法會執行

3)Spring開啓的事務默認情況下如果碰到運行期例外,運行期例外一般也叫做unchecked例外,這種例外事務會回滾,還有一種叫做checked例外,這種例外事務不會回滾

如果想讓checked例外的方法進行回滾操作,可以使用@Transactional()註解來修改它的行爲

@Transactional(rollbackFor=Exception.class)

    publicvoid delete(Integer personid) throws Exception{

       jdbcTemplate.update("delete from person where id=?", new Object[]{personid},

              newint[]{java.sql.Types.INTEGER});

       thrownew Exception("運行期例外");

}

rollbackFor這個屬性指定了即使出現了checked這種例外,它也會對這種事務進行回滾

相反,如果拋出的是運行期例外,可以使用noRollbackFor指定它不進行回滾

@Transactional(noRollbackFor=RuntimeException.class)

    publicvoid delete(Integer personid) throws Exception{

       jdbcTemplate.update("delete from person where id=?", new Object[]{personid},

              newint[]{java.sql.Types.INTEGER});

       thrownew RuntimeException("運行期例外");

}

8.   有的方法不需要事務的支持,例如像查詢方法等,可以聲明該方法不支持事務

@Transactional(propagation=Propagation.NOT_SUPPORTED)

    public Person getPerson(Integerpersonid) {     

       return (Person)jdbcTemplate.queryForObject("select * from person where id=?", new Object[]{personid},

              newint[]{java.sql.Types.INTEGER}, newPersonRowMapper());

}

當我們打入了這個事務傳播屬性時,Spring容器在方法執行前就不會開啓事務了

 

 

----------------------Springjdbc整合(配置事務)----------------------

1.    配置事務時,需要在xml配置文件中引入用於聲明事務的tx命名空間

事務的配置方式有兩種:註解方式和基於XML配置方式。

 

Bean.xml配置文件中引入用於聲明事務的tx命名空間

<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:context="http://www.springframework.org/schema/context"

       xmlns:tx="http://www.springframework.org/schema/tx"

       xsi:schemaLocation="http://www.springframework.org/schema/beans

          http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

http://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx-2.5.xsd

http://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-2.5.xsd

http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-2.5.xsd">

 

採用註解方式配置事務

創建Spring容器的事務管理器

<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

    <property name="dataSource" ref="dataSource"/>

</bean>

添加解析註解的處理器

<tx:annotation-driventransaction-manager="txManager"/>

註解本身不能開啓事務,只能起到配置的作用,之所以註解能夠起作用,是因爲添加了可以解析註解的處理器,transaction-manager用來指定一個事務管理器

在類中添加事務的註解

@Service@Transactional

publicclass PersonServiceBean implements PersonService {

}

可以與自動裝配一起使用,定義該類使用事務處理,並自動掃描創建該類的bean

 

採用XML方式配置事務

依然使用com.jdbcTemplate包下的類文件

修改Bean.xml配置文件

<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

<property name="dataSource"ref="dataSource"/>

</bean>

 

<aop:config>

    <aop:pointcut id="transactionPointcut"expression="execution(* cn.itcast.service..*.*(..))"/>

    <aop:advisor advice-ref="txAdvice" pointcut-ref="transactionPointcut"/>

</aop:config>

<tx:advice id="txAdvice" transaction-manager="txManager">

    <tx:attributes>

<tx:method name="get*"read-only="true" propagation="NOT_SUPPORTED"/>

<tx:method name="*"/>

    </tx:attributes>

</tx:advice>

 

<bean id="personService" class="cn.itcast.service.impl.PersonServiceBean">

<property name="dataSource"ref="dataSource"/>

</bean>

 

 

---------------------------事務管理與傳播屬性---------------------------

事務的級別

1.   REQUIRED:業務方法需要在一個事務中運行。如果方法運行時,已經處在一個事務中,那麼加入到該事務,否則爲自己創建一個新的事務。

2.    NOT_SUPPORTED:聲明方法不需要事務。如果方法沒有關聯到一個事務,容器不會爲它開啓事務。如果方法在一個事務中被調用,該事務會被掛起,在方法調用結束後,原先的事務便會恢復執行。

3.    REQUIRESNEW:屬性表明不管是否存在事務,業務方法總會爲自己發起一個新的事務。如果方法已經運行在一個事務中,則原有事務會被掛起,新的事務會被創建,直到方法執行結束,新事務纔算結束,原先的事務纔會恢復執行。

4.   MANDATORY:該屬性指定業務方法只能在一個已經存在的事務中執行,業務方法不能發起自己的事務。如果業務方法在沒有事務的環境下調用,容器就會拋出例外。

5.   SUPPORTS:這一事務屬性表明,如果業務方法在某個事務範圍內被調用,則方法成爲該事務的一部分。如果業務方法在事務範圍外被調用,則方法在沒有事務的環境下執行。

6.   Never:指定業務方法絕對不能在事務範圍內執行。如果業務方法在某個事務中執行,容器會拋出例外,只有業務方法沒有關聯到任何事務,才能正常執行。

7.   NESTED如果一個活動的事務存在,則運行在一個嵌套的事務中. 如果沒有活動事務, 則按REQUIRED屬性執行.它使用了一個單獨的事務,這個事務擁有多個可以回滾的保存點。內部事務的回滾不會對外部事務造成影響。它只對DataSourceTransactionManager事務管理器起效,

增刪改查的方法默認的事務級別不同:

Update方法默認的事務傳播屬性爲NESTED

 

 

------------------------------事務隔離級別-----------------------------

數據庫系統提供了四種事務隔離級別供用戶選擇。不同的隔離級別採用不同的鎖類型來實現,在四種隔離級別中,Serializable的隔離級別最高,Read Uncommited的隔離級別最低。大多數據庫默認的隔離級別爲Read Commited,如SqlServer,當然也有少部分數據庫默認的隔離級別爲Repeatable Read ,如Mysql

Read Uncommited:讀未提交數據(會出現髒讀,不可重複讀和幻讀)

Read Commited:讀已提交數據(會出現不可重複讀和幻讀)

Repeatable Read:可重複讀(會出現幻讀)

Serializable:串行化

髒讀:一個事務讀取到另一事務未提交的更新新據。

不可重複讀:在同一事務中,多次讀取同一數據返回的結果有所不同。換句話說就是,後續讀取可以讀到另一事務已提交的更新數據。相反,“可重複讀”在同一事務中多次讀取數據時,能夠保證所讀數據一樣,也就是,後續讀取不能讀到另一事務已提交的更新數據。

幻讀:一個事務讀取到另一事務已提交的insert數據。

 

 

---------Spring2.x+Hibernate3.x+Struts1.x整合開發(Jar包的選擇)---------

hibernate核心安裝包下的:

hibernate3.jar

lib/required/*.jar

lib/optional/ehcache-1.2.3.jar

hibernate 註解安裝包下的

lib/test/slf4j-log4j12.jar

Spring安裝包下的

dist/spring.jar

dist/modules/spring-webmvc-struts.jar

lib/jakarta-commons/commons-logging.jar

commons-dbcp.jarcommons-pool.jar

lib/aspectj/aspectjweaver.jaraspectjrt.jar

lib/cglib/cglib-nodep-2.1_3.jar

lib/j2ee/common-annotations.jar

lib/log4j/log4j-1.2.15.jar

Struts

下載struts-1.3.8-lib.zip,需要使用到解壓目錄下的所有jar,建議把jstl-1.0.2.jarstandard-1.0.2.jar更換爲1.1版本。Spring中已經存在一個antlr-2.7.6.jar,所以把struts中的antlr-2.7.2.jar刪除,避免jar衝突。

數據庫驅動jar

根據所使用的數據庫來選擇

 

 

------------------------Spring2.x+Hibernate3.x-----------------------

Hibernate的配置文件可以不寫,完全依靠Spring的Bean.xml文件進行配置,由Spring來構造sessionFactory和TransactionManager的實例

以Student程序爲例

1.   載入jar包

2.   創建數據表

CREATETABLE `student` (

  `id` int(11) NOT NULL auto_increment,

  `name` varchar(20) NOT NULL,

  PRIMARY KEY (`id`)

)ENGINE=InnoDB DEFAULT CHARSET=gbk;

 

3.   創建javabean和*.hbm.xml

publicclass Student implements java.io.Serializable {

 

    private Integer id;

    private String name;

 

    public Integer getId() {

       returnthis.id;

    }

    publicvoid setId(Integer id) {

       this.id = id;

    }

    public String getName() {

       returnthis.name;

    }

    publicvoid setName(String name) {

       this.name = name;

    }

}

<hibernate-mapping>

    <class name="com.bean.Student"table="student" catalog="hibernate">

        <id name="id"type="java.lang.Integer">

            <column name="id"/>

            <generator class="native"/>

        </id>

        <property name="name"type="java.lang.String">

            <column name="name"length="20" not-null="true"/>

        </property>

    </class>

</hibernate-mapping>

4.   配置Bean.xml文件

(1) 配置數據源,目地是爲了提高性能,減少鏈接對象的創建數量

<bean id="dataSource"

       class="org.apache.commons.dbcp.BasicDataSource">

       <property name="driverClassName"

           value="com.mysql.jdbc.Driver">

       </property>

<property name="url"

value="jdbc:mysql://localhost:3306/itcast?useUnicode=true&amp;characterEncoding=UTF-8">

</property>

       <property name="username"value="root"></property>

       <property name="password"value="root"></property>

    </bean>

在url中,應該是寫爲useUnicode=true&characterEncoding=UTF-8,但是&號是xml文件中的特殊字符,要對它進行轉義爲&amp;characterEncoding=UTF-8指定了客戶端使用什麼編碼與數據庫進行通訊

(2) 創建sessionFactory

其中所使用的LocalSessionFactoryBean類除了會創建一個單例的sessionFactory對象之外,專門集成Hibernate做一些額外的工作,例如:接管Hibernate的事務管理

    <bean id="sessionFactory"class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">

        <property name="dataSource" ref="dataSource"></property>

       <property name="hibernateProperties">

           <value>

                 hibernate.dialect=org.hibernate.dialect.MySQL5Dialect

                 hibernate.hbm2ddl.auto=update

                hibernate.show_sql=true

                 hibernate.format_sql=true

            </value>

        </property>

       <property name="mappingResources">

        <list>

                <value>com/bean/Student.hbm.xml</value>

        </list>

       </property>

    </bean>

hibernateProperties配置Hibernate的屬性,mappingResources指定Hibernate的映射檔

(3) 創建DAO類的實例

    <bean id="studentDAO"class="com.dao.StudentDAO">

       <property name="sessionFactory"ref="sessionFactory">

</property>

    </bean>

(4) 事務管理

    <bean id="txManager"class="org.springframework.orm.hibernate3.HibernateTransactionManager">

        <property name="sessionFactory" ref="sessionFactory"/>

    </bean>

    <tx:annotation-driven transaction-manager="txManager"/>

Spring爲我們開啓事務,底層是需要使用session來創建,這裏使用的HibernateTransactionManager是關於Hibernate的事務管理器,因爲它要得到session,通過session來開啓一個事務

需要通過sessionFactory來能得到session,所以就會有一個sessionFactory屬性,用前邊創建好的sessionFactory實例給該屬性注入進去

5.   創建DAO類,抽取接口(並添加事務管理的註解)

publicclass StudentDAO extends HibernateDaoSupport implements StudentDAOService{

 

    publicvoid saveStudent(Student s){

       HibernateTemplate template=this.getHibernateTemplate();

       template.save(s);

    }

}

界面

publicinterface StudentDAOService {

}

6.   測試類

publicstaticvoid main(String[] args) {

       Student s=new Student();

       s.setName("Shiki");

       ApplicationContext ctx=new ClassPathXmlApplicationContext("Bean.xml");

       StudentDAO dao=(StudentDAO)ctx.getBean("studentDAO");

       dao.saveStudent(s);

}

 

 

--------------------------Spring 2.x+Struts1.x-----------------------

Struts與Spring集成方案共有兩種方式:

1.   Struts集成Spring

2.   Spring集成Struts

 

 

------------------------Struts 1.x集成Spring2.x----------------------

1.   載入jar包

2.   在web容器中實例化spring容器(利用Listener監聽器來實例化)

在web.xml文件中加入

<!-- 指定spring的配置文件,默認從web根目錄尋找配置文件,我們可以通過spring提供的classpath:前綴指定從類路徑下尋找 -->

<context-param>

    <param-name>contextConfigLocation</param-name>

    <param-value>classpath:beans.xml</param-value>

</context-param>

    <!-- Spring容器進行實例化 -->

<listener>

<listener-class>

org.springframework.web.context.ContextLoaderListener

</listener-class>

</listener>

Listener在啓動的時候會向application中放入Spring容器實例,也就是可以從application獲取到Spring的容器實例,從而調用getBean方法

3.   在web容器中配置struts,

<servlet>

    <servlet-name>struts</servlet-name>

    <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>

    <init-param>

       <param-name>config</param-name>

       <param-value>/WEB-INF/struts-config.xml</param-value>

    </init-param>

    <load-on-startup>0</load-on-startup>

</servlet>

<servlet-mapping>

    <servlet-name>struts</servlet-name>

    <url-pattern>*.do</url-pattern>

</servlet-mapping>

4.   創建Action,併爲該Action在struts-config.xml文件中添加配置信息

publicclass PersonAction extends Action {

 

@Override

public ActionForward execute(ActionMapping mapping, ActionFormarg1, HttpServletRequest request, HttpServletResponse response) throws Exception {

      

       WebApplicationContext ctx=

WebApplicationContextUtils.getWebApplicationContext(this.getServlet().getServletContext());

       PersonService personService=

(PersonService)ctx.getBean("personService");

       request.setAttribute("person",personService.getPersons());

       return mapping.findForward("list");

    }

}

 

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE struts-config PUBLIC

    "-//Apache Software Foundation//DTD Struts Configuration1.1//EN"

    "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">

 

<struts-config>

 

  <action-mappings>

    <action path="/person"

           type="com.Spring2.x_Struts1.x.PersonAction.java"

           validate="false">

           <forward name="list" path="/WEB-INF/page/personlist.jsp"></forward>

    </action>

  </action-mappings>

 

</struts-config>

5.    在Spring配置文件中構造bean實例

<bean id="personService" class="com.jdbcTemplate.PersonServiceBean">

       <property name="dataSource" ref="dataSource"/>

 </bean>

引用com.jdbcTemplate包中PersonPersonServicePersonServiceBean

6.    編寫personlist.jsp頁面,在頁面中迭代出person中的數

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>

<%@taglib uri="http://struts.apache.org/tags-bean"prefix="bean" %>

<%@taglib uri="http://struts.apache.org/tags-logic"prefix="logic" %>

<%@ taglib uri="http://struts.apache.org/tags-html"prefix="html" %>

<%@ taglib uri="http://struts.apache.org/tags-tiles"prefix="tiles" %>

 

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html>

  <head>

  </head>

 

  <body>

    <logic:iterate id="person"name="person" scope="request">

       id=<bean:writename="id"/>,name=<bean:write name="name"/>

    </logic:iterate>

  </body>

</html>

 

 

-----------------------Spring 2.x集成Struts1.x -----------------------

1.   試想,在Struts集成Spring的Action中,

WebApplicationContext ctx=

WebApplicationContextUtils.getWebApplicationContext(this.getServlet().getServletContext());

       PersonService personService=

(PersonService)ctx.getBean("personService");

這段複雜的代碼,我們是否可以使用Spring的依賴注入功能來實現呢?

所以,我們打算將Action交給Spring管理,然後Action就不再由Struts框架給我們生成,而是由Spring容器幫我們創建

把Action交給Spring管理後,我們可以使用依賴注入在action中注入業務層的bean。

但要注意一點,要確保Action的path屬性值與bean的名稱相同。

Struts配置:

<action path="/person/list" ...>

</action>

Spring 配置:

<bean name="/person/list" class="cn.itcast.web.action.PersonAction"/>

默認配置bean的名稱爲id屬性,但是/屬於特殊字符,所以只能使用name屬性來配置

2.   在struts配置文件中添加進spring的請求控制器,該請求控制器會先根據action的path屬性值到spring容器中尋找跟該屬性值同名的bean。如果尋找到即使用該bean處理用戶請求

<controller>

<set-propertyproperty="processorClass"

value="org.springframework.web.struts.DelegatingRequestProcesso"/>

</controller>

請求管理器org.springframework.web.struts.DelegatingRequestProcessor的處理過程。

當處理器根據/person/list.do這個名稱到Spring容器中找不到這個bean的時候,就會交給Struts進行處理了,Struts就會根據action中的type屬性將Action對象new出來,放在緩存裏邊去

 

 

----------------------Spring 3.0 Security安全框架----------------------

 

1.   加入Security的jar包

2.   修改web.xml配置文件

<?xml version="1.0"encoding="UTF-8"?>

<web-app version="2.5"xmlns="http://java.sun.com/xml/ns/javaee"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee

    http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

    <!-- 安全框架的配置 -->

    <filter>

       <filter-name>springSecurityFilterChain</filter-name><!-- 名子不能更換 -->

       <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>

    </filter>

    <filter-mapping>

       <filter-name>springSecurityFilterChain</filter-name>

       <url-pattern>/*</url-pattern><!-- 過濾所有的請求操作 -->

    </filter-mapping>

 

<!-- Spring的配置 ,加載Spring的配置文件-->

    <context-param>

       <param-name>contextConfigLocation</param-name>

       <param-value>/WEB-INF/applicationContext.xml</param-value>

    </context-param>

 

    <listener>

       <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

    </listener>

 

    <welcome-file-list>

       <welcome-file>index.jsp</welcome-file>

    </welcome-file-list>

</web-app>

3.   在Spring的配置文件中加入Security的命名空間

<?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:security="http://www.springframework.org/schema/security"

    xmlns:p="http://www.springframework.org/schema/p"

    xsi:schemaLocation="http://www.springframework.org/schema/beans

    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

    http://www.springframework.org/schema/security

    http://www.springframework.org/schema/security/spring-security-3.0.xsd">

</beans>

4.    配置Spring配置文件

<!-- 安全級別的設定 -->

    <security:http auto-config="true">

       <security:intercept-url pattern="/**" access="ROLE_UESR"/>

       <!-- 要求登錄的用戶必須擁有某個角色 -->

    </security:http>

 

security:http表示所有與web命名空間相關的上級,

intercept-url表示要攔截的url形式,比如

<intercept-urlpattern=”/secure/**” access=”isAuthenticated()” />

表示根目錄下的secure目錄需要經過驗證後才能訪問的。

Pattern用來匹配請求當中的內容,access表示模式哪一種權限的使用者

form-login是Spring Security自動爲你生成的一個簡陋的登錄頁面,即使你沒有創建任何登錄頁面,當然你也可以修改,但不建議你修改,因爲你可以不使用默認的,可以採用如下方式:<security:form-login login-page="/login.jsp"/>自定義一個登錄頁面。

 

容易出錯的地方

先看一個小例子:

<http use-expressions="true">

<intercept-url pattern="/secure/extreme/**"

access="hasRole('ROLE_SUPERVISOR')"/>

<intercept-url pattern="/secure/**"access="isAuthenticated()" />

<!—

Disable web URIauthorization, as we're using <global-method-security> and have @Securedthe services layer instead

<intercept-urlpattern="/listAccounts.html" access="isRememberMe()" />

<intercept-urlpattern="/post.html" access="hasRole('ROLE_TELLER')" />

-->

 

<http use-expressions="true">

表示這裏的配置可以使用一種表達式,這種表達式就是類似於isAuthenticated()這樣的寫法,在後面會看到與這種寫法不一樣但同樣可以達到相同效果的寫法。

在Spring翻譯文檔(也是翻譯自官方文檔)或英文官方文檔中,給出的與上面例子功能相似的說明大概是這樣的:

  <httpauto-config='true'>

   <intercept-url pattern="/**" access="ROLE_USER" />

  </http>

注意到與上面例子不同的地方了嗎?

還是注意第一行,這回不是<httpuse-expressions=”true”>而是<http auto-config=’true’>了,而下面的配置

<intercept-urlpattern=”/**” access=”ROLE_USER” />也與上面的寫法不同,事實上如果是<httpuse-expressions=”true”>的話,這句access=”ROLE_USER”就是錯的,正確的寫法就應該爲:hasRole(‘ROLE_USER’)。

這不得不說這是Spring Security的文檔與例子不搭配的一個低級錯誤,因爲當一個使用者在打開例子又看到文檔說明時,他往往不知道這兩者有何區別,就如同我剛使用的時候一樣,我在使用<httpuse-expressions=”true”>的同時,將access=”ROLE_USER”這種寫法也寫了進來,結果可想而知,報了錯!報錯信息諸如:

org.apache.jasper.JasperException:java.lang.IllegalArgumentException: Failed to evaluate

expression 'ROLE_SUPERVISOR'

就是說use-expressions這種表示法是不認識access=”ROLE_USER”這種寫法的,另外,當使用了<httpuse-expressions=”true”>時,一定要在配置中至少有一個符合use-expressions的表示法,否則就會報類似如下錯誤:

org.springframework.expression.spel.SpelEvaluationException:EL1008E:(pos 0): Field or property

'ROLE_SUPERVISOR' cannot befound on object of type 'org.springframework.security.web.access.

expression.WebSecurityExpressionRoot'

 

    <!-- 認證管理 -->

    <security:authentication-manager alias="auther_manager">

       <security:authentication-provider>

       <!-- 進行密碼的加密操作 -->

       <security:password-encoder hash="md5"/>

           <security:user-service>

              <security:user name="admin"  password="21232f297a57a5a743894a0e4a801fc3"authorities="ROLE_UESR"/><!-- 通過用戶名,設定角色信息 -->

           </security:user-service>

       </security:authentication-provider>

    </security:authentication-manager>

 

    <!-- 添加提示信息的顯示操作 -->

<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">

    <property

name="basename" value="classpath:message_zh_CN">

</property>

</bean>

<!-- 登錄的錯誤的信息,會作爲異常存放在sesssion,key : SPRING_SECURITY_LAST_EXCEPTION -->

 

5.   在這個例子中

<security:user name="admin"  password="21232f297a57a5a743894a0e4a801fc3"authorities="ROLE_UESR"/> security:user卷標中的password屬性,對密碼的信息進行了加密--MD5加密

 

在登錄頁面中輸入密碼後,安全框架自動對密碼進行MD5加密,然後在與配置文件中的密碼進行比較

那麼如何生成一個字符串的MD5碼呢

import org.springframework.security.authentication.encoding.Md5PasswordEncoder;

import sun.security.provider.MD5;

 

publicclass Test {

    publicstaticvoid main(String[] args) {

       Md5PasswordEncoder encoder=new Md5PasswordEncoder();

       String s = encoder.encodePassword("xxx", null);

       System.out.println(s);

    }

}

6.    創建index.jsp檔,用來測試一下Spring的安全框架是否配置成功

<%@ page language="java"import="java.util.*" pageEncoding="gbk"%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTDHTML 4.01 Transitional//EN">

<html>

  <head>

  </head>

  <body>

        這是首頁 ,歡迎你!!!!

        <br>

        經歷Spring Security框架後,Session的屬性

        <br>

        ${sessionScope.SPRING_SECURITY_LAST_USERNAME}

        ${sessionScope.SPRING_SECURITY_CONTEXT}

  </body>

</html>

當有請求到來的時候,Spring Security框架開始查找要訪問的資源是否存在訪問的權利

要訪問index.jsp頁面時,自動產生一個登錄的頁面,如果登錄成功,也就是表示具有訪問的權利,就可以連結到index.jsp頁面了

7.    由自己來編寫login.jsp登錄頁面,不使用安全框架所生成的登錄頁面

在瀏覽器中進入登錄頁面後,右鍵選擇”查看源文件”,可以看到由安全框架所生成的登錄頁面的一些信息,例如form中action的配置,input中name的寫法等

 

<html>

  <head>

   </head>

  <body>

    <h3>用戶的登錄</h3>

    <font color=red>

    </font><br>

    <form action="/SS/j_spring_security_check" method="post">

        用戶名:<input type="text" name="j_username"><br>

        密碼:<input type="password" name="j_password"><br>

        <input type="submit" value="登錄">

    </form>

   

  </body>

</html>

 

action="/SS/j_spring_security_check"

name="j_username"

這些信息都有由安全框架規定的格式,所以我們就可以依靠此配置信息來創建基於安全框架的自己的登錄頁面

 

在Spring的配置文件中配置有關login.jsp登錄頁面的內容

<!-- 安全級別的設定 -->

    <security:http auto-config="true">

       <security:form-login login-page="/login.jsp"/>

       <!-- 配置如果沒有權限,定位登錄頁面 -->

       <security:intercept-url pattern="/login.jsp" filters="none"/>

       <!-- 將登錄的頁面,設置成爲非過濾狀態 -->

       <security:intercept-url pattern="/**" access="ROLE_UESR"/>

       <!-- 要求登錄的用戶必須擁有某個角色 -->

    </security:http>

 

創建login.jsp

<%@ page language="java" import="java.util.*" pageEncoding="gbk"%>

<html>

  <head>

   </head>

  <body>

    <h3>用戶的登錄</h3>

    <font color=red>

    ${sessionScope.SPRING_SECURITY_LAST_EXCEPTION.message}</font><br>

    <form action="${pageContext.request.contextPath }/j_spring_security_check"method="post">

        用戶名:<input type="text" name="j_username"><br>

        密碼:<input type="password" name="j_password"><br>

        <input type="submit" value="登錄">

    </form>

  </body>

</html>

 

再次進行測試,但是會發現一個新的問題

如果輸入的內容不正確,不會產生任何提示,那麼如何顯示錯誤信息呢?

這樣就需要使用安全框架所提供的”國際化輸出”功能來實現

安全框架中把所有的錯誤都綁定爲了一個異常,而且提供了一個國際化文件,該文件的位置在

spring-security-core-3.0.2.RELEASE/org/springframework/security/路徑下

會有很多不同語言版本的資源文件

其中messages_zh_CN.properties就是中文的資源文件

找到該檔後,繼續對Spring的配置文件添加國際化輸出的內容

<!-- 添加提示信息的顯示操作 -->

<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">

<property name="basename"value="classpath:message_zh_CN">

</property>

<property name="basename"value="classpath:org.springframework.security/message_zh_CN">

</property>

</bean>

<!-- 登錄的錯誤的信息,會作爲異常存放在sesssion  ,key :SPRING_SECURITY_LAST_EXCEPTION -->

 

在登錄頁面中來獲取該國際化輸出信息,用一個表達式來封裝,如果登錄不成功就提示出來

<font color=red>

${sessionScope.SPRING_SECURITY_LAST_EXCEPTION.message}

</font><br>

但是又會有一個問題出現,如果用戶名或密碼錯誤,提示的信息是”壞的憑證”,那麼用戶有如何能看得懂呢?

可以自己重寫編寫一個配置文件,但是key必須與原先的資源文件中使用的key對應起來,且必須明白其key的含義,最後將key與自己編寫的中文的value對應起來,放入新的檔中

再Spring的配置文件中,修改配置文件的路徑,例如:

message_zh_CN.properties文件

AbstractUserDetailsAuthenticationProvider.badCredentials=/u60A8/u65E0/u6743/u8BBF/u95EE/u8BE5/u8D44/u6E90,/u8BF7/u767B/u5F55

將該文件放置在src目錄下,配置其路徑爲:

<property name="basename" value="classpath:message_zh_CN"></property>

8.    將配置文件中配置的用戶名和密碼等信息保存在數據庫中,以便實現對數據庫的操作

創建數據表,添加資料

創建數據庫表時要注意,users表和authorities是安全框架中自帶的用戶表和權限表,表中的字段名一定不能自定義,要與安全框架中指定的名稱相同,否則會出現不匹配的問題

/*用戶表*/

create table users(

    username varchar(50) primary key,

    password varchar(50) not null,

    enable` tinyint(1) not null,

);

/*權限表*/

create table authorities(

    username varchar(50) not null,

    authority varchar(50) not null,

    constraint fk_author_users foreign key (username) referencesusers(username)

);

/*創建索引*/

create unique indexix_auth_username on authorities(username,authority)

/*插入相關的資料*/

insert intousers(username,password,enable) values('admin','21232f297a57a5a743894a0e4a801fc3',1);

insert intousers(username,password,enable) values('user','ee11cbb19052e40b07aac0ca060c23ee',1);

 

insert into authorities values('admin','ROLE_ADMIN');

insert into authorities values('user','ROLE_USER');

 

在Spring的配置文件中,創建數據源(注意建表時字段的名子)

<!-- 創建數據源 -->

<bean id="dataSource"       class="org.springframework.jdbc.datasource.DriverManagerDataSource>

<!-- 更換成爲Spring的數據源管理 -->

<property name="driverClassName" value="com.mysql.jdbc.Driver"/>

<property name="url" value="jdbc:mysql://localhost:3306/test"/>

<property name="username" value="root"></property>

<property name="password" value="1234"></property>

</bean>

 

修改Spring的配置文件,去掉”認證管理”部分,添加”新的認證管理

存儲數據是,注意是否使用MD5的加密操作,配合Spring的認證權限的設置

<!--新的認證管理(新的數據庫的驗證) -->

    <security:authentication-manager alias="manager">

      

       <security:authentication-provider>

           <security:password-encoder hash="md5" />

              <security:jdbc-user-servicedata-source-ref="dataSource"/> 

       </security:authentication-provider>

    </security:authentication-manager>

 

修改index.jsp頁面的內容

<%@ page language="java" import="java.util.*" pageEncoding="gbk"%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html>

  <head>

  </head>

  <body>

        這是首頁 ,歡迎你!!!!

        <br>

        <a href="admin.jsp">進入admin.jsp頁面</a>

  </body>

</html>

 

添加一個admin.jsp頁面

<%@ page language="java" import="java.util.*" pageEncoding="gbk"%>

<html>

  <head>

   </head>

  <body>

    this is Admin page

  </body>

</html>

 

修改Spring的配置文件,重新設定安全級別,當用戶通過登錄進入index.jsp後,只有ROLE_ADMIN權限的用戶可以進入admin.jsp,其他權限均不可以

<!-- 安全級別的設定 -->

<security:httpauto-config="true">

<!-- 配置如果沒有權限,定位登錄頁面 -->

<security:form-login login-page="/login.jsp" />

<!-- 將登錄的頁面,設置成爲非過濾狀態 -->

<security:intercept-url pattern="/login.jsp" filters="none"/>

<!-- 該頁面不進行權限的驗證 -->

<security:intercept-url pattern="/admin.jsp" access="ROLE_ADMIN"/>

<!--必須是admin用戶才能訪問  -->

<security:intercept-url pattern="/**" access="ROLE_USER"/>

<!-- 過濾所有的請求 -->

</security:http>

 

所以按照上述的過程配置後,運行會出錯

這是由於沒有配置首頁,也就是index.jsp對訪問者的權限操作,重新修改配置文件中的安全級別的設定

<!-- 安全級別的設定 -->

<security:httpauto-config="true">

<!-- 配置如果沒有權限,定位登錄頁面 -->

<security:form-login login-page="/login.jsp" />

<!-- 將登錄的頁面,設置成爲非過濾狀態 -->

<security:intercept-url pattern="/login.jsp" filters="none"/>

<!-- 該頁面不進行權限的驗證 -->

<security:intercept-url pattern="/admin.jsp" access="ROLE_ADMIN"/>

<!--必須是admin用戶才能訪問  -->

<security:intercept-url pattern="/index.jsp" access="ROLE_ADMIN,ROLE_USER"/>

<!--首頁訪問的權限  -->

<security:intercept-url pattern="/**" access="ROLE_USER"/>

<!-- 過濾所有的請求 -->

</security:http>

 

運行程序後,如果該用戶沒有權限訪問admin.jsp頁面,就會報一個異常:

HTTP Status 403 – Access is denied

那麼我們可以自己定義一個accessAdmin.jsp錯誤頁面來提示使用者沒有權限登錄

<%@ page language="java" import="java.util.*" pageEncoding="gbk"%>

<html>

  <head>

   </head>

  <body>

    您的訪問權限不足,請更換身份登錄

  </body>

</html>

在Spring的配置文件中,要配置該錯誤頁面

修改安全級別的設定

<security:http

auto-config="true" access-denied-page="/accessAdmin.jsp">

……

</security:http>

access-denied-page="/accessAdmin.jsp"表示如果用戶訪問該接口不具備相應的權限,按麼就會跳轉到access-denied-page屬性所指定的頁面中

 

9.    在頁面中獲取登錄用戶的信息

有兩種方式:使用Shared組件和使用卷標的方式

 

使用Shared組件

<%@ page language="java" import="java.util.*" pageEncoding="gbk"%>

<%@page import="org.springframework.security.core.context.SecurityContext"%>

<%@page import="org.springframework.security.core.context.SecurityContextHolder"%>

<%@page import="org.springframework.security.core.Authentication"%>

<%@page import="org.springframework.security.core.userdetails.UserDetails"%>

 

<html>

  <head>

  </head>

  <body>

    This is admin page. <br>

    <%

    SecurityContextsecContext=SecurityContextHolder.getContext();//獲取SpringSecurity的上下文

    Authentication auth=secContext.getAuthentication();

    //獲取認證的對象

    Object p=auth.getPrincipal();

    //獲取主體物件

    String username="";

    if(p instanceofUserDetails){//UserDetails這個類是屬於權限認證內容的,是Spring安全框架給提供的

        username=((UserDetails)p).getUsername();

    }else{

        username=p.toString();

    }

    out.print(username);

    %>

  </body>

</html>

 

使用標籤的方式

<%@ page language="java" import="java.util.*" pageEncoding="gbk"%>

<%@taglib uri="http://www.springframework.org/security/tags"prefix="sec" %>

<html>

  <head>

  </head>

  <body>

    This is admin page. <br>

    歡迎:<sec:authenticationproperty="name"></sec:authentication>

  </body>

</html>

 

10.  根據使用者權限的不同,讓不同權限的使用者所看到的頁面信息也不同

例如在這個例子中,ROLE_ADMIN權限的用戶登錄後,可以點擊”進入admin.jsp頁面這個連接進入到admin.jsp頁面中,而ROLE_USER權限的用戶就不具備此功能,所以就不能讓此權限的用戶登錄後看到”進入admin.jsp頁面”這個超鏈接

所以要使用AOP面向切面的技術,實現對方法的攔截控制

 

先使用Spring安全框架所提供的卷標來實現此功能,主要用於不同的權限建造不同組件的時候,大多數使用這個,可以直接在頁面中配置,我們來修改admin.jsp文件

<%@ page language="java" import="java.util.*" pageEncoding="gbk"%>

<%@taglib uri="http://www.springframework.org/security/tags"prefix="sec" %>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html>

  <head>

  </head>

  <body>

        這是首頁 ,歡迎你!!!!

        <br>

 

        <sec:authorize ifAllGranted="ROLE_ADMIN">

        <a href="admin.jsp">進入admin.jsp頁面</a>

        </sec:authorize>

        <!-- 表示只有該權限的用戶登錄後,才能在頁面中顯示卷標對中所包含的內容 -->

  </body>

</html>

 

11.  Spring安全框架的web架構是完全基於Servlet的過濾器,其中安全的過濾器鏈可以管理request和response,還可以製作一批次的安全組件

<!-- 配置Spring 過濾器鏈 -->

<security:filter-chain-map path-type="ant">

<security:filter-chain  pattern="/webServices/**"filters="

       securityContextPersistenceFilterWithASFalse,

       basicAuthenticationFilter,

    "/>

</security:filter-chain-map>

Pattern屬性表示要對什麼作爲請求,filters來配置一些標準型過濾器

 

12.  AOP技術的使用,方法權限的控制   

 

 

 

抽象類的使用

在實際的類中,pig繼承類animal

 

 

 

 

Timer任務調度器

每月前10天每隔15分鐘觸發一次

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

   

 

 

 

 

 

Spring的事務配置

Spring配置文件中關於事務配置總是由三個組成部分,分別是DataSource、TransactionManager和代理機制這三部分,無論哪種配置方式,一般變化的只是代理機制這部分。

    DataSource、TransactionManager這兩部分只是會根據數據訪問方式有所變化,比如使用Hibernate進行數據訪問 時,DataSource實際爲SessionFactory,TransactionManager的實現爲 HibernateTransactionManager。

    具體如下圖:

    根據代理機制的不同,總結了五種Spring事務的配置方式,配置文件如下:

    第一種方式:每個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"
    xmlns:context
="http://www.springframework.org/schema/context"
    xmlns:aop
="http://www.springframework.org/schema/aop"
    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
           http://www.springframework.org/schema/context/spring-context-2.5.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">


    
<bean id="sessionFactory"  
            class
="org.springframework.orm.hibernate3.LocalSessionFactoryBean">  
        
<property name="configLocation" value="classpath:hibernate.cfg.xml" />  
        
<property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
    
</bean>  

    
<!-- 定義事務管理器(聲明式的事務) -->  
    
<bean id="transactionManager"
        class
="org.springframework.orm.hibernate3.HibernateTransactionManager">
        
<property name="sessionFactory" ref="sessionFactory" />
    
</bean>
    
    
<!-- 配置DAO -->
    
<bean id="userDaoTarget" class="com.bluesky.spring.dao.UserDaoImpl">
        
<property name="sessionFactory" ref="sessionFactory" />
    
</bean>
    
    
<bean id="userDao"  
        class
="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">  
           
<!-- 配置事務管理器 -->  
           
<property name="transactionManager" ref="transactionManager" />     
        
<property name="target" ref="userDaoTarget" />  
         
<property name="proxyInterfaces" value="com.bluesky.spring.dao.GeneratorDao" />
        
<!-- 配置事務屬性 -->  
        
<property name="transactionAttributes">  
            
<props>  
                
<prop key="*">PROPAGATION_REQUIRED</prop>
            
</props>  
        
</property>  
    
</bean>  
</beans>

    第二種方式:所有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"
    xmlns:context
="http://www.springframework.org/schema/context"
    xmlns:aop
="http://www.springframework.org/schema/aop"
    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
           http://www.springframework.org/schema/context/spring-context-2.5.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">


    
<bean id="sessionFactory"  
            class
="org.springframework.orm.hibernate3.LocalSessionFactoryBean">  
        
<property name="configLocation" value="classpath:hibernate.cfg.xml" />  
        
<property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
    
</bean>  

    
<!-- 定義事務管理器(聲明式的事務) -->  
    
<bean id="transactionManager"
        class
="org.springframework.orm.hibernate3.HibernateTransactionManager">
        
<property name="sessionFactory" ref="sessionFactory" />
    
</bean>
    
    
<bean id="transactionBaseProxy"  
            class
="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"  
            lazy-init
="true" abstract="true">  
        
<!-- 配置事務管理器 -->  
        
<property name="transactionManager" ref="transactionManager" />  
        
<!-- 配置事務屬性 -->  
        
<property name="transactionAttributes">  
            
<props>  
                
<prop key="*">PROPAGATION_REQUIRED</prop>  
            
</props>  
        
</property>  
    
</bean>    
   
    
<!-- 配置DAO -->
    
<bean id="userDaoTarget" class="com.bluesky.spring.dao.UserDaoImpl">
        
<property name="sessionFactory" ref="sessionFactory" />
    
</bean>
    
    
<bean id="userDao" parent="transactionBaseProxy" >  
        
<property name="target" ref="userDaoTarget" />   
    
</bean>
</beans>

第三種方式:使用攔截器

<?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"
    xmlns:aop
="http://www.springframework.org/schema/aop"
    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
           http://www.springframework.org/schema/context/spring-context-2.5.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">


    
<bean id="sessionFactory"  
            class
="org.springframework.orm.hibernate3.LocalSessionFactoryBean">  
        
<property name="configLocation" value="classpath:hibernate.cfg.xml" />  
        
<property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
    
</bean>  

    
<!-- 定義事務管理器(聲明式的事務) -->  
    
<bean id="transactionManager"
        class
="org.springframework.orm.hibernate3.HibernateTransactionManager">
        
<property name="sessionFactory" ref="sessionFactory" />
    
</bean>
   
    
<bean id="transactionInterceptor"  
        class
="org.springframework.transaction.interceptor.TransactionInterceptor">  
        
<property name="transactionManager" ref="transactionManager" />  
        
<!-- 配置事務屬性 -->  
        
<property name="transactionAttributes">  
            
<props>  
                
<prop key="*">PROPAGATION_REQUIRED</prop>  
            
</props>  
        
</property>  
    
</bean>
      
    
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">  
        
<property name="beanNames">  
            
<list>  
                
<value>*Dao</value>
            
</list>  
        
</property>  
        
<property name="interceptorNames">  
            
<list>  
                
<value>transactionInterceptor</value>  
            
</list>  
        
</property>  
    
</bean>  
  
    
<!-- 配置DAO -->
    
<bean id="userDao" class="com.bluesky.spring.dao.UserDaoImpl">
        
<property name="sessionFactory" ref="sessionFactory" />
    
</bean>
</beans>

第四種方式:使用tx卷標配置的攔截器

<?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"
    xmlns:aop
="http://www.springframework.org/schema/aop"
    xmlns:tx
="http://www.springframework.org/schema/tx"
    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
           http://www.springframework.org/schema/context/spring-context-2.5.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">


    
<context:annotation-config />
    
<context:component-scan base-package="com.bluesky" />

    
<bean id="sessionFactory"  
            class
="org.springframework.orm.hibernate3.LocalSessionFactoryBean">  
        
<property name="configLocation" value="classpath:hibernate.cfg.xml" />  
        
<property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
    
</bean>  

    
<!-- 定義事務管理器(聲明式的事務) -->  
    
<bean id="transactionManager"
        class
="org.springframework.orm.hibernate3.HibernateTransactionManager">
        
<property name="sessionFactory" ref="sessionFactory" />
    
</bean>

    
<tx:advice id="txAdvice" transaction-manager="transactionManager">
        
<tx:attributes>
            
<tx:method name="*" propagation="REQUIRED" />
        
</tx:attributes>
    
</tx:advice>
    
    
<aop:config>
        
<aop:pointcut id="interceptorPointCuts"
            expression
="execution(* com.bluesky.spring.dao.*.*(..))" />
        
<aop:advisor advice-ref="txAdvice"
            pointcut-ref
="interceptorPointCuts" />        
    
</aop:config>      
</beans>

第五種方式:全註解

<?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"
    xmlns:aop
="http://www.springframework.org/schema/aop"
    xmlns:tx
="http://www.springframework.org/schema/tx"
    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
           http://www.springframework.org/schema/context/spring-context-2.5.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">


    
<context:annotation-config />
    
<context:component-scan base-package="com.bluesky" />

    
<tx:annotation-driven transaction-manager="transactionManager"/>

    
<bean id="sessionFactory"  
            class
="org.springframework.orm.hibernate3.LocalSessionFactoryBean">  
        
<property name="configLocation" value="classpath:hibernate.cfg.xml" />  
        
<property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
    
</bean>  

    
<!-- 定義事務管理器(聲明式的事務) -->  
    
<bean id="transactionManager"
        class
="org.springframework.orm.hibernate3.HibernateTransactionManager">
        
<property name="sessionFactory" ref="sessionFactory" />
    
</bean>
    
</beans>

此時在DAO上需加上@Transactional註解,如下:

package com.bluesky.spring.dao;

import java.util.List;

import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.stereotype.Component;

import com.bluesky.spring.domain.User;

@Transactional
@Component("userDao")
public class UserDaoImpl extends HibernateDaoSupport implements UserDao {

    
public List<User> listUsers() {
        
return this.getSession().createQuery("from User").list();
    }
    
    
}

 

注意的幾點:

1 @Transactional 只能被應用到public方法上, 對於其它非public的方法,如果標記了@Transactional也不會報錯,但方法沒有事務功能.

2 默認情況下,一個有事務方法, 遇到RuntiomeException會回滾 . 遇到 受檢查的異常不會回滾 的. 要想所有異常都回滾,要加上 @Transactional( rollbackFor={Exception.class,其它異常}) .

@Transactional 的所有可選屬性如下:

屬性

類型

默認值

說明

propagation

Propagation枚舉

REQUIRED

事務傳播屬性 (下有說明)

isolation

isolation枚舉

DEFAULT

事務隔離級別 (另有說明)

readOnly

boolean

false

是否只讀

timeout

int

-1

超時(秒)

rollbackFor

Class[]

{}

需要回滾的異常類

rollbackForClassName

String[]

{}

需要回滾的異常類名

noRollbackFor

Class[]

{}

不需要回滾的異常類

noRollbackForClassName

String[]

{}

不需要回滾的異常類名

事務的隔離級別 有如下可選:

可以去看spring源碼 : org.springframework.transaction.annotation.Isolation

我的"隔離級別"相關文章   有不明白的,可以去看看.

DEFAULT

採用數據庫默認隔離級別

READ_UNCOMMITTED

請看"隔離級別"相關文章

READ_COMMITTED

請看"隔離級別"相關文章

REPEATABLE_READ

請看"隔離級別"相關文章

SERIALIZABLE

請看 "隔離級別"相關文章

 

 

 

 

 

 

 

 

 

 

 

 

 

 

事務的傳播屬性 ,有如下可選

可以去看spring源碼 : org.springframework.transaction.annotation.Propagation

REQUIRED

業務方法需要在一個事務中運行,如果方法運行時,已處在一個事務中,那麼就加入該事務,否則自己創建一個新的事務.這是spring默認的傳播行爲.

SUPPORTS

如果業務方法在某個事務範圍內被調用,則方法成爲該事務的一部分,如果業務方法在事務範圍外被調用,則方法在沒有事務的環境下執行.

MANDATORY

只能在一個已存在事務中執行,業務方法不能發起自己的事務,如果業務方法在沒有事務的環境下調用,就拋異常

REQUIRES_NEW

業務方法總是會爲自己發起一個新的事務,如果方法已運行在一個事務中,則原有事務被掛起,新的事務被創建,直到方法結束,新事務才結束,原先的事務纔會恢復執行.

NOT_SUPPORTED

聲明方法需要事務,如果方法沒有關聯到一個事務,容器不會爲它開啓事務.如果方法在一個事務中被調用,該事務會被掛起,在方法調用結束後,原先的事務便會恢復執行.

NEVER

聲明方法絕對不能在事務範圍內執行,如果方法在某個事務範圍內執行,容器就拋異常.只有沒關聯到事務,才正常執行.

NESTED

如 果一個活動的事務存在,則運行在一個嵌套的事務中.如果沒有活動的事務,則按REQUIRED屬性執行.它使用了一個單獨的事務, 這個事務擁有多個可以回滾的保證點.內部事務回滾不會對外部事務造成影響, 它只對DataSourceTransactionManager 事務管理器起效.

 

 

隔離級別:

數據庫提供了四種事務隔離級別, 不同的隔離級別採用不同的鎖類開來實現.

在四種隔離級別中, Serializable的級別最高, Read Uncommited級別最低.

大多數數據庫的默認隔離級別爲: Read Commited,如Sql Server , Oracle.

少數數據庫默認的隔離級別爲Repeatable Read, 如MySQL InnoDB存儲引擎

即使是最低的級別,也不會出現 第一類 丟失 更新問題 .

  • Read Uncommited :讀未提交數據( 會出現髒讀,不可重複讀,幻讀 ,避免了 第一類丟失 更新 )
  • Read Commited :讀已提交的數據(會出現不可重複讀,幻讀)
  • Repeatable Read :可重複讀(會出現幻讀)
  • Serializable :串行化

丟失 更新   :
當兩個或多個事務選擇同一行,然後基於最初選定的值更新該行時,會發生丟失更新問題。每個事務都不知道其它事務的存在。最後的更新將重寫由其它事務所做的更新,這將導致數據丟失。   
例:
事務A和事務B同時修改某行的值,
1.事務A將數值改爲1並提交
2.事務B將數值改爲2並提交。
這時數據的值爲2,事務A所做的更新將會丟失。
解決辦法:對行加鎖,只允許併發一個更新事務。

髒讀: 一個事務讀到另一個事務未提交的更新數據

例:

1.Mary的原工資爲1000, 財務人員將Mary的工資改爲了8000(但未提交事務)        
2.Mary
讀取自己的工資 ,發現自己的工資變爲了8000,歡天喜地!
3.
而財務發現操作有誤,回滾了事務,Mary的工資又變爲了1000, 像這樣,Mary記取的工資數8000是一個髒資料。

不可重複讀: 在同一個事務中,多次讀取同一數據,返回的結果有所不同. 換句話說就是,後續讀取可以讀到另一個事務已提交的更新數據. 相反"可重複讀"在同一事務多次讀取數據時,能夠保證所讀數據一樣,也就是後續讀取不能讀到另一事務已提交的更新數據.

例:

1.在事務1中,Mary 讀取了自己的工資爲1000,操作並沒有完成
2.在事務2中,這時財務人員修改了Mary的工資爲2000,並提交了事務.
3.在事務1中,Mary 再次讀取自己的工資時,工資變爲了2000
解決辦法:如果只有在修改事務完全提交之後纔可以讀取數據,則可以避免該問題。

幻讀: 一個事務讀取到另一個事務已提交的insert數據.

:

第一個事務對一個表中的數據進行了修改,這種修改涉及到表中的全部資料行。同時 (此時第一事務還未提交) ,第二個事務向表中插入一行新資料。這時第一個事務再去讀取表時,發現表中還有沒有修改的資料行,就好象發生了幻覺一樣。
發佈了45 篇原創文章 · 獲贊 122 · 訪問量 16萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章