Spring入門

一 Spring概述

  • Spring沒有太多的新的東西,它只是抽象了大量的JavaEE應用中的常用代碼,將它們抽象成一個框架,通過使用Spring可以大幅度地提高開發效率,並可以保證整個應用具有良好的設計。
  • Spring框架號稱JavaEE應用的一站式解決方案,Spring本身提供了一個設計優良的MVC框架:Spring MVC,使用Spring框架則可以直接使用該MVC框架。但是Spring卻沒有提供完整的持久層框架
  • 這可以理解成一種“空”,但這種“空”反而是Spring框架的魅力所在。Spring能與大部分持久層框架無縫整合:Hibernate、JPA、MyBatis、甚至直接使用JDBC,隨便我們喜歡,無論哪種持久層框架,Spring都會爲我們提供無縫的整合以及極好的簡化。
  • 從這個意義上看,Spring更像一種中間層容器,Spring向上可以與MVC框架無縫整合,向下可以與各種持久層框架無縫整合,具有強大的生命力。由於Spring框架的特殊地位,所以輕量級JavaEE應用通常都會使用Spring。實際上,輕量級JavaEE這個概念也是由Spring框架衍生出來的,Spring框架暫時都沒有較好的替代框架。

Spring簡介

  • Spring是一個從實際開發中抽取出來的框架,因此它完成了大量開發中的通用步驟,留給開發者的僅僅是與特定應用相關的部分,從而大大提供了企業級應用的開發效率。
  • Spring爲企業應用的開發提供了一個輕量級的解決方案。該解決方案包括:基於依賴注入的核心機制、基於AOP(Aspect Oriented Programming,面向側面的程序設計)的聲明式事務管理、與各種持久層技術的整合,以及優秀的Web MVC框架等。Spring致力於JavaEE應用各層的解決方案,而不是僅僅專注於某一層的方案。可以說:Spring是企業應用開發的“一站式”選擇,Spring貫穿表現層、業務層、持久層。然而,Spring並不想取代那些已有的框架,而是以高度的開發性與它們無縫整合。

Spring的優點

  • 低侵入式設計,代碼的污染極低 (對代碼的影響較低)
  • 獨立於各種服務器,基於Spring框架的應用,可以真正實現Write One,Run Any Where的承諾。
  • Spring的IoC容器降低了業務對象替換的複雜性,提高了組件之間的解耦。
  • Spring的AOP支持允許將一些通用任務如安全、事務、日誌等進行集中處理,從而提供了更好的複用。
  • Spring的ORM(Object Relation Mapping,對象關係映射)和DAO(Data Access Object,數據訪問對象)提供了與第三方持久層框架的良好整合,並簡化了底層的數據庫訪問。
  • Spring的高度開放性,並不強制應用完全依賴Spring,開發者可以自由選用Spring框架的部分或全部。

Spring在項目中所處的位置

image

Spring核心功能

作爲工廠(容器):

 作爲工廠負責創建,管理所有的java對象,這些對象稱爲Bean,正是因爲Sping框架是容器性質的所以它才能夠稱爲一站式的,他管理什麼樣的對象就具有什麼功能

IOC(控制反轉==依賴注入)

 Spring通過依賴注入管理Bean之間的依賴關係,不僅可以爲Bean注入普通屬性,還可以注入其他Bean的引用,這樣就實現瞭解耦,控制反轉依賴注入實際上就是一回事
 控制反轉:從調用者的角度,獲取依賴對象原先的主動獲取到現在的被動接收
 依賴注入:從Spring的角度,將被依賴對象賦值給調用者的成員變量

AOP(面向切面)

縱向代碼橫向抽取

二 Spring項目搭建

導包

image

image

創建一個對象

image

書寫配置註冊對象到容器
  • 位置任意(建議放在src下)
  • 配置文件名稱任意(建議applicationContext.xml)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--將User對象交給Spring容器進行管理-->
    <!--name是得到該對象的標識,根據class通過反射得到該類-->
   <bean name="user" class="com.langsin.bean.User"></bean>

</beans>
  • 代碼測試
package test1;

import com.langsin.bean.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Demo1 {

    @Test
    public void fun1(){
        //1.創建容器對象
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

        //2.向容器'要' User對象
        User user = (User) applicationContext.getBean("user");

        //3.打印
        System.out.println(user);

    }

}

Spring中的概念(思想)

IOC

 ==Inverse of Control 反轉控制,將我們創建對象的方式反轉了!==
以前對象的創建是由我們開發人員自己維護,包括依賴關係也是自己注入,例如在service層我們自己new一個dao,使用Spring之後,這種依賴關係可以由Spring完成創建以及注入
 ==反轉控制就是反轉了對象的創建方式,從我們自己創建反轉給了程序(Spring)==

DI

image
通過依賴注入的技術實現了控制反轉

Worker實例與具體的Axe的實現類沒有任何關係,worker實例僅僅與Axe接口耦合,這就保證了Worker實例與Axe實例之間的鬆耦合——這也是Spring強調面向接口編程的原因。

