Spring 知識點——認識、springIOC的使用方式、事務管理(AOP)

Spring 總結

1、認識Spring

  Spring是一個開源框架,Spring是於2003 年興起的一個輕量級的Java 開發框架,由Rod Johnson 在其著做Expert One-On-One J2EE Development and Design中闡述的部分理念和原型衍生而來。它是爲了解決企業應用開發的複雜性而建立的。(能夠增長輔助理解:讓代碼更加趨於「高內聚、低耦合」)框架的主要優點之一就是其分層架構,分層架構容許使用者選擇使用哪個組件,同時爲 J2EE 應用程序開發提供集成的框架。Spring使用基本的JavaBean來完成之前只可能由EJB完成的事情。然而,Spring的用途不只限於服務器端的開發。從簡單性、可測試性和鬆耦合的角度而言,任何Java應用均可以從Spring中受益。Spring的核心是控制反轉(IoC)和麪向切面(AOP)。簡單來講,Spring是一個分層的JavaSE/EE full-stack(一站式) 輕量級開源框架。java

  Spring以前是引用EJB的,簡單對EJB瞭解一下:mysql

  小結a.EJB實現原理: 就是把原來放到客戶端實現的代碼放到服務器端,並依靠RMI進行通訊。web

  b.RMI實現原理 :就是經過Java對象可序列化機制實現分佈計算。spring

  c.服務器集羣: 就是經過RMI的通訊,鏈接不一樣功能模塊的服務器,以實現一個完整的功能。sql

  EJB組件:能夠理解爲:把你編寫的軟件中那些須要執行制定的任務的類,不放到客戶端軟件上了,而是給他打成包放到一個服務器上了express

  EJB 就是將那些"類"放到一個服務器上,用C/S 形式的軟件客戶端對服務器上的"類"進行調用。編程

理解RMI 以前,須要理解兩個名詞:
對象的序列化
分佈式計算與RPC數組

  對象的序列化概念:對象的序列化過程就是將對象狀態轉換成字節流和從字節流恢復對象。相對於將對象轉換爲0與1的字節流彙編語言;緩存

  RPC是 「」Remote Procedure Call「的縮寫,也就是遠程過程調用;簡單理解就是調用遠程計算機上的一個函數;」安全

  兩者結合就是RMI

  RMI中文名稱是遠程方法調用,它就是利用Java 對象序列化的機制實現分佈式計算,實現遠程類對象的實例化以及調用的方法。說的更清楚些,就是利用對象序列化來實現遠程調用,也就是上面兩個概念的結合體,利用這個方法來調用遠程的類的時候,就不須要編寫Socket 程序了,也不須要把對象進行序列化操做,直接調用就好了很是方便。

  優勢這種機制給分佈計算的系統設計、編程都帶來了極大的方便。只要按照RMI 規則設計程序,能夠沒必要再過問在RMI 之下的網絡細節了也就是說,能夠將相似Java 哈西表這樣的複雜類型做爲一個參數進行傳遞。

  缺點 若是是較爲簡單的方法調用,其執行效率也許會比本地執行慢不少,即便和遠程Socket機制的簡單數據返回的應用相比,也會慢一些,緣由是,其在網絡間須要傳遞的信息不只僅包含該函數的返回值信息,還會包含該對象序列化後的字節內容。

  EJB 是以RMI 爲基礎的

  EJB 所謂的"服務羣集"的概念。就是將原來在一個計算機上運算的幾個類,分別放到其餘計算機上去運行,以便分擔運行這幾個類所須要佔用的CPU 和內存資源。同時,也能夠將不一樣的軟件功能模塊放到不一樣的服務器上,當須要修改某些功能的時候直接修改這些服務器上的類就好了,修改之後全部客戶端的軟件都被修改了。

2、Spring的優勢

  方便解耦,簡化開發 (高內聚低耦合)

  Spring就是一個大工廠(容器),能夠將全部對象建立和依賴關係維護,交給Spring管理

  spring工廠是用於生成bean

  AOP編程的支持

  Spring提供面向切面編程,能夠方便的實現對程序進行權限攔截、運行監控等功能

  聲明式事務的支持 

  只須要經過配置就能夠完成對事務的管理,而無需手動編程

  方便程序的測試 

  Spring對Junit4支持,能夠經過註解方便的測試Spring程序

  方便集成各類優秀框架 

  Spring不排斥各類優秀的開源框架,其內部提供了對各類優秀框架(如:Struts、Hibernate、MyBatis、Quartz等)的直接支持

  下降JavaEE API的使用難度

  Spring 對JavaEE開發中很是難用的一些API(JDBC、JavaMail、遠程調用等),都提供了封裝,使這些API應用難度大大下降

  爲何要用Spring?像上面說的,高內聚低耦合;本身的理解是經過IOC模式能夠完全解決這種耦合,它把耦合統一放入到xml中,經過一個容器在別的類須要的時候把這個依賴關係造成;即把須要的藉口實現注入到須要它的類中,是對「依賴注入」的一種理解。還能夠這樣去理解,(我的理解)IOC模式相似看作工廠模式的升級版,當作一個大工廠;即:大工廠要生產的對象都是在XML文件中給出定義,而後利用java的反射編程,再根據XML中給出的類名生成相應的對象:提升靈活性、可維護性;

  優勢:對象生成放入XML中,須要實現的子類變得很簡單;

  缺點:對象生成是使用反射編程,在效率上有些損耗;

        缺乏IDE重構操做,若是在Eclipse或者其它的開發工具裏面對類更名字,則須要手動改XML文件;

  其中:IOC最大改變不是代碼上,而是思想上,「主從換位」,使得程序的整個體系變得靈活;也能夠理解爲:IOC的實現必須依賴抽象,而不是依賴實現;對象不寫死;

  Spring控制反轉是解決對象建立問題(對象建立交給別人);依賴注入是對象關係的處理(經過set方法依賴注入,設置了value值)

 

