1、Spring
1.1、 簡介
Spring : 春天 —>給軟體行業帶來了春天
2002年,Rod Jahnson首次推出了Spring架構雛形interface21架構。
2004年3月24日,Spring架構以interface21架構為基礎,經過重新設計,釋出了1.0正式版。
很難想象Rod Johnson的學曆 , 他是悉尼大學的博士,然而他的專業不是計算機,而是音樂學。
Spring理念:簡化伺服器的開發,使現有的技術更加容易使用,本身就是一個大雜燴,整合了現有的技術架構。
- SSH:Struct2 + Spring + Hibernate
- SSM: SpringMvc + Spring + MyBatis
官網 :
http://spring.io/
官方下載下傳位址 :
https://repo.spring.io/libs-release-local/org/springframework/spring/
GitHub :
https://github.com/spring-projects
Maven位址:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.8</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
1.2 、優點
- Spring是一個免費的開源的架構(容器)
- Spring是一個輕量級的,非入侵式的架構
- 控制反轉(IOC),面向切面程式設計(AOP) 【重點】
- Inversion of Control
- Aspect-Oriented Programming
- 支援事物的處理,對架構整合的支援
總結:Spring就是一個輕量級的控制反轉(IOC),和面向切面(AOP)的程式設計的架構
1.3、 組成
Spring 架構是一個分層架構,由 7 個定義良好的子產品組成。Spring 子產品建構在核心容器之上,核心容器定義了建立、配置和管理 bean 的方式 .
- 核心容器: 核心容器提供 Spring 架構的基本功能。核心容器的主要元件是 BeanFactory,它是工廠模式的實作。BeanFactory 使用控制反轉(IOC) 模式将應用程式的配置和依賴性規範與實際的應用程式代碼分開。
- Spring 上下文: Spring 上下文是一個配置檔案,向 Spring 架構提供上下文資訊。Spring 上下文包括企業服務,例如 JNDI、EJB、電子郵件、國際化、校驗和排程功能。
- Spring AOP:通過配置管理特性,Spring AOP 子產品直接将面向切面的程式設計功能 , 內建到了 Spring 架構中。是以,可以很容易地使 Spring 架構管理任何支援 AOP的對象。Spring AOP 子產品為基于 Spring 的應用程式中的對象提供了事務管理服務。通過使用 Spring AOP,不用依賴元件,就可以将聲明性事務管理內建到應用程式中。
- Spring DAO:JDBC DAO 抽象層提供了有意義的異常層次結構,可用該結構來管理異常處理和不同資料庫供應商抛出的錯誤消息。異常層次結構簡化了錯誤處理,并且極大地降低了需要編寫的異常代碼數量(例如打開和關閉連接配接)。Spring DAO 的面向 JDBC 的異常遵從通用的 DAO 異常層次結構。
- Spring ORM:Spring 架構插入了若幹個 ORM 架構,進而提供了 ORM 的對象關系工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。所有這些都遵從 Spring 的通用事務和 DAO 異常層次結構。
- Spring Web 子產品:Web 上下文子產品建立在應用程式上下文子產品之上,為基于 Web 的應用程式提供了上下文。是以,Spring 架構支援與 Jakarta Struts 的內建。Web 子產品還簡化了處理多部分請求以及将請求參數綁定到域對象的工作。
- Spring MVC 架構:MVC 架構是一個全功能的建構 Web 應用程式的 MVC 實作。通過政策接口,MVC 架構變成為高度可配置的,MVC 容納了大量視圖技術,其中包括 JSP、Velocity、Tiles、iText 和 POI。
1.4、組成
Spring Boot與Spring Cloud
- Spring Boot 是 Spring 的一套快速配置腳手架,可以基于Spring Boot 快速開發單個微服務;
- Spring Cloud是基于Spring Boot實作的;
- Spring Boot專注于快速、友善內建的單個微服務個體,Spring Cloud關注全局的服務治理架構;
- Spring Boot使用了限制優于配置的理念,很多內建方案已經幫你選擇好了,能不配置就不配置 , Spring Cloud很大的一部分是基于Spring Boot來實作,Spring Boot可以離開Spring Cloud獨立使用開發項目,但是Spring Cloud離不開Spring Boot,屬于依賴的關系。
- SpringBoot在SpringClound中起到了承上啟下的作用,如果你要學習SpringCloud必須要學習SpringBoot。
2、IOC
2.1、 引入IOC
建立一個空白的maven項目,使用這個項目來說明IOC原理
分析實作
先按照原來的方式寫一段代碼
1、先寫一個UserDao接口
public interface UserDao {
public void getUser();
}
2、再去寫Dao的實作類
public class UserDaoImpl implements UserDao {
@Override
public void getUser() {
System.out.println("擷取使用者資料");
}
}
3、然後去寫UserService的接口
public interface UserService {
public void getUser();
}
4、最後寫Service的實作類
public class UserServiceImpl implements UserService {
private UserDao userDao = new UserDaoImpl();
@Override
public void getUser() {
userDao.getUser();
}
}
5、測試一下
@Test
public void test(){
//這是我們原來的方式 , 開始大家也都是這麼去寫的對吧 . 那我們現在修改一下 .
UserService service = new UserServiceImpl();
service.getUser();
}
把Userdao的實作類增加一個
public class UserDaoMySqlImpl implements UserDao {
@Override
public void getUser() {
System.out.println("MySql擷取使用者資料");
}
}
緊接着我們要去使用MySql的話 , 我們就需要去service實作類裡面修改對應的實作
public class UserServiceImpl implements UserService {
private UserDao userDao = new UserDaoMySqlImpl();
@Override
public void getUser() {
userDao.getUser();
}
}
在假設, 我們再增加一個Userdao的實作類
public class UserDaoOracleImpl implements UserDao {
@Override
public void getUser() {
System.out.println("Oracle擷取使用者資料");
}
}
那麼我們要使用Oracle , 又需要去service實作類裡面修改對應的實作 . 假設我們的這種需求非常大 , 這種方式就根本不适用了, 甚至反人類對吧 , 每次變動 , 都需要修改大量代碼 . 這種設計的耦合性太高了, 牽一發而動全身 .
那我們如何去解決呢 ?
我們可以在需要用到他的地方 , 不去實作它 , 而是留出一個接口 , 利用set , 我們去代碼裡修改下
public class UserServiceImpl implements UserService{
private UserDao userDao ;
//利用set實作動态的值的注入
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void getUser() {
userDao.getUser();
}
}
現在去我們的測試類裡 , 進行測試 ;
public class MyTest {
@Test
public void test(){
UserServiceImpl service = new UserServiceImpl();
service.setUserDao(new UserDaoOracleImpl());
service.getUser();
}
}
- 之前,程式是主動建立對象,控制權在程式員手上!
- 使用了set注入後,程式不再具有主動性,而是變成了被動的接受對象!
這種思想 , 從本質上解決了問題 , 我們程式員不再去管理對象的建立了 , 更多的去關注業務的實作 . 耦合性大大降低 . 這也就是IOC的原型 !
2.2、IOC本質
控制反轉IoC(Inversion of Control),是一種設計思想,DI(依賴注入)是實作IoC的一種方法,也有人認為DI隻是IoC的另一種說法。沒有IoC的程式中 , 我們使用面向對象程式設計 , 對象的建立與對象間的依賴關系完全寫死在程式中,對象的建立由程式自己控制,控制反轉後将對象的建立轉移給第三方,個人認為所謂控制反轉就是:獲得依賴對象的方式反轉了。
IoC是Spring架構的核心内容,使用多種方式完美的實作了IoC,可以使用XML配置,也可以使用注解,新版本的Spring也可以零配置實作IoC。
Spring容器在初始化時先讀取配置檔案,根據配置檔案或中繼資料建立與組織對象存入容器中,程式使用時再從Ioc容器中取出需要的對象。
采用XML方式配置Bean的時候,Bean的定義資訊是和實作分離的,而采用注解的方式可以把兩者合為一體,Bean的定義資訊直接以注解的形式定義在實作類中,進而達到了零配置的目的。
控制反轉是一種通過描述(XML或注解)并通過第三方去生産或擷取特定對象的方式。在Spring中實作控制反轉的是IoC容器,其實作方法是依賴注入(Dependency Injection,DI)。
HelloSpring
編寫代碼
1、編寫一個Hello實體類
public class Hello {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Hello{" +
"name='" + name + '\'' +
'}';
}
}
2、編寫我們的spring檔案 , 這裡我們命名為beans.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就是java對象 , 由Spring建立和管理-->
<!--使用Spring來建立對象,在Spring中這些都稱為Bean
類型 變量名 = new 類型()
Hello hello = new Hello
id=變量名
class=需要 new 的類型或對象
property=給對象中的屬性指派
-->
<bean id="hello" class="com.yt.pojo.Hello">
<property name="name" value="Spring"/>
</bean>
</beans>
3、我們可以去進行測試了
@Test
public void test(){
//解析beans.xml檔案 , 生成管理相應的Bean對象
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//getBean : 參數即為spring配置檔案中bean的id .
Hello hello = (Hello) context.getBean("hello");
System.out.println("hello.toString() = " + hello.toString());
}
思考
- Hello 對象是誰建立的 ? 【hello 對象是由Spring建立的】
- Hello 對象的屬性是怎麼設定的 ? hello 對象的屬性是由Spring容器設定的
- 這個過程就叫控制反轉 :
- 控制 : 誰來控制對象的建立 , 傳統應用程式的對象是由程式本身控制建立的 , 使用Spring後 , 對象是由Spring來建立的
- 反轉 : 程式本身不建立對象 , 而變成被動的接收對象 .
依賴注入 : 就是利用set方法來進行注入的.
IOC是一種程式設計思想,由主動的程式設計變成被動的接收
可以通過new ClassPathXmlApplicationContext去浏覽一下底層源碼 .
修改案例一
我們在案例一中, 新增一個Spring配置檔案beans.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="MysqlImpl" class="com.yt.dao.impl.UserDaoMysqlImpl"/>
<bean id="OracleImpl" class="com.yt.dao.impl.UserDaoOracleImpl"/>
<bean id="ServiceImpl" class="com.yt.service.impl.UserServiceImpl">
<!--注意: 這裡的name并不是屬性 , 而是set方法後面的那部分 , 首字母小寫-->
<!--引用另外一個bean , 不是用value 而是用 ref-->
<property name="userDao" ref="OracleImpl"/>
</bean>
</beans>
測試!
OK , 到了現在 , 我們徹底不用再程式中去改動了 , 要實作不同的操作 , 隻需要在xml配置檔案中進行修改 , 所謂的IoC,一句話搞定 : 對象由Spring 來建立 , 管理 , 裝配 !
@Test
public void test2(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserServiceImpl serviceImpl = (UserServiceImpl) context.getBean("ServiceImpl");
serviceImpl.getUser();
}
2.3、IOC建立對象方式
通過無參構造方法來建立
1、
User.java
public class User {
private String name;
public User() {
System.out.println("user無參構造方法");
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void show(){
System.out.println("name="+ name );
}
}
2、
beans.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="user" class="com.yt.pojo.User">
<property name="name" value="yt"/>
</bean>
</beans>
3、測試類
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//在執行getBean的時候, user已經建立好了 , 通過無參構造
User user = (User) context.getBean("user");
//調用對象的方法 .
user.show();
}
結果可以發現,在調用show方法之前,User對象已經通過無參構造初始化了!
通過有參構造方法來建立
1、
User.java
public class User {
private String name;
public User(String name) {
this.name = name;
System.out.println("有參構造");
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void show(){
System.out.println("name="+ name );
}
}
2、beans.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">
<!-- 第一種根據index參數下标設定 -->
<!-- 第二種根據參數類型設定 -->
<!-- 第三種根據參數名字設定 -->
<bean id="user" class="com.yt.pojo.User">
<!-- <constructor-arg index="0" value="yt"/>-->
<!-- <constructor-arg type="java.lang.String" value="yt"/>-->
<constructor-arg name="name" value="yt"/>
</bean>
</beans>
3、測試
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//在執行getBean的時候, user已經建立好了 , 通過無參構造
User user = (User) context.getBean("user");
//調用對象的方法 .
user.show();
}
結論:在配置檔案加載的時候。其中管理的對象都已經初始化了!
3、Spring配置
3.1、 alias
别名
alias 設定别名 , 為bean設定别名 , 可以設定多個别名
<!--設定别名:在擷取Bean的時候可以使用别名擷取-->
<alias name="user" alias="user2"/>
3.2、 bean
Bean的配置
<!--bean就是java對象,由Spring建立和管理-->
<!-- id bean的唯一辨別符,相當于變量名,要唯一,如果沒有配置id,name就是預設辨別符
如果配置id,又配置了name,那麼name是别名
name可以設定多個别名,可以用逗号,分号,空格隔開
如果不配置id和name,可以根據applicationContext.getBean(.class)擷取對象;
class:bean對象所對應的全限定名=包名+類名
-->
<bean id="userT" class="com.yt.pojo.UserT" name="userT2,userT3">
</bean>
3.3 、import
import
一般用于團隊開發,他可以将多個配置檔案,導入合并為一個總的配置檔案
這樣使用的時候就隻需要調用總的配置檔案就行了
4、依賴注入
4.1、構造器注入
我們在之前的案例已經講過了
4.2、Set 注入 (重點)
概念
- 依賴注入(Dependency Injection,D)
- 依賴 : 指Bean對象的建立依賴于容器 . Bean對象的依賴資源 .
- 注入 : 指Bean對象所依賴的資源 , 由容器來設定和裝配 .
【環境搭建】
- 複雜類型
public class Address { private String address; public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } @Override public String toString() { return "Address{" + "address='" + address + '\'' + '}'; } }
- 真實測試對象
public class Student { private String name; private Address address; private String[] books; private List<String> hobbys; private Map<String,String> card; private Set<String> games; private String wife; private Properties info; }
- beans.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="student" class="com.yt.pojo.Student"> <!--第一種,普通值注入,直接value指派--> <property name="name" value="yt"/> <!--Bean注入,ref--> <property name="address" ref="address"/> <!--數組注入--> <property name="books"> <array> <value>西遊記</value> <value>水浒傳</value> <value>三國演義</value> </array> </property> <!--list注入--> <property name="hobbys"> <list> <value>聽歌</value> <value>敲代碼</value> <value>看電影</value> </list> </property> <!--map注入--> <property name="card"> <map> <entry key="身份證" value="111111222233334444"></entry> <entry key="銀行卡" value="1221232355664444000"></entry> </map> </property> <!--set注入--> <property name="games"> <set> <value>LOL</value> <value>CSGO</value> <value>GTA5</value> </set> </property> <!-- null值--> <property name="wife"> <null/> </property> <!--Properties注入--> <property name="info"> <props> <prop key="學号">2021-06-21</prop> <prop key="性别">男</prop> </props> </property> </bean> <bean id="address" class="com.yt.pojo.Address"> <property name="address" value="地球"/> </bean> </beans>
- 測試類
public class MyTest { @Test public void test(){ ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); Student student = (Student) context.getBean("student"); System.out.println(student.toString()); } } Student{ name='yt', address=Address{address='地球'}, books=[西遊記, 水浒傳, 三國演義], hobbys=[聽歌, 敲代碼, 看電影], card={ 身份證=111111222233334444, 銀行卡=1221232355664444000 }, games=[LOL, CSGO, GTA5], wife='null', info={學号=2021-06-21, 性别=男} }
4.3、拓展方式注入(C,P)
P命名空間
User.java【注意:這裡沒有有參構造器!】
public class User {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
P命名空間注入 : 需要在頭檔案中加入限制檔案
導入限制 : xmlns:p="http://www.springframework.org/schema/p"
userbeans.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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--p命名空間注入,可以直接注入屬性的值,類似于 property -->
<bean id="user" class="com.yt.pojo.User" p:name="yt" p:age="18"/>
</beans>
c 命名空間注入 : 需要在頭檔案中加入限制檔案
導入限制 : xmlns:c="http://www.springframework.org/schema/c"
發現問題:爆紅了,剛才我們沒有寫有參構造!
解決:把有參構造器加上,這裡也能知道,c 就是所謂的構造器注入!
userbeans.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:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--c命名空間注入,通過構造器(有參)注入,類似于 constructor-arg -->
<bean id="user2" class="com.yt.pojo.User" c:age="18" c:name="yt"/>
</beans>
4.4、Bean的作用域
在Spring中,那些組成應用程式的主體及由Spring IoC容器所管理的對象,被稱之為bean。簡單地講,bean就是由IoC容器初始化、裝配及管理的對象 .
幾種作用域中,request、session作用域僅在基于web的應用中使用(不必關心你所采用的是什麼web應用架構),隻能用在基于web的Spring ApplicationContext環境。
-
單例模式(Spring預設模式)
當一個bean的作用域為Singleton,那麼Spring IoC容器中隻會存在一個共享的bean執行個體,并且所有對bean的請求,隻要id與該bean定義相比對,則隻會傳回bean的同一執行個體。Singleton是單例類型,就是在建立起容器時就同時自動建立了一個bean的對象,不管你是否使用,他都存在了,每次擷取到的對象都是同一個對象。注意,Singleton作用域是Spring中的預設作用域。要在XML中将bean定義成singleton,可以這樣配置:
-
原型模式:每次從容器中取出(get)時,都會産生一個新對象。
當一個bean的作用域為Prototype,表示一個bean定義對應多個對象執行個體。Prototype作用域的bean會導緻在每次對該bean請求(将其注入到另一個bean中,或者以程式的方式調用容器的getBean()方法)時都會建立一個新的bean執行個體。Prototype是原型類型,它在我們建立容器的時候并沒有執行個體化,而是當我們擷取bean的時候才會去建立一個對象,而且我們每次擷取到的對象都不是同一個對象。根據經驗,對有狀态的bean應該使用prototype作用域,而對無狀态的bean則應該使用singleton作用域。在XML中将bean定義成prototype,可以這樣配置:
-
Request
當一個bean的作用域為Request,表示在一次HTTP請求中,一個bean定義對應一個執行個體;即每個HTTP請求都會有各自的bean執行個體,它們依據某個bean定義建立而成。該作用域僅在基于web的Spring ApplicationContext情形下有效。考慮下面bean定義:
針對每次HTTP請求,Spring容器會根據loginAction bean的定義建立一個全新的LoginAction bean執行個體,且該loginAction bean執行個體僅在目前HTTP request内有效,是以可以根據需要放心的更改所建執行個體的内部狀态,而其他請求中根據loginAction bean定義建立的執行個體,将不會看到這些特定于某個請求的狀态變化。當處理請求結束,request作用域的bean執行個體将被銷毀。
-
Session
當一個bean的作用域為Session,表示在一個HTTP Session中,一個bean定義對應一個執行個體。該作用域僅在基于web的Spring ApplicationContext情形下有效。考慮下面bean定義:
針對某個HTTP Session,Spring容器會根據userPreferences bean定義建立一個全新的userPreferences bean執行個體,且該userPreferences bean僅在目前HTTP Session内有效。與request作用域一樣,可以根據需要放心的更改所建立執行個體的内部狀态,而别的HTTP Session中根據userPreferences建立的執行個體,将不會看到這些特定于某個HTTP Session的狀态變化。當HTTP Session最終被廢棄的時候,在該HTTP Session作用域内的bean也會被廢棄掉。
5、Bean的自動裝配
- 自動裝配是Spring滿足Bean依賴的一種方式
- Spring會在上下文中自動尋找,并自動給Bean裝配屬性
在Spring中,有三種裝配的方式
- 在xml中顯示的配置
- 在java中顯示配置
- 隐式的自動裝配bean【重點】
Spring的自動裝配需要從兩個角度來實作,或者說是兩個操作:
- 元件掃描(component scanning):spring會自動發現應用上下文中所建立的bean;
- 自動裝配(autowiring):spring自動滿足bean之間的依賴,也就是我們說的IoC/DI;
元件掃描和自動裝配組合發揮巨大威力,使得顯示的配置降低到最少。
5.1、測試
1、建立一個項目
2、建立兩個實體類,Cat Dog 都有一個叫的方法
public class Cat {
public void shout() {
System.out.println("miao~");
}
}
public class Dog {
public void shout() {
System.out.println("wang~");
}
}
3、建立一個類 People
public class People {
private Cat cat;
private Dog dog;
private String name;
public Cat getCat() {
return cat;
}
public void setCat(Cat cat) {
this.cat = cat;
}
public Dog getDog() {
return dog;
}
public void setDog(Dog dog) {
this.dog = dog;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "People{" +
"cat=" + cat +
", dog=" + dog +
", name='" + name + '\'' +
'}';
}
}
4、編寫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:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="cat" class="com.yt.pojo.Cat"/>
<bean id="dog" class="com.yt.pojo.Dog"/>
<bean id="people" class="com.yt.pojo.People" p:name="yt" p:cat="cat" p:cat-ref="cat" p:dog="dog" p:dog-ref="dog"/>
</beans>
5、測試
public class MyTest {
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
People people = (People) context.getBean("people");
people.getCat().shout();
people.getDog().shout();
}
}
結果正常輸出,環境OK
5.2、ByName自動裝配
autowire byName (按名稱自動裝配)
由于在手動配置xml過程中,常常發生字母缺漏和大小寫等錯誤,而無法對其進行檢查,使得開發效率降低。
采用自動裝配将避免這些錯誤,并且使配置簡單化。
測試:
1、修改bean配置,增加一個屬性 autowire=“byName”
<!-- byName: 會在容器上下文中查找,和自己對象set方法後面的值對應的 beanId -->
<bean id="people" class="com.yt.pojo.People" p:name="yt" autowire="byName"/>
2、再次測試,結果依舊成功輸出!
3、我們将 cat 的bean id修改為 cat222
4、再次測試, 執行時報空指針java.lang.NullPointerException。因為按byName規則找不對應set方法,真正的setCat就沒執行,對象就沒有初始化,是以調用時就會報空指針錯誤。
小結:
當一個bean節點帶有 autowire byName的屬性時。
将查找其類中所有的set方法名,例如setCat,獲得将set去掉并且首字母小寫的字元串,即cat。
去spring容器中尋找是否有此字元串名稱id的對象。
如果有,就取出注入;如果沒有,就報空指針異常。
5.3、ByType自動裝配
使用autowire byType首先需要保證:同一類型的對象,在spring容器中唯一。如果不唯一,會報不唯一的異常。
NoUniqueBeanDefinitionException
測試:
1、将user的bean配置修改一下 : autowire=“byType”
<!-- byType: 會在容器上下文中查找,和自己對象屬性類型相同的bean (比對class) 全局唯一 -->
<bean id="people" class="com.yt.pojo.People" p:name="yt" autowire="byType"/>
2、測試,正常輸出
3、在注冊一個cat 的bean對象!
<bean id="dog" class="com.kuang.pojo.Dog"/>
<bean id="cat" class="com.kuang.pojo.Cat"/>
<bean id="cat2" class="com.kuang.pojo.Cat"/>
<bean id="user" class="com.kuang.pojo.User" autowire="byType">
<property name="str" value="qinjiang"/>
</bean>
4、測試,報錯:NoUniqueBeanDefinitionException
5、删掉cat2,将cat的bean名稱改掉!測試!因為是按類型裝配,是以并不會報異常,也不影響最後的結果。甚至将id屬性去掉,也不影響結果。
這就是按照類型自動裝配!
小結:
- byName:需要保證所有bean的id唯一,并且這個bean需要和自動注入的屬性的set方法的一緻
- byType:需要保證所有bean的class唯一,并且這個bean需要和自動注入的屬性的類型一緻
5.4、使用注解自動裝配
jdk1.5開始支援注解,spring2.5開始全面支援注解。
準備工作:利用注解的方式注入屬性。
1、導入限制
<?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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
</beans>
2、開啟屬性注解支援!
<context:annotation-config/>
@Autowired
- @Autowired是按類型自動轉配的,不支援id比對。
- 需要導入 spring-aop的包!
- 直接在屬性上使用即可!也可以在set方式上使用
- 使用Autowired我們可以不用編寫set方法了,前提是你這個自動裝配的屬性在IOC(Spring)容器中尋找,切符合名字byname
- 如果顯示的定義了Autowired的required屬性為false,就說明這個對象可以為null
- @Nullable 字段标記了這個注解,說明這個字段可以為空
-
@Autowired private Cat cat; @Autowired @Qualifier(value = "dog222") private Dog dog; private String name;
- 如果Autowired自動裝配的環境比較複雜的時候,有多個對象,自動裝配無法通過一個注解【Autowired】完成的時候,我們可以使用**@Qualifier**(value = “beanid”)去配置Autowired使用,指定一個唯一的Bean注入對象
拓展:@Resource(java注解)
- @Resource如有指定的name屬性,先按該屬性進行byName方式查找裝配;
- 其次再進行預設的byName方式進行裝配;
- 如果以上都不成功,則按byType的方式自動裝配。
- 都不成功,則報異常。
- @Resource(name = “beanid”)在配置環境複雜的時候,也可以去指定一個唯一的Bean注入對象,等同于@Qualifier(value = “beanid”),不過@Resource使用較少
@Resource與@Autowired差別
- 都是用來自動裝配的。都可以寫在字段上,或寫在setter方法上。
- @Autowired 是通過byType的方式實作,而且必須要求這個對象存在,否則報錯
- @Resource預設通過byName方式實作,如果找不到名字,則通過byType方式實作,都找不到,則報錯
- 執行順序不同:@Autowired通過byType實作,@Resource預設使用byName方式實作
6、使用注解開發
- 導入限制,開啟注解支援
<?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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--指定要掃描的包,這個報下的注解就會生效--> <context:component-scan base-package="com.yt.pojo"/> <context:annotation-config/> </beans>
- @Component
- @Component:元件,放在類上,說明這個類被Spring管理了,就是Bean,不用再去xml檔案配置!
-
//等同于<bean id="user" class="com.yt.pojo.User"/> @Component public class User { public String name="yt"; }
- 屬性注解@Value
- 可以放在屬性上或者屬性的set方法上
-
//等同于:<property name="name" value="yt"/> @Value("yt") public String name;
-
衍生的注解
@Component有幾個衍生的注解,在web開發中,會按照MVC三層架構分層
- dao :【@Repository】
- service :【@Service】
- controller :【@Controller】
- 作用域 @Scope
-
小結 :
xml與注解
- xml更加萬能,适用于任何場合,維護簡單友善
- 注解不是自己類使用不了,維護相對複雜
- xml用來管理bean
- 注解隻負責屬性的注入
- 在使用的過程中,隻需要注意一個問題,必須讓注解生效,就需要開啟注解的支援
7、使用java的方式配置Spring
實體類
@Component
public class User {
@Value("yt")
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
appConfig配置類
//這個也會被Spring容器接管,注冊到容器中,因為他本身就是一個@Component
//@Configuration代表這是一個配置類,等同行與之前的beans.xml
//@ComponentScan 指定要掃描的包,這個包下的注解就會生效
//@ComponentScan 等同于<context:component-scan base-package="com.yt.pojo"/>
//@Import 引入别的配置類
@Configuration
@ComponentScan("com.yt.pojo")
public class YtConfig {
// 注冊一個bean,等同于之前的bean标簽
// 方法的名字相當于bean标簽中的id
// 這個方法中傳回值相當于bean标簽中的class
@Bean
public User getUser(){
//傳回要注入到bean的對象
return new User();
}
}
小結:
這種純java的配置方式,在SpringBoot中随處可見
8、代理模式
為什麼要學習代理模式,因為AOP的底層機制就是動态代理!
代理模式:
- 靜态代理
- 動态代理
學習AOP之前,我們先要了解一下代理模式
8.1、靜态代理
角色分析:
- 抽象角色:一般會使用接口或者抽象類來解決 (房源)
- 真實角色:被代理的角色 (房東)
- 代理角色:代理真實角色,代理真實角色後,我們一般會做一些附屬操作 (尋找房源之類)
- 客戶:通路代理對象的人 (有租房需求的人)
代碼實作
Rent.java
即抽象角色
//抽象角色:租房
public interface Rent {
/**
* 租房
*/
public void rent();
}
Host.java
即真實角色
//真實角色:房東,房東要出租房子
public class Host implements Rent{
@Override
public void rent() {
System.out.println("房東要出租房子了");
}
}
Proxy.java
即代理角色
//代理角色:中介
public class Proxy implements Rent{
private Host host;
public Proxy() {
}
public Proxy(Host host) {
this.host = host;
}
@Override
public void rent() {
seeHouse();
heTOng();
host.rent();
fare();
}
public void seeHouse(){
System.out.println("中介帶你看房");
}
public void fare(){
System.out.println("收中介費");
}
public void heTOng(){
System.out.println("簽租憑合同");
}
}
Client.java
即客戶
//客戶類,一般客戶都會去找代理!
public class Client {
public static void main(String[] args) {
//房東要租房
Host host = new Host();
//中介幫助房東
Proxy proxy = new Proxy(host);
//你去找中介!
proxy.rent();
}
}
分析:在這個過程中,你直接接觸的就是中介,就如同現實生活中的樣子,你看不到房東,但是你依舊租到了房東的房子通過代理,這就是所謂的代理模式,程式源自于生活,是以學程式設計的人,一般能夠更加抽象的看待生活中發生的事情。
靜态代理的好處:
- 可以使得我們的真實角色更加純粹 . 不再去關注一些公共的事情 .
- 公共的業務由代理來完成 . 實作了業務的分工 ,
- 公共業務發生擴充時變得更加集中和友善 .
缺點:
- 一個真實角色,就會産生一個代理角色,代碼量會翻倍,開發效率變低
我們想要靜态代理的好處,又不想要靜态代理的缺點,是以 , 就有了動态代理
8.2、 靜态代理再了解
練習步驟:
1、建立一個抽象角色,比如咋們平時做的使用者業務,抽象起來就是增删改查!
//抽象角色:增删改查業務
public interface UserService {
void add();
void delete();
void update();
void query();
}
2、我們需要一個真實對象來完成這些增删改查操作
//真實對象,完成增删改查操作的人
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 query() {
System.out.println("查詢了一個使用者");
}
}
3、需求來了,現在我們需要增加一個日志功能,怎麼實作!
- 思路1 :在實作類上增加代碼 【麻煩!】
- 思路2:使用代理來做,能夠不改變原來的業務情況下,實作此功能就是最好的了!
4、設定一個代理類來處理日志!代理角色
//代理角色,在這裡面增加日志的實作
public class UserServiceProxy implements UserService{
private UserServiceImpl userService;
public void setUserService(UserServiceImpl userService) {
this.userService = userService;
}
@Override
public void add() {
log("add");
userService.add();
}
@Override
public void delete() {
log("delete");
userService.delete();
}
@Override
public void update() {
log("update");
userService.update();
}
@Override
public void query() {
log("query");
userService.query();
}
public void log(String msg){
System.out.println("執行了"+msg+"方法");
}
}
5、測試通路類:
public class Client {
public static void main(String[] args){
//真實業務
UserServiceImpl userService = new UserServiceImpl();
//代理類
UserServiceProxy userServiceProxy = new UserServiceProxy();
//使用代理類實作日志功能!
userServiceProxy.setUserService(userService);
userServiceProxy.add();
}
}
OK,到了現在代理模式大家應該都沒有什麼問題了,重點大家需要了解其中的思想;
我們在不改變原來的代碼的情況下,實作了對原有功能的增強,這是AOP中最核心的思想
聊聊AOP:縱向開發,橫向開發
8.3、動态代理模式
- 動态代理的角色和靜态代理的一樣 .
- 動态代理的代理類是動态生成的 . 靜态代理的代理類是我們提前寫好的
- 動态代理分為兩類 : 一類是基于接口動态代理 , 一類是基于類的動态代理
- 基于接口的動态代理----JDK動态代理
- 基于類的動态代理–cglib
- 現在用的比較多的是 javasist 來生成動态代理 . 百度一下javasist
- 我們這裡使用JDK的原生代碼來實作,其餘的道理都是一樣的!
JDK的動态代理需要了解兩個類
核心 :
InvocationHandler
和
Proxy
, 打開JDK幫助文檔看看
【InvocationHandler:調用處理程式】
【Proxy : 代理】
代碼實作
抽象角色和真實角色和之前的一樣!
//抽象角色:租房
public interface Rent {
/**
* 租房
*/
public void rent();
}
Host.java
即真實角色
//真實角色:房東,房東要出租房子
public class Host implements Rent{
@Override
public void rent() {
System.out.println("房東要出租房子了");
}
}
ProxyInvocationHandler.java
即代理角色
public class ProxyInvocationHandler implements InvocationHandler {
private Rent rent;
public void setRent(Rent rent) {
this.rent = rent;
}
//生成代理類,重點是第二個參數,擷取要代理的抽象角色!之前都是一個角色,現在可以代理一類角色
public Object getProxy() {
return Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(), this);
}
// proxy : 代理類
//method : 代理類的調用處理程式的方法對象.
// 處理代理執行個體上的方法調用并傳回結果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
seeHouse();
//核心:本質利用反射實作!
Object result = method.invoke(rent, args);
fare();
return result;
}
public void seeHouse(){
System.out.println("中介帶你看房");
}
public void fare(){
System.out.println("收中介費");
}
public void heTOng(){
System.out.println("簽租憑合同");
}
}
Client.java
public class Client {
public static void main(String[] args){
//真實角色
Host host = new Host();
//代理執行個體的調用處理程式
ProxyInvocationHandler pih = new ProxyInvocationHandler();
//将真實角色放置進去!
pih.setRent(host);
//動态生成對應的代理類!
Rent proxy = (Rent)pih.getProxy();
proxy.rent();
}
}
核心:一個動态代理 , 一般代理某一類業務 , 一個動态代理可以代理多個類,代理的是接口!
8.4、深化了解
我們來使用動态代理實作代理我們後面寫的UserService!
我們也可以編寫一個通用的動态代理實作的類!所有的代理對象設定為Object即可!
public class ProxyInvocationHandler implements InvocationHandler {
private Object target;
public void setTarget(Object target) {
this.target = target;
}
//生成代理類,重點是第二個參數,擷取要代理的抽象角色!之前都是一個角色,現在可以代理一類角色
public Object getProxy() {
return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
// proxy : 代理類
//method : 代理類的調用處理程式的方法對象.
// 處理代理執行個體上的方法調用并傳回結果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log(method.getName());
//核心:本質利用反射實作!
Object result = method.invoke(target, args);
return result;
}
public void log(String methodName) {
System.out.println("執行了" + methodName + "方法");
}
}
測試
public class Client {
public static void main(String[] args){
UserServiceImpl userService = new UserServiceImpl();
ProxyInvocationHandler pih = new ProxyInvocationHandler();
pih.setTarget(userService);
UserService proxy = (UserService) pih.getProxy();
proxy.add();
}
}
測試,增删改查,檢視結果!
動态代理的好處
靜态代理有的它都有,靜态代理沒有的,它也有!
- 可以使得我們的真實角色更加純粹 . 不再去關注一些公共的事情 .
- 公共的業務由代理來完成 . 實作了業務的分工 ,
- 公共業務發生擴充時變得更加集中和友善 .
- 一個動态代理 , 一般代理某一類業務
- 一個動态代理可以代理多個類,代理的是接口!
9、AOP
9.1、什麼是aop
AOP(Aspect Oriented Programming)意為:面向切面程式設計,通過預編譯方式和運作期動态代理實作程式功能的統一維護的一種技術。AOP是OOP的延續,是軟體開發中的一個熱點,也是Spring架構中的一個重要内容,是函數式程式設計的一種衍生範型。利用AOP可以對業務邏輯的各個部分進行隔離,進而使得業務邏輯各部分之間的耦合度降低,提高程式的可重用性,同時提高了開發的效率。
9.2、aop在spring中的作用
提供聲明式事務;允許使用者自定義切面
以下名詞需要了解下:
- 橫切關注點:跨越應用程式多個子產品的方法或功能。即是,與我們業務邏輯無關的,但是我們需要關注的部分,就是橫切關注點。如日志 , 安全 , 緩存 , 事務等等 …
- 切面(ASPECT):橫切關注點 被子產品化 的特殊對象。即,它是一個類。
- 通知(Advice):切面必須要完成的工作。即,它是類中的一個方法。
- 目标(Target):被通知對象。
- 代理(Proxy):向目标對象應用通知之後建立的對象。
- 切入點(PointCut):切面通知 執行的 “地點”的定義。
- 連接配接點(JointPoint):與切入點比對的執行點。
SpringAOP中,通過Advice定義橫切邏輯,Spring中支援5種類型的Advice:
即 Aop 在 不改變原有代碼的情況下 , 去增加新的功能 .
9.3、 使用spring實作aop
使用AOP織入,需要導入一個依賴包【重點】
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
</dependency>
方式一:使用Spring的API接口
首先編寫我們的業務接口和實作類
public interface UserService {
void add();
void delete();
void update();
void select();
}
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 select() {
System.out.println("查詢了一個使用者");
}
}
然後去寫我們的增強類 , 我們編寫兩個 , 一個前置增強 一個後置增強
public class Log implements MethodBeforeAdvice {
//method : 要執行的目标對象的方法
//objects : 被調用的方法的參數
//target : 目标對象
@Override
public void before(Method method, Object[] objects, Object target) throws Throwable {
System.out.println(target.getClass().getName()+"的"+method.getName()+"被執行了!");
}
}
public class AfterLog implements AfterReturningAdvice {
//returnValue 傳回值
//method被調用的方法
//args 被調用的方法的對象的參數
//target 被調用的目标對象
@Override
public void afterReturning( Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("執行了" + target.getClass().getName()
+"的"+method.getName()+"方法,"
+"傳回值:"+returnValue);
}
}
最後去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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userService" class="com.yt.service.UserServiceImpl"/>
<bean id="log" class="com.yt.log.Log"/>
<bean id="afterLog" class="com.yt.log.AfterLog"/>
<!--在頭檔案導入Aop限制
方式一:使用原生的Spring API接口-->
<aop:config>
<!--切入點 expression:表達式 expression(要執行的位置):-->
<aop:pointcut id="pointcut" expression="execution(* com.yt.service.UserServiceImpl.*())"/>
<!--執行環繞增加-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
</beans>
定義切點:
expression="execution(* com.iandf.service.UserServiceImpl.*(..))
對這個表達式分析:
- 表示傳回值的方法
- com.iandf.service.UserServiceImpl.* 表示UserServiceImpl類中的任意方法
- (…)表示任意參數
測試
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//動态代理代理的是接口
UserService userService = (UserService) context.getBean("userService");
userService.add();
}
Aop的重要性 : 很重要 . 一定要了解其中的思路 , 主要是思想的了解這一塊 .
Spring的Aop就是将公共的業務 (日志 , 安全等) 和領域業務結合起來 , 當執行領域業務時 , 将會把公共業務加進來 . 實作公共業務的重複利用 . 領域業務更純粹 , 程式猿專注領域業務 , 其本質還是動态代理 .
方式二:使用自定義類實作AOP【切面定義】
第一步 : 寫我們自己的一個切入類
public class DiyPointcut {
public void before() {
System.out.println("====方法執行前====");
}
public void after() {
System.out.println("====方法執行後====");
}
}
去spring中配置
<!--第二種方式自定義實作-->
<!--注冊bean-->
<bean id="diy" class="com.yt.diy.DiyPointcut"/>
<!--aop的配置-->
<aop:config>
<!--第二種方式:使用AOP的标簽實作-->
<aop:aspect ref="diy">
<aop:pointcut id="diyPointcut" expression="execution(* com.yt.service.UserServiceImpl.*())"/>
<aop:before method="before" pointcut-ref="diyPointcut"/>
<aop:after method="after" pointcut-ref="diyPointcut"/>
</aop:aspect>
</aop:config>
測試:
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//動态代理代理的是接口
UserService userService = (UserService) context.getBean("userService");
userService.add();
}
方式三:使用注解實作AOP
第一步:編寫一個注解實作的增強類
@Aspect
public class AnnotationPointCut {
@Before("execution(* com.yt.service.UserServiceImpl.*())")
public void before() {
System.out.println("====方法執行前====");
}
@After("execution(* com.yt.service.UserServiceImpl.*())")
public void after() {
System.out.println("====方法執行後====");
}
//在環繞增強中,我們可以給定一個參數,代表我們要擷取處理切入的點
@Around("execution(* com.yt.service.UserServiceImpl.*())")
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("環繞前");
System.out.println("簽名:"+jp.getSignature());
//執行目标方法proceed
Object proceed = jp.proceed();
System.out.println("環繞後");
System.out.println(proceed);
}
}
第二步:在Spring配置檔案中,注冊bean,并增加支援注解的配置
<!--第三種方式:注解實作-->
<bean id="annotationPointcut" class="com.yt.diy.AnnotationPointCut"/>
<!--開啟注解支援-->
<aop:aspectj-autoproxy/>
aop:aspectj-autoproxy:說明
- 通過aop命名空間的<aop:aspectj-autoproxy />聲明自動為spring容器中那些配置@aspectJ切面的bean建立代理,織入切面。當然,spring 在内部依舊采用AnnotationAwareAspectJAutoProxyCreator進行自動代理的建立工作,但具體實作的細節已經被<aop:aspectj-autoproxy />隐藏起來了
- <aop:aspectj-autoproxy />有一個proxy-target-class屬性,預設為false,表- 示使用jdk動态代理織入增強,當配為<aop:aspectj-autoproxy poxy-target-class=“true”/>時,表示使用CGLib動态代理技術織入增強。不過即使proxy-target-class設定為false,如果目标類沒有聲明接口,則spring将自動使用CGLib動态代理。
10、整合MyBatis
步驟:
1、導入相關的jar包
- junit
- mybatis
- mysql資料庫
- spring相關的
- aop織入
- mybatis-spring 【new】
2、編寫配置檔案
3、測試
10.1、回憶MyBatis
- 編寫實體類
@Data public class User { private int id; private String name; private String pwd; }
- 編寫核心配置檔案
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <properties resource="db.properties"/> <typeAliases> <package name="com.yt.pojo"/> </typeAliases> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments> <mappers> <!--注冊mapper--> <mapper class="com.yt.dao.UserMapper"/> </mappers> </configuration>
- 編寫接口
public interface UserMapper { List<User> getUserList(); }
- 編寫Mapper.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.yt.dao.UserMapper"> <select id="getUserList" resultType="user"> select * from mybatis.user </select> </mapper>
- 測試
@Test public void test(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> userList = mapper.getUserList(); for (User user : userList) { System.out.println("user = " + user); } }
10.2、MyBatis-Spring
10.2.1、 使用方式一 基于SqlSessionTemplate
1、編寫實體類User
2、在spring-dao.xml配置檔案中配置資料源 這裡使用的資料源是spring-jdbc依賴下的DriverManagerDataSource。也可以使用dbcp c3po druid
<!--DataSource : 使用Spring的資料源替換MyBatis的配置
我們這裡使用spring提供的JDBC
-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf8&useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
3、編寫UserMapper和UserMapper.xml 和原生開發一樣
4、在spring-dao.xml配置檔案中建立SqlSessionFactory
<!--sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!--綁定mybatis配置檔案-->
<property name="configLocation" value="mybatis-config.xml"/>
<property name="mapperLocations" value="com/yt/dao/*.xml"/>
</bean>
5、在spring-dao.xml配置檔案中建立SqlSessionTemplate
<!--SqlSessionTemplate:就是我們使用的sqlSession -->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<!--隻能使用構造器注入-->
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
6、編寫UserMapper實作類
public class UserMapperImpl implements UserMapper{
//原來我們所有的操作,都是用sqlSession來執行,現在是SqlSessionTemplate
private SqlSessionTemplate sqlSession;
public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}
@Override
public List<User> getUserList() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return mapper.getUserList();
}
}
7、将剛剛寫好的spring-dao.xml和UserMapper實作類整合到項目的配置檔案中
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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<import resource="spring-dao.xml"/>
<bean id="userMapper" class="com.yt.dao.UserMapperImpl">
<property name="sqlSession" ref="sqlSession"/>
</bean>
</beans>
8、測試
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
List<User> userList = userMapper.getUserList();
for (User user : userList) {
System.out.println("user = " + user);
}
}
10.2.2 、spring整合mybatis開發和原生mybatis開發的差別
- 在基礎的 MyBatis 用法中,是通過 SqlSessionFactoryBuilder 來建立 SqlSessionFactory 的。而在 MyBatis-Spring 中,則使用 SqlSessionFactoryBean 來建立。
- 在 MyBatis 中,你可以使用 SqlSessionFactory 來建立 SqlSession。一旦你獲得一個 session 之後,你可以使用它來執行映射了的語句,送出或復原連接配接,最後,當不再需要它的時候,你可以關閉 session。而在spring中,使用sqlSessionFactory來建立sqlSessionTemplate,你可以使用它來執行映射了的語句。
- SqlSessionTemplate 是 MyBatis-Spring 的核心。作為 SqlSession 的一個實作,這意味着可以使用它無縫代替你代碼中已經在使用的 SqlSession。模闆可以參與到 Spring 的事務管理中,并且由于其是線程安全的,可以供多個映射器類使用,你應該總是用 SqlSessionTemplate 來替換 MyBatis 預設的 DefaultSqlSession 實作。在同一應用程式中的不同類之間混雜使用可能會引起資料一緻性的問題。
- spring需要提供一個UserMapper接口的實作類,實作類組合了一個SqlSessionTemplate對象,有這個對象來對事務進行統一的管理
10.2.3、 使用方式二 基于SqlSessionDaoSupport
步驟:
1、編寫實體類User
2、在spring-dao.xml配置檔案中配置資料源
3、編寫UserMapper和UserMapper.xml 和原生開發一樣
4、在spring-dao.xml配置檔案中建立SqlSessionFactory
5、編寫UserMapper實作類并繼承SqlSessionDaoSupport 不用編寫SqlSessionTemplate
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper {
@Override
public List<User> getUserList() {
return getSqlSession().getMapper(UserMapper.class).getUserList();
}
}
6、在applicationContext.xml中注冊UserMapperImpl2 的bean
<bean id="userMapper2" class="com.yt.dao.UserMapperImpl2">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
7、測試方法
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper userMapper = context.getBean("userMapper2", UserMapper.class);
List<User> userList = userMapper.getUserList();
for (User user : userList) {
System.out.println("user = " + user);
}
}
11、聲明式事務
11.1、回顧事務
- 要麼都成功,要麼都失敗
- 事務在項目開發中,十分重要,涉及到資料的一緻性問題,不能馬虎
- 確定完整性和一緻性
11.2、Spring中的事務管理
事務管理分類:
- 聲明式事務:使用aop織入事務,對事務進行管理
- 程式設計式事務:在代碼中顯示的執行事務
聲明式事務:AOP【推薦】
使用aop織入事務
xmlns:tx="http://www.springframework.org/schema/tx"
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
1、配置聲明式事務
<!-- 配置聲明式事務-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
2、配置事務通知 需要配置tx命名空間
<!-- 結合AOP實作事務的織入-->
<!-- 配置事務通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- 給那些方法配置事物-->
<!-- 配置事務的傳播特性: new propagation-->
<tx:attributes>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
3、配置切入點
<!-- 配置事物切入-->
<aop:config>
<aop:pointcut id="txPointCut" expression="execution(* com.yt.dao.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>
程式設計式事務:需要在代碼中,進行事物的管理 【不建議使用】
為什麼需要事務?
- 如果不配置事務,會出現資料送出不一緻的情況
- 如果我們不在Spring中去配置聲明式事務,我們就需要在代碼中手動配置事務
- 事務在項目的開發中,十分重要,涉及到資料的一緻性和完整性的問題,不容馬虎