Spring中的工廠(容器)

image

BeanFactory接口(過時)

原始接口功能較爲單一
該接口實現類的特點是每次在獲得對象時纔會創建對象爲了節省內存(當時硬件限制很大)

ApplicationContext

新一代的容器:每次容器啓動時就會創建容器中所有配置的對象,並提供更多的功能
典型的實現類:從類路徑下加載配置文件:
==ClassPathXmlApplicationContex(“”)==;
從硬盤絕對路徑下加載配置文件:
==FileSystemXmlApplicationContext(“”)==

可以同時加載多個xml配置文件
ApplicationContext允許以聲明式方式操作容器,無須手動創建它。可利用如ContextLoader的支持類,在Web應用啓動時自動創建ApplicationContext。當然也可採用編程方式創建ApplicationContext。

ApplicationContext除了提供BeanFactory所支持的全部功能外,還有如下額外方法:
1. ApplicationContext默認會預初始化所有的singletion Bean,也可通過配置取消預初始化。
2. ApplicationContext繼承MessageSource接口,因此提供國際化支持。
3. 資源訪問,比如訪問URL和文件。
4. 事件機制。
5. 同時加載多個配置文件。
6. 以聲明式方式啓動並創建Spring容器。

當系統創建ApplicationContext容器時,默認會預初始化所有的singletion Bean。也就是說,==當Application
Context容器初始化完成後,容器會自動初始化所有的singletion Bean,包括調用構造器創建該Bean的實例(靜態工廠實例,工廠)並根據property元素執行setter方法==這意味着:系統前期創建ApplicationContext時將有==較大的系統開銷==,但一旦ApplicationContext初始化完成,程序後面獲取singletion Bean實例時將擁有較好的性能。
爲了阻止Spring容器預初始化容器中的singleton Bean,可以在bean元素指定==lazy-init=”true”==,

Spring配置講解

Bean元素配置

從本質上來看,Spring容器就是超級大工廠,Spring容器中的Bean就是該工廠的產品。Spring容器能產生哪些產品,則完全取決於開發者在配置文件中的配置。
對於開發者而言,使用Spring框架主要是做兩件事:1、開發Bean。2、配置Bean。對於Spring來說,它要做的就是根據配置文件來創建Bean實例,並調用Bean實例的方法完成“依賴注入”——這就是IoC的本質。這就要求開發者在使用Spring框架時,==眼中看到的是“XML配置”,心中想的是“Java代碼”==。

<?xml version="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.xsd">

    <!--將User對象交給Spring容器來管理-->
    <!--
        Bean元素:使用該元素描述需要Spring容器管理的對象
            name屬性:給被管理的對象起個名字,獲得該對象時根據該name獲取.名稱可以重複(無意義),可以使用特殊字符
            class屬性:被管理者的完整類名,通過反射創建該類

            id屬性:比較老的一個屬性,作用與name屬性完全一樣,不能重複且不能使用特殊字符
    -->
    <bean name="user" class="com.xiaoming.bean.User"></bean>
</beans>

Spring創建對象的3種方式

  • 默認構造器創建
//實體類
public class User {
    private String usrename;
    private Integer age;

    public User(){
        System.out.println("User對象的空參構造");
    }
    ....
}

//配置文件
    <!--創建方式1:空參構造創建 -->
    <bean name="user" class="com.xiaoming.bean.User"/>

//測試方法

    //空參構造 重要 即使覆蓋了
    @Test
    public void testDefault(){
        User user = (User) applicationContext.getBean("user");
        System.out.println(user);
    }

    //會打印一次 無參構造
  • 靜態工廠創建
//工廠類
package com.xiaoming.create;

import com.xiaoming.bean.User;

public class UserFactory {

    public static User creatUser(){
        System.out.println("靜態工廠創建User");
        return new User();
    }

    public User creatUser2(){
        System.out.println("實例工廠創建User");
        return new User();
    }

}

//配置文件
       //創建方式2:靜態工廠創建
        //調用UserFactory的creatUser()方法創建名爲user2的user對象
        //放入容器
    <bean name="user2" class="com.xiaoming.create.UserFactory"
        factory-method="creatUser"/>

//測試方法
//會打印一次靜態工廠的方法,跟user的無參構造器
  • 實例工廠
<!--工廠類同上-->
    <!--
        創建方式3:
            調用UserFactory的createUser2()方法創建名爲user3的實例對象
    -->
    <bean name="userFactroy" class="com.xiaoming.create.UserFactory"/>
    <bean name="user3" factory-bean="userFactroy" factory-method="creatUser2"/>

<!--測試略 同上  -->

Bean元素進階

scope屬性(常見)

  • singleton(默認值): 絕大時候使用
    單例對象,在容器中只會存在一個實例,並且是容器啓動時就創建
User u1 = (User) applicationContext.getBean("user");
User u2 = (User) applicationContext.getBean("user");
System.out.println(u1==u2);