三:Spring技術

一、 Spring的體系結構(借鑑圖片

 

二、 導入jar包

  4 + 1 : 4個核心(beans、core、context、expression) + 1個依賴(commons-loggins…jar)必需要引入的5個!若是缺乏,會報錯:找不到bean對象;

(具體的運用參考例子代碼)

三、依賴注入裝配Bean 基於註解

  這是從容器中獲取Bean的方式之一;註解一個類,使用@註解去取代 xml配置文件。

    @Component("id") 取代 <bean id="" class="">  

  在web開發中,@Component註解提供3種(@功能是同樣的)

    @Repository :dao層

    @Service:       service層

    @Controller:    web層

  用set方法注入,能夠用:@Value(" 引用值"),裏面的引用值能夠按照 類型、名稱 去注入;

4.生命週期(我的以爲容易忽略的點

  初始化:@PostConstruct    銷燬:@PreDestroy

5.做註解使用前提

  添加命名空間,讓spring掃描含有註解類;相對於組件掃描,掃描含有註解的類

<!-- 組件掃描,掃描含有註解的類 -->

<context:component-scan        basepackage="包名">

</context:component-scan>

 

4、AOP(面向切面編程)

一、什麼是Aop?

  AOP是OOP(面向對象編程)的延續,是軟件開發中的一個熱點,也是Spring框架中的一個重要內容,是函數式編程的一種衍生範型。利用AOP能夠對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度下降,提升程序的可重用性,同時提升了開發的效率。

  AOP採起橫向抽取機制,取代了傳統縱向繼承體系重複性代碼

  經典應用:事務管理、性能監視、安全檢查、緩存 、日誌等

  Spring AOP使用純Java實現,不須要專門的編譯過程和類加載器,在運行期經過代理方式向目標類織入加強代碼

  AspectJ是一個基於Java語言的AOP框架,Spring2.0開始,Spring AOP引入對Aspect的支持,AspectJ擴展了Java語言,提供了一個專門的編譯器,在編譯時提供橫向代碼的織入

2AOP實現原理

  aop底層將採用代理機制進行實現。

  接口 + 實現類 :spring採用 jdk 的動態代理Proxy。

  實現類:spring 採用 cglib字節碼加強。

3 AOP術語【掌握】

  1.target:目標類,須要被代理的類。例如:UserService

  2.Joinpoint(鏈接點):所謂鏈接點是指那些可能被攔截到的方法。例如:全部的方法

  3.PointCut 切入點:已經被加強的鏈接點。例如:addUser()

  4.advice 通知/加強,加強代碼。例如:after、before

  5. Weaving(織入):是指把加強advice應用到目標對象target來建立新的代理對象proxy的過程.

  6.proxy 代理類

  7. Aspect(切面): 是切入點pointcut和通知advice的結合

一個線是一個特殊的面。 一個切入點和一個通知,組成成一個特殊的面。

(具體的理解看例子代碼)

 5、代碼例子及理解

一、概述:

  Spring框架,能夠解決對象建立以及對象之間依賴關係的一種框架。且能夠和其餘框架一塊兒使用;Spring與Struts, Spring與hibernate(起到整合(粘合)做用的一個框架)
  Spring提供了一站式解決方案:
    1) Spring Core spring的核心功能: IOC容器, 解決對象建立及依賴關係
    2) Spring Web Spring對web模塊的支持。
      - 能夠與struts整合,讓struts的action建立交給spring
      - spring mvc模式
    3) Spring DAO Spring 對jdbc操做的支持 【JdbcTemplate模板工具類】
    4) Spring ORM spring對orm的支持:
      既能夠與hibernate整合,【session】
      也可使用spring的對hibernate操做的封裝
    5)Spring AOP 切面編程
    6)SpringEE spring 對javaEE其餘模塊的支持

 

二、核心配置文件: applicationContext.xml 

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
 4     xmlns:context="http://www.springframework.org/schema/context"
 5     xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
 6     xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
 7     http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd 
 8     http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
 9     http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
10          <!--此處添加其它標籤-->     
11 </beans>

 

