日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

JavaWeb开发框架——Spring

發布時間:2023/12/31 java 62 豆豆
生活随笔 收集整理的這篇文章主要介紹了 JavaWeb开发框架——Spring 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目錄

1、Spring簡介

1.1、Spring是什么

1.2、Spring發展歷程

1.3、Spring的優勢

1.3.1、方便解耦,簡化開發

1.3.2、AOP編程的支持

1.3.3、聲明式事務的支持

1.3.4、方便程序的測試

1.3.5、方便繼承各種優秀框架

1.3.6、降低JavaEE API 的使用難度

1.3.7、Java源碼的經典學習范例

1.4、Spring的體系結構

2、Spring快速入門

2.1、Spring程序開發步驟

2.2、Spring的開發步驟

3、Spring配置文件

3.1、Bean標簽基本配置

3.2、Bean標簽范圍配置

3.3、Bean生命周期

3.3.1、生命周期控制方式1

3.3.2、生命周期控制方式2

3.4、Bean實例化的四種方式

3.5、Bean的依賴注入分析

3.5.1、Bean的依賴注入概念

3.6、Bean的依賴注入方式

3.6.1、setter注入——引用類型

3.6.2、setter注入——簡單類型

3.6.3、構造器(構造方法)注入——引用類型

3.6.4、構造器(構造方法)注入——簡單類型

3.6.5、p命名空間方式注入

3.6.6、注入集合數據類型

3.6.7、依賴注入方式的選擇

3.6.8、案例——數據源對象管理

3.7、依賴自動裝配

3.7.1、自動裝配方式

3.7.2、依賴自動裝配的特征

3.8、Spring加載外部properties文件

3.9、引入其他配置文件(分模塊開發)

4、Spring相關API

4.1、ApplicationContext的繼承體系

4.2、ApplicationContext的實現類

4.3、getBean()方法使用

5、Spring配置數據源

5.1、數據源(連接池)的作用

5.2、數據源的開發步驟

5.3、Spring配置數據源

5.4、抽取jdbc配置文件

6、Spring注解開發

6.1、Spring原始注解

6.1.1、@Scope & @PostConstruct & @PreDestroy

6.1.2、@Autowired & @Qualifier 實現引用類型依賴注入

6.1.3、@Value 實現簡單類型依賴注入

6.2、Spring新注解

6.2.1、@Configuration & @ComponentScan實現替代Spring配置文件

6.2.2、@Import & @Bean 實現配置類分離和第三方bean管理

6.3、XML配置對比注解配置

7、Spring整合MyBatis

8、Spring整合Junit

8.1、原始Junit測試Spring的問題

8.2、上述問題解決思路

8.3、Spring整合Junit步驟

9、Spring集成Web環境

9.1、ApplicationContext應用上下文獲取方式

9.2、Spring提供獲取應用上下文的工具

9.3、Spring集成web環境步驟

10、Spring JdbcTemplate的基本使用

10.1、JdbcTemplate概述

10.2、JdbcTemplate開發步驟

10.3、Spring產生JdbcTemplate對象

11、Spring AOP

11.1、什么是AOP

10.2、AOP的作用及其優勢

10.3、AOP的底層實現

10.4、AOP的動態代理技術

10.4.1、基于JDK的動態代理

10.4.2、基于cglib的動態代理

10.5、AOP相關概念

10.5.1、AOP切入點表達式

10.5.2、AOP通知類型

10.6、AOP開發明確的事項

10.6.1、需要編寫的內容

10.6.2、AOP技術實現的內容

10.6.3、AOP底層使用哪種代理方式

10.7、AOP工作流程

10.8、基于XML的AOP開發

10.8.1、快速入門

10.8.2、XML配置AOP詳解

10.9、基于注解的AOP開發

10.9.1、快速入門

10.9.2、注解配置AOP詳解

10.9.3、AOP通知獲取數據

10.10、AOP案例

10.10.1、案例1——測量業務層接口萬次執行效率

10.10.2、案例2——百度網盤密碼數據兼容處理

11、Spring的事務控制

11.1、事務相關定義

11.1.1、事務角色

11.1.2、事務的隔離級別

11.1.3、事務的傳播行為

11.2、編程式事務控制相關對象

11.2.1、PlatformTransactionManager

11.2.2、TransactionDefinition

11.2.3、TransactionStatus

11.3、基于XML的聲明式事務控制

11.3.1、什么是聲明式事務控制

11.3.2、聲明式事務控制的實現

11.4、基于注解的聲明式事務控制

11.4.1、使用步驟

11.4.2、注解配置聲明式事務控制解析

11.5、Spring事務相關配置

11.5、Spring事務案例

11.5.1、案例1——銀行賬戶轉賬

11.5.2、案例2——轉賬業務追加日志


1、Spring簡介

1.1、Spring是什么

Spring是分層的Java SE/EE 應用full-stack輕量級開源框架,以IoC(Inversion Of Control:反轉控制)和AOP(Aspect Oriented Programming:面向切面編程)為內核。

  • IoC(Inversion of Control):控制反轉。使用對象時,由主動new產生對象轉換為由外部提供對象,此過程中對象創建控制權由程序轉移到外部,此思想稱為控制反轉。
  • DI(Dependency Injection):依賴注入。

Spring技術對IoC思想進行了實現:

  • Spring提供了一個容器,用來充當IoC思想中的“外部”;
  • IoC容器負責對象的創建、初始化等一系列工作,被創建或被管理的對象在IoC容器中統稱為Bean。

提供了展現層SpringMVC和持久層Spring JDBCTemplate以及業務層事務管理等眾多的企業級應用技術,還能整合開源世界眾多著名的第三方框架和類庫,逐漸成為使用最多的JavaEE企業應用開源框架。

1.2、Spring發展歷程

1.3、Spring的優勢

1.3.1、方便解耦,簡化開發

通過Spring提供的IoC容器,可以講對象間的依賴關系交由Spring進行控制,避免編碼所造成的過度耦合,用戶也不必在為單例模式類、屬性文件解析等這些很底層的需求編寫代碼,可以更專注上層的應用。

1.3.2、AOP編程的支持

通過Spring的AOP功能,方便進行面向切面編程,許多不容易用傳統OOP實現的功能可以通過AOP輕松實現。

1.3.3、聲明式事務的支持

可以將我們從單調煩悶的事務管理代碼中解脫出來,通過聲明式方式靈活地進行事務管理,提高開發效率和質量。

1.3.4、方便程序的測試

可以用非容器依賴的編程方式進行幾乎所有的測試工作,測試不再是昂貴的操作,而是隨手可做的事情。

1.3.5、方便繼承各種優秀框架

Spring對各種優秀框架(Struts、Hibemate、Hessian、Quartz等)的支持。

1.3.6、降低JavaEE API 的使用難度

Spring對JavaEE API(如JDBC、JavaMail、遠程調用等)進行了薄薄的封裝層,使這些API的使用難度大為降低。

1.3.7、Java源碼的經典學習范例

Spring的源代碼設計精妙、結構清晰、匠心獨用,處處體現著大師對Java設計模式靈活運用以及對Java技術的高深造詣。它的源代碼無疑是Java技術的最佳實踐的范例。

1.4、Spring的體系結構

  • Data Access:數據訪問;
  • Data Integration:數據集成;
  • Web:Web開發;
  • AOP:面向切面編程;
  • Aspects:AOP思想實現;
  • Core Container:核心容器。
  • Test:單元測試與集成測試。

2、Spring快速入門

2.1、Spring程序開發步驟