//結果爲true
//user對象是從容器中拿的現成的
  • prototype(多例原型)
    被標識的多例對象,每次在獲取時纔會創建,並且每次創建都是新的,容器啓動時不默認創建該對象
<!--容器啓動時不會根據這個bean元素創建實例對象-->
<bean name="user" class="com.xiaoming.bean.User" scope="prototype"/>


User u1 = (User) applicationContext.getBean("user");
User u2 = (User) applicationContext.getBean("user");
System.out.println(u1==u2);
//打印結果是false
  • request(沒用)
    在WEB環境下,對象與request生命週期一致(在一次請求中)
  • session(沒用)
    在WEB環境下,對象與session生命週期一致(在一次會話中)

生命週期屬性

  • init-method(初始化方法)
    可以配置一個方法作爲生命週期初始化方法,spring會在對象創建之後立即調用
  • destory-mthod(銷燬方法)
    可以配置一個方法作爲生命週期銷燬方法,spring會在銷燬該對象之前調用
//在User對象中加入生命週期方法

    public void init(){
        System.out.println("初始化方法");
    }

    public void destory(){
        System.out.println("銷燬方法");
    }


//配置文件
<bean name="user" class="com.xiaoming.bean.User" init-method="init"
    destroy-method="destory"/>

Spring分模塊配置

爲了防止配置文件內容過多不便於管理,我們可以在一個主配置文件當中引入其他配置文件

<!--導入其他配置文件-->
<!--從類路徑下加載-->
<import resource="com/xiaoming/create/applicationContext.xml">

Spring屬性注入

  • set方法注入
    set方法注入是最常用的也是最簡單的
//配置文件
<?xml version="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.xsd">

    <!--練習屬性注入-->

    <!--set方式注入:-->
    <bean name="user" class="com.xiaoming.bean.User">
        <!--爲User對象中名爲username的屬性注入tom作爲值-->
        <property name="usrename" value="tom"/>
        <property name="age" value="18"/>
        <!--引用類型注入:爲car屬性注入下方配置的car對象-->
        <property name="car" ref="car"/>
    </bean>

    <bean name="car" class="com.xiaoming.bean.Car">
        <property name="name" value="蘭博基尼"/>
        <property name="color" value="黃色"/>
    </bean>

</beans>


//測試
    @Test
    public void testSet(){
        User user = (User) applicationContext.getBean("user");
        System.out.println(user);
    }
  • 構造函數注入
    <!--
        構造函數注入:
            name屬性:指定構造函數的屬性名
            index屬性:指定參數的索引
            type屬性:參數的類型
         通過以上三個屬性能確定唯一一個構造函數
        -->
    <bean name="user2" class="com.xiaoming.bean.User">
        <constructor-arg name="usrename" value="張三" type="java.lang.String" index="0"/>
        <constructor-arg name="age" value="20" index="1"/>
        <constructor-arg name="car" ref="car"/>
    </bean>
  • p名稱空間注入(瞭解)
    新研發出來的,但是bean配置方式已經深入人心
    image
  • spel注入(瞭解)
    image

Spring複雜類型注入

//實體類
package com.xiaoming.bean;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Properties;

public class CollectionBean {
    private Object[] arr; //數組類型注入
    private List list;  //list set 類型注入
    private Map map; //map類型注入
    private Properties pro; //properties類型注入
    ...
}
  • 數組類型
  <!--1.array注入-->
    <bean name="array" class="com.xiaoming.bean.CollectionBean">
        <!--如果數組中只有一個元素或者對象那麼直接使用value或者ref-->
        <!--<property name="arr" value="tom"/>-->

        <!--array注入多個元素-->
        <property name="arr">
            <array>
                <value>tom</value>
                <value>jierui</value>
                <ref bean="car"/>
            </array>
        </property>
    </bean>
  • List類型
    <!--2.list測試跟array一樣-->
    <bean name="list" class="com.xiaoming.bean.CollectionBean">
        <!--如果數組中只有一個元素或者對象那麼直接使用value或者ref-->
        <!--<property name="list" value="tom"/>-->
        <!--array注入多個元素-->
        <property name="list">
            <list>
                <!--普通類型-->
                <value>tom</value>
                <value>jierui</value>
                <!--引用類型-->
                <ref bean="car"/>
            </list>
        </property>
    </bean>
  • Map類型
    <!--map注入-->
    <bean name="map" class="com.xiaoming.bean.CollectionBean">
        <property name="map">
            <map>
                <!--key value對的形式-->
                <entry key="zhangsan" value="張三"/>
                <entry key="車對象" value-ref="car"/>
            </map>
        </property>
    </bean>
  • Properties類型
    <!--Properties注入-->
    <bean name="properties" class="com.xiaoming.bean.CollectionBean">
        <property name="pro">
            <props>
                <prop key="ahha">哈哈</prop>
                <prop key="username">張三</prop>
            </props>
        </property>
    </bean>