三、獲取IOC容器對象的方法以下:(核心代碼

  從IOC容器中獲取對象有兩種方法:@註解 和 <bean> 標籤 

 1 public class TestApp {
 2         //通常使用第二種方法,第一中方法瞭解一下;
 3     // 1. 經過工廠類獲得IOC容器建立的對象
 4     @Test
 5     public void testIOC(){
 6         // 原先把拿到另一個類的對象,通常的方法是:User user = new User(); 
 7         
 8         // 如今,把對象的建立交給spring的IOC容器
 9         Resource resource = new ClassPathResource("applicationContext.xml");
10         // 而後,建立容器對象(Bean的工廠),能夠理解爲: IOC容器 = 工廠類 + applicationContext.xml
11         BeanFactory factory = new XmlBeanFactory(resource);
12         // 獲得容器建立的對象,下面以獲取User類中的對象爲例子;
13         User user = (User) factory.getBean("user");
14         // 再user去調用方法,此處省略
15         
16     }
17     
18     //2. (方便)直接獲得IOC容器對象 
19     @Test
20     public void test2(){
21         // 獲得IOC容器對象
22         ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
23         // 從容器中獲取bean,也是獲取User類中對象的例子
24         User user = (User) ac.getBean("user");
25         // 再user去調用方法,此處省略
26     }
27 }

 

四、bean對象建立的細節(代碼中解釋,具體結合下面的 5 、 spring IOC 容器去理解

 1 /**
 2      * 1) 對象建立: 單例/多例 
 3      *     scope="singleton", 默認值, 即 默認是單例    【service/dao/工具類】
 4      *  scope="prototype", 多例;                 【Action對象】
 5      * 
 6      * 2) 何時建立?
 7      *       scope="prototype"  在用到對象的時候,才建立對象。
 8      *    scope="singleton"  在啓動(容器初始化以前), 就已經建立了bean,且整個應用只有一個。
 9      * 3)是否延遲建立
10      *       lazy-init="false"  默認爲false,  不延遲建立,即在啓動時候就建立對象
11      *       lazy-init="true"   延遲初始化, 在用到對象的時候才建立對象
12      *    (只對單例有效)
13      * 4) 建立對象以後,初始化/銷燬
14      *       init-method="xx"       【對應對象的xx方法,在對象建立愛以後執行 】
15      *    destroy-method="xx"  【在調用容器對象的xx方法時候執行,(容器用實現類)】
16      */
17     @Test
18     public void testIOC2(){
19         // 獲得IOC容器對象  【用實現類,由於要調用銷燬的方法】
20         ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
21         //容器建立,從容器中獲取bean;同樣以User類爲例子;
22     
23         User user1 = (User) ac.getBean("user");
24         User user2 = (User) ac.getBean("user");
25         
26         System.out.println(user1);
27         System.out.println(user2);
28         
29         // 銷燬容器對象 
30         ac.destroy();
31     }

 

五、SpringIOC容器

  SpringIOC容器是spring核心內容,它的做用主要解決對象的建立和處理對象的依賴關係

    建立對象, 有幾種方式:

    1) 調用無參數構造器

    2) 帶參數構造器

    3) 工廠建立對象

        工廠類,靜態方法建立對象

        工廠類,非靜態方法建立對象

  理解以下:(工廠的實例方法簡單來講就是調用的思想)

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4     xmlns:p="http://www.springframework.org/schema/p"
 5     xmlns:context="http://www.springframework.org/schema/context"
 6     xsi:schemaLocation="
 7         http://www.springframework.org/schema/beans
 8         http://www.springframework.org/schema/beans/spring-beans.xsd
 9         http://www.springframework.org/schema/context
10         http://www.springframework.org/schema/context/spring-context.xsd">
11     
12     <!--對象建立的方法以下:(結合上面的獲取IOC容器中對象的代碼一塊兒看,以User類對象被獲取爲例) -->
13     
14     <!-- 1. 默認無參數構造器 
15     <bean id="user1" class="包名.User"></bean>
16     -->
17     
18     <!-- 2. 帶參數構造器 -->
19         <!-- index能夠來控制value輸出的順序 -->
20     <bean id="user2" class="包名.User">
21         <constructor-arg index="0" type="int" value="100"></constructor-arg>
22         <constructor-arg index="1" type="java.lang.String" value="Jack"></constructor-arg>
23     </bean>
24     
25     <!-- 建立字符串,值是"Jack",讓其它bean區調用 -->
26     <bean id="str" class="java.lang.String">
27         <constructor-arg value="Jack"></constructor-arg>
28     </bean>
29     <bean id="user3" class="包名.User">
30         <constructor-arg index="0" type="int" value="100"></constructor-arg>
31         <constructor-arg index="1" type="java.lang.String" ref="str"></constructor-arg>
32     </bean>
33     
34     
35     <!-- 3. 工廠類建立對象 -->
36     <!-- # 3.1 工廠類,實例方法(用工廠去調用方法) -->
37     <!-- 先建立工廠 -->
38     <bean id="factory" class="包名.類名"></bean>
39     <!-- 在建立user對象,用factory方的實例方法 -->
40     <bean id="user4" factory-bean="factory" factory-method="實例方法名"></bean>
41     
42     
43     <!-- # 3.2 工廠類: 靜態方法 -->
44     <!-- 
45         class 引用工廠對象
46         factory-method  必定是工廠裏面的「靜態方法」
47      -->
48     <bean id="user" class="包名.類名" factory-method="靜態方法名"></bean>

 