問題提出:

  • 管理什么?(Service與Dao)
  • 如何將被管理的對象告知IoC容器?(配置)
  • 被管理的對象交給IoC容器,如何獲取到IoC容器?(接口)
  • IoC容器得到后,如何從容器中獲取bean?(接口方法)
  • 使用Spring導入哪些坐標?(pom.xml)

  • 導入Spring開發的基本包坐標;
  • 編寫Dao接口和實現類;
  • 創建Spring核心配置文件;
  • 在Spring配置文件中配置UserDaoImpl;
  • 使用Spring的API獲得Bean實例。
  • 2.2、Spring的開發步驟

  • 導入坐標;
  • 創建Bean;
  • 創建applicationContext.xml;
  • 在配置文件中進行配置;
  • 創建ApplicationContext對象getBean。
  • 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"><bean id="userDao" class="com.clp.impl.UserDaoImpl"></bean></beans>

    注意:bean定義時id屬性在同一個上下文中不能重復。

    package com.clp.impl;class UserDaoImpl implements com.clp.UserDao {@Overridepublic void save() {System.out.println("save running ...");} }package com.clp.demo;import com.clp.UserDao; import javafx.application.Application; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;public class UserDaoDemo {public static void main(String[] args) {ApplicationContext app = new ClassPathXmlApplicationContext("ApplicationContext.xml");UserDao userDao = (UserDao) app.getBean("userDao");userDao.save();} }

    3、Spring配置文件

    3.1、Bean標簽基本配置

    用于配置對象交由Spring來創建。默認情況下它調用的是類中的無參構造函數,如果沒有無參構造函數則不能創建成功。

    類別描述
    名稱<bean>
    類型標簽
    所屬<beans>標簽
    功能定義Spring核心容器管理的對象
    格式

    <beans>

    ??????? <bean></bean>

    <beans>

    屬性列表

    id:bean的id,使用容器可以通過id值獲取對應的bean,在一個容器中id值唯一。

    name:bean的別名,可以設置多個,使用,(逗號)或;(分號)或 (空格)分隔,可以用來代替id。

    class:bean的類型,即配置的bean的全路徑類名。

    scope:定義bean的作用范圍。singleton為單例(默認),prototype為非單例。

    init-method:生命周期初始化方法。

    destroy-method:生命周期銷毀方法。

    autowire:自動裝配類型。

    factory-method:bean工廠方法,應用于靜態工廠或實例工廠。

    factory-bean:實例工廠bean。

    lazy-init:控制bean延遲加載。

    范例

    <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl" />

    <bean id="bookService" name="service service2 bookEbi"? class="com.itheima.service.impl.BookServiceImpl" />

    <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl" scope="prototype" />

    類別描述
    名稱<property>
    類型標簽
    所屬<bean>標簽
    功能bean的屬性注入
    格式

    <bean>

    ??????? <property></property>

    </bean>

    屬性列表

    name:屬性名稱。

    value:注入的普通屬性值。

    ref:注入的對象引用值。

    范例 <bean id="dataSource1" class="com.alibaba.druid.pool.DruidDataSource"><!-- 使用setter依賴注入方式 --><property name="driverClassName" value="com.mysql.jdbc.Driver" /><property name="url" value="jdbc:mysql://localhost:3306/mydb" /><property name="username" value="root" /><property name="password" value="123456" /> </bean>

    注意事項:獲取bean無論是通過id還是name獲取,如果無法獲取到,將拋出異常NoSuchBeanDefinitionException。NoSuchBeanDefinitionException: No bean named 'bookServiceImpl' available 。

    3.2、Bean標簽范圍配置

    scope:指對象的作用范圍,取值如下:singleton:默認值,單例的。prototype:多例的。request:WEB項目中,Spring創建一個Bean的對象,將對象存入到request域中。session:WEB項目中,Spring創建一個Bean的對象,將對象存入到session域中。global session:WEB項目中,應用在Portlet環境,如果沒有Portlet環境那么globalSession相當于session。Bean的創建時機:當scope的取值為singleton時:Bean的實例化個數:1個;Bean的實例化時機:當Spring核心文件被加載時,實例化配置的Bean實例。Bean的生命周期:對象創建:當應用加載,創建容器時,對象就被創建了;對象運行:只要容器在,對象一直或者。對象銷毀:當應用卸載,銷毀容器時,對象就被銷毀了。當scope的取值為prototype時:Bean的實例化個數:多個。Bean的實例化時機:當調用getBean()方法時實例化Bean。對象創建:當使用對象時,創建新的對象實例。對象運行:只要對象在使用中,就一直活著。對象銷毀:當對象長時間不用時,被Java的垃圾回收器回收了。

    適合交給容器進行管理的bean:表現層對象;業務層對象;數據層對象;工具對象。

    不適合交給容器進行管理的對象:封裝實體的域對象。

    3.3、Bean生命周期

    生命周期:從創建到消亡的完整過程。

    Bean生命周期:Bean從創建到銷毀的整體過程。

    - 初始化容器1、創建對象(內存分配)2、執行構造方法3、執行屬性注入(set操作)4、執行bean初始化方法 - 使用bean1、執行業務操作 - 關閉/銷毀容器1、執行bean銷毀方法

    Bean的銷毀時機:

    - 容器關閉前觸發bean的銷毀- 關閉容器方式:1、手工關閉容器ConfigurationApplicationContext接口的close()操作2、注冊關閉鉤子,在虛擬機退出前先關閉容器再退出虛擬機ConfigurationApplicationContext接口registerShutdownHook()操作

    Bean生命周期控制:在Bean創建后到銷毀前做一些事情。

    3.3.1、生命周期控制方式1

    提供生命周期控制方法:class UserDaoImpl implements com.clp.UserDao {public UserDaoImpl() {System.out.println("UserDaoImpl創建..");}public void init() {System.out.println("初始化方法");}public void destroy() {System.out.println("銷毀方法");}@Overridepublic void save() {System.out.println("save running ...");} } 配置生命周期控制方法:init-method:指定類中的初始化方法名稱。destroy-method:指定類中銷毀方法名稱。<?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"><bean id="userDao" class="com.clp.impl.UserDaoImpl" init-method="init" destroy-method="destroy"></bean></beans> public class App {public static void main(String[] args) {// 3、獲取IoC容器ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");// 4.1、獲取bean:BookDao,參數為bean 的idBookDao bookDao = (BookDao) ctx.getBean("bookDao");bookDao.save();ctx.close();// Spring容器注冊關閉鉤子。表示Java虛擬機在關閉之前會先將容器關閉掉 // ctx.registerShutdownHook();// 4.2、獲取bean:BookService,參數為bean的id // BookService bookService = (BookService) aCtx.getBean("bookService"); // bookService.save();} }

    3.3.2、生命周期控制方式2

    提供生命周期控制方法:/*** 實現initializingBean和DisposableBean接口,重寫其中的方法*/ public class BookServiceImpl implements BookService, InitializingBean, DisposableBean {public BookDao bookDao;public void setBookDao(BookDao bookDao) {System.out.println("set ...");this.bookDao = bookDao;}@Overridepublic void save() {System.out.println("book service save ...");bookDao.save();}/*** 在調用完所有setXxx()的setter方法之后會調用該方法,如本類的setBookDao()方法* @throws Exception*/@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("book service afterPropertiesSet");}@Overridepublic void destroy() throws Exception {System.out.println("book service destroy...");} } applicationContext.xml中配置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"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="bookDao" class="com.clp.dao.impl.BookDaoImpl"/><bean id="bookService" class="com.clp.service.impl.BookServiceImpl"><property name="bookDao" ref="bookDao" /></bean> </beans>

    3.4、Bean實例化的四種方式

    bean本質上就是對象,創建bean使用構造方法完成。

    - 無參構造方法實例化:- 提供可訪問的構造方法 public UserDao() {}注意:無參構造方法如果不存在,將拋出異常BeanCreationException- 配置<beanid="userDao"class="com.clp.impl.UserDaoImpl"init-method="init"destroy-method="destroy"</bean>- 工廠靜態方法實例化:public class StaticFactory {public static UserDao getUserDao() {return new UserDaoImpl();}}<beanid="userDao"class="com.clp.factory.StaticFactory"<!-- 指定為靜態工廠中的某個方法返回的對象 -->factory-method="getUserDao"></bean>- 工廠實例方法實例化:public class UserDaoFactory {public UserDao getUserDao() {return new UserDaoImpl();}}<!-- 先造工廠對象 --><bean id="userDaoFactory" class="com.clp.factory.UserDaoFactory" /><!-- 再造UserDao對象 --><bean id="userDao" factory-bean="userDaoFactory" factory-method="getUserDao" />- 工廠實例方法實例化-改進版(使用FactoryBean): public class UserDaoFactoryBean implements FactoryBean<UserDao> {/*** 代替原始實例工廠中創建對象的方法* @return* @throws Exception*/@Overridepublic UserDao getObject() throws Exception {return new UserDaoImpl();}/*** 獲取對象的類型* @return*/@Overridepublic Class<?> getObjectType() {return UserDao.class;}/*** 該對象是否為單例* @return*/@Overridepublic boolean isSingleton() {return true;}}<!-- 使用factorybean實例化 --><bean id="userDao" class="com.clp.factory.UserDaoFactoryBean" />

    3.5、Bean的依賴注入分析

    目前UserService實例和UserDao實例都存在于Spring容器中,當前的做法是在容器外部獲得UserService實例和UserDao實例,然后在程序中進行結合。

    因為UserService和UserDao都在Spring容器中,而最終程序直接使用的是UserService,所以可以在Spring容器中,將UserDao設置到UserService內部。

    3.5.1、Bean的依賴注入概念

    依賴注入(Dependency Injection):它是Spring框架核心IOC的具體實現。

    在編寫程序時,通過控制反轉,把對象的創建交給了Spring,但是代碼中不可能出現沒有依賴的情況。IOC解耦只是降低它們的依賴關系,但不會消除。例如:業務層仍會調用持久層的方法。

    那這種業務層和持久層的依賴關系,在使用Spring之后,就讓Spring來維護了。簡單地說,就是坐等框架把持久層對象傳入業務層,而不用我們自己去獲取。

    3.6、Bean的依賴注入方式

    思考:怎么將UserDao注入到UserService內部呢?/ 向一個類中傳遞數據的方式有幾種?1、構造方法2、普通方法(set方法)思考:依賴注入描述了在容器中建立bean與bean之間依賴關系的過程,如果bean運行需要的是數字或字符串呢?1、引用類型2、簡單類型(基本數據類型與String)3、集合類型 - 依賴注入方式setter注入:簡單類型引用類型構造器注入:簡單類型引用類型

    3.6.1、setter注入——引用類型

    在bean中定義引用類型屬性并提供可訪問的set方法:

    public class BookServiceImpl implements BookService {public BookDao bookDao;public UserDao userDao;public void setBookDao(BookDao bookDao) {System.out.println("set ...");this.bookDao = bookDao;}public void setUserDao(UserDao userDao) {this.userDao = userDao;} }

    配置中使用property標簽ref屬性注入引用類型對象:

    applicationContext.xml:...<bean id="bookDao" class="com.clp.dao.impl.BookDaoImpl"/><bean id="userDao" class="com.clp.dao.impl.UserDaoImpl"/><bean id="bookService" class="com.clp.service.impl.BookServiceImpl"><!-- property標簽中的name屬性對應該bean的setXxx()方法的xxx(首字母變小寫)--><property name="bookDao" ref="bookDao" /><property name="userDao" ref="userDao" /></bean>...

    3.6.2、setter注入——簡單類型

    在bean中定義引用類型屬性并提供可訪問的set方法:

    public class BookDaoImpl implements BookDao {private int connectionNum;private String databaseName;public void setConnectionNum(int connectionNum) {this.connectionNum = connectionNum;}public void setDatabaseName(String databaseName) {this.databaseName = databaseName;}@Overridepublic void save() {System.out.println("book dao save ..." + databaseName + ", " + connectionNum);} }

    配置中使用property標簽value屬性注入簡單類型數據:

    applicationContext.xml:...<bean id="bookDao" class="com.clp.dao.impl.BookDaoImpl"><!-- value屬性代表值(直接寫入值) --><property name="databaseName" value="mysql" /><property name="connectionNum" value="10" /></bean>...

    3.6.3、構造器(構造方法)注入——引用類型

    在bean中定義引用類型屬性并提供可訪問的構造方法:

    public class BookServiceImpl implements BookService, InitializingBean, DisposableBean {private BookDao bookDao;private UserDao userDao;public BookServiceImpl(BookDao bookDao, UserDao userDao) {this.bookDao = bookDao;this.userDao = userDao;} }

    配置中使用constructor-arg標簽ref屬性注入引用類型對象:

    applicationContext.xml:...<bean id="bookDao" class="com.clp.dao.impl.BookDaoImpl" /><bean id="userDao" class="com.clp.dao.impl.UserDaoImpl" /><bean id="bookService" class="com.clp.service.impl.BookServiceImpl"><!-- name指的是構造方法中的形參名稱;ref指的是要注入的bean的id --><constructor-arg name="bookDao" ref="bookDao" /><constructor-arg name="userDao" ref="userDao" /></bean>...

    3.6.4、構造器(構造方法)注入——簡單類型

    public class BookDaoImpl implements BookDao {private int connectionNum;private String databaseName;public BookDaoImpl(int connectionNum, String databaseName) {this.connectionNum = connectionNum;this.databaseName = databaseName;} } applicationContext.xml:...<!-- 方案1:name屬性為形參名 value為形參的值 --><bean id="bookDao" class="com.clp.dao.impl.BookDaoImpl"><constructor-arg name="databaseName" value="mysql" /><constructor-arg name="connectionNum" value="666" /></bean><!-- 方案2:使用type屬性代替name,解決形參名稱與name屬性耦合的問題。type為形參的類型 --><bean id="bookDao" class="com.clp.dao.impl.BookDaoImpl"><constructor-arg type="java.lang.String" value="mysql" /><constructor-arg type="int" value="666" /></bean><!-- 方案3:使用index屬性代替type,來解決參數類型重復問題。index為形參的索引(從0開始)--><bean id="bookDao" class="com.clp.dao.impl.BookDaoImpl"><constructor-arg index="0" value="666"/><constructor-arg index="1" value="mysql"/></bean>...

    3.6.5、p命名空間方式注入

    P命名空間本質也是set()方法注入,但比起上述的set()方法注入更加方便,主要體現在配置文件中,如下:

    • 首先,需要引入P命名空間:xmlns:p="http://www.springframework.org/schema/p"
    • 其次,需要修改注入方式:<bean id="userServiceId" class="com.clp.service.impl.UserServiceImpl" p:userDao-ref="userDaoId" />
    <?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:p="http://www.springframework.org/schema/p"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><!-- 配置bean<bean>標簽表示配置bean,id屬性表示給bean起名字,class屬性表示給bean定義類型--><beanid="userDaoId"class="com.clp.dao.impl.UserDaoImpl"init-method="init"destroy-method="destroy"/><beanid="userService"class="com.clp.service.impl.UserServiceImpl"><constructor-arg name="userDao" ref="userDaoId"></constructor-arg></bean> </beans> package com.clp.service.impl;import com.clp.dao.UserDao; import com.clp.service.UserService; import org.springframework.context.support.ClassPathXmlApplicationContext;public class UserServiceImpl implements UserService {private UserDao userDao; // public void setUserDao(UserDao userDao) { // this.userDao = userDao; // }public UserServiceImpl(UserDao userDao) {this.userDao = userDao;}@Overridepublic void save() {userDao.save();} }

    3.6.6、注入集合數據類型

    除了對象的引用可以注入,普通數據類型,集合等都可以在容器中進行注入。

    public class BookDaoImpl implements BookDao {private int[] array;private List<String> list;private Set<String> set;private Map<String, String> map;private Properties properties;public void setArray(int[] array) {this.array = array;}public void setList(List<String> list) {this.list = list;}public void setSet(Set<String> set) {this.set = set;}public void setMap(Map<String, String> map) {this.map = map;}public void setProperties(Properties properties) {this.properties = properties;}@Overridepublic void save() {System.out.println("book dao save ...");System.out.println("遍歷數組" + Arrays.toString(array));System.out.println("遍歷List" + list);System.out.println("遍歷Set" + set);System.out.println("遍歷Map" + map);System.out.println("遍歷Properties" + properties);} } applicationContext.xml:...<bean id="bookDao" class="com.clp.dao.impl.BookDaoImpl"><!-- name屬性對應BookDao的setXxx()中的xxx(首字母小寫)--><property name="array"><array><value>100</value><value>200</value><value>300</value><!-- 如果是引用類型,則格式如下 --> <!-- <ref bean="beanId" />--></array></property><property name="list"><list><value>Laaa</value><value>Lbbb</value><value>Lccc</value></list></property><property name="set"><set><value>Saaa</value><value>Sbbb</value><!-- 同名的會自動過濾 --><value>Sccc</value><value>Sccc</value></set></property><property name="map"><map><entry key="Mkey1" value="Mvalue1" /><entry key="Mkey2" value="Mvalue2" /><entry key="Mkey3" value="Mvalue3" /></map></property><property name="properties"><props><prop key="Pkey1">Pvalue1</prop><prop key="Pkey2">Pvalue2</prop><prop key="Pkey3">Pvalue3</prop></props></property></bean>... public class App {public static void main(String[] args) {// 3、獲取IoC容器ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");// 4.1、獲取bean:BookDao,參數為bean 的idBookDao bookDao = (BookDao) ctx.getBean("bookDao");bookDao.save();} }結果: book dao save ... 遍歷數組[100, 200, 300] 遍歷List[Laaa, Lbbb, Lccc] 遍歷Set[Saaa, Sbbb, Sccc] 遍歷Map{Mkey1=Mvalue1, Mkey2=Mvalue2, Mkey3=Mvalue3} 遍歷Properties{Pkey3=Pvalue3, Pkey2=Pvalue2, Pkey1=Pvalue1}

    3.6.7、依賴注入方式的選擇

    • 強制使用構造器進行,使用setter注入有概率不進行注入導致null對象出現;
    • 可選依賴使用setter注入進行,靈活性強;
    • Spring框架倡導使用構造器,第三方框架內部大多數采用構造器注入的形式進行數據初始化,相對嚴謹;
    • 如果有必要可以兩者同時使用,使用構造器完成強制依賴的注入,使用setter注入完成可選依賴的注入;
    • 實際開發過程中還要根據實際情況分析,如果受控對象沒有提供setter方法就必須使用構造器注入;
    • 自己開發的模塊推薦使用setter注入。

    3.6.8、案例——數據源對象管理

    添加依賴(坐標):

    pom.xml:...<!-- druid數據源 --><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.16</version></dependency><!-- c3p0數據源 --><dependency><groupId>c3p0</groupId><artifactId>c3p0</artifactId><version>0.9.1.2</version></dependency><!-- 使用c3p0是需要用到的依賴 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.6</version></dependency>...

    配置數據源對象作為Spring容器管理的bean:

    applicationContext.xml:...<!-- 管理DruidDataSource對象 --><bean id="dataSource1" class="com.alibaba.druid.pool.DruidDataSource"><!-- 使用setter依賴注入方式 --><property name="driverClassName" value="com.mysql.jdbc.Driver" /><property name="url" value="jdbc:mysql://localhost:3306/mydb" /><property name="username" value="root" /><property name="password" value="123456" /></bean><bean id="dataSource2" class="com.mchange.v2.c3p0.ComboPooledDataSource"><!-- 使用setter依賴注入方式 --><property name="driverClass" value="com.mysql.jdbc.Driver"/><property name="jdbcUrl" value="jdbc:mysql://localhost:3306/mydb"/><property name="user" value="root"/><property name="password" value="123456"/></bean>... 測試代碼:public class App {public static void main(String[] args) {// 3、獲取IoC容器ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");DataSource dataSource1 = (DataSource) ctx.getBean("dataSource1");DataSource dataSource2 = (DataSource) ctx.getBean("dataSource2");System.out.println(dataSource1);System.out.println(dataSource2);} }

    3.7、依賴自動裝配

    IoC容器根據bean所依賴的資源在容器中自動查找并注入到bean中的過程稱為自動裝配。

    3.7.1、自動裝配方式

    • 按類型(常用);
    • 按名稱;
    • 按構造方法;
    • 不啟動自動裝配。
    public class BookServiceImpl implements BookService {private BookDao bookDao;public void setBookDao(BookDao bookDao) {this.bookDao = bookDao;}@Overridepublic void save() {System.out.println("book service save ...");bookDao.save();} } - 配置中使用bean標簽的autowire屬性設置自動裝配的類型applicationContext.xml:...<bean id="bookDao" class="com.clp.dao.impl.BookDaoImpl" /><!-- 按類型裝配(常用):要求容器的的該類型的bean只有一個 --><bean id="bookService1" class="com.clp.service.impl.BookServiceImpl" autowire="byType" /><!-- 按名稱匹配:按BookServiceImpl的setXxx()方法的xxx(首字母小寫)進行匹配,將容器中id與之相同的bean注入--><bean id="bookService2" class="com.clp.service.impl.BookServiceImpl" autowire="byName" />...

    3.7.2、依賴自動裝配的特征

    • 自動裝配用于引用類型依賴注入,不能對簡單類型進行操作;
    • 使用按類型裝配時(byType)必須保障容器中相同類型的bean唯一,推薦使用;
    • 使用按名稱裝配時(byName)必須保障容器中具有指定名稱的bean,因變量名與配置耦合,不推薦使用;
    • 自動裝配優先級低于setter注入與構造器注入,同時出現時自動裝配配置失效。

    3.8、Spring加載外部properties文件

    jdbc.properties:jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/ssmdb jdbc.username=root jdbc.password=123456 applicationContext.xml:...<!-- 1、開啟context命名空間<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/beanshttp://www.springframework.org/schema/beans/spring-beans-3.2.xsdhttp://www.springframework.org/schema/context <-這里http://www.springframework.org/schema/context/spring-context-3.2.xsd"> <-這里</beans>--><!-- 2、使用context空間加載properties文件。 system-properties-mode="NEVER"表示不加載系統屬性--><!-- 方式1:加載多個properties --><context:property-placeholder location="jdbc.properties, other.properties" system-properties-mode="NEVER"/><!-- 方式2:加載所有properties,classpath:*.properties意思是從類加載路徑讀取properties文件 --><context:property-placeholder location="classpath:*.properties" system-properties-mode="NEVER" /><!-- 方式2的擴充版:加載所有properties,classpath*:*.properties意思是從類加載路徑以及其他jar包下讀取properties文件。--><context:property-placeholder location="classpath*:*.properties" system-properties-mode="NEVER" /><!-- 3、使用屬性占位符${}讀取properties文件中的屬性 --><bean id="dataSource1" class="com.alibaba.druid.pool.DruidDataSource"><!-- 使用setter依賴注入方式 --><property name="driverClassName" value="${jdbc.driver}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/></bean>...

    3.9、引入其他配置文件(分模塊開發)

    實際開發中,Spring的配置內容非常多,這就導致Spring配置很繁雜且體積很大,所以,可以將部分配置拆解到其他的配置文件中,而在Spring主配置文件通過import標簽進行加載。

    <import resource="applicationContext-xxx.xml" />

    4、Spring相關API

    4.1、ApplicationContext的繼承體系

    applicationContext:接口類型,代表應用上下文,可以通過其實例獲得Spring容器中的Bean對象。

    • BeanFactory是Ioc容器的頂層接口,初始化BeanFactory對象時,加載的bean延遲加載。
    • ApplicationContext接口是Spring容器的核心接口,初始化時bean立即加載。
    • ApplicationContext接口提供基礎的bean操作相關方法,通過其他接口擴展其功能。
    • ApplicationContext接口常用初始化類:① ClassPathXMLApplicationContext;② FileSystemXMLApplicationContext。

    4.2、ApplicationContext的實現類

    ClassPathXmlApplicationContext:它是從類的根路徑下(resource文件夾下)加載配置文件,推薦使用這種。FileSystemXmlApplicationContext:它是從磁盤路徑上加載配置文件,配置文件可以在磁盤的任意位置。AnnotationConfigApplicationContext:當使用注解配置容器對象時,需要使用此類來創建Spring容器,它用來讀取注解。

    4.3、getBean()方法使用

    public Object getBean(String name);當參數的數據類型是字符串時,標識根據Bean的id從容器中獲得Bean實例,返回是Object,需要強轉。public <T> T getBean(Class<T> requiredType);當參數的數據類型是Class類型時,標識根據類型從容器中匹配Bean實例,當容器中相同類型的Bean有多個時,則此方法會報錯。public <T> T getBean(String beanName, Class<T> beanType);使用bean名稱獲取并指定類型。

    5、Spring配置數據源

    5.1、數據源(連接池)的作用

  • 數據源(連接池)是提高程序性能出現的。
  • 實現實例化數據源,初始化部分連接資源。
  • 使用連接資源時從數據源中獲取。
  • 使用完畢后將連接資源歸還給數據源。
  • 常見的數據源(連接池):DBCP、C3P0、BoneCP、Druid等。

    package test;import com.alibaba.druid.pool.DruidDataSource; import com.alibaba.druid.pool.DruidPooledConnection; import com.mchange.v2.c3p0.ComboPooledDataSource; import org.junit.Test;import java.beans.PropertyVetoException; import java.sql.Connection; import java.sql.SQLException; import java.util.ResourceBundle;public class DataSourceTest {@Test//測試手動創建 c3p0 數據源public void test1() throws PropertyVetoException, SQLException {ComboPooledDataSource dataSource = new ComboPooledDataSource();dataSource.setDriverClass("com.mysql.jdbc.Driver");dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/bysj?characterEncoding=utf-8");dataSource.setUser("root");dataSource.setUser("123456");Connection connection = dataSource.getConnection();System.out.println(connection);connection.close();}@Test//測試手動創建 c3p0 數據源(加載properties配置文件)public void test3() throws PropertyVetoException, SQLException {//讀取配置文件ResourceBundle rb = ResourceBundle.getBundle("jdbc");String driver = rb.getString("jdbc.driver");String url = rb.getString("jdbc.url");String username = rb.getString("jdbc.username");String password = rb.getString("jdbc.password");//創建數據源對象,設置連接參數ComboPooledDataSource dataSource = new ComboPooledDataSource();dataSource.setDriverClass(driver);dataSource.setJdbcUrl(url);dataSource.setUser(username);dataSource.setPassword(password);Connection connection = dataSource.getConnection();System.out.println(connection);connection.close();}@Test//測試手動創建 druid 數據源public void test2() throws PropertyVetoException, SQLException {DruidDataSource dataSource = new DruidDataSource();dataSource.setDriverClassName("com.mysql.jdbc.Driver");dataSource.setUrl("jdbc:mysql://localhost:3306/bysj?characterEncoding=utf-8");dataSource.setUsername("root");dataSource.setPassword("123456");DruidPooledConnection connection = dataSource.getConnection();System.out.println(connection);connection.close();} }

    5.2、數據源的開發步驟

  • 導入數據源的坐標和數據庫驅動坐標;
  • 創建數據源對象;
  • 設置數據源的基本連接數據;
  • 使用數據源獲取連接資源和歸還連接資源。
  • 5.3、Spring配置數據源

    可以將DataSource的創建權交給Spring去完成。

    <?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"><beanid="dataSource"class="com.mchange.v2.c3p0.ComboPooledDataSource" ><property name="driverClass" value="com.mysql.jdbc.Driver"></property><property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/bysj?characterEncoding=utf-8"></property><property name="user" value="root"></property><property name="password" value="123456"></property></bean> </beans> package test;import com.alibaba.druid.pool.DruidDataSource; import com.alibaba.druid.pool.DruidPooledConnection; import com.mchange.v2.c3p0.ComboPooledDataSource; import javafx.application.Application; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;import javax.sql.DataSource; import java.beans.PropertyVetoException; import java.sql.Connection; import java.sql.SQLException; import java.util.ResourceBundle;public class DataSourceTest {@Test//測試Spring容器產生數據源對象public void test4() throws PropertyVetoException, SQLException {ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");DataSource dataSource = app.getBean(DataSource.class);Connection connection = dataSource.getConnection();System.out.println(connection);connection.close();} }

    5.4、抽取jdbc配置文件

    applicationContext.xml加載jdbc.properties配置文件獲得連接信息。

    首先,需要引入context命名空間和約束路徑:命名空間:xmlns:context="http://www.springframework.org/schema/context"約束路徑:http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdSpring容器加載properties文件:<context:property-placeholder location="xx.properties" /><property name="" value="${key}" /> <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"><!--加載外部的properties文件--><context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder><beanid="dataSource"class="com.mchange.v2.c3p0.ComboPooledDataSource" ><property name="driverClass" value="${jdbc.driver}"></property><property name="jdbcUrl" value="${jdbc.url}"></property><property name="user" value="${jdbc.username}"></property><property name="password" value="${jdbc.password}"></property></bean> </beans>

    6、Spring注解開發

    Spring是輕代碼而重配置的框架,配置比較繁重,影響開發效率,所以注解開發是一種趨勢,注解代替xml配置文件可以簡化配置,提高開發效率。

    6.1、Spring原始注解

    @Component 使用在類上用于實例化Bean- Spring提供@Component注解的3個衍生注解:@Controller使用在web層類上用于實例化Bean@Service使用在service層上用于實例化Bean@Repository使用在dao層類上用于實例化Bean@Autowired 使用在字段上根據類型依賴注入@Qualifier 結合@Autowired一起使用用于根據名稱進行依賴注入@Resource 相當于@Autowired+@Qualifier,按照名稱進行注入。@Value 注入普通屬性@Scope 標注Bean的作用范圍@PostConstruct 使用在方法上標注該方法是Bean的初始化方法@PreDestroy 使用在方法上標注該方法是Bean的銷毀方法

    注意:使用注解進行開發時,需要在applicationContext.xml中配置組件掃描,作用是指定哪個包及其子包下的Bean需要進行掃描以便識別使用注解配置的類、字段和方法。

    <!--注解的組件掃描--> <context:component-scan base-package='com.clp'></context:component-scan>

    6.1.1、@Scope & @PostConstruct & @PreDestroy

    //<bean id="userServiceId" class="service.impl.UserServiceImpl"> //@Component("userServiceId") @Service("userServiceId") @Scope("singleton") public class UserServiceImpl implements UserService {@Value("${jdbc.driver}") //從容器中找鍵為jdbc.driver的值,并賦給driverprivate String driver;//<property name="userDao" ref="userDaoId"></property> // @Autowired //按照數據類型從Spring容器中進行匹配的 // @Qualifier("userDaoId") //按照id名稱從Spring容器中進行匹配的,但是注意此處@Qualifier需要結合@Autowired一起使用@Resource(name = "userDaoId") //@Resource相當于@Qualifier+@Autowiredprivate UserDao userDao;//使用xml配置需要set()方法,使用注解方式可以不寫set()方法 // public void setUserDao(UserDao userDao) { // this.userDao = userDao; // }@Overridepublic void save() {System.out.println(driver);userDao.save();}@PostConstructpublic void init() {System.out.println("service對象的初始化方法");}@PreDestroypublic void destroy() {System.out.println("service對象的銷毀方法");} }

    6.1.2、@Autowired & @Qualifier 實現引用類型依賴注入

    @Service public class BookServiceImpl implements BookService {/*** 使用@Autowired注解開啟自動裝配模式:按類型依賴注入(通過暴力反射)* 如果有多個相同類型的bean,則需再添加@Qualifier指定bean的名稱(bean的id)*/@Autowired@Qualifier("bookDao1")private BookDao bookDao;@Overridepublic void save() {System.out.println("book service save ...");bookDao.save();} }

    注意:

    • 自動裝配基于反射設計創建對象并暴力反射對應屬性為私有屬性初始化數據,因此無需提供setter方法;
    • 自動裝配建議使用無參構造方法創建對象(默認),如果不提供對應構造方法,請提供唯一的構造方法;
    • @Qualifier注解無法單獨使用,必須配合@Autowired注解使用。

    6.1.3、@Value 實現簡單類型依賴注入

    @Repository("bookDao") public class BookDaoImpl implements BookDao {@Value("mysql")private String dbName;@Overridepublic void save() {System.out.println("book dao save ..." + dbName);} }

    注入properties文件中的值:

    - 配置類加上@PropertiesSource注解指定要加載的properties文件@Configuration@ComponentScan("com.clp")@PropertySource("classpath:jdbc.properties") <-這里public class SpringConfig {} 注意:如果需要加載多個properties文件,使用數組方式加載:@PropertiesSource({"jdbc.properties", "xxx.properties"}) 注意:路徑僅支持單一文件配置,多文件請使用數組格式配置,不允許使用通配符*。允許添加"classpath:"前綴。- 配置依賴注入@Repository("bookDao")public class BookDaoImpl implements BookDao {@Value("${jdbc.url}")private String dbName;@Overridepublic void save() {System.out.println("book dao save ..." + dbName);}}

    6.2、Spring新注解

    使用上面的注解還不能全部替代xml配置文件,還需要使用注解替代的配置如下:

    • 非自定義的Bean的配置:<bean>
    • 加載properties文件的配置:<context:property-placeholder>
    • 組件掃描的配置:<context:component-scan>
    • 引入其他文件:<import>

    Spring 3.0升級了純注解開發模式,使用Java類代替配置文件,開啟了Spring快速開發賽道。在純注解開發中,Java類代替Spring核心配置文件。

    @Configuration 用于指定當前類是一個Spring配置類(相當于一個applicationContext.xml),當創建容器時會從該類上加載注解。@ComponentScan 用于指定Spring在初始化容器時要掃描的包。 作用和在Spring的xml配置文件中的<context:component-scan base-package="com.clp" />一樣@Bean 使用在方法上,標注該方法的返回值存儲到Spring容器中。@PropertySource 用于加載.properties文件中的配置@Import 用于導入其他配置類

    6.2.1、@Configuration & @ComponentScan實現替代Spring配置文件

    <?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:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-3.2.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-3.2.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx-3.2.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-3.2.xsd"><!-- 配置組件掃描 service 和 mapper --><context:component-scan base-package="com.clp" /> </beans>

    上述配置文件可用以下類替換:

    @Configuration @ComponentScan("com.clp") public class SpringConfig { }@Configuration注解用于設定當前類為配置類。 @ComponentScan注解用于設定掃描路徑,此注解只能添加一次,多個數據請用數組格式:@ComponentScan({"com.clp.service", "com.clp.dao"})

    讀取Spring核心配置文件初始化容器對象切換為讀取Java配置類初始化容器對象:

    public class AppAnno {public static void main(String[] args) {AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);BookDao bookDao = (BookDao) ctx.getBean("bookDao");bookDao.save();} }

    測試代碼:

    public class AppAnno {public static void main(String[] args) {AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);BookDao bookDao1 = (BookDao) ctx.getBean(BookDao.class);BookDao bookDao2 = (BookDao) ctx.getBean(BookDao.class);System.out.println(bookDao1);System.out.println(bookDao2);} }

    6.2.2、@Import & @Bean 實現配置類分離和第三方bean管理

    - 使用獨立的配置類管理第三方bean:public class JdbcConfig {// 1、定義一個方法獲得要管理的對象// 2、添加@Bean表示當前方法的返回值是一個bean@Beanpublic DataSource dataSource() {DruidDataSource dataSource = new DruidDataSource();dataSource.setDriverClassName("com.mysql.jdbc.Driver");dataSource.setUrl("jdbc:mysql://localhost:3306/ssmdb");dataSource.setUsername("root");dataSource.setPassword("123456");return dataSource;}}- 將獨立的配置類加入核心配置:- 方式1:導入式。使用@Import注解手動加入配置類到核心配置,此注解只能添加一次,多個數據請用數組格式public class JdbcConfig {@Beanpublic DataSource dataSource() {DruidDataSource dataSource = new DruidDataSource();// 相關配置return dataSource;}}@Configuration@Import({JdbcConfig.class})public class SpringConfig {}- 方式2:掃描式。使用@ComponentScan注解掃描配置類所在的包,加載對應的配置類信息。@Configurationpublic class JdbcConfig {@Beanpublic DataSource dataSource() {DruidDataSource dataSource = new DruidDataSource();// 相關配置return dataSource;}}@Configuration@ComponentScan({"com.clp.config", "com.clp.service", "com.clp.dao"})public class SpringConfig {}

    第三方bean的依賴注入(創建該bean還需要其他東西):

    public class JdbcConfig {/** 簡單類型的依賴注入使用@Value* */@Value("com.mysql.jdbc.Driver")private String driver;@Value("jdbc:mysql://localhost:3306/ssmdb")private String url;@Value("root")private String username;@Value("123456")private String password;// 1、定義一個方法獲得要管理的對象// 2、添加@Bean表示當前方法的返回值是一個bean@Bean/** 引用類型的依賴注入:主需要為bean定義方法設置形參即可,容器會根據 類型 自動裝配對象。* */public DataSource dataSource(BookDao bookDao) {System.out.println(bookDao);DruidDataSource dataSource = new DruidDataSource();dataSource.setDriverClassName(driver);dataSource.setUrl(url);dataSource.setUsername(username);dataSource.setPassword(password);return dataSource;} }

    6.3、XML配置對比注解配置

    功能XML配置注解
    定義bean

    bean標簽:

    ??????? - id屬性

    ??????? - class屬性

    @Component

    ??????? @Controller

    ??????? @Service

    ??????? @Repository

    @ComponentScan

    設置依賴注入

    setter注入(set方法)

    ??????? 引用/簡單

    構造器逐日(構造方法)

    ??????? 引用/簡單

    自動裝配

    @Autowired

    ??????? @Qualifier

    @Value

    配置第三方bean

    bean標簽

    靜態工廠、實例工廠、FactoryBean

    @Bean
    作用范圍

    bean標簽:

    ??????? - scope屬性

    @Scope
    生命周期

    bean標簽:

    ??????? init-method

    ??????? destroy-method

    @PostConstruct

    @PreDestroy

    7、Spring整合MyBatis

    MyBatis程序核心對象分析:

    // 1、創建SqlSessionFactoryBean對象 SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); // 2、加載sqlMapConfig.xml配置文件 InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml"); // 3、創建SqlSessionFactory對象 SqlSessionFactory sqlSessionFactory = sqlSessionFactory.build(inputStream); // 4、獲取SqlSession SqlSession sqlSession = sqlSessionFactory.openSession(); // 5、執行SqlSession對象執行查詢,獲取結果User AccountDao accountDao = sqlSession.getMapper(AccountDao.class); Account account = accountDao。findById(2); System.out.println(account); // 6、釋放資源 sqlSession.close();

    整合MyBatis:

    pom.xml中導入坐標:...<dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.0.5.RELEASE</version></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>1.3.1</version></dependency>... 編寫配置類: public class JdbcConfig {/** 簡單類型的依賴注入使用@Value* */@Value("com.mysql.jdbc.Driver")private String driver;@Value("jdbc:mysql://localhost:3306/ssmdb")private String url;@Value("root")private String username;@Value("123456")private String password;// 1、定義一個方法獲得要管理的對象// 2、添加@Bean表示當前方法的返回值是一個bean@Bean/** 引用類型的依賴注入:主需要為bean定義方法設置形參即可,容器會根據 類型 自動裝配對象。* */public DataSource dataSource(BookDao bookDao) {System.out.println(bookDao);DruidDataSource dataSource = new DruidDataSource();dataSource.setDriverClassName(driver);dataSource.setUrl(url);dataSource.setUsername(username);dataSource.setPassword(password);return dataSource;} }public class MyBatisConfig {/*** SqlSessionFactoryBean專門產生SqlSessionFactory* @return*/@Beanpublic SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();ssfb.setTypeAliasesPackage("com.clp.domain"); // 設置實體類別名ssfb.setDataSource(dataSource); // 設置數據源return ssfb;}/*** 加載Dao層的映射信息* @return*/@Beanpublic MapperScannerConfigurer mapperScannerConfigurer() {MapperScannerConfigurer msc = new MapperScannerConfigurer();msc.setBasePackage("com.clp.dao"); // 設置映射配置所在的包return msc;} }@Configuration @Import({JdbcConfig.class, MyBatisConfig.class}) @ComponentScan("com.clp") @PropertySource("classpath:jdbc.properties") public class SpringConfig { }

    8、Spring整合Junit

    8.1、原始Junit測試Spring的問題

    在測試類中,每個測試方法都有以下兩行代碼:

    ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml"); IAccountService as = ac.getBean("accountService",IAccountService.class);

    這兩行代碼的作用是獲取容器,如果不寫的話,直接回提示空指針異常,所以又不能輕易刪掉。

    8.2、上述問題解決思路

    • 讓SpringJunit負責創建Spring容器,但是需要將配置文件的名稱告訴它。
    • 將需要進行測試Bean直接在測試類中進行注入。

    8.3、Spring整合Junit步驟

  • 導入spring集成Junit的坐標; pom.xml:...<dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.2.10.RELEASE</version></dependency>...
  • 使用@Runwith注解替換原來的類運行器;
  • 使用@ContextConfiguration指定配置文件或配置類;
  • 使用@Autowired注入需要測試的對象;
  • 創建測試方法進行測試。 // 使用Spring整合JUnit專用的類加載器 @RunWith(SpringJUnit4ClassRunner.class) // 指定Spring的配置類 @ContextConfiguration(classes = SpringConfig.class) public class AccountServiceTest {@Autowiredprivate AccountService accountService;@Testpublic void testFindAll() {System.out.println(accountService.findAll());} }
  • 9、Spring集成Web環境

    9.1、ApplicationContext應用上下文獲取方式

    應用上下文是通過new ClasspathXmlApplicationContext(spring配置文件)方式獲取的,但是每次從容器中獲得Bean時都要編寫new ClasspathXmlApplicationContext(spring配置文件),這樣的弊端是配置文件加載多次,應用上下文對象創建多次。

    在Web項目中,可以使用ServletContextListener監聽Web應用的啟動,我們可以在Web應用啟動時,就加載Spring的配置文件,創建應用上下文對象ApplicationContext,再將其存儲到最大的域servletContext域中,這樣就可以再任意位置從域中獲得應用上下文ApplicationContext對象了。

    9.2、Spring提供獲取應用上下文的工具

    Spring提供了一個監聽器ContextLoaderListener,該監聽器內部加載Spring配置文件,創建應用上下文對象,并存儲到ServletContext域中,提供了一個客戶端工具WebApplicationContextUtils供使用者獲得應用上下文對象。

    所以我們需要做的只有兩件事:

    在web.xml中配置ContextLoaderListener監聽器(要導入spring-web坐標);

    使用WebApplicationContextUtils獲得應用上下文對象ApplicationContext。

    web.xml:<!DOCTYPE web-app PUBLIC"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN""http://java.sun.com/dtd/web-app_2_3.dtd" ><web-app><display-name>Archetype Created Web Application</display-name><!--全局初始化參數--><context-param><param-name>contextConfigLocation</param-name><param-value>classpath:applicationContext.xml</param-value></context-param><!--配置監聽器--><listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener></web-app> package listener;import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import service.UserService;import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.annotation.WebListener;@WebListener public class ContextLoaderListener implements ServletContextListener {@Overridepublic void contextInitialized(ServletContextEvent sce) {//將Spring的應用上下文對象存儲到ServletContext域中ServletContext servletContext = sce.getServletContext();String contextConfigLocation = servletContext.getInitParameter("contextConfigLocation");ApplicationContext app = new ClassPathXmlApplicationContext(contextConfigLocation);servletContext.setAttribute("app",app);System.out.println("spring容器創建完畢");}@Overridepublic void contextDestroyed(ServletContextEvent sce) {} } package web;import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils; import service.UserService;import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException;@WebServlet public class UserServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {this.doPost(req,resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {ServletContext servletContext = req.getServletContext();//ApplicationContext app = (ApplicationContext) servletContext.getAttribute("app");ApplicationContext app = WebApplicationContextUtils.getWebApplicationContext(servletContext);UserService userService = app.getBean(UserService.class);userService.save();} }

    9.3、Spring集成web環境步驟

  • 配置ContextLoaderListener監聽器;
  • 使用WebApplicationContextUtils獲得應用上下文。
  • 10、Spring JdbcTemplate的基本使用

    10.1、JdbcTemplate概述

    它是spring框架中提供的一個對象,是對原始繁瑣的Jdbc API對象的簡單封裝。spring框架為我們提供了很多的操作模板類。例如:操作關系型數據的JdbcTemplate和HibernateTemplate,操作nosql數據庫的RedisTemplate,操作消息隊列的JmsTemplate等等。

    10.2、JdbcTemplate開發步驟

  • 導入spring-jdbc和spring-tx坐標;
  • 創建數據庫表和實體;
  • 創建JdbcTemplate對象;
  • 執行數據庫操作。
  • package com.test;import com.mchange.v2.c3p0.ComboPooledDataSource; import org.junit.Test; import org.springframework.jdbc.core.JdbcTemplate;import java.beans.PropertyVetoException;public class JdbcTemplateTest {@Test//測試JdbcTemplate開發步驟public void test1() throws PropertyVetoException {//創建數據源對象ComboPooledDataSource dataSource = new ComboPooledDataSource();dataSource.setDriverClass("com.mysql.jdbc.Driver");dataSource.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/db?characterEncoding=utf-8");dataSource.setUser("root");dataSource.setPassword("123456");JdbcTemplate jdbcTemplate = new JdbcTemplate();//設置數據源對象,知道數據庫在哪jdbcTemplate.setDataSource(dataSource);//執行操作int row = jdbcTemplate.update("insert into account values(?,?)", "李四", 5000);System.out.println(row);} }

    10.3、Spring產生JdbcTemplate對象

    我們可以將JdbcTemplate的創建權交給Spring,將數據源DataSource的創建權也交給Spring,在Spring容器內部將數據源DataSource注入到JdbcTemplate模板對象中,配置如下:

    jdbc.properties:jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/db>characterEncoding=utf-8 jdbc.username=root jdbc.password=123456 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"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"><!--配置數據源對象--> <!-- <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">--> <!-- <property name="driverClass" value="com.mysql.jdbc.Driver" />--> <!-- <property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/db?characterEncoding=utf-8" />--> <!-- <property name="user" value="root" />--> <!-- <property name="password" value="123456" />--> <!-- </bean>--><!--加載外部的jdbc.properties--><context:property-placeholder location="classpath:jdbc.properties" /><bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"><property name="driverClass" value="${jdbc.driver}"/><property name="jdbcUrl" value="${jdbc.url}"/><property name="user" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/></bean><!--配置jdbc模板對象--><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref="dataSource" /></bean></beans> @Test//測試spring產生jdbcTemplate模板對象public void test2() {ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");JdbcTemplate jdbcTemplate = app.getBean(JdbcTemplate.class);int row = jdbcTemplate.update("insert into account values(?,?)", "王五", 30);System.out.println(row);app.close();} package com.test;import com.domain.Account; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;import java.util.List;@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class JdbcTemplateCRUDTest {@Autowiredprivate JdbcTemplate jdbcTemplate;@Testpublic void test1() {jdbcTemplate.update("update account set money=? where name=?",10000,"張三");}@Testpublic void test2() {List<Account> accountList = jdbcTemplate.query("select * from account", new BeanPropertyRowMapper<Account>(Account.class));System.out.println(accountList);}@Testpublic void test3() {Account account = jdbcTemplate.queryForObject("select * from account where name=?",new BeanPropertyRowMapper<Account>(Account.class), "張三");System.out.println(account);}@Testpublic void test4() {Long count = jdbcTemplate.queryForObject("select count(*) from account",Long.class);System.out.println(count);} }

    11、Spring AOP

    11.1、什么是AOP

    AOP(Aspect Oriented Programming)的縮寫,意思為面向切面編程,是通過預編譯方式和運行期動態代理實現程序功能的統一維護的一種技術。

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

    10.2、AOP的作用及其優勢

    作用:在程序運行期間,在不修改源碼的情況下對方法進行功能增強。

    優勢:減少重復代碼,提高開發效率,并且易于維護。

    10.3、AOP的底層實現

    實際上,AOP的底層是通過Spring提供的動態代理技術實現的。在運行期間,Spring通過動態代理技術動態地生成代理對象,代理對象方法執行時進行增強功能地接入,再去調用目標對象地方法,從而完成功能的增強。

    10.4、AOP的動態代理技術

    常用的動態代理技術:

    • JDK代理:基于接口的動態代理技術。
    • cglib代理:基于父類的動態代理技術。

    10.4.1、基于JDK的動態代理

    package com.proxy.jdk;public class Advance {public void before() {System.out.println("前置增強..");}public void after() {System.out.println("后置增強..");} } package com.proxy.jdk;public interface TargetInterface {public void save(); } package com.proxy.jdk;public class Target implements TargetInterface{@Overridepublic void save() {System.out.println("save running...");} } package com.proxy.jdk;import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy;public class ProxyTest {public static void main(String[] args) {//創建目標對象final Target target = new Target();//創建增強對象final Advance advance = new Advance();//返回值就是動態生成的代理對象TargetInterface proxy = (TargetInterface) Proxy.newProxyInstance(//目標對象的類加載器target.getClass().getClassLoader(),//目標對象相同的字節碼對象數組target.getClass().getInterfaces(),new InvocationHandler() {@Override//調用代理對象的任何方法,實質執行的都是invoke方法public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//前置增強advance.before();//執行目標方法Object invoke = method.invoke(target, args);//后置增強advance.after();return invoke;}});//調用代理對象的方法proxy.save();} } 結果:前置增強.. save running... 后置增強..

    10.4.2、基于cglib的動態代理

    package com.proxy.cglib;import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy;import java.lang.reflect.Method;public class ProxyTest {public static void main(String[] args) {//創建目標對象final Target target = new Target();//創建增強對象final Advance advance = new Advance();//返回值就是動態生成的代理對象,基于cglib//1、創建增強器Enhancer enhancer = new Enhancer();//2、設置父類(目標)enhancer.setSuperclass(Target.class);//3、設置回調enhancer.setCallback(new MethodInterceptor() {@Overridepublic Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {//執行前置advance.before();//執行目標Object invoke = method.invoke(target, args);//執行后置advance.after();return invoke;}});//4、創建代理對象Target proxy = (Target) enhancer.create();proxy.save();} }

    10.5、AOP相關概念

    Spring的AOP實現底層就是對上面的動態代理的代碼進行了封裝,封裝后我們只需要對需要關注的部分進行代碼編寫,并通過配置的方式完成指定目標的方法增強。

    AOP常用的術語如下:

    • Target(目標對象):原始功能去掉共性功能對應的類產生的對象,這種對象是無法直接完成最終工作的。
    • Proxy(代理對象):目標對象無法直接完成工作,需要對其進行功能回填,通過原始對象的代理對象實現。SpringAOP的核心本質是采用代理模式實現的。
    • Joinpoint(連接點):程序執行過程中的任意位置,在SpringAOP中,這些點指的是方法,因為Spring只支持方法類型的連接點。
    • Pointcut(切入點):所謂切入點是指我們要對哪些Joinpoint進行攔截的定義(匹配連接點的式子)。在SpringAOP中,一個切入點可以只描述一個具體方法,也可以匹配多個方法:① 一個具體方法:com.itheima.dao包下的BookDao接口中的無形參無返回值的save()方法;② 匹配多個方法:所有的save()方法,所有的get開頭的方法,所有以Dao結尾的接口中的任意方法,所有帶有一個參數的方法。注意:連接點包含切入點,切入點一定在連接點中。
    • Advice(通知/增強):所謂通知是指攔截到Joinpoint(切入點)之后所要做的操作,也就是共性功能。在SpringAOP中,功能最終以方法的形式呈現。通知類:定義通知的類。
    • Aspect(切面):是切入點和通知(引介)的結合。
    • Weaving(織入):是指把增強應用到目標對象來創建新的代理對象的過程。Spring采用動態代理織入,而AspectJ采用編譯期織入和類裝載期織入。

    10.5.1、AOP切入點表達式

    • 切入點:要進行增強的方法。
    • 切入點表達式:要進行增強的方法的描述形式。
    - 描述方式1:執行com.itheima.dao包下的BookDao接口中的無參數update()方法 execution(void com.itheima.dao.BookDao.update())- 描述方式2: execution(void com.itheima.dao.impl.BookDaoImpl.update())

    1、切點表達式的寫法:

    - 切入點表達式標準格式:動作關鍵字execution(訪問修飾符 返回值類型 包名.類/接口名.方法名(參數) 異常名)例:execution(public User com.itheima.service.UserService.findById(int))動作關鍵字:描述切入點的行為動作,例如execution表示執行到指定切入點。訪問修飾符:public,private等。訪問修飾符可以省略;返回值類型:可以使用星號*代表任意;包名:可以使用星號*代表任意。包名與類名之間一個點.代表當前包下的類;兩個點..表示當前包及其子包下的類;類/接口名:可以使用星號*代表任意;方法名:可以使用星號*代表任意;參數:參數列表可以使用兩個點..表示任意個數,任意類型的參數列表。異常名:方法定義中拋出指定異常,可以省略。- 可以使用通配符描述切入點,快速描述*:單個獨立的任意符號,可以獨立實現,也可以作為前綴或者后綴的匹配符出現。例:execution(public * com.itheima.*.UserService.find* (*))匹配com.itheima包下的任意包中的UserService類或接口中所有find開頭的帶有一個參數的方法。..:多個連續的任意符號,可以獨立出現,常用語簡化包名與參數的書寫。例:execution(public User com..UserService.findById (..))匹配com包下的任意包中的UserService類或接口中所有名稱為findById的方法+:專用于匹配子類類型。例:execution(* *..*Service+.*(..))

    2、切入點表達式的書寫技巧

    • 所有代碼按照標準規范開發,否則以下技巧全部失效;
    • 描述切入點通常描述接口,而不描述實現類;
    • 訪問控制修飾符針對接口開發均采用public描述(可省略訪問控制修飾符描述);
    • 返回值類型對于增刪改類使用精準類型加速匹配,對于查詢類使用*通配快速描述;
    • 包名書寫盡量不使用..匹配,效率過低,常用*做單個包描述匹配,或精準匹配;
    • 接口名/類名書寫名稱與模塊相關的采用*匹配,例如UserService書寫成*Service,綁定業務層接口名;
    • 方法名書寫以動詞進行精準匹配,名詞采用*匹配,例如getByI書寫成getBy*,selectAll書寫成selectAll;
    • 參數規則較為復雜,根據業務方法靈活調整;
    • 通常不使用異常作為匹配規則。

    10.5.2、AOP通知類型

    AOP通知描述了抽取的共性功能,根據共性功能抽取的位置不同,最終運行代碼時要將其加入到合理的位置。

    AOP通知功分為5種類型:

    • 前置通知;
    • 后置通知;
    • 環繞通知(重點);
    • 返回后通知(了解);
    • 拋出異常后通知(了解)。

    10.6、AOP開發明確的事項

    10.6.1、需要編寫的內容

    • 編寫核心業務代碼(目標類的目標方法);
    • 編寫切面類,切面類中有通知(增強功能方法);
    • 在配置文件中,配置織入關系,即將哪些通知與哪些連接點進行結合。

    10.6.2、AOP技術實現的內容

    Spring框架監控切入點方法的執行,一旦監控到切入點方法被運行,使用代理機制,動態創建目標對象的代理對象,根據通知類別,在代理對象的對應位置,將通知對應的功能織入,完成完成的代碼邏輯運行。

    10.6.3、AOP底層使用哪種代理方式

    在Spring中,框架會根據目標類是否實現了接口來決定采用哪種動態代理的方式。

    10.7、AOP工作流程

  • Spring容器啟動;
  • 讀取所有切面配置中的切入點; /*** 切面類*/ @Component // 該類受Spring管理 @Aspect // 說明該類作為AOP處理 public class MyAdvice {/*** 切入點1:注解@Pointcut說明該方法是一個切入點方法。method1()注解配置了該切入點,故會被讀取*/@Pointcut("execution(void com.clp.dao.BookDao.save())")private void pt1() {}/*** 切入點2(沒有配置該切入點的切面,故不會讀取該切入點)*/@Pointcut("execution(void com.clp.dao.BookDao.update())")private void pt2() {}/*** 切面:綁定切點和通知(作為切面)*/@Before("pt1()")public void method1() {System.out.println(System.currentTimeMillis());} }
  • 初始化bean,判定bean對應的類中的方法是否匹配到任意切入點:① 匹配失敗,創建對象;② 匹配成功,創建原始對象(目標對象)的代理對象。
  • 獲取bean執行方法:① 獲取的bean不是代理對象,調用方法并執行,完成操作;② 獲取的bean是代理對象,根據代理對象的運行模式運行原始方法與增強的內容,完成操作。
  • 10.8、基于XML的AOP開發

    10.8.1、快速入門

  • 導入AOP相關坐標;
  • 創建目標接口和目標類(內部有切點);
  • 創建切面類(內部有增強方法);
  • 將目標類和切面類的對象創建權交給Spring;
  • 在applicationContext.xml中配置織入關系;
  • 測試代碼。
  • pom.xml:...<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.0.5.RELEASE</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.8.4</version></dependency>... package com.aop;public interface TargetInterface {public void save(); } package com.aop;public class Target implements TargetInterface {@Overridepublic void save() {System.out.println("save running...");} } package com.aop;public class MyAspect {public void before() {System.out.println("前置增強...");} } applicationContext.xml:...<!--配置目標對象--><bean id="target" class="com.aop.Target"></bean><!--配置切面對象--><bean id="myAspect" class="com.aop.MyAspect"></bean><!--配置織入:告訴Spring框架,哪些方法(切點)需要進行哪些增強(前置、后置)--><aop:config><!--聲明切面--><aop:aspect ref="myAspect"><!--切面:切點+通知--><aop:before method="before" pointcut="execution(public void com.aop.Target.save())"></aop:before></aop:aspect></aop:config>... package com.test;import com.aop.TargetInterface; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class AOPTest {@Autowiredprivate TargetInterface target;@Testpublic void test1() {target.save();} }

    10.8.2、XML配置AOP詳解

    1、通知的類型:

    通知的配置語法:

    ??????? <aop:通知類型 method="切面類中方法名" pointcunt="切點表達式"></aop:通知類型>

    名稱標簽說明
    前置通知<aop:before>用于配置前置通知。指定增強的方法在切入點方法之前執行。
    后置通知<aop:after-returning>用于配置后置通知。指定增強的方法在切入點方法之后執行。
    環繞通知<aop:around>用于配置環繞通知。指定增強的方法在切入點方法之前和之后都執行。
    異常拋出通知<aop:throwing>用于配置異常拋出通知。指定增強的方法在出現異常時執行。
    最終通知<aop:after>用于配置最終通知。無論增強方式執行是否有異常都會執行。
    package com.aop;import org.aspectj.lang.ProceedingJoinPoint;public class MyAspect {public void before() {System.out.println("前置增強...");}public void afterReturning() {System.out.println("后置增強...");}//ProceedingJoinPoint :正在執行的連接點, 即 切點public Object around(ProceedingJoinPoint pjp) throws Throwable {System.out.println("環繞前增強...");//切點方法Object proceed = pjp.proceed();System.out.println("環繞后增強...");return proceed;}public void afterThrowing() {System.out.println("異常拋出增強...");}private void after() {System.out.println("最終增強...");} }

    2、切點表達式的抽取

    當多個增強的切點表達式相同時,可以將切點表達式進行抽取,在增強中使用pointcut-ref屬性代替pointcut屬性來引用抽取后的切點表達式。

    applicationContext.xml:...<!--配置織入:告訴Spring框架,哪些方法(切點)需要進行哪些增強(前置、后置)--><aop:config><!--聲明切面--><aop:aspect ref="myAspect"><!--切面:切點+通知--><aop:pointcut id="myPointcut" expression="execution(* com.aop.*.*(..))"/><aop:before method="before" pointcut-ref="myPointcut"></aop:before></aop:aspect></aop:config>...

    10.9、基于注解的AOP開發

    10.9.1、快速入門

  • 導入坐標(pom.xml); pom.xml: 注意:spring-context坐標依賴spring-aop坐標...<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.10.RELEASE</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.4</version></dependency>...
  • 制作連接點方法(原始操作,Dao接口與實現類); @Repository("bookDaoImpl") public class BookDaoImpl implements BookDao {@Overridepublic void update() {System.out.println("book dao update ...");} }
  • 制作切面類;
  • 定義切入點:切入點定義依托一個不具有實際意義的方法進行,即無參數、無返回值,方法無實際邏輯;
  • 綁定切入點與通知關系(切面),并指定通知添加到原始連接點的具體執行位置(添加@Before注解為連接點方法執行之前執行該通知); @Configuration @ComponentScan("com.clp") @EnableAspectJAutoProxy // 告訴Spring,容器中有注解開發的AOP public class SpringConfig { }/*** 切面類*/ @Component // 該類受Spring管理 @Aspect // 說明該類作為AOP處理 public class MyAdvice {/*** 切入點:注解@Pointcut說明該方法是一個切入點方法*/@Pointcut("execution(void com.clp.dao.BookDao.update())")private void pt() {}/*** 切面:綁定切點和通知(作為切面),方法為通知,加上@Before綁定切入點方法pt()*/@Before("pt()")public void method1() {System.out.println(System.currentTimeMillis());}public void method2() {} }
  • 在applicationContext.xml中配置的方式代碼案例:

    applicationContext.xml:...<!--配置組件掃描--><context:component-scan base-package="com.anno" /><!--AOP自動代理--><aop:aspectj-autoproxy />... package com.anno;import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component;@Component("myAspect") @Aspect //標注當前MyAspect是一個切面類 public class MyAspect {//配置前置增強@Before("execution(* com.anno.*.*(..))")public void before() {System.out.println("前置增強...");}public void afterReturning() {System.out.println("后置增強...");}//ProceedingJoinPoint :正在執行的連接點, 即 切點public Object around(ProceedingJoinPoint pjp) throws Throwable {System.out.println("環繞前增強...");//切點方法Object proceed = pjp.proceed();System.out.println("環繞后增強...");return proceed;}public void afterThrowing() {System.out.println("異常拋出增強...");}private void after() {System.out.println("最終增強...");} }

    10.9.2、注解配置AOP詳解

    1、切點表達式的抽取

    同xml配置aop一樣,我們可以將切點表達式抽取。抽取方式是在切面內定義方法,在該方法上使用@Pointcut注解定義切點表達式,然后再在增強注解中進行引用。

    @Component("myAspect")@Aspectpublic class MyAspect {@Before("MyAspect.myPoint()")public void before() {System.out.println("前置代碼增強..");}@Pointcut("execution(* com.anno.*.*(..))")public void myPoint() {}}

    2、AOP注解通知的類型

    通知的配置語法:@通知注解("切點表達式")

    名稱類型位置作用相關屬性
    @Before方法注解通知方法定義的上方設置當前通知方法與切入點之間的綁定關系,當前通知方法在原始切入點方法前運行value(默認):切入點方法名,格式為類名.方法名()
    @After方法注解通知方法定義的上方設置當前通知方法與切入點之間的綁定關系,當前通知方法在原始切入點方法后運行value(默認):切入點方法名,格式為類名.方法名()
    @Around方法注解通知方法定義的上方設置當前通知方法與切入點之間的綁定關系,當前通知方法在原始切入點方法前后運行
    @AfterReturning方法注解通知方法定義的上方設置當前通知方法與切入點之間的綁定關系,當前通知方法在原始切入點方法正常執行完畢后運行value(默認):切入點方法名,格式為類名.方法名()
    @AfterThrowing方法注解通知方法定義的上方設置當前通知方法與切入點之間的綁定關系,當前通知方法在原始切入點方法運行拋出異常后執行

    @Around注解使用注意事項:

    • 環繞通知必須依賴形參ProceedingJoinPoint才能實現對原始方法的調用,進而實現原始方法調用前后同時添加通知;
    • 通知中如果未使用ProceedingJoinPoint對原始方法進行調用將跳過原始方法的執行;
    • 對原始方法的調用可以不接收返回值,通知方法設置成void即可,如果接收返回值,必須設定為Object類型;
    • 原始方法的返回值如果是void類型,通知方法的返回值類型可以設置成void,也可以設置成Object;
    • 由于無法預知原始方法運行后是否會拋出異常,因此環繞通知方法必須拋出Throwable對象。

    代碼演示:

    @Configuration @ComponentScan("com.clp") @EnableAspectJAutoProxy // 告訴Spring,容器中有注解開發的AOP public class SpringConfig { }@Repository("bookDaoImpl") public class BookDaoImpl implements BookDao {@Overridepublic void update() {System.out.println("book dao update is running ...");}@Overridepublic int select() {System.out.println("book dao select is running ...");return 100;} }/*** 切面類*/ @Component // 該類受Spring管理 @Aspect // 說明該類作為AOP處理 public class MyAdvice {/*** 切點*/@Pointcut("execution(void com.clp.dao.BookDao.update())")private void pt() {}@Pointcut("execution(int com.clp.dao.BookDao.select())")private void pt2() {}/*** 切點 + 通知 = 切面*/@Before("pt()")public void before() {System.out.println("before advice ...");}@After("pt()")public void after() {System.out.println("after advice ...");}@After("pt2()")public void afterSelect() {System.out.println("after advice ...");}@Around("pt()")public void around(ProceedingJoinPoint pjp) throws Throwable {System.out.println("around before advice ...");pjp.proceed(); // 表示對原始操作的調用System.out.println("around after advice ...");}@Around("pt2()")public Object aroundSelect(ProceedingJoinPoint pjp) throws Throwable {System.out.println("around before advice ...");int res = (int) pjp.proceed();// 表示對原始操作的調用System.out.println("around after advice ...");return res + 111;}/*** 切點方法正常執行完才會執行該通知*/@AfterReturning("pt2()")public void afterReturning() {System.out.println("after returning advice ...");}/*** 切點方法拋出異常才會執行該通知*/@AfterThrowing("pt2()")public void afterThrowing() {System.out.println("afterThrowing advice ...");} }

    10.9.3、AOP通知獲取數據

    獲取切入點方法的參數:

    • JoinPoint:適用于前置、后置、返回后、拋出異常后通知;
    • ProceedingJoinPoint:適用于環繞通知。

    獲取切入點方法返回值:

    • 返回后通知;
    • 環繞通知。

    獲取切入點方法運行異常信息:

    • 拋出異常后通知;
    • 環繞通知。
    @Repository("bookDaoImpl") public class BookDaoImpl implements BookDao {@Overridepublic String findName(int id, String password) {System.out.println("id: " + id);return "itcast";} }/******************************************************************************/ /*** 切面類*/ @Component // 該類受Spring管理 @Aspect // 說明該類作為AOP處理 public class MyAdvice {/*** 切點*/@Pointcut("execution(* com.clp.dao.impl.BookDaoImpl.findName(..))")private void pt() {}/*** 切點 + 通知 = 切面** @param jp :JoinPoint對象描述了連接點方法的運行狀態,可以獲取到原始方法的調用參數*/@Before("pt()")public void before(JoinPoint jp) {Object[] args = jp.getArgs();System.out.println("before advice ..." + Arrays.toString(args));}@After("pt()")public void after(JoinPoint jp) {Object[] args = jp.getArgs();System.out.println("after advice ..." + Arrays.toString(args));}/*** ProceedingJoinPoint是JoinPoint的子類* 環繞通知中可以手工獲取切入點方法中出現的異常信息,得到的結果即為原始方法的返回值** @param pjp* @return* @throws Throwable*/@Around("pt()")public Object around(ProceedingJoinPoint pjp) throws Throwable {Object[] args = pjp.getArgs();System.out.println("around before advice ..." + Arrays.toString(args));args[0] = 666; // 修改切點方法的第1個參數Object ret = pjp.proceed(args);// 表示對原始操作的調用System.out.println("around after advice ..." + Arrays.toString(args));return ret;}/*** 如果原始方法有返回值,那么就將返回值 “裝到” 變量ret中** @param joinPoint:必須為方法中的第1個參數(可省略)* @param ret:原始方法的返回值*/@AfterReturning(value = "pt()", returning = "ret")public void afterReturning(JoinPoint joinPoint, Object ret) {System.out.println("afterReturning advice ..." + ret); // ret 為返回結果(參數名必須與注解中returning=的值相同)}/*** 拋出異常后通知可以獲取切入點方法中出現的異常信息,使用形參可以接收對應的異常對象** @param t:方法拋出的異常*/@AfterThrowing(value = "pt()", throwing = "t")public void afterThrowing(Throwable t) {System.out.println("afterThrowing advice ...");} }/******************************************************************************/ public class AppAnno {public static void main(String[] args) {AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);BookDao bookDao = (BookDao) ctx.getBean("bookDaoImpl");bookDao.findName(100, "abc");} }結果: around before advice ...[100, abc] before advice ...[666, abc] id: 666 afterReturning advice ...itcast after advice ...[666, abc] around after advice ...[666, abc]

    10.10、AOP案例

    10.10.1、案例1——測量業務層接口萬次執行效率

    需求:任意業務層接口執行均可顯示其執行效率(執行時長)。

    分析:

    • 業務功能:業務層接口執行前后分別記錄時間,求差值得到執行效率;
    • 通知類型選擇前后均可以增強的類型——環繞通知。

    補充說明:當前測試的接口執行效率僅僅是一個理論值,并不是一次完整的執行過程。

    代碼演示:

    public class JdbcConfig {/** 簡單類型的依賴注入使用@Value* */@Value("com.mysql.jdbc.Driver")private String driver;@Value("jdbc:mysql://localhost:3306/ssmdb")private String url;@Value("root")private String username;@Value("123456")private String password;// 1、定義一個方法獲得要管理的對象// 2、添加@Bean表示當前方法的返回值是一個bean/** 引用類型的依賴注入:主需要為bean定義方法設置形參即可,容器會根據 類型 自動裝配對象。* */@Beanpublic DataSource dataSource() {DruidDataSource dataSource = new DruidDataSource();dataSource.setDriverClassName(driver);dataSource.setUrl(url);dataSource.setUsername(username);dataSource.setPassword(password);return dataSource;} }/*********************************************************************************/ public class MyBatisConfig {/*** SqlSessionFactoryBean專門產生SqlSessionFactory* @return*/@Beanpublic SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();ssfb.setTypeAliasesPackage("com.clp.domain"); // 設置實體類別名ssfb.setDataSource(dataSource); // 設置數據源return ssfb;}/*** 加載Dao層的映射信息* @return*/@Beanpublic MapperScannerConfigurer mapperScannerConfigurer() {MapperScannerConfigurer msc = new MapperScannerConfigurer();msc.setBasePackage("com.clp.dao");return msc;} }/*********************************************************************************/ @Configuration @Import({JdbcConfig.class, MyBatisConfig.class}) @ComponentScan("com.clp") @PropertySource("classpath:jdbc.properties") @EnableAspectJAutoProxy // 告訴Spring,容器中有注解開發的AOP public class SpringConfig { }/*********************************************************************************/ @Component @Aspect public class ProjectAdvice {/*** 匹配業務層的所有方法*/@Pointcut("execution(* com.clp.service.*Service.*(..))")private void servicePc() {}@Around("ProjectAdvice.servicePc()")public void runSpeed(ProceedingJoinPoint pjp) throws Throwable {// 獲取執行的簽名信息Signature signature = pjp.getSignature();// 通過簽名獲取執行類型(接口名)String className = signature.getDeclaringTypeName();// 通過簽名獲取執行操作名稱(方法名)String name = signature.getName();// 記錄時間long start = System.currentTimeMillis();for (int i = 0; i < 10000; i++) {pjp.proceed();}long end = System.currentTimeMillis();System.out.println("萬次執行:"+ className + "." + name + " ----> " + (end - start) + "ms");} }/*********************************************************************************/ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = SpringConfig.class) public class AccountServiceTest {@Autowiredprivate AccountService accountService;@Testpublic void testFindById() {Account account = accountService.findById(3);}@Testpublic void testFindAll() {accountService.findAll();} }結果: 萬次執行:com.clp.service.AccountService.findAll---->1651ms

    10.10.2、案例2——百度網盤密碼數據兼容處理

    分析:

  • 在業務方法執行之前對所有的輸入參數進行格式處理——trim();
  • 使用處理后的參數調用原始方法——環繞通知中存在對原始方法的調用。
  • @Repository public class ResourcesDaoImpl implements ResourcesDao {@Overridepublic boolean readResources(String url, String password) {System.out.println(password.length());// 模擬校驗return password.equals("root");} }@Component @Aspect public class DataAdvice {@Pointcut("execution(boolean com.clp.service.*Service.*(*, *))")private void servicePc() {}@Around("servicePc()")public Object trimStr(ProceedingJoinPoint pjp) throws Throwable {Object[] args = pjp.getArgs();// 對原始參數的每一個參數進行操作for (int i = 0; i < args.length; i++) {// 判斷參數是不是字符串,如果是,就處理空格if (args[i].getClass().equals(String.class)) {// 去除數據,trim()操作后,更新數據args[i] = args[i].toString().trim();}}Object ret = pjp.proceed(args);return ret;} }@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = SpringConfig.class) public class ResourcesServiceTest {@Autowiredprivate ResourcesService resourcesService;@Testpublic void test() {boolean flag = resourcesService.openUrl("http://pan.baidu.com/aaa", " root ");System.out.println(flag);} }結果: 4 true

    11、Spring的事務控制

    事務作用:在數據層保障一系列的數據庫操作同時成功同時失敗。

    Spring事務作用:在數據層或業務層保障一系列的數據庫操作同時成功同時失敗。

    11.1、事務相關定義

    11.1.1、事務角色

    • 事務管理員:發起事務方,在Spring中通常指代業務層開啟事務的方法;
    • 事務協調員:加入事務方,在Spring中通常指代數據層方法,也可以是業務層方法。

    11.1.2、事務的隔離級別

    設置隔離級別,可以解決事務并發產生的問題,如臟讀、不可重復讀和虛讀。

    • ISOLATION_DEFAULT
    • ISOLATION_READ_UNCOMMITTED
    • ISOLATION_READ_COMMITTED
    • ISOLATION_REPEATABLE_READ
    • ISOLATION_SERIALIZABLE

    11.1.3、事務的傳播行為

    事務傳播行為:事務協調員對事務管理員所攜帶事務的處理態度。

  • REQUIRED:如果當前沒有事務,就新建一個事務,如果已經存在一個事務中,加入到這個事務中。一般的選擇(默認值)。
  • SUPPORTS:支持當前事務,如果當前沒有事務,就以非事務方式執行(沒有事務)。
  • MANDATORY:使用當前的事務,如果當前沒有事務,就拋出異常。
  • REQUERS_NEW:新建事務,如果當前在事務中,把當前事務掛起。
  • NOT_SUPPORTED:以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。
  • NEVER:以非事務方式運行,如果當前存在事務,拋++出異常。
  • NESTED:如果當前存在事務,則在嵌套事務內執行;如果當前沒有事務,則執行REQUIRED類似的操作。
  • 超時時間:默認值是-1,沒有超時限制。如果有,以秒為單位進行設置。
  • 是否只讀:建議查詢時設置為只讀。
  • 傳播屬性事務管理員事務協調員
    REQUIRED(默認)

    開啟T

    加入T
    未開啟T新建T2
    REQUIRES_NEW

    開啟T

    新建T2

    未開啟T新建T2
    SUPPORTS開啟T加入T
    未開啟T
    NOT_SUPPORTED開啟T
    未開啟T
    MANDATORY開啟T加入T
    未開啟TERROR
    NEVER開啟TERROR
    未開啟T
    NESTED設置savePoint,一旦事務回滾,事務將回滾到savePoint處,交由客戶端響應提交/回滾

    11.2、編程式事務控制相關對象

    11.2.1、PlatformTransactionManager

    PlatformTransactionManager接口是Spring的事務管理器,它里面提供了我們常用的操作事務的方法。

    方法說明
    TransactionStatus getTransaction(TransactionDefination defination)獲取事務的狀態信息
    void commit(TransactionStatus status)提交事務
    void rollback(TransactionStatus)回滾事務

    注意:PlatformTransactionManager是接口類型,不同的Dao層技術則有不同的實現類,例如:Dao層技術是jdbc或mybatis時:org.springframework.jdbc.datasource.DataSourceTransactionManager;Dao層技術是hibernate時:org.springframework.orm.hibernate5.HibernateTransactionManager。

    11.2.2、TransactionDefinition

    TransactionDefinition是事務的定義信息對象,里面有如下方法:

    方法說明
    int getIsolationLevel()獲得事務的隔離級別
    int getPropogationBehavior()獲得事務的傳播行為
    int getTimeout()獲得超時時間
    boolean isReadOnly()是否只讀

    11.2.3、TransactionStatus

    TransactionStatus接口提供的是事務具體的運行狀態,方法介紹如下:

    方法說明
    boolean hasSavepoint()是否存儲回滾點
    boolean isCompleted()事務是否完成
    boolean isNewTransaction()是否是新事務
    boolean isRollbackOnly()事務是否回滾

    11.3、基于XML的聲明式事務控制

    11.3.1、什么是聲明式事務控制

    Spring 的聲明式事務控制顧名思義就是采用聲明的方式來處理事務。這里所說的聲明,就是指在配置文件中聲明,用在Spring配置文件中聲明式地處理事務來代替代碼式的處理事務。

    聲明式事務處理的作用

    • 事務管理不侵入開發的組件。具體來說,業務邏輯對象不會意識到正在事務管理之中,事實上也應該如此,因為事務管理是屬于系統層面的服務,而不是業務邏輯的一部分,如果要改變事務管理策劃的話,也只需要在定義文件中重新配置即可。
    • 在不需要事務管理的時候,只要在設定文件上修改一下,即可移去事務管理服務,無需改變代碼重新編譯,這樣維護起來極其方便。

    注意:Spring聲明式事務控制底層就是AOP。

    11.3.2、聲明式事務控制的實現

    聲明式事務控制明確事項:

    • 誰是切點?
    • 誰是通知?
    • 配置切面?
    applicationContext.xml:...<!--目標對象 內部的方法就是切點--><bean id="accountService" class="com.service.impl.AccountServiceImpl"><property name="accountDao" ref="accountDao" /></bean><!--配置平臺事務管理器--><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource" /></bean><!--通知 事務的增強(須引入命名空間:tx)--><tx:advice id="txAdvice" transaction-manager="transactionManager"><!--對不同的方法配置各自的參數--><tx:attributes><tx:method name="*" /><tx:method name="transfer" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" /></tx:attributes></tx:advice><!--配置事務的aop織入--><aop:config><aop:advisor advice-ref="txAdvice" pointcut="execution(* com.service.impl.*.*(..))" /></aop:config>...

    其中,<tx:method>代表切點方法的事務參數的配置。

    • name:切點方法的名稱。
    • isolation:事務的隔離級別。
    • propagation:事務的傳播行為。
    • timeout:超時時間。
    • read-only:是否只讀。

    11.4、基于注解的聲明式事務控制

    11.4.1、使用步驟

    步驟:

  • 在業務層接口上添加Spring事務管理。注意:Spring注解式事務通常添加在業務層接口中而不會添加到業務層實現類中,降低耦合。注解式事務可以添加到業務方法上表示當前方法開啟事務,也可以添加到接口上表示當前接口所有方法開啟事務。 - 方式1:在實現類配置: @Service("accountService") @Transactional(isolation = Isolation.REPEATABLE_READ) //表示該對象下的所有方法都為這種配置,若方法有自己的配置,則方法用自己的配置 public class AccountServiceImpl {@Autowiredprivate AccountDaoImpl accountDao;// <!--通知 事務的增強(須引入命名空間:tx)--> // <tx:advice id="txAdvice" transaction-manager="transactionManager"> // <!--對不同的方法配置各自的參數--> // <tx:attributes> // <tx:method name="*" /> // <tx:method name="transfer" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" /> // </tx:attributes> // </tx:advice>@Transactional(isolation = Isolation.READ_COMMITTED,propagation = Propagation.REQUIRED)public void transfer(String outMan, String inMan, double money) {accountDao.out(outMan, money);accountDao.in(inMan,money);}@Transactional(isolation = Isolation.DEFAULT)public void xxx() {} }- 方式2:在接口配置 @Transactional(isolation = Isolation.REPEATABLE_READ) //表示該接口下的所有方法都為這種配置,若方法有自己的配置,則方法用自己的配置 public interface AccountService {/*** 轉賬操作* @param out:轉出方* @param in:轉入方* @param money:金額*/@Transactionalvoid transfer(String out, String in, Double money); }
  • 配置事務管理器。 - 配置平臺事務管理器方式1: applicationContext.xml:...<!--配置平臺事務管理器--><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource" /></bean><!--組件掃描--><context:component-scan base-package="com.dao" /><context:component-scan base-package="com.service" /><!--事務的注解驅動--><tx:annotation-driven transaction-manager="transactionManager" />...- 配置平臺事務管理器方式2: public class JdbcConfig {/** 簡單類型的依賴注入使用@Value* */@Value("com.mysql.jdbc.Driver")private String driver;@Value("jdbc:mysql://localhost:3306/ssmdb")private String url;@Value("root")private String username;@Value("123456")private String password;// 1、定義一個方法獲得要管理的對象// 2、添加@Bean表示當前方法的返回值是一個bean/** 引用類型的依賴注入:主需要為bean定義方法設置形參即可,容器會根據 類型 自動裝配對象。* */@Beanpublic DataSource dataSource() {DruidDataSource dataSource = new DruidDataSource();dataSource.setDriverClassName(driver);dataSource.setUrl(url);dataSource.setUsername(username);dataSource.setPassword(password);return dataSource;}/*** 創建平臺事務管理器* @param dataSource* @return*/@Beanpublic PlatformTransactionManager transactionManager(DataSource dataSource) {DataSourceTransactionManager dtm = new DataSourceTransactionManager();dtm.setDataSource(dataSource);return dtm;} }
  • 開啟注解式事務驅動。 @Configuration @Import({JdbcConfig.class, MyBatisConfig.class}) @ComponentScan("com.clp") @PropertySource("classpath:jdbc.properties") @EnableTransactionManagement // 開啟注解式事務驅動 public class SpringConfig { }public class MyBatisConfig {/*** SqlSessionFactoryBean專門產生SqlSessionFactory* @return*/@Beanpublic SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();ssfb.setTypeAliasesPackage("com.clp.domain"); // 設置實體類別名ssfb.setDataSource(dataSource); // 設置數據源return ssfb;}/*** 加載Dao層的映射信息* @return*/@Beanpublic MapperScannerConfigurer mapperScannerConfigurer() {MapperScannerConfigurer msc = new MapperScannerConfigurer();msc.setBasePackage("com.clp.dao");return msc;} }
  • 11.4.2、注解配置聲明式事務控制解析

    • 使用@Transactional在需要進行事務控制的類或者方法上修飾,注解可用的屬性同xml配置方式,例如:隔離級別、傳播行為等。
    • 注解使用在類上,那么該類下的所有方法都使用同一套注解參數配置。
    • 使用在方法上,不同的方法可以采用不同的事務參數配置。
    • XML配置文件(applicationContext.xml)中要開啟事務的注解驅動<tx:annotation-driven />

    注解聲明式事務控制的配置要點:

    • 平臺事務管理器配置(xml方式/配置類方式);
    • 事務通知的配置(@Transactional注解配置);
    • 事務注解驅動的配置<tx:annotation-driven/>。

    11.5、Spring事務相關配置

    屬性作用示例
    readOnly設置是否為只讀事務readOnly=true(只讀事務)
    timeout設置事務超時時間timeout=-1(永不超時)
    rollbackFor設置事務回滾異常(class)rollbackFor=如NullPointException.class
    rollbackForClassName設置事務回滾異常(String)同上,格式為字符串
    noRollbackFor設置事務不回滾異常(class)noRollbackFor=如NullPointException.class
    noRollbackForClassName設置事務不回滾異常(String)同上,格式為字符串
    propagation設置事務傳播行為...
    public interface AccountService {@Transactional(readOnly = true, timeout = -1, rollbackFor = {IOException.class})void transfer(String out, String in, Double money); }

    11.5、Spring事務案例

    11.5.1、案例1——銀行賬戶轉賬

    模擬銀行賬戶間轉賬業務。

    需求:實現任意兩個賬戶間轉賬操作。

    需求微縮:A賬戶減錢,B賬戶加錢。

    分析:

    • 數據層提供基礎操作,指定賬戶減錢(outMoney),指定賬戶加錢(inMoney);
    • 業務層提供轉賬操作(transfer),調用減錢與加錢的操作;
    • 提供2個賬號和操作金額執行轉賬操作;
    • 基于Spring整合MyBatis環境搭建上述操作。
    @Service public class AccountServiceImpl implements AccountService {@Autowiredprivate AccountDao accountDao;@Overridepublic void transfer(String out, String in, Double money) {accountDao.outMoney(out, money);accountDao.inMoney(in, money);} }

    結果分析:

    • 程序正常執行時,賬戶金額A減B加,沒有問題;
    • 程序出現異常后,轉賬失敗,但是異常之前操作成功,異常之后操作失敗,整體業務失敗。

    11.5.2、案例2——轉賬業務追加日志

    需求:實現任意兩個賬戶間轉賬操作,并對每次轉賬操作在數據庫進行留痕。

    需求微縮:A賬戶減錢,B賬戶加錢,數據庫記錄日志。

    分析:

    • 基于轉賬操作案例添加日志模塊,實現數據庫中記錄日志;
    • 業務層轉賬操作(transfer),調用減錢、加錢與記錄日志功能。

    實現效果預期:無論轉賬是否成功,均進行轉賬操作的日志留痕。

    步驟:

  • 在業務層接口上添加Spring事務,設置事務傳播行為為REQUIRES_NEW(即需要新事務)。 @Service public class LogServiceImpl implements LogService {@Autowiredprivate LogDao logDao;@Override@Transactional(propagation = Propagation.REQUIRES_NEW)public void log(String out, String in, Double money) {logDao.log("轉賬操作由" + out + "到" + in + ", 金額:" + money);} }
  • 總結

    以上是生活随笔為你收集整理的JavaWeb开发框架——Spring的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    亚洲欧美va | 色婷婷在线观看视频 | 国产专区欧美专区 | 在线视频观看成人 | 国产 成人 久久 | 色婷婷 亚洲| 成人a在线观看高清电影 | 在线观看 国产 | 天堂网av 在线 | 麻豆91在线看 | 国产一区二区精品久久 | 激情在线免费视频 | 国产精品成人a免费观看 | 免费日韩av电影 | 97成人在线视频 | 久久狠狠亚洲综合 | 日本在线视频一区二区三区 | 亚洲日本va午夜在线电影 | 操少妇视频 | 日韩精品免费一区二区在线观看 | 久久久久久毛片 | 国产精品国产亚洲精品看不卡 | 国产一在线精品一区在线观看 | 91在线资源| 一区 二区电影免费在线观看 | 国产精品视频地址 | 亚洲精品久 | 色就干| 九九电影在线 | 日韩在线视频国产 | 一区二区三区在线播放 | 在线观看亚洲精品视频 | 成人在线视频免费观看 | 中文字幕影片免费在线观看 | 国产精品一区免费在线观看 | 日韩精品视频免费在线观看 | 91亚洲精品国偷拍 | 久久国产三级 | 日韩三级.com | 日韩欧美高清一区二区三区 | 美女视频黄是免费的 | 久久久影视 | 噜噜色官网 | 日韩在线观看视频在线 | 99产精品成人啪免费网站 | 日韩区欠美精品av视频 | 2019中文最近的2019中文在线 | 在线激情网| 欧美色噜噜 | 色综合久久88 | 超碰人人干人人 | 亚洲人成在线观看 | 天天天操操操 | 亚洲精品久久久久www | 国产一级片免费播放 | 久久欧美精品 | 国产一级电影在线 | 在线亚洲人成电影网站色www | 人人精久| 国产日韩精品在线 | 成人精品久久久 | 久久免费看av | 国产精品大片在线观看 | 精品国产乱码久久久久久1区二区 | 一级欧美日韩 | 久久资源总站 | а天堂中文最新一区二区三区 | www.久久久.cum| 免费福利在线视频 | 成人在线视频观看 | 国产精品一区二区免费在线观看 | 成人免费在线视频观看 | 成人资源在线 | 精品成人a区在线观看 | av女优中文字幕在线观看 | 一二区精品 | 日本精品一区二区 | 91高清一区 | 麻豆视频免费在线观看 | 免费又黄又爽 | 六月丁香综合网 | 在线免费黄色片 | 成片免费观看视频大全 | 久久99国产精品二区护士 | 亚洲精品欧美精品 | 在线成人av| 亚洲一区二区黄色 | 国产 日韩 在线 亚洲 字幕 中文 | 91香蕉视频污在线 | 在线免费观看黄色大片 | 伊人国产在线观看 | 日韩激情综合 | 91新人在线观看 | 日韩电影在线观看中文字幕 | 国产欧美精品在线观看 | av在线一 | 日韩精品在线观看视频 | 国产 色 | 国产成人精品在线 | 日本久久免费视频 | 黄色影院在线观看 | 久久综合爱 | 在线观看一级视频 | 一区二区三区免费在线观看视频 | 国产女人18毛片水真多18精品 | 亚洲午夜久久久久久久久电影网 | 91视频这里只有精品 | 免费福利片2019潦草影视午夜 | 亚洲视频免费 | 国产成人精品一区二区三区福利 | .国产精品成人自产拍在线观看6 | 黄色日视频 | 中文字幕观看av | 国内久久精品 | 少妇搡bbb | 精品视频资源站 | 成年人免费看片网站 | 8x成人免费视频 | 韩日成人av| 午夜精品久久久久99热app | 欧美一级片在线免费观看 | 国内精品视频在线播放 | 中文字幕精品一区久久久久 | 91九色视频观看 | 婷婷在线免费观看 | 亚洲波多野结衣 | 成人wwwxxx视频 | 久久在线免费观看 | 91精品老司机久久一区啪 | 天天色中文 | 免费男女羞羞的视频网站中文字幕 | 国产在线综合视频 | 国产aa精品| 国产精品毛片一区视频播 | 最近中文字幕视频网 | 亚洲视频播放 | 在线视频 影院 | 激情网站 | 在线之家免费在线观看电影 | 国产一卡二卡在线 | 在线观看免费高清视频大全追剧 | 天天操 夜夜操 | 久久久精品成人 | 日本黄色免费网站 | 久久久私人影院 | 免费视频久久久 | 在线播放日韩av | 一本色道久久综合亚洲二区三区 | 国产精品一区二区三区四区在线观看 | 国产一线天在线观看 | 一二三区视频在线 | 亚洲欧美视频一区二区三区 | 日韩午夜剧场 | 久久精品99北条麻妃 | 国产免费一区二区三区网站免费 | 久久精品一区二区三区国产主播 | 日b视频国产| 日韩精品欧美专区 | 国产精品久久久久久久久久久久久 | 亚洲日b视频 | 高清av网 | 久久综合久久久 | 天天爱天天射天天干天天 | 狠狠干网 | 免费三级影片 | 国产亚洲精品女人久久久久久 | 国产精品情侣视频 | 久久99精品久久久久久清纯直播 | 色婷婷亚洲精品 | 久久久蜜桃一区二区 | 黄色视屏免费在线观看 | 在线精品在线 | 69久久99精品久久久久婷婷 | 天天爱av导航 | 日本午夜在线亚洲.国产 | 四虎成人免费观看 | 一区二区精品在线 | 午夜色婷婷 | 色综合久久综合中文综合网 | 国内精品久久久久影院一蜜桃 | 日韩3区 | 91久久久久久久一区二区 | 日韩视频一区二区在线 | 国产精品亚洲视频 | 99热超碰在线 | 久久成人在线 | 国产精品一区二区视频 | 91精品高清| 一区二区三区在线免费播放 | 亚洲一区二区视频在线 | 中文字幕日本在线观看 | 97av视频在线观看 | 中文字幕在线观看免费观看 | 91久久黄色 | 国产精品久久久久久69 | 中文字幕在线久一本久 | 国产色综合 | 激情综合站 | 国产精品久久久久久a | 91视频在线自拍 | 欧美性生活免费 | 2021久久 | 精品一区三区 | 亚洲综合最新在线 | 超级碰99| 亚洲最新视频在线 | 天天插视频 | 一区二区三区电影 | 精品一区二区三区香蕉蜜桃 | 精品999在线观看 | 国产一级免费播放 | 免费午夜av | 精品福利网 | 99视频在线观看视频 | 国产一区二区三区黄 | 在线观看免费一区 | 成人啪啪18免费游戏链接 | 欧美黄色特级片 | 91精品系列 | 日本久久久久久科技有限公司 | 91黄色在线视频 | 国产精品一区二区久久精品爱涩 | 国产一级视频 | 免费观看午夜视频 | 欧美日韩在线视频一区 | 色吧久久 | 国产黑丝一区二区三区 | 中文字幕在线观看免费高清电影 | 丁香av在线 | 免费91在线 | 九九免费在线观看视频 | 激情电影影院 | h文在线观看免费 | 成人免费视频网站 | 亚洲美女免费精品视频在线观看 | 欧美日韩精品国产 | 精品uu | 懂色av一区二区在线播放 | 国产成人一区二区三区在线观看 | 国产在线毛片 | 在线免费观看麻豆视频 | 国内成人精品2018免费看 | 久久免费看a级毛毛片 | 亚洲五月综合 | 国产精品成人品 | 18网站在线观看 | av中文字幕在线观看网站 | 最近中文字幕第一页 | 在线观看国产麻豆 | 欧美狠狠色 | 美国人与动物xxxx | av一区二区三区在线观看 | 99精品免费 | 久久不卡av| 国产在线精品区 | 在线看成人片 | 国产精品毛片一区二区三区 | 亚洲禁18久人片 | 久久丁香| 亚洲精品在线视频网站 | 国产亚洲视频在线免费观看 | 成人在线视频在线观看 | 一级成人免费视频 | 男女拍拍免费视频 | 国产美女精品视频 | 天天操人| 国产日产精品一区二区三区四区 | 亚洲3级 | a级国产乱理伦片在线观看 亚洲3级 | 亚洲精品av中文字幕在线在线 | 中文字幕在线视频网站 | 在线视频欧美日韩 | 免费观看www小视频的软件 | 丁香激情视频 | av在线播放中文字幕 | 欧美91精品久久久久国产性生爱 | 亚洲国产精品资源 | 亚洲精品视频在线播放 | 奇米网在线观看 | 福利一区二区 | 特级西西444www高清大视频 | 九色激情网 | 中国一级片在线播放 | 欧美成人亚洲 | 中文字幕国内精品 | 欧美日韩三级 | 国产成人av电影 | 高清在线观看av | 丁香色婷婷 | 69精品视频在线观看 | 日韩理论片在线 | 久久99精品国产 | 婷婷五月色综合 | 欧美日韩三区二区 | 久热av| 天天综合人人 | 正在播放 久久 | 7799av| 天天操夜夜逼 | 精品免费视频. | 日韩一区二区三区免费视频 | 久久久久久久久久久电影 | 视频在线观看亚洲 | 国产精品一级视频 | 日韩3区| 91视频 - 88av | 亚洲黄污 | www.av中文字幕.com | 亚洲一区二区视频在线播放 | 亚欧日韩av | 国产精品一区二区果冻传媒 | 国产视| 国产精品私人影院 | 久久免费精品一区二区三区 | 免费在线观看污网站 | 精品国模一区二区 | 国产高清不卡一区二区三区 | 国产一级大片免费看 | 99久久精品久久久久久清纯 | 成人在线播放免费观看 | 狠狠网亚洲精品 | 国产精品女同一区二区三区久久夜 | 国产午夜三级一区二区三 | 亚洲色五月 | 久久精品一二区 | 中文字幕国产亚洲 | 久久黄网站 | 欧美日韩aaaa | 青草视频网| 国产在线色视频 | 天天操天天吃 | 91人人插| 日韩毛片精品 | 国产做a爱一级久久 | 免费日韩 | 91麻豆免费视频 | 久久不射电影院 | 中文字幕免费一区二区 | 免费在线播放黄色 | 中文字幕亚洲不卡 | www.久热| 欧美色婷婷 | 国产视频首页 | 精品成人免费 | av成人免费在线看 | av福利在线免费观看 | 婷婷激情影院 | 久久久免费看片 | 国产精品美女毛片真酒店 | 婷婷在线精品视频 | 看毛片的网址 | 91在线观看视频 | 99热在| 国产高清久久久久 | 国产裸体视频bbbbb | 青青草国产精品 | 一区二区三区四区精品 | 高清免费av在线 | 国产玖玖精品视频 | 在线黄色国产 | 在线亚洲小视频 | 日日夜夜骑 | 久久精品国产一区二区三区 | 999日韩| 天堂在线一区二区 | 免费久久精品视频 | 中文字幕第一页在线播放 | 国产精品九九视频 | 国产精品去看片 | 欧美日韩不卡在线视频 | 国产亚洲精品成人av久久影院 | 欧美视频网址 | 人人澡人人澡人人 | 亚洲天天摸日日摸天天欢 | 精品视频中文字幕 | 在线a视频 | 欧美色图亚洲图片 | 人人澡人人模 | 小草av在线播放 | 在线视频a | 在线成人中文字幕 | 国产字幕在线播放 | 亚洲在线a| 五月婷婷中文 | 亚洲免费在线 | 日韩精品一区二区三区不卡 | 亚洲伊人色| 97在线观看免费高清 | 国产黄色特级片 | 亚洲日日日 | 91网在线观看 | 亚洲一级电影在线观看 | 久草在线资源观看 | 国产一区二区在线免费 | 精品免费久久久久久 | 欧美在线视频不卡 | 国产一区二区在线免费观看 | 亚洲日日夜夜 | 久久激情精品 | 色视频在线免费观看 | 国产精品久久久久影院 | 91视频这里只有精品 | 国产69精品久久app免费版 | 中文有码在线 | 久久免费影院 | 国内外成人在线 | 欧洲色综合 | 天天干天天操天天操 | 国产一区二区三区四区大秀 | 91精品免费在线观看 | 九色视频网址 | 91mv.cool在线观看 | 九九九在线观看 | 成人作爱视频 | 久久免费电影网 | 日韩精品在线看 | 少妇超碰在线 | 欧美天天综合网 | 成片免费观看视频 | www夜夜 | 亚洲三级在线播放 | 精品伊人久久久 | 中文字幕永久 | 爱爱一区 | www.玖玖玖 | 久久一视频 | 国产成人亚洲在线观看 | www国产精品com | 国产精品永久免费 | 福利网在线 | 免费网站在线观看人 | 色综合久久88色综合天天人守婷 | 免费国产亚洲视频 | 久久久天堂| 国产精久久久 | 成人国产精品电影 | 在线免费色 | 久久久精品国产一区二区三区 | 精品在线观看一区二区 | 久久综合中文字幕 | 免费看黄在线 | 国产精品视频最多的网站 | 97人人网 | 99精品在线免费 | 免费看国产一级片 | 久久午夜精品影院一区 | 久久久久免费电影 | 亚洲国产午夜精品 | 久久久久久久久福利 | 久草五月 | 人人干人人搞 | 91理论片午午伦夜理片久久 | 亚洲 中文 欧美 日韩vr 在线 | 久久精品电影网 | 亚洲成人999 | 五月天伊人 | 午夜美女福利直播 | 国产精品久久久久影院 | 在线国产福利 | 久久久www成人免费精品 | 国内揄拍国产精品 | 天天操天天干天天综合网 | 不卡电影一区二区三区 | 国产高清精品在线观看 | 最新日本中文字幕 | 亚洲综合精品视频 | 丁香婷婷综合激情五月色 | 免费在线观看av | 91丨九色丨国产在线观看 | 亚洲国产成人在线观看 | 激情文学综合丁香 | 在线视频麻豆 | 高清av在线 | 黄色一级免费网站 | 日韩一区二区三区在线看 | 天堂av在线免费观看 | www国产亚洲| 在线观看国产www | 啪一啪在线 | 日韩视频中文字幕 | 日韩久久在线 | www.91国产 | 欧美精品中文字幕亚洲专区 | 网址你懂的在线观看 | 狠狠干综合 | 天天操夜夜干 | 亚洲精品美女久久久久网站 | 欧美激情综合色 | 国产专区在线看 | 日韩中文字幕免费视频 | 欧美日韩中文字幕在线视频 | 在线视频观看国产 | 久久99国产精品 | 日韩电影在线视频 | 国产精品美女www爽爽爽视频 | 午夜久草 | 天天色天天色天天色 | 亚洲视频国产 | 国产夫妻av在线 | 视频一区视频二区在线观看 | 亚洲一区二区三区四区精品 | 中文字幕在线观看一区 | 香蕉手机在线 | 在线午夜av| 国产高清日韩 | 欧美一级视频免费 | 亚洲视频资源在线 | 在线韩国电影免费观影完整版 | 久草在线中文视频 | 欧美日韩高清一区二区 国产亚洲免费看 | 久久精品亚洲精品国产欧美 | 黄色最新网址 | 国产精品久久久av久久久 | 成人av在线亚洲 | 不卡的av中文字幕 | 在线va视频| 成人在线视频论坛 | 国产亚洲精品v | 天天射综合网视频 | 激情视频免费在线 | 丁香久久综合 | 粉嫩av一区二区三区四区在线观看 | 狠狠狠狠狠狠狠狠 | 在线免费观看视频a | 五月天久久 | 成年美女黄网站色大片免费看 | 色综合咪咪久久网 | 久久成人免费 | 免费色网站 | 超碰97在线资源 | 国产美女在线精品免费观看 | 久草视频在| 午夜影视一区 | 国产999精品 | 午夜国产福利视频 | 日韩精品一区二区三区视频播放 | 日韩欧美电影在线 | 欧美一区二区三区免费观看 | 黄色大全视频 | 成人h视频在线 | 美女视频一区二区 | 综合精品久久久 | 久久国产精品99久久久久久进口 | 成人小视频在线免费观看 | 国产白浆在线观看 | 97成人精品视频在线播放 | 91精品国产麻豆 | 深夜福利视频一区二区 | 在线亚洲观看 | 欧美久久久久久久久久久久 | 国产精品综合久久久久久 | 五月天激情开心 | 中文字幕一区二区三区视频 | 国产高清视频免费 | 天天拍天天色 | 亚洲成av人影片在线观看 | www久久国产 | 国内精品视频久久 | 中文字幕乱视频 | 精品亚洲一区二区三区 | 国产精品国产亚洲精品看不卡15 | 2000xxx影视 | 1024手机看片国产 | 国产精品久久久久一区二区国产 | 免费男女网站 | www.久久色| 国产精品久久久久久久久久免费看 | 欧美亚洲国产精品久久高清浪潮 | 蜜臀久久99精品久久久久久网站 | 中文字幕亚洲在线观看 | 91av网站在线观看 | 成人久久久久久久久久 | 一本色道久久精品 | 91九色在线视频观看 | 欧美日韩在线观看不卡 | 波多野结衣在线观看一区二区三区 | 国产视频在线一区二区 | 91最新网址| 黄网站色欧美视频 | 99性视频| 人人插人人搞 | 四虎最新域名 | 国产福利小视频在线 | 丝袜美腿一区 | 日本大片免费观看在线 | 日韩专区一区二区 | japanese黑人亚洲人4k | 国产99免费视频 | 九九国产精品视频 | 中文字幕在线观看av | 国产精品video | 日本精品视频在线观看 | 久久免费电影 | 免费网站v | 久草在线观看视频免费 | 天天色天天艹 | 亚洲首页 | 日女人免费视频 | 久久超级碰视频 | 2019中文字幕网站 | 日韩电影一区二区三区在线观看 | 亚洲国产视频a | 欧美精品一区二区三区四区在线 | 在线免费观看欧美日韩 | 日韩有码在线观看视频 | 欧美精品资源 | 特级西西444www大胆高清无视频 | 五月天激情在线 | 色噜噜在线观看视频 | 久久久久久电影 | 香蕉网在线 | 亚洲精品国产成人av在线 | 狠狠色狠狠色综合日日小说 | av大片免费在线观看 | 国产美女精品人人做人人爽 | 久久午夜免费视频 | 在线免费观看黄色 | 国产精品久久久久久爽爽爽 | 激情欧美丁香 | 国产又粗又硬又长又爽的视频 | 97视频网址| 欧美精品久久久 | 99久久精品免费看国产一区二区三区 | 日韩久久精品一区二区三区 | 精品欧美日韩 | 亚洲mv大片欧洲mv大片免费 | 久久伦理网| 精品久久久久久一区二区里番 | 久久久99国产精品免费 | 黄色av在| 天天射天天操天天 | 美女网站视频免费黄 | 日韩精品中文字幕有码 | 中文字幕一区二区三区四区久久 | 天天干天天干天天干天天干天天干天天干 | 国产一级视频在线 | 国产成人精品av在线观 | 丁香婷婷激情五月 | 精品久久久久久综合 | 欧美日韩伦理在线 | 国产精品成人国产乱一区 | 国产五月婷婷 | 国产精品video爽爽爽爽 | 国产精彩视频一区 | 欧美日韩在线观看视频 | 国内久久精品视频 | 狠狠干夜夜 | 超碰在线成人 | 久久高清免费视频 | 亚洲精品资源在线 | 亚洲美女视频在线观看 | 国内精品在线观看视频 | 麻豆视频在线免费 | 99操视频 | 日韩大片免费观看 | 在线成人高清电影 | 亚洲专区一二三 | www.av免费 | 成人免费观看视频大全 | 丝袜美腿在线视频 | 在线观看中文字幕第一页 | 在线看国产视频 | 久草视频在线免费看 | 欧美日韩精品久久久 | 国产亚洲情侣一区二区无 | 伊人婷婷久久 | 91黄视频在线 | 91成年视频 | 欧美精品一区二区三区四区在线 | 91久久丝袜国产露脸动漫 | 日韩欧美精品在线观看视频 | 久久国产电影 | 欧美热久久 | 九精品 | 亚洲麻豆精品 | 久久综合久久综合这里只有精品 | 免费黄色激情视频 | 国产精品综合在线观看 | 中文字幕 在线 一 二 | 97视频网址 | 91手机电影 | 丁香久久综合 | 在线免费性生活片 | 国产私拍在线 | 亚洲国产美女精品久久久久∴ | 久久精品2 | 国产青草视频在线观看 | 中文字幕av全部资源www中文字幕在线观看 | 久精品视频在线 | 久久国产精品二国产精品中国洋人 | 欧美午夜一区二区福利视频 | 狂野欧美激情性xxxx | 久久久久久久影视 | 五月婷婷.com | av成人动漫在线观看 | 久久五月情影视 | 伊人色综合久久天天 | 欧美黄色特级片 | 亚洲午夜av | a级国产乱理论片在线观看 伊人宗合网 | 婷婷六月激情 | 日韩av在线免费看 | 中文字幕中文 | 久久中文字幕在线视频 | 91九色蝌蚪国产 | 天天天干 | 久久五月激情 | 国产精品综合av一区二区国产馆 | 国内成人av | 91麻豆网| 深夜免费福利在线 | 亚洲精品高清在线 | 欧美韩日精品 | 亚洲一级黄色片 | 欧美日韩高清国产 | 日本巨乳在线 | 伊人久在线 | 四虎在线免费观看 | 三日本三级少妇三级99 | 中文字幕91视频 | 亚洲夜夜网 | 狠狠干我 | 国产在线成人 | 亚洲九九爱| 久久国产麻豆 | 丁香婷婷激情啪啪 | 久久av网址| 国产不卡一二三区 | 久久99精品久久只有精品 | 很黄很黄的网站免费的 | 亚洲精品久久久蜜臀下载官网 | 91成人精品一区在线播放 | 日韩欧美一区二区在线 | 久草手机视频 | 天天草天天干 | 国产精品久久久久久久久久白浆 | av午夜电影 | 日韩在线视频不卡 | 九九精品毛片 | 天天色天天操天天爽 | 黄色大片国产 | 999久久久久 | 久久免费视频在线观看 | 人人看人人草 | 97超碰超碰久久福利超碰 | 国产手机免费视频 | 久久精品—区二区三区 | 韩日成人av | 欧美性另类 | 狠狠干夜夜操 | 在线免费观看的av | 亚洲夜夜网| 午夜在线日韩 | 有没有在线观看av | av高清网站在线观看 | 欧美日韩在线播放 | 精品在线观看一区二区 | 精品国产欧美一区二区 | 国产精品免费在线播放 | 中文字幕精品一区久久久久 | 美国三级黄色大片 | 亚洲国产三级在线观看 | 九九国产视频 | 亚洲码国产日韩欧美高潮在线播放 | 亚洲午夜av久久乱码 | 91精品成人 | 91精品国自产在线 | 国产中文字幕免费 | 亚洲国产综合在线 | www.香蕉| 久久五月精品 | 天天天天天天干 | 99综合影院在线 | 国产午夜激情视频 | 欧美日韩免费在线观看视频 | 久久久久综合网 | 久久久久免费视频 | 91精品一区二区三区久久久久久 | 亚洲乱码在线观看 | 91探花系列在线播放 | 亚洲精品久久在线 | 国产精品免费高清 | 91.精品高清在线观看 | 欧美性免费 | 亚洲三区在线 | 91网站在线视频 | 久久午夜视频 | 日韩高清免费在线 | 国产中文字幕一区二区三区 | 国产一区视频在线播放 | 国产一级h| 国产精品毛片一区二区 | 91视频在线 | 一级国产视频 | 国产裸体永久免费视频网站 | 天天色棕合合合合合合 | 能在线观看的日韩av | 欧美一级乱黄 | 91精品国产福利在线观看 | 亚洲成aⅴ人在线观看 | 久久99久久99| 青春草免费在线视频 | 超碰在线97观看 | 国产精品永久在线 | 91经典在线 | 日韩成人邪恶影片 | 四虎8848免费高清在线观看 | 久久一二三四 | 91天天操 | 中文字幕在线观看国产 | 麻豆va一区二区三区久久浪 | 亚洲精品久久久久中文字幕二区 | 一区二区三区动漫 | 亚洲视频资源在线 | 91在线视频网址 | 精品伊人久久久 | 久久精品国产v日韩v亚洲 | 亚洲激情校园春色 | 国产在线观看 | 国产高清无线码2021 | 日韩深夜在线观看 | 亚洲粉嫩av | 国产日韩精品在线 | 亚洲成人影音 | 国产一级91 | 999久久| 天天操天天摸天天干 | 99色免费 | 国产在线高清精品 | 天天干天天草天天爽 | 欧美巨乳波霸 | 日本中文字幕视频 | 香蕉久草 | 99久久精品国产系列 | 国产一区免费在线观看 | 国产成人一区二区三区影院在线 | 狠狠操操网| 国产美女精品视频免费观看 | 久久免费福利 | 免费网站观看www在线观看 | 精品久久国产一区 | 色av婷婷| 欧美了一区在线观看 | 亚洲免费精彩视频 | 亚洲成人免费观看 | 特级毛片爽www免费版 | 91亚·色| 久久亚洲成人网 | 免费亚洲精品视频 | 国产精品毛片一区二区在线看 | 在线观看免费av网 | 99久久综合狠狠综合久久 | 国产中文字幕久久 | 国产色综合 | av免费看网站 | 在线观看一区视频 | a午夜电影 | 国产亚洲一级高清 | 性色av香蕉一区二区 | 国内一级片在线观看 | 久草在线视频国产 | 99亚洲精品视频 | 亚洲一区日韩精品 | 亚洲欧美激情精品一区二区 | 久草在线最新视频 | 九九99| 国产美腿白丝袜足在线av | 久久你懂得 | 国产老太婆免费交性大片 | 五月婷婷.com | 2019中文字幕第一页 | 99久久久久久久久久 | 日韩电影在线观看一区二区三区 | 日韩欧美国产视频 | 欧美成人在线免费 | 黄色一级大片免费看 | 日韩在线视频免费播放 | 天天干天天草 | 欧美日韩高清在线一区 | 免费国产一区二区 | 黄色a一级片 | 成+人+色综合 | av丝袜在线 | 国产精品高 | 精品久久久久久综合日本 | 91中文字幕在线视频 | 欧美在线视频一区二区三区 | 精品日韩在线一区 | 99精品免费久久久久久久久 | 色视频在线观看 | 国产精品久久久久久久久久妇女 | 国产美女精品视频 | 激情综合色综合久久综合 | 精品美女久久久久 | 婷婷国产一区二区三区 | 在线观看你懂的网站 | 免费视频91 | 久久999精品 | av福利在线免费观看 | 日韩美女免费线视频 | 亚洲欧美视频在线 | 国产亚洲精品xxoo | 欧美精品一区二区免费 | 综合色中色 | 久久久www成人免费毛片麻豆 | 久精品视频在线观看 | 男女激情免费网站 | 久久伊人爱 | 中文字幕在线观看一区二区三区 | 色噜噜狠狠狠狠色综合久不 | 日韩www在线 | 超碰在线人人艹 | 国产精彩视频一区二区 | 91精品久久久久久综合乱菊 | 日韩精品久久一区二区 | 精品999在线观看 | 黄影院| 国产精品视频在线观看 | 亚洲六月丁香色婷婷综合久久 | 夜夜骑日日 | 一级黄色免费网站 | a视频在线观看 | 中文字幕在线看视频国产中文版 | 玖玖综合网 | 日韩a级黄色 | 亚洲网站在线 | 99国产情侣在线播放 | 国产不卡视频在线 | 久久黄色小说视频 | 欧美性粗大hdvideo | 日韩精品一区二区不卡 | 久久久久久久久久影院 | 色综合a | 久久人操 | 日本公妇在线观看 | 日韩资源在线观看 | 亚洲乱码精品 | 蜜臀久久99精品久久久无需会员 | 国产视频日本 | 午夜12点 | 在线视频久久 | 最近中文字幕在线 | 这里只有精品视频在线观看 | 五月综合色 | 99re亚洲国产精品 | 香蕉网在线观看 | 久久只精品99品免费久23小说 | 成人在线黄色 | av一区二区三区在线播放 | 高清av免费一区中文字幕 | 在线观看视频你懂得 | 色瓜 | 久久九九国产视频 | 99在线高清视频在线播放 | 中文欧美字幕免费 | 国产一区二区免费 | 黄色精品久久 | 亚洲成人资源在线 | 国产精品一区二区三区观看 | 一级黄色片在线 | 四虎国产永久在线精品 | 国产成人黄色在线 | 欧美日韩另类在线观看 | 国产视频亚洲视频 | 成人av一区二区兰花在线播放 | 91香蕉视频色版 | 成人cosplay福利网站 | 岛国av在线不卡 | 天天天干 | 亚洲精品乱码 | 亚洲做受高潮欧美裸体 | 久久综合久久综合这里只有精品 | 成人永久免费 | 亚洲欧洲精品一区二区精品久久久 | 欧美日韩国产色综合一二三四 | 国产精品嫩草在线 | 亚洲视频分类 | 国产免费久久av | 亚洲深夜影院 | 欧美狠狠操| 国产伦理久久精品久久久久_ | 在线国产视频一区 | av电影一区二区三区 | 91在线免费播放视频 | 国产免费美女 | 天天在线操 | 一区二区三区观看 | 国产正在播放 | 很污的网站 | 超碰97人人干 | 久久久影视 | 欧美日韩国语 | 992tv在线观看网站 | 亚洲综合国产精品 | 亚洲午夜精品久久久 | www视频免费在线观看 | 国产精品久久嫩一区二区免费 | 国产在线观看高清视频 |