註解開發Spring

  • 導包(4+2+1)
    1:spring-aop包
  • 爲主配置文件引入新的約束,並開啓註解代理配置文件
    引入了context約束
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd ">

    <!--
        指定掃描 com.xiaoming.bean包下所有類的註解
        注意:在掃描時會掃描指定包下的子孫包
        com.xiaoming:掃描該包下所有的包中類的註解
        剩下的工作就是在類中只用註解完成配置
    -->
    <context:component-scan base-package="com.xiaoming.bean2"></context:component-scan>
</beans>
  • 將對象註冊到容器中
//相當於 <bean name="user" class="..">
@Component("user")      
    @Repository("user")  //dao層
    @Service("user")     //service層
    @Controller("user")  //web層  
      //這三個註解跟上面這個功能一樣,爲了讓標註類本身用途更加清晰
      //Spring會在後續的版本中對其增強
public class User {
    ...
}
  • 修改對象作用範圍
//指定對象的作用域
@Scope(scopeName = "prototype")
public class User {
    ...
}
  • 值類型注入
//通過字段注入,這裏使用反射給Filed賦值破壞了封裝性
@Value("張三")
private String usrename;
@Value("18")
private Integer age;

----------------
//通過set方法賦值,推薦使用
@Value("張三2")
public String getUsrename() {
    return usrename;
}
  • 引用類型注入

@Autowired //自動裝配
//如果匹配到多個類型相同的對象,將無法選擇注入哪一個
@Qualifier() //告訴spring容器使用哪個名稱的對象 
//上述兩個一塊是用


//給引用對象注入值  手動注入,指定那個名稱的對象 推薦使用
@Resource(name = "car")
private Car car;
  • 生命週期方法
@PostConstruct //在對象在創建之後調用
public void init(){
    System.out.println("初始化方法");
}

@PreDestroy //在對象銷燬之前調用
public void destory(){
    System.out.println("銷燬方法");
}

image

XML和註解

  • XML 結構清晰
  • 註解 開發方便(屬性注入)
  • 實際開發過程中還有一種XML跟註解整合開發:
    Bean由XML配置,使用的屬性由註解注入

Spring整合junit

  • 導包(4+2+2)
    2: aop + test

  • 代碼

//幫我們創建容器
@RunWith(SpringJUnit4ClassRunner.class)
//指定創建容器時使用哪個配置文件
@ContextConfiguration("classpath:com/xiaoming/annotation/applicationContext.xml")
public class TestAnnotation {

    //將容器中名爲user的對象注入變量u中
    @Resource(name = "user")
    private User u ;

    @Test
    public void testJunit(){
        System.out.println(u);
    }

}

Spring Aop(動態AOP性能相較於靜態AOP較差)

aop思想介紹

縱向重複代碼橫向抽取
image

image

概述

1. 什麼是AOP的技術?
   /* 在軟件業,AOP爲Aspect Oriented Programming的縮寫,意爲:面向切面編程
    * AOP是一種編程範式,隸屬於軟工範疇,指導開發者如何組織程序結構
    * AOP最早由AOP聯盟的組織提出的,制定了一套規範.Spring將AOP思想引入到框架中,必須遵守AOP聯盟的規範
    * 通過預編譯方式和運行期動態代理實現程序功能的統一維護的一種技術
    * AOP是OOP的延續,是軟件開發中的一個熱點,也是Spring框架中的一個重要內容,是函數式編程的一種衍生範型
    * 利用AOP可以對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度降低,提高程序的可重用性,同時提高了開發的效率

2. AOP:面向切面編程.(思想.---解決OOP遇到一些問題)
3. AOP採取橫向抽取機制,取代了傳統縱向繼承體系重複性代碼(性能監視、事務管理、安全檢查、緩存)

4. 爲什麼要學習AOP
    * 可以在不修改源代碼的前提下,對程序進行增強!!

功能

  • 對程序進行增強在不修改源代碼的情況下
  • aop可以進行權限校驗,日誌記錄,性能監控,事務控制

Spring中的aop

spring能夠爲容器中管理的對象生成動態代理對象,以前我們需要使用動態代理技術,調用如下代碼Proxy.newproxyInstance(load,cs,handler)生成代理對象
而Spring能夠幫我們自動生成代理對象

Spring實現aop的原理

  • 動態代理(優先使用)
    被代理對象必須要有接口產生代理對象,,如果沒有接口就無法產生代理對象
  • cglib代理
    第三方的代理技術,能對任何類生成代理對象,原理是對目標對象進行繼承代理,他會把被代理對象進行一個繼承,例如生成一個UserService代理對象,他實際上是UserService的自類,如果目標對象被final修飾name該類無法被cglib代理

  • 觀光代碼(瞭解)

//接口
public interface UserService {
    public void add();
    public void delete();
    public void update();
    public void quey();

}

//實現類
public class UserServiceImpl implements UserService {
    @Override
    public void add() {
        System.out.println("添加用戶");
    }

    @Override
    public void delete() {
        System.out.println("刪除用戶");
    }

    @Override
    public void update() {
        System.out.println("修改用戶");
    }

    @Override
    public void quey() {
        System.out.println("查詢用戶");
    }

}


//代理工廠(這裏爲了演示方便,傳的參數讓工廠類去繼承了)

public class UserServiceProxyFactory  
            implements MethodInterceptor , InvocationHandler{

    private UserService us;


    public UserService getUserServiceProxyByJdk(){
      return (UserService) Proxy.newProxyInstance(this.getClass().getClassLoader(),UserServiceImpl.class.getInterfaces(),this);
    }


    public UserService getUserServiceProxyBycglib() {
        /**
         * 因爲Spring整合了cglib(第三方jar)代理所以直接用不用導包
         */
        //幫我們成代理對象
        Enhancer enhancer = new Enhancer();
        //設置對誰進行代理
        enhancer.setSuperclass(UserServiceImpl.class);
        //代理要做什麼
        enhancer.setCallback(this);
        //創建代理對象
        return (UserService) enhancer.create();

    }

    //cjlib代理需要傳的參數 CallBack 跟Handler接口中的invoke一樣
    @Override
    public Object intercept(Object proxyObj, Method method, Object[] arg, MethodProxy methodProxy) throws Throwable {
        //前置增強
        System.out.println("打開事務");
        //調用目標方法
        Object result = methodProxy.invokeSuper(proxyObj, arg);
        //後置增強
        System.out.println("提交事務");
        //返回目標對象
        return result;
    }

    //動態代理
    @Override
    public Object invoke(Object proxyObj, Method method, Object[] arg) throws Throwable {
        //前置增強
        System.out.println("開啓事務");
        //執行目標方法
        Object invoke = method.invoke(us, arg);
        //後置增強
        System.out.println("提交事務");
        return invoke;
    }

    public void setUs(UserService us) {
        this.us = us;
    }
}