5.1 對象依賴關係

  Spring中,如何給對象的屬性賦值?  DI, 依賴注入】

    a、 經過構造函數

    b、 經過set方法給屬性注入值

    c、 p名稱空間

    d、自動裝配(瞭解)

    e、 註解  

   (經常使用)Set方法注入值

 1 <!-- 
 2         構造函數
 3         <bean id="xx" class="包名.類名">
 4         <constructor-arg value="xx"></constructor-arg>
 5     </bean>
 6  -->
 7 <!-- 
 8         set方法注入值
 9         <bean id="xx" class="包名.類名">
10        <property name="xx" value="xx"></property>
11     </bean>
12  -->
13 <!--對象的注入: name是對象    ref去引用,實例化對象 -->
14 <!-- dao 插入 -->
15     <bean id="xxdao" class="包名. UserDao"></bean>
16 
17 <!-- service 插入 -->
18     <bean id="xxService" class="包名.UserService">
19         <property name="userDao" ref="xxDao"></property>
20     </bean>
21     
22 <!-- action 插入 -->
23     <bean id="xxAction" class="包名.UserAction">
24         <property name="userService" ref="xxService"></property>
25     </bean>    

 

   或者另一種方式去注入對象:內部bean方法

1     <bean id="xx" class="包名.類名">
2         <property name="xxService">
3             <bean class="包名.類名">
4                 <property name="xxDao">
5                     <bean class="包名.類名"></bean>
6                 </property>
7             </bean>
8         </property>
9     </bean>

 

  p 名稱空間注入屬性值 

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4     xmlns:p="http://www.springframework.org/schema/p"
 5     xmlns:context="http://www.springframework.org/schema/context"
 6     xsi:schemaLocation="
 7         http://www.springframework.org/schema/beans
 8         http://www.springframework.org/schema/beans/spring-beans.xsd
 9         http://www.springframework.org/schema/context
10         http://www.springframework.org/schema/context/spring-context.xsd">
11     
12     
13     <!-- 
14         給對象屬性注入值:p 名稱空間(方式)
15              (spring3.0以上版本才支持)
16      -->
17      <bean id="xx" class="包名.類名"></bean>
18      
19      <bean id="xx2" class="包名.類名" p:name-ref="xx"></bean>
20      
21      <bean id="xx3" class="包名.類名" p:name-ref="xx2"></bean>
22     
23     
24     <!-- 傳統的注入: 
25      <bean id="xx" class="包名.類名" >
26          <property name="name" value="xxx"></property>
27      </bean>
28     -->
29     <!-- p名稱空間優化後 -->
30     <bean id="xx" class="包名.類名" p:name="xx"></bean>
31      
32 </beans>   

 

  自動裝配(通常不使用)

  根據名稱自動裝配:autowire="byName"
    - 自動去IOC容器中找與屬性名同名的引用的對象,並自動注入

  也能夠定義到全局, 這樣就不用每一個bean節點都去寫autowire=」byName」

  根據類型自動裝配:autowire="byType"
  必須確保改類型在IOC容器中只有一個對象;不然報錯。
總結:
  Spring提供的自動裝配主要是爲了簡化配置; 可是不利於後期的維護。(通常不推薦使用)

 

<!--         自動裝配        -->  
    <bean id="xxDao" class="包名.類名"></bean>    
    <bean id="xxService" class="包名.類名" autowire="byName"></bean>
    <!-- 根據「名稱」自動裝配: userAction注入的屬性,會去Ioc容器中自動查找與屬性同名的對象 -->
    <bean id="xxAction" 
class="包名.類名" autowire="byName"></bean>

 

 1 <!--定義全局,就不用每一個bean節點寫上 autowire="byName"-->
 2 <?xml version="1.0" encoding="UTF-8"?>
 3 <beans xmlns="http://www.springframework.org/schema/beans"
 4     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 5     xmlns:p="http://www.springframework.org/schema/p"
 6     xmlns:context="http://www.springframework.org/schema/context"
 7     xsi:schemaLocation="
 8         http://www.springframework.org/schema/beans
 9         http://www.springframework.org/schema/beans/spring-beans.xsd
10         http://www.springframework.org/schema/context
11         http://www.springframework.org/schema/context/spring-context.xsd" default-autowire="byName">   
12         <!--根據名稱自動裝配(全局)-->    
13     <!--   自動裝配   -->  
14     <bean id="xx" class="包名.類名"></bean>    
15     <bean id="xx2" class="包名.類名"></bean>
16     <bean id="xx3" class="包名.類名"></bean>
17 </beans>  

 

 1 <!--必須確保改類型在IOC容器中只有一個對象;不然報錯-->
 2 <?xml version="1.0" encoding="UTF-8"?>
 3 <beans xmlns="http://www.springframework.org/schema/beans"
 4     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 5     xmlns:p="http://www.springframework.org/schema/p"
 6     xmlns:context="http://www.springframework.org/schema/context"
 7     xsi:schemaLocation="
 8         http://www.springframework.org/schema/beans
 9         http://www.springframework.org/schema/beans/spring-beans.xsd
10         http://www.springframework.org/schema/context
11         http://www.springframework.org/schema/context/spring-context.xsd" default-autowire="byType">
12     <!-- 自動裝配 -->  
13         
14     <!-- 若是根據類型自動裝配: 必須確保IOC容器中只有一個該類型的對象 -->
15     <bean id="xx" class="包名.類名"></bean>
16     
17     
18     <!--   報錯: 由於上面已經有一個該類型的對象,且使用了根據類型自動裝配
19     <bean id="xx" class="包名.類名" autowire="byType"></bean>
20      -->
21 </beans>  

  注:相對於id不一樣,有相同的class;