//測試代碼

public class Demo {

    @Test
    public void fun1(){
        /**
         * 動態代理:針對實現了接口的類進行代理
         */

        UserServiceImpl us = new UserServiceImpl();
        UserServiceProxyFactory factory = new UserServiceProxyFactory();
        factory.setUs(us);
        UserService userService = factory.getUserServiceProxyByJdk();
        userService.add();


        //代理對象與被代理對象實現了想同的接口
        //代理對象與被代理對象沒有繼承關係(相當於是兄弟)
        System.out.println(userService instanceof  UserServiceImpl);

    }


    @Test
    public void fun2(){
        /**
         * cglib代理:針對沒有實現接口的類進行代理 生成當前類的子類對象
         */
        UserServiceProxyFactory factory = new UserServiceProxyFactory();
        UserService userService = factory.getUserServiceProxyBycglib();
        userService.add();

        //代理對象是被代理對象的子類
        System.out.println(userService instanceof UserServiceImpl);

    }
}

Spring Aop 常見名詞學習(基於AspectJ)

1. Joinpoint(連接點)   
    * 所謂連接點是指那些被攔截到的點。在spring中,這些點指的是方法,因爲spring
      只支持方法類型的連接點
2. Pointcut(切入點)
    * 所謂切入點是指我們要對哪些Joinpoint進行攔截的定義
3. Advice(通知/增強)
    * 所謂通知是指攔截到Joinpoint之後所要做的事情就是通知.通知分爲前置通知,
      後置通知,異常通知,最終通知,環繞通知(切面要完成的功能)
4. Introduction(引介)
    * 引介是一種特殊的通知在不修改類代碼的前提下, Introduction可以在
      運行期爲類動態地添加一些方法或Field
5. Target(目標對象)
    * 代理的目標對象
6. Weaving(織入)
    * 是指把增強應用到目標對象來創建新的代理對象的過程
7. Proxy(代理)
    * 一個類被AOP織入增強後,就產生一個結果代理類
8. Aspect(切面)
    * 是切入點和通知的結合,以後咱們自己來編寫和配置的

image

Spring中aop的演示

導包(4+2+2+2)

  • 4+2
    核心包+日誌包
  • +2
    spring-aspects, spring-aop
  • +2
    第三方jar包 aopalliance, weaver

準備目標對象

public class UserServiceImpl implements UserService {
    @Override
    public void add() {
        System.out.println("添加用戶");
    }

    @Override
    public void delete() {
        System.out.println("刪除用戶");
    }

    @Override
    public void update() {
        System.out.println("修改用戶");
    }

    @Override
    public void quey() {
        System.out.println("查詢用戶");
    }

}

準備通知

public class MyAdvice {
    /**
     * - 前置通知
     *      目標方法運行之前
     * - 後置通知(如果出現異常不會調用)
     *      目標方法調用之後
     * - 環繞通知
     *      目標方法之前跟之後都調用
     * - 異常攔截通知
     *      如果出現異常就調用
     * - 最終通知(無論是否出現異常都調用)
     *      在目標方法之後調用
     */

    //前置通知
    public void before(){
        System.out.println("這是前置通知!!");
    }
    //後置通知
    public void afterReturning(){
        System.out.println("這是後置通知(如果出現異常不會調用)!!");
    }
    //環繞通知  必須這麼寫別問爲什麼,這種寫法跟動態代理差不多
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("這是環繞通知之前的部分!!");
        //調用目標方法
        Object proceed = pjp.proceed();
        System.out.println("這是環繞通知之後的部分!!");
        return proceed;
    }
    //異常通知
    public void afterException(){
        System.out.println("出事啦!出現異常了!!");
    }
    //最終通知
    public void after(){
        System.out.println("這是後置通知(出現異常也會調用)!!");
    }
}

將通知織入目標對象中

<!--配置: 將通知織入到目標對象-->
<aop:config>
<!--
    配置切入點 PointCut(要增強的方法):以下是切入點表達式
    格式:
    execution([修飾符] 返回值類型 包名.類名.方法名(參數))
    public void com.langsin.spring.aop.UserServiceImpl.add() //要增強的單個方法
    void com.langsin.spring.aop.UserServiceImpl.add() //簡寫
    * com.langsin.spring.aop.UserServiceImpl.add() //進一步簡寫
    * com.langsin.spring.aop.UserServiceImpl.*()   //增強該類下的所有的方法(僅限無參的)
    * com.langsin.spring.aop.*ServiceImpl.*(..)    //增強該包下以ServiceImpl結尾的所有方法(參數不限)
    * com.langsin.spring.aop..*ServiceImpl.*(..)
-->
    <!--配置切入點表達式:那些類的那些方法需要增強-->
        <aop:pointcut id="pc" expression="execution(* com.langsin.spring.aop.UserServiceImpl.*(..))"></aop:pointcut>
    <!--配置通知(切面)-->
        <aop:aspect ref="myAdvice">
            <!--配置前置通知: 將MyAdvice中的before方法作爲前置通知-->
            <aop:before method="before" pointcut-ref="pc"/>
            <!--配置後置通知-->
            <aop:after-returning method="afterReturning" pointcut-ref="pc"/>
            <!--配置環繞通知-->
            <aop:around method="around" pointcut-ref="pc" />
            <!--配置異常攔截通知-->
            <aop:after-throwing method="afterException" pointcut-ref="pc"/>
            <!--配置最終通知-->
            <aop:after method="after" pointcut-ref="pc"/>
        </aop:aspect>
    </aop:config>

切入點表達式

1. 再配置切入點的時候,需要定義表達式,重點的格式如下:execution(public * *(..)),具體展開如下:
    * 切入點表達式的格式如下:
        * execution([修飾符] 返回值類型 包名.類名.方法名(參數))

    * 修飾符可以省略不寫,不是必須要出現的。
    * 返回值類型是不能省略不寫的,根據你的方法來編寫返回值。可以使用 * 代替。
    * 包名例如:com.itheima.demo3.BookDaoImpl
        * 首先com是不能省略不寫的,但是可以使用 * 代替
        * 中間的包名可以使用 * 號代替
        * 如果想省略中間的包名可以使用 .. 

    * 類名也可以使用 * 號代替,也有類似的寫法:*DaoImpl
    * 方法也可以使用 * 號代替
    * 參數如果是一個參數可以使用 * 號代替,如果想代表任意參數使用 ..

image

使用註解配置 Spring AOP 開發

  • 配置配置文件
<?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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">


    <!--<bean name="userService" class="com.xiaoming.annotationAop.UserServiceImpl"/>-->
    <!--<bean name="advice" class="com.xiaoming.annotationAop.MyAdvice"/>-->
    <!--
        指定掃描 com.xiaoming.annotationAop包下所有類的註解
        注意:在掃描時會掃描指定包下的子孫包
        com.xiaoming:掃描該包下所有的包中類的註解
    -->
    <context:component-scan base-package="com.xiaoming.annotationAop"></context:component-scan>
    <!--配置目標對象-->
    <!--配置通知-->

    <!--開啓使用註解完成織入-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

</beans>
  • 配置目標對象
//配置目標對象
@Service("userService")
public class UserServiceImpl implements UserService {
    @Override
    public void add() {
        System.out.println("添加用戶");
    }

    @Override
    public void delete() {
        System.out.println("刪除用戶");
    }

    @Override
    public void update() {
        System.out.println("修改用戶");
    }

    @Override
    public void quey() {
        System.out.println("查詢用戶");
    }
}
  • 定義切面Bean(配置切入點跟通知)
//配置通知
@Service("advice")
//定義切面Bean
@Aspect
public class MyAdvice {

    //配置切點表達式
    @Pointcut("execution(* com.xiaoming.annotationAop.UserServiceImpl.*(..))")
    public void pc(){}