六、註解(從IOC容器中獲取對象的另一種方式:@註解)(推薦使用,方便些,簡化容器的配置

  使用註解步驟:
    a、先引入context名稱空間
      xmlns:context="http://www.springframework.org/schema/context"
    b、開啓註解掃描
      <context:component-scan base-package="包名"></context:component-scan>
    c、使用註解
      經過註解的方式,把對象加入Ioc容器。

      建立對象以及處理對象依賴關係,相關的註解:
        @Component 指定把一個對象加入IOC容器

        @Repository : 在持久層使用
        @Service 做用同:在業務邏輯層使用
        @Controller 做用同:在控制層使用

        @Resource 屬性注入

總結:
  一、使用註解,能夠簡化配置,且能夠把對象加入IOC容器,及處理依賴關係(DI)
  二、註解能夠和XML配置一塊兒使用。

 6、AOP(事務管理)相關知識點

一、Java的代理模式(3種)(Spring中是動態代理對象

  理解:代理對象與目標對象的關係,能夠看作代理對象是對目標對象的擴展,並調用出目標對象;關係以下圖:

 

1.1 靜態代理

  使用靜態代理時,代理對象和被代理對象須要實現相同的接口,反過來理解就是須要先定義接口或者父類;代碼例子以下:

接口:Dao.java

1 /**
2  * 接口
3  */
4 public interface Dao {
5     void save();
6 }

 

目標對象:sonDao.java

1 /**
2  * 接口實現
3  * 目標對象
4  */
5 public class sonDao implements Dao {
6     public void save() {
7         System.out.println("----接口的實現----");
8     }
9 }

 

代理對象:DaoProxy.java

 1   /**
 2    * 代理對象,靜態代理
 3    */
 4   public class DaoProxy implements Dao{
 5       //接收保存目標對象
 6       private Dao target;
 7       public DaoProxy(Dao target){
 8           this.target=target;
 9       }
10  
11      public void save() {
12          System.out.println("開始事務...");
13          target.save();//執行目標對象的方法
14          System.out.println("提交事務...");
15      }
16  }

 

測試類:Test.java

 1 /**
 2  * 測試類
 3  */
 4 public class Test {
 5     public static void main(String[] args) {
 6         //目標對象
 7         sonDao target = new sonDao();
 8 
 9         //代理對象,把目標對象傳給代理對象,創建代理關係
10         DaoProxy proxy = new DaoProxy(target);
11 
12         proxy.save();//執行的是代理的方法
13     }
14 }

 

小結:優勢就是在不修改目標對象的基礎上對目標功能的一些擴展;

     缺點是代理對象和目標對象都要寫同樣的接口,後續可能會形成代理類太多;

 

2.2  動態代理 

  也叫JDK代理,接口代理;其中代理對象,不須要實現接口,它的生成,是利用JDK的API,在動態的內存中構建代理對象;

  理解:

  首先JDK的proxy方式實現的動態代理,目標對象必須有接口;

代理對象:

 1 package XX;
 2 
 3 
 4 import java.lang.reflect.InvocationHandler;
 5 import java.lang.reflect.Method;
 6 import java.lang.reflect.Proxy;
 7 
 8 public class ProxyTest2 {
 9 
10     public static void main(String[] args) {
11         
12         final Target target = new Target();
13         
14         //動態建立代理對象
15         //JDK的API中存在一個生成動態代理的方法:newProxyInstance
16         公共接口類 proxy = (公共接口類) Proxy.newProxyInstance(
17                 //參數:loader
18                 target.getClass().getClassLoader(),
19                 //目標對象實現全部接口字節碼數組:interfaces
20                 target.getClass().getInterfaces(),
21                 //具體代理接口: InvocationHandler
22                 new InvocationHandler() {
23                     @Override
24                     //被執行幾回?------- 看代理對象調用方法幾回
25                     //代理對象調用接口相應方法 都是調用invoke
26                     /*
27                      * proxy:是代理對象
28                      * method:表明的是目標方法的字節碼對象
29                      * args:表明是調用目標方法時參數
30                      */
31                     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
32                         //反射知識點
33                         Object invoke = method.invoke(target, args);//目標對象的相應方法
34                         //retrun返回的值給代理對象
35                         return invoke;
36                     }
37                 }
38             );
39         
40         //調用invoke---Method:目標對象的method1方法  args:null  返回值null        
41         proxy.method1();
42         //調用invoke---Method:目標對象的method2方法  args:null  返回值method2
43         String method2 = proxy.method2();
44         //調用invoke-----Method:目標對象的method3方法 args:Object[]{100}  返回值100
45         int method3 = proxy.method3(100);
46         
47         System.out.println(method2);
48         System.out.println(method3);
49         
50     }
51     
52 } 

 

  代碼還能夠加上一個加強方法的過濾器

 1 package XX;
 2 
 3 
 4 import java.io.IOException;
 5 import java.io.UnsupportedEncodingException;
 6 import java.lang.reflect.InvocationHandler;
 7 import java.lang.reflect.Method;
 8 import java.lang.reflect.Proxy;
 9 
10 import javax.servlet.Filter;
11 import javax.servlet.FilterChain;
12 import javax.servlet.FilterConfig;
13 import javax.servlet.ServletException;
14 import javax.servlet.ServletRequest;
15 import javax.servlet.ServletResponse;
16 import javax.servlet.http.HttpServletRequest;
17 import javax.servlet.http.HttpServletRequestWrapper;
18 
19 import com.sun.corba.se.impl.protocol.giopmsgheaders.ReplyMessage_1_0;
20 
21 public class XX implements Filter{
22 
23     @Override
24     public void init(FilterConfig filterConfig) throws ServletException {
25         // TODO Auto-generated method stub
26         
27     }
28 
29     @Override
30     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
31             throws IOException, ServletException {
32     HttpServletRequest req = (HttpServletRequest) request;    
33     
34     HttpServletRequest enhanceRequset = (HttpServletRequest) Proxy.newProxyInstance(
35             req.getClass().getClassLoader(), 
36             req.getClass().getInterfaces(),
37             new InvocationHandler() {
38                 
39                 @Override
40                 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
41                    //對getParameter進行加強
42                     if(method.getName().equals("getParameter")){
43                         String invoke = (String) method.invoke(req, args);//亂碼
44                         invoke = new String(invoke.getBytes("iso8859-1"),"UTF-8");//轉碼
45                         return invoke;
46                     }
47                     return method.invoke(req, args);
48                 }
49             });
50         chain.doFilter(enhanceRequset, response);//放行
51     }
52 
53     @Override
54     public void destroy() {
55         // TODO Auto-generated method stub
56         
57     }
58 
59     
60 }

   

  其中還有一個靜態方法:

Class<?>[ ] interfaces :目標對象實現的接口的類型,使用泛型方式確認類型

  測試類 App.java

 1 /**
 2  * 測試類
 3  */
 4 public class App {
 5     public static void main(String[] args) {
 6         // 目標對象
 7         接口類 target = new 目標對象類();
 8         // 【原始的類型 class 包名.類名】
 9         System.out.println(target.getClass());
10 
11         // 給目標對象,建立代理對象
12         接口類 proxy = (接口類) new ProxyFactory(target).getProxyInstance();
13         // class $Proxy0   內存中動態生成的代理對象
14         System.out.println(proxy.getClass());
15 
16         // 執行方法   【代理對象】
17         proxy.save();
18     }
19 }

 

  查看生產的代理類(代碼):

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true" );

1.3 Cglib 代理

  實現的方法是繼承,其中要2個jar包:asm-x.x.jar  、  cglib-x.x.x.jar

  相對於靜態和動態代理比較,它們都須要實現一個接口的目標對象,若是對象只是一個單獨的對象,其中沒有任何接口的實現,那麼這種狀況就能夠用目標子類的方式去實現代理,這種方式也叫Cglib代理;簡稱子類代理,從內存中構建子類對象從而實現對目標對象的功能擴展

  Cglib是一個強大的高性能的代碼生成包,它的底層是經過使用一個小而塊的字節碼處理框架ASM來轉換字節碼並生成新的類。(通常不使用ASM,由於要對JVM內部結構掌握精通)它能在運行期擴展java類與實現java接口;如:Spring AOP和synaop,爲他們提供方法的interception(攔截);

  Cglib子類代理的實現

  先要引入jar包,如:spring-core-3.2.5.jar , 而後在內存中動態構建子類;其中代理的類不能爲final,不然報錯;目標對象的方法若是爲final/static,那麼就不會被攔截,即不會執行目標對象額外的業務方法;方法是final/static則沒法進行代理。

  目標對象類(沒有接口)