    //前置通知
    @Before("MyAdvice.pc()")
    public void before(){
        System.out.println("這是前置通知!!");
    }

    //後置通知
    @AfterReturning("MyAdvice.pc()")
    public void afterReturning(){
        System.out.println("這是後置通知(如果出現異常不會調用)!!");
    }

    //環繞通知  必須這麼寫別問爲什麼,這種寫法跟動態代理差不多
    @Around("MyAdvice.pc()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("這是環繞通知之前的部分!!");
        //調用目標方法
        Object proceed = pjp.proceed();
        System.out.println("這是環繞通知之後的部分!!");
        return proceed;
    }
    //異常通知
    @AfterThrowing("MyAdvice.pc()")
    public void afterException(){
        System.out.println("出事啦!出現異常了!!");
    }

    //最終通知
    @After("MyAdvice.pc()")
    public void after(){
        System.out.println("這是最終通知(出現異常也會調用)!!");
    }
}
  • 測試
@RunWith(SpringJUnit4ClassRunner.class)//幫我們創建容器
@ContextConfiguration("classpath:com/xiaoming/annotationAop/applicationContext.xml")//指定創建容器時使用哪個配置文件
public class TestAopByAnnotation {

    //將名爲userService的對象注入到該變量中
    @Resource(name = "userService")
    private UserService userService;

    @Test
    public void testAopByAnnotation(){
        userService.add();
    }
}

Spirng整合JDBC

  • Spring提供了很多模板整合Dao的技術
    image
  • Spirng提供了直接封裝JDBC技術的對象
    JDBCTemplate ==> JDBC模板對象
    與DBUtils中的QueryRunner對象非常相似
    image
步驟
  • 導包
    4+2+spring-test+spring-aop+c3p0+JDBC驅動+spring-jdbc+spring-tx事務
  • 書寫DaoImpl
//使用JDBC模板實現增刪改查
public class UserDaoImpl implements UserDao {

    private JdbcTemplate jt = null;

    @Override
    public void addUser(User user) {
        String sql = "insert into user values(?,?,?,?)";
        jt.update(sql, user.getId(),user.getUsername(),user.getPassword(),user.getGrade());
    }

    @Override
    public void delete(String id) {
        String sql = "delete from user where id = ?";
        jt.update(sql, id);
    }

    @Override
    public void update(User user) {
        String sql = "update user set username = ? where id = ?";
        jt.update(sql, user.getUsername(),user.getId());
    }

    @Override
    public User getUserByid(String id) {
        String sql = "select * from user where id = ?";
        return (User)jt.query(sql, new BeanPropertyRowMapper<>(User.class),id);
    }

    @Override
    public List<User> getList() {
        String sql = "select * from user ";
        return (List<User>)jt.query(sql, new BeanPropertyRowMapper<>(User.class));
    }

    //提供set方法
    public void setJt(JdbcTemplate jt) {
        this.jt = jt;
    }
}
  • Spring配置依賴關係
    image
<?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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!--將數據庫連接池配置到Spring容器中-->
    <bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/customers"/>
        <property name="driverClass" value="com.mysql.jdbc.Driver"/>
        <property name="user" value="root"/>
        <property name="password" value="root"/>
    </bean>

    <!--將JdbcTemplate配置到到Spring容器中,並配置dataSource-->
    <bean name="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--將UserDao配置到Spring容器中並配置jdbcTemplate-->
    <bean name="userDao" class="com.xiaoming.jdbc.UserDaoImpl">
        <property name="jt" ref="jdbcTemplate"/>
    </bean>

</beans>
  • 測試略
進階
  • JDBCDaoSupport
    image
    image

  • 讀取外部的perties配置
    db.properties 注意加前綴

jdbc.jdbcUrl=jdbc:mysql://localhost:3306/customers
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.user=root
jdbc.password=root

Spring中的配置

    <!--指定Spring讀取db.properties配置-->
    <context:property-placeholder location="classpath*:db.properties" />

    <!--將數據庫連接池配置到Spring容器中-->
    <bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driverClass}"/>
        <property name="jdbcUrl" value="${jdbc.jdbcUrl}"/>
        <property name="user" value="${jdbc.user}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

Spring中AOP事務

事務的特性

  • 原子性(Atomicity)
    事務中所有操作是不可再分割的原子單位。事務中所有操作要麼全部執行成功,要麼全部執行失敗。
  • 一致性(Consistency)
    ==其他特性都是爲一致性服務的==,事務執行後,數據庫狀態與其它業務規則保持一致
  • 隔離性(Isolation)
    隔離性是指在併發操作中,不同事務之間應該隔離開來,使每個併發中的事務不會相互干擾。
  • 持久性(Durability)
    一旦事務提交成功,事務中所有的數據操作都必須被持久化到數據庫中,即使提交事務後,數據庫馬上崩潰,在數據庫重啓時,也必須能保證通過某種機制恢復數據.

事務的併發問題

  • 髒讀
    讀到另一個事務的未提交的更新數據,即讀到了髒數據
  • 不可重複讀
    一個事務讀到了另一個事務已經提交的update 的數據導致多次查詢結果不一致
  • 幻讀(虛讀)
    一個事務讀到了另一個事務已經提交的insert 的數據導致多次查詢結果不一致

事務的隔離級別

image
image
image
image

Spring中封裝了事務的管理代碼

```
##### 平臺事務管理器  
**==PlatformTransactionManager接口)==**  
因爲使用不同框架,不同的技術,操作事務的代碼不近相同,所以Spring提供了一個接口
- DataSourceTransactionManager  
針對JDBC 以及Mybais提供的實現類
- HibernateTransitionmanager  
針對Hibernate提供的實現類  

**==注意在Spring中玩事務管理,最爲核心的對象就是TransactionManager對象==**

#### spring管理事務的屬性介紹
- **事務的隔離級別**  
讀未提交,讀已提交,可重複讀,串行化
- **是否只讀**  
true 只讀; false 可以進行增刪改
 - **事務的傳播行爲**
 ![image](https://gitee.com/helloword7/Images/raw/master/ee/spring/28.png)

### Spring管理事務的方式(編碼式|聲明式)
#### 以銀行轉賬爲例
- 接口

public interface AccountDao {
//加錢
void addMoney(Integer id,Double money);
//減錢
void jianQain(Integer id,Double money);
}

public interface AccountService {
//轉賬
void zhuanZhang(Integer from ,Integer to ,Double money);
}

- 實現類

//1
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
@Override
public void addMoney(Integer id, Double money) {
String sql = “UPDATE tb_account SET money = money + ? WHERE id = ? “;
super.getJdbcTemplate().update(sql,money,id);
}

@Override
public void jianQain(Integer id, Double money) {
    String sql = "UPDATE tb_account SET  money = money - ? WHERE id = ? ";
    super.getJdbcTemplate().update(sql,money,id);
}

}
//2
public class AccountServiceImpl implements AccountService {

private AccountDao accountDao;

public void zhuanZhang(Integer from, Integer to, Double money) {
        accountDao.jianQain(from,money);
        accountDao.addMoney(to,money);
}

public void setAccountDao(AccountDao accountDao) {
    this.accountDao = accountDao;
}

}

### Spring管理事物的方式: 
#### 1.編碼式(瞭解)
- **配置連接池**






- **配置平臺事務管理器的相關實現類**




- **配置TransactionTemplate模板(編碼式事務管理)**

//編程式事務管理.需要自己手動在每個業務方法中實現事務


- **將事務模板注入Service**

 ![image](https://gitee.com/helloword7/Images/raw/master/ee/spring/31.png)
- **在Service中調用模板**

![image](https://gitee.com/helloword7/Images/raw/master/ee/spring/32.png)

#### 2.XML配置式(aop)
使用SpringAop來管理事務,目標對象我們已經寫好了,Spring還爲我們提供了一個關於事務的通知,所以我們在Spring中使用事務不需要跟上面編碼式那樣自己寫,配一配就完事了
- **導包,導入新的約束**  
![image](https://gitee.com/helloword7/Images/raw/master/ee/spring/33.png)


beans: 最基本  
context:讀取properties配置  
aop:配置aop  
tx:配置事務通知  

- **同樣需要配置連接池**  






- **配置事務管理器** 
<!--
    配置核心事務管理器:DataSourceTransactionManager 依賴於連接池
      適用場景:mybatis,jdbctemplate,本地事務
-->



- **配置所需要的其他bean**







- **配置事務通知** 




    <!--企業中常見的兩套模板(增刪改查)-->
    <!--隔離級別跟傳播行爲用默認值挺好-->
    <tx:method name="save*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" />
    <tx:method name="persist*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" />
    <tx:method name="update*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" />
    <tx:method name="modify*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" />
    <tx:method name="delete*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" />
    <tx:method name="remove*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" />
    <tx:method name="get*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="true" />
    <tx:method name="find*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="true" />
    <tx:method name="zhuanZhang" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" />
</tx:attributes>

- **配置將通知織入目標對象** 











### 使用Xml配置Aop事務完整版

### 使用註解開發Spring AOP事務
配置文件

Service層

package com.xiaoming.transaction;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;

/**
* Created by 桑曉明 on 2018/01/13.
*/
@Service(“accountService”)
@Transactional(isolation = Isolation.REPEATABLE_READ,propagation = Propagation.REQUIRED,readOnly = false)
public class AccountServiceImpl implements AccountService {

@Resource(name = "accountDao")
private AccountDao accountDao;

public void zhuanZhang(Integer from, Integer to, Double money) {
        accountDao.jianQain(from,money);

// int a = 10/0;
accountDao.addMoney(to,money);
}

public void setAccountDao(AccountDao accountDao) {
    this.accountDao = accountDao;
}

}

“`

發佈了25 篇原創文章 · 獲贊 5 · 訪問量 6655
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章