1 /**
2  * 目標對象,沒有實現任何接口
3  */
4 public class XX {
5 
6     public void save() {
7         System.out.println("XXXXXX");
8     }
9 }

 

  代理工廠類

 1 /**
 2  * Cglib子類代理工廠
 3  * 在內存中動態構建一個子類對象
 4  */
 5 public class XX implements MethodInterceptor{
 6     //維護目標對象
 7     private Object target;
 8 
 9     public XX(Object target) {
10         this.target = target;
11     }
12 
13     //給目標對象建立一個代理對象
14     public Object getProxyInstance(){
15         //1.工具類
16         Enhancer en = new Enhancer();
17         //2.設置父類
18         en.setSuperclass(target.getClass());
19         //3.設置回調函數
20         en.setCallback(this);
21         //4.建立子類(代理對象)
22         return en.create();
23 
24     }
25 
26     @Override
27     public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
28         System.out.println("開始事務...");
29 
30         //執行目標對象的方法
31         Object returnValue = method.invoke(target, args);
32 
33         System.out.println("提交事務...");
34 
35         return returnValue;
36     }
37 }

  

  測試類

 1 /**
 2  * 測試類
 3  */
 4 public class Test{
 5 
 6     @Test
 7     public void test(){
 8         //目標對象
 9         XX target = new XX();
10 
11         //代理對象
12         XX proxy = (XX)new 代理工廠類(target).getProxyInstance();
13 
14         //執行代理對象的方法
15         proxy.save();
16     }
17 }

 

  DefaultAopProxyFactory的源碼(有興趣可看

 1 // Source code recreated from a .class file by IntelliJ IDEA
 2 // (powered by Fernflower decompiler)
 3 
 4 package org.springframework.aop.framework;
 5 
 6 import java.io.Serializable;
 7 import java.lang.reflect.Proxy;
 8 import org.springframework.aop.SpringProxy;
 9 
10 public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
11     public DefaultAopProxyFactory() {
12     }
13 
14     public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
15         if (!config.isOptimize() && !config.isProxyTargetClass() && !this.hasNoUserSuppliedProxyInterfaces(config)) {
16             return new JdkDynamicAopProxy(config);
17         } else {
18             Class<?> targetClass = config.getTargetClass();
19             if (targetClass == null) {
20                 throw new AopConfigException("TargetSource cannot determine target class: Either an interface or a target is required for proxy creation.");
21             } else {
22                 return (AopProxy)(!targetClass.isInterface() && !Proxy.isProxyClass(targetClass) ? new ObjenesisCglibAopProxy(config) : new JdkDynamicAopProxy(config));
23             }
24         }
25     }
26 
27     private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
28         Class<?>[] ifcs = config.getProxiedInterfaces();
29         return ifcs.length == 0 || ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0]);
30     }
31 }

 

  小結:加入容器的目標對象有實現接口,用JDK代理

                          目標對象沒有實現接口,用Cglib代理   

                          目標對象實現了接口,且強制使用cglib代理,則會使用cglib代理。

二、程序中事務控制

  2.1 流程

    通常是先用戶訪問,而後Action到Service到Dao;若是調用service執行成功,則說明業務是成功的,由於事務應該在service層統一控制

  2.2 事務控制的初步瞭解

    編程式事務控制:本身手動控制的事務;

    Jdbc代碼:Conn.setAutoCommite(false); // 設置手動控制事務
    Hibernate代碼:Session.beginTransaction(); // 開啓一個事務
    【細粒度的事務控制: 能夠對指定的方法、指定的方法的某幾行添加事務控制】
   (比較靈活,但開發起來比較繁瑣: 每次都要開啓、提交、回滾.)

    聲明式事務控制
      聲明式事務管理就是Spring提供了對事務的管理。
      Spring提供了對事務控制的實現。用戶若是想用Spring的聲明式事務管理,只須要在配置文件中配置便可; 不想使用時直接移除配置。這個實現了對事務控制的最大程度的解耦。
      Spring聲明式事務管理的核心實現就是基於Aop。
  【粗粒度的事務控制: 只能給整個方法應用事務,不能夠對方法的某幾行應用事務。】(由於aop攔截的是方法。)

    Spring聲明式事務管理器類:
      Jdbc技術:DataSourceTransactionManager
      Hibernate技術:HibernateTransactionManager

三、聲明式事務管理的實現

  首先要引入spring-aop相關的4個jar文件,再引入aop名稱空間,由於XML配置方法須要引入,最後引入tx名稱空間(事務方式必須引入);

XML去實現

Dao.java類

 1 public class Dao {
 2     
 3     // 容器注入JdbcTemplate對象
 4     private JdbcTemplate jdbcTemplate;
 5     public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
 6         this.jdbcTemplate = jdbcTemplate;
 7     }
 8 
 9     public void save(Dept dept){
10         String sql = "insert into t_dept (deptName) values(?)";
11         jdbcTemplate.update(sql,dept.getDeptName());
12     }
13 }

 

service類

 1 public class Service {
 2     
 3     // 容器注入dao對象
 4     private Dao deptDao;
 5     public void setDao(Dao deptDao) {
 6         this.deptDao = deptDao;
 7     }
 8 
 9     /*
10      * 事務控制
11      */
12     public void save(Dept dept){
13         // 第一次調用
14         deptDao.save(dept);
15         
16         int i = 1/0; // 異常: 整個Service.save()執行成功的要回滾
17         
18         // 第二次調用
19         deptDao.save(dept);
20     }
21 }

 

  測試類

 1 @Test
 2     public void test() throws Exception {
 3         //容器對象
 4         ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
 5         
 6         // 模擬數據
 7         Dept dept = new Dept();
 8         dept.setDeptName("測試");
 9         
10         Service Service = (Service) ac.getBean("deptService");
11         Service.save(dept);
12         
13     } 

 

  bean.xmk (Spring管理配置)

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 4     xmlns:p="http://www.springframework.org/schema/p"
 5     xmlns:context="http://www.springframework.org/schema/context"
 6     xmlns:aop="http://www.springframework.org/schema/aop"
 7     xmlns:tx="http://www.springframework.org/schema/tx"
 8     xsi:schemaLocation="http://www.springframework.org/schema/beans
 9          http://www.springframework.org/schema/beans/spring-beans.xsd
10           http://www.springframework.org/schema/context
11          http://www.springframework.org/schema/context/spring-context.xsd
12          http://www.springframework.org/schema/aop
13          http://www.springframework.org/schema/aop/spring-aop.xsd
14          http://www.springframework.org/schema/tx
15           http://www.springframework.org/schema/tx/spring-tx.xsd">
16 
17     
18     <!--  數據源對象: C3P0鏈接池 -->
19     <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
20         <property name="XX" value="com.mysql.jdbc.Driver"></property>
21         <property name="jdbcUrl" value="jdbc:mysql:XX"></property>
22         <property name="user" value="root"></property>
23         <property name="password" value="root"></property>
24         <property name="initialPoolSize" value="XX"></property>
25         <property name="maxPoolSize" value="XX"></property>
26         <property name="maxStatements" value="XX"></property>
27         <property name="acquireIncrement" value="XX"></property>
28     </bean>
29     
30     <!-- JdbcTemplate工具類實例 -->
31     <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
32         <property name="dataSource" ref="dataSource"></property>
33     </bean>
34     
35     <!--  dao實例 -->
36     <bean id="deptDao" class="XX">
37         <property name="jdbcTemplate" ref="jdbcTemplate"></property>
38     </bean>
39  
40     <!-- service實例 -->
41     <bean id="deptService" class="XX">
42         <property name="deptDao" ref="deptDao"></property>
43     </bean>
44     
45     <!--  Spring聲明式事務管理配置:配置事務管理器類 -->
46     
47     <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
48         <property name="dataSource" ref="dataSource"></property>
49     </bean>
50     
51     <!-- 配置事務加強 -->
52     <tx:advice id="txAdvice" transaction-manager="txManager">
53         <tx:attributes>
54             <tx:method name="get*" read-only="true"/>
55             <tx:method name="find*" read-only="true"/>
56             <tx:method name="*" read-only="false"/>
57         </tx:attributes>
58     </tx:advice>
59     
60     <!--Aop配置: 攔截哪些方法(切入點表表達式) + 應用上面的事務加強配置 -->
61     <aop:config>
62         <aop:pointcut expression="execution(* cn.itcast.a_tx.DeptService.*())" id="pt"/>
63         <aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
64     </aop:config>
65     
66 </beans>     

 

  還有一種方法:註解(推薦使用,更簡單)

  先引入Aop相關的jar包文件,再在 bean.xml中指定註解方式實現聲明式事務管理及管理器,最後在須要添加事務控制的地方寫上:@Transactional ;例如:

  應用事務的註解
  定義到方法上: 當前方法應用spring的聲明式事務
  定義到類上: 當前類的全部的方法都應用Spring聲明式事務管理;
  定義到父類上: 當執行父類的方法時候應用事務。

  bean.xml(核心代碼)

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 4     xmlns:p="http://www.springframework.org/schema/p"
 5     xmlns:context="http://www.springframework.org/schema/context"
 6     xmlns:aop="http://www.springframework.org/schema/aop"
 7     xmlns:tx="http://www.springframework.org/schema/tx"
 8     xsi:schemaLocation="http://www.springframework.org/schema/beans
 9          http://www.springframework.org/schema/beans/spring-beans.xsd
10           http://www.springframework.org/schema/context
11          http://www.springframework.org/schema/context/spring-context.xsd
12          http://www.springframework.org/schema/aop
13          http://www.springframework.org/schema/aop/spring-aop.xsd
14          http://www.springframework.org/schema/tx
15           http://www.springframework.org/schema/tx/spring-tx.xsd">
16 
17     
18     <!--  C3P0鏈接池 -->
19     <bean id="dataSource" class="xx">
20         <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
21         <property name="jdbcUrl" value="jdbc:mysql:xx"></property>
22         <property name="user" value="root"></property>
23         <property name="password" value="root"></property>
24         <property name="initialPoolSize" value="xx"></property>
25         <property name="maxPoolSize" value="xx"></property>
26         <property name="maxStatements" value="xx"></property>
27         <property name="acquireIncrement" value="xx"></property>
28     </bean>
29     
30     <!--JdbcTemplate工具類實例 -->
31     <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
32         <property name="dataSource" ref="dataSource"></property>
33     </bean>
34     
35     <!-- 事務管理器類 -->
36     <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
37         <property name="dataSource" ref="dataSource"></property>
38     </bean>
39     
40     <!-- 開啓註解掃描 -->
41     <context:component-scan base-package="cn.itcast.b_anno"></context:component-scan>
42     
43     <!-- 註解方式實現事務: 指定註解方式實現事務 -->
44     <tx:annotation-driven transaction-manager="txManager"/>
45 </beans>     

 

  事務傳播行爲

  Propagation.REQUIRED:指定當前的方法必須在事務的環境下執行,若是當前運行的方法,已經存在事務, 就會加入當前的事務;
  Propagation.REQUIRED_NEW:必須在事務的環境下執行的方法,若是當前運行的方法,已經存在事務: 事務會掛起; 會始終開啓一個新的事務,執行完後; 剛纔掛起的事務才繼續運行。
 

Class Log{
        Propagation.REQUIRED  
        insertLog();  
}

    Propagation.REQUIRED
    Void  saveDept(){
        insertLog();    // 加入當前事務
        .. 異常, 會回滾
        saveDept();
    }


    Class Log{
        Propagation.REQUIRED_NEW  
        insertLog();  
}

   Propagation.REQUIRED
    Void  saveDept(){
        insertLog();    // 始終開啓事務
        .. 異常, 日誌不會回滾
        saveDept();
    }

   end...  

相關文章
相關標籤/搜索