視頻:https://www.bilibili.com/video/BV1WE411d7Dv
1、Spring
1.1、簡介
spring官網: https://spring.io/projects/spring-framework#overview
官方下載: https://repo.spring.io/release/org/springframework/spring/
GitHub: https://github.com/spring-projects/spring-framework
Spring Web MVC: spring-webmvc最新版
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
1.2、優點
- Spring是一個開源的免費框架(容器)!
- Spring是一個輕量級的非入侵式的框架
- 控制反轉(IOC),面向切面編程(AOP)!
- 支持事務的處理,對框架整合的支持
開源免費容器,輕量級非侵入式,控制反轉,面向切面,支持事務,支持框架整合
Spring是一個輕量級的控制反轉(IOC)和面向切面(AOP)編程的框架
1.3、組成
1.4、擴展
現代化的java開發 -> 基於Spring的開發
2、IOC理論推導
傳統的調用
-
UserDao
package dao; public interface UserDao { void getUser(); }
-
UserDaoImp
package dao; public class UserDaoImpl implements UserDao{ public void getUser() { System.out.println("默認獲取用戶數據"); } }
-
UserSevice
package Service; public interface UserService { void getUser(); }
-
UserServiceImp
package Service; import dao.UserDao; import dao.UserDaoImpl; public class UserServiceImpl implements UserService{ UserDao userDao = new UserDaoImpl(); public void getUser(){ userDao.getUser(); } }
測試
package holle0;
import Service.UserService;
import Service.UserServiceImpl;
public class MyTest0 {
public static void main(String[] args) {
// 用戶實際調用的是業務層,dao層他們不需要接觸
UserService userService = new UserServiceImpl();
userService.getUser();
}
}
在我們之前的業務中,用戶的需求可能會影響我們原來的代碼,我們需要根據用戶的需求去修改原代碼!如果程序代碼量十分大,修改一次的成本代價十分昂貴!
改良:我們使用一個Set接口實現。已經發生了革命性的變化!
//在Service層的實現類(UserServiceImpl)增加一個Set()方法
//利用set動態實現值的注入!
private UserDao userDao;
public void setUserDao(UserDao userDao){
this.userDao = userDao;
}
set() 方法實際上是動態改變了 UserDao userDao 的 初始化部分(new UserDaoImpl())
測試中加上
((UserServiceImpl)userService).setUserDao(new UserDaoImpl());
- 之前,程序是主動創建對象!控制權在程序猿手上!
- 使用了set注入后,程序不再具有主動性,而是變成了被動的接受對象!(主動權在客戶手上)
本質上解決了問題,程序員不用再去管理對象的創建
系統的耦合性大大降低,可以更專注在業務的實現上
這是IOC(控制反轉)的原型,反轉(理解):主動權交給了用戶
IOC本質
3、HolleSpring
在父模塊中導入jar包
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
pojo的Hello.java
package pojo;
public class Hello {
private String str;
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
@Override
public String toString() {
return "Holle [str=" + str + "]";
}
}
在resource里面的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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--在Spring中創建對象,在Spring這些都稱為bean
類型 變量名 = new 類型();
Holle holle = new Holle();
bean = 對象(holle)
id = 變量名(holle)
class = new的對象(new Holle();)
property 相當於給對象中的屬性設值,讓str="Spring"
-->
<bean id="hello" class="pojo.Hello">
<property name="str" value="Spring"/>
</bean>
</beans>
測試類MyTest
package holle1;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import pojo.Hello;
public class MyTest {
public static void main(String[] args) {
//獲取Spring的上下文對象
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//我們的對象下能在都在spring·中管理了,我們要使用,直接取出來就可以了
Hello holle = (Hello) context.getBean("hello");
System.out.println(holle.toString());
}
}
核心用set注入,所以必須要有下面的se()方法
//Hello類
public void setStr(String str) {
this.str = str;
}
思考:
IOC:對象由Spring 來創建,管理,裝配!
彈幕評論里面的理解:
原來這套程序是:你寫好菜單買好菜,客人來了自己把菜炒好招待,就相當於你請人吃飯
現在這套程序是:你告訴樓下餐廳,你要哪些菜,客人來的時候,餐廳把做好的你需要的菜送上來
IoC:炒菜這件事,不再由你自己來做,而是委托給了第三方__餐廳來做
此時的區別就是,如果我還需要做其他的菜,我不需要自己搞菜譜買材料再做好,而是告訴餐廳,我要什么菜,什么時候要,你做好送來
.
在前面第一個module試試引入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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userDaomSql" class="dao.UserDaoMysqlImpl"></bean>
<bean id="userServiceImpl" class="service.UserServiceImp">
<!--ref引用spring中已經創建很好的對象-->
<!--value是一個具體的值,基本數據類型-->
<property name="userDao" ref="userDaomSql"/>
</bean>
</beans>
第一個module改良后測試
package holle0;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import service.UserServiceImpl;
public class MyTest0 {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserServiceImpl userServiceImpl = (UserServiceImpl) context.getBean("userServiceImpl");
userServiceImpl.getUser();
}
}
總結:
所有的類都要裝配的beans.xml 里面;
所有的bean 都要通過容器去取;
容器里面取得的bean,拿出來就是一個對象,用對象調用方法即可;
4、IOC創建對象的方式
- 使用無參構造創建對象,默認。
- 使用有參構造(如下)
下標賦值
index指的是有參構造中參數的下標,下標從0開始;
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user" class="pojo.User">
<constructor-arg index="0" value="chen"/>
</bean>
</beans>
類型賦值(不建議使用)
<bean id="user" class="pojo.User">
<constructor-arg type="java.lang.String" value="kuang"/>
</bean>
直接通過參數名(掌握)
<bean id="user" class="pojo.User">
<constructor-arg name="name" value="kuang"></constructor-arg>
</bean>
<!-- 比如參數名是name,則有name="具體值" -->
注冊bean之后就對象的初始化了(類似 new 類名())
彈幕評論:
name方式還需要無參構造和set方法,index和type只需要有參構造
就算是new 兩個對象,也是只有一個實例(單例模式:全局唯一)
User user = (User) context.getBean("user");
User user2 = (User) context.getBean("user");
system.out.println(user == user2)//結果為true
總結:在配置文件加載的時候,容器(< bean>)中管理的對象就已經初始化了
5、Spring配置
5.1、別名
<bean id="user" class="pojo.User">
<constructor-arg name="name" value="chen"></constructor-arg>
</bean>
<alias name="user" alias="userLove"/>
<!-- 使用時
User user2 = (User) context.getBean("userLove");
-->
5.2、Bean的配置
<!--id:bean的唯一標識符,也就是相當於我們學的對象名
class:bean對象所對應的會限定名:包名+類型
name:也是別名,而且name可以同時取多個別名 -->
<bean id="user" class="pojo.User" name="u1 u2,u3;u4">
<property name="name" value="chen"/>
</bean>
<!-- 使用時
User user2 = (User) context.getBean("u1");
-->
5.3、import
import一般用於團隊開發使用,它可以將多個配置文件,導入合並為一個
假設,現在項目中有多個人開發,這三個人復制不同的類開發,不同的類需要注冊在不同的bean中,我們可以利
用import將所有人的beans.xml合並為一個總的!
-
張三(beans.xm1)
-
李四(beans2.xm1)
-
王五(beans3.xm1)
-
applicationContext.xml
<import resource="beans.xm1"/> <import resource="beans2.xml"/> <import resource="beans3.xm1"/>
使用的時候,直接使用總的配置就可以了
彈幕評論:
按照在總的xml中的導入順序來進行創建,后導入的會重寫先導入的,最終實例化的對象會是后導入xml中的那個
6、依賴注入(DI)
6.1、構造器注入
第4點有提到
6.2、set方式注入【重點】
依賴注入:set注入!
- 依賴:bean對象的創建依賴於容器
- 注入:bean對象中的所有屬性,由容器來注入
【環境搭建】
-
復雜類型
Address類
-
真實測試對象
Student類
-
beans.xml
-
測試
MyTest3
Student類
package pojo;
import java.util.*;
@Get
@Set
public class Student {
//別忘了寫get和set方法(用lombok注解也行)
private String name;
private Address address;
private String[] books;
private List<String> hobbies;
private Map<String, String> card;
private Set<String> game;
private Properties infor;
private String wife;
@Override
public String toString() {
return "Student{" +"\n"+
"name='" + name + '\'' +"\n"+
", address=" + address.toString() +"\n"+
", books=" + Arrays.toString(books) +"\n"+
", hobbies=" + hobbies +"\n"+
", card=" + card +"\n"+
", game=" + game +"\n"+
", infor=" + infor +"\n"+
", wife='" + wife + '\'' +"\n"+
'}';
}
}
Address類
package pojo;
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 + '\'' +
'}';
}
}
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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="address" class="pojo.Address">
<property name="address" value="address你好" />
</bean>
<bean id="student" class="pojo.Student">
<!--第一種,普通值注入 -->
<property name="name" value="name你好" />
<!--第二種,ref注入 -->
<property name="address" ref="address" />
<!--數組注入 -->
<property name="books">
<array>
<value>三國</value>
<value>西游</value>
<value>水滸</value>
</array>
</property>
<!--list列表注入 -->
<property name="hobbies">
<list>
<value>唱</value>
<value>跳</value>
<value>rap</value>
<value>籃球</value>
</list>
</property>
<!--map鍵值對注入 -->
<property name="card">
<map>
<entry key="username" value="root" />
<entry key="password" value="root" />
</map>
</property>
<!--set(可去重)注入 -->
<property name="game">
<set>
<value>wangzhe</value>
<value>lol</value>
<value>galname</value>
</set>
</property>
<!--空指針null注入 -->
<property name="wife">
<null></null>
</property>
<!--properties常量注入 -->
<property name="infor">
<props>
<prop key="id">20200802</prop>
<prop key="name">cbh</prop>
</props>
</property>
</bean>
</beans>
MyTest3
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import pojo.Student;
public class MyTest3 {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Student stu = (Student) context.getBean("student");
System.out.println(stu.toString());
}
}
6.3、拓展注入
官方文檔位置
pojo增加User類
package pojo;
public class User {
private String name;
private int id;
public User() {
}
public User(String name, int id) {
super();
this.name = name;
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Override
public String toString() {
return "User [name=" + name + ", id=" + id + "]";
}
}
注意: beans 里面加上這下面兩行
使用p和c命名空間需要導入xml約束
xmlns:p=“http://www.springframework.org/schema/p”
xmlns:c=“http://www.springframework.org/schema/c”
?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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--p命名空間注入/set注入,可以直接注入屬性的值-》property-->
<bean id="user" class="pojo.User" p:name="cxk" p:id="20" >
</bean>
<!--c命名空間,通過構造器注入,需要寫入有參和無參構造方法-》construct-args-->
<bean id="user2" class="pojo.User" c:name="cbh" c:id="22"></bean>
</beans>
測試
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
User user = context.getBean("user",User.class);//確定class對象,就不用再強轉了
System.out.println(user.toString());
6.4、Bean作用域
-
單例模式(默認)
<bean id="user2" class="pojo.User" c:name="cxk" c:age="19" scope="singleton"></bean>
彈幕評論:單例模式是把對象放在pool中,需要再取出來,使用的都是同一個對象實例
-
原型模式: 每次從容器中get的時候,都產生一個新對象!
<bean id="user2" class="pojo.User" c:name="cxk" c:age="19" scope="prototype"></bean>
- 其余的request、session、application這些只能在web開放中使用!
7、Bean的自動裝配
- 自動裝配是Spring滿足bean依賴的一種方式
- Spring會在上下文自動尋找,並自動給bean裝配屬性
在Spring中有三種裝配的方式
-
在xml中顯示配置
-
在java中顯示配置
-
隱式的自動裝配bean 【重要】
-
環境搭建:一個人有兩個寵物
-
byType自動裝配:byType會自動查找,和自己對象set方法參數的類型相同的bean
保證所有的class唯一(類為全局唯一)
-
byName自動裝配:byName會自動查找,和自己對象set對應的值對應的id
保證所有id唯一,並且和set注入的值一致
<!-- 找不到id和多個相同class --> <bean id="cat1" class="pojo.Cat"/> <bean id="cat2" class="pojo.Cat"/> 找不到 id=cat,且有兩個Cat
7.1測試:自動裝配
pojo的Cat類
public class Cat {
public void shut(){
System.out.println("miao");
}
}
pojo的Dog類
public class Dog {
public void shut(){
System.out.println("wow");
}
}
pojo的People類
package pojo;
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 + '\'' +
'}';
}
}
xml配置 -> byType 自動裝配
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="cat" class="pojo.Cat"/>
<bean id="dog" class="pojo.Dog"/>
<!--byType會在容器自動查找,和自己對象屬性相同的bean
例如,Dog dog; 那么就會查找pojo的Dog類,再進行自動裝配
-->
<bean id="people" class="pojo.People" autowire="byType">
<property name="name" value="cbh"></property>
</bean>
</beans>
xml配置 -> byName 自動裝配
<bean id="cat" class="pojo.Cat"/>
<bean id="dog" class="pojo.Dog"/>
<!--byname會在容器自動查找,和自己對象set方法的set后面的值對應的id
例如:setDog(),取set后面的字符作為id,則要id = dog 才可以進行自動裝配
-->
<bean id="people" class="pojo.People" autowire="byName">
<property name="name" value="cbh"></property>
</bean>
彈幕評論:byName只能取到小寫,大寫取不到
7.2、使用注解實現自動裝配
jdk1.5支持的注解,spring2.5支持的注解
The introduction of annotation-based configuration raised the question of whether this approach is “better” than XML.(翻譯:基於注釋的配置的引入提出了一個問題,即這種方法是否比XML“更好”)
- 導入context約束
xmlns:context="http://www.springframework.org/schema/context"
- 配置注解的支持:< context:annotation-config/>
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
</beans>
7.2.1、@Autowired
默認是byType方式,如果匹配不上,就會byName
在屬性上個使用,也可以在set上使用
我們可以不用編寫set方法了,前提是自動裝配的屬性在Spring容器里,且要符合ByName 自動裝配
public class People {
@Autowired
private Cat cat;
@Autowired
private Dog dog;
private String name;
}
@Nullable 字段標記了這個注解,說明該字段可以為空
public name(@Nullable String name){
}
//源碼
public @interface Autowired {
boolean required() default true;
}
如果定義了Autowire的require屬性為false,說明這個對象可以為null,否則不允許為空(false表示找不到裝配,不拋出異常)
7.2.2、@Autowired+@Qualifier
@Autowired不能唯一裝配時,需要@Autowired+@Qualifier
如果@Autowired自動裝配環境比較復雜。自動裝配無法通過一個注解完成的時候,可以使用@Qualifier(value = “dog”)去配合使用,指定一個唯一的id對象
public class People {
@Autowired
private Cat cat;
@Autowired
@Qualifier(value = "dog")
private Dog dog;
private String name;
}
彈幕評論:
如果xml文件中同一個對象被多個bean使用,Autowired無法按類型找到,可以用@Qualifier指定id查找
7.2.3、@Resource
默認是byName方式,如果匹配不上,就會byType
public class People {
Resource(name="cat")
private Cat cat;
Resource(name="dog")
private Dog dog;
private String name;
}
彈幕評論:
Autowired是byType,@Autowired+@Qualifier = byType || byName
Autowired是先byteType,如果唯一則注入,否則byName查找。resource是先byname,不符合再繼續byType
區別:
@Resource和@Autowired的區別:
- 都是用來自動裝配的,都可以放在屬性字段上
- @Autowired通過byType的方式實現,而且必須要求這個對象存在!【常用】
- @Resource默認通過byname的方式實現,如果找不到名字,則通過byType實現!如果兩個都找不到的情況下,就報錯!【常用】
- 執行順序不同:@Autowired通過byType的方式實現。@Resource默認通過byname的方式實現
8、使用注解開發
在spring4之后,使用注解開發,必須要保證aop包的導入
使用注解需要導入contex的約束
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
</beans>
8.1、bean
彈幕評論:
有了< context:component-scan>,另一個< context:annotation-config/>標簽可以移除掉,因為已經被包含進去了。
<!--指定要掃描的包,這個包下面的注解才會生效
別只掃一個com.kuang.pojo包-->
<context:component-scan base-package="com.kuang"/>
<context:annotation-config/>
1234
//@Component 組件
//等價於<bean id="user" classs"pojo.User"/>
@Component
public class User {
public String name ="秦疆";
}
8.2、屬性如何注入@value
@Component
public class User {
//相當於<property name="name" value="kuangshen"/>
@value("kuangshen")
public String name;
//也可以放在set方法上面
//@value("kuangshen")
public void setName(String name) {
this.name = name;
}
}
8.3、衍生的注解
@Component有幾個衍生注解,會按照web開發中,mvc架構中分層。
- dao (@Repository)
- service(@Service)
- controller(@Controller)
這四個注解的功能是一樣的,都是代表將某個類注冊到容器中
8.4、自動裝配置
@Autowired:默認是byType方式,如果匹配不上,就會byName
@Nullable:字段標記了這個注解,說明該字段可以為空
@Resource:默認是byName方式,如果匹配不上,就會byType
8.5、作用域@scope
//原型模式prototype,單例模式singleton
//scope("prototype")相當於<bean scope="prototype"></bean>
@Component
@scope("prototype")
public class User {
//相當於<property name="name" value="kuangshen"/>
@value("kuangshen")
public String name;
//也可以放在set方法上面
@value("kuangshen")
public void setName(String name) {
this.name = name;
}
}
8.6、小結
xml與注解:
- xml更加萬能,維護簡單,適用於任何場合
- 注解,不是自己的類使用不了,維護復雜
最佳實踐:
- xml用來管理bean
- 注解只用來完成屬性的注入
- 要開啟注解支持
9、使用Java的方式配置Spring
不使用Spring的xml配置,完全交給java來做!
Spring的一個子項目,在spring4之后,,,它成為了核心功能
實體類:pojo的User.java
//這里這個注解的意思,就是說明這個類被Spring接管了,注冊到了容器中
@component
public class User {
private String name;
public String getName() {
return name;
}
//屬性注入值
@value("QINJIANG')
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "user{" +
"name='" + name + '\''+
'}';
}
}
彈幕評論:要么使用@Bean,要么使用@Component和ComponentScan,兩種效果一樣
配置文件:config中的kuang.java
@Import(KuangConfig2.class),用@import來包含KuangConfig2.java
//這個也會Spring容器托管,注冊到容器中,因為他本米就是一個@Component
// @Configuration表這是一個配置類,就像我們之前看的beans.xml,類似於<beans>標簽
@Configuration
@componentScan("com.Kuang.pojo") //開啟掃描
//@Import(KuangConfig2.class)
public class KuangConfig {
//注冊一個bean , 就相當於我們之前寫的一個bean 標簽
//這個方法的名字,就相當於bean 標簽中的 id 屬性 ->getUser
//這個方法的返同值,就相當於bean 標簽中的class 屬性 ->User
//@Bean
public User getUser(){
return new User(); //就是返回要注入到bean的對象!
}
}
彈幕評論:ComponentScan、@Component("pojo”) 這兩個注解配合使用
測試類
public class MyTest {
public static void main(String[ ] args) {
//如果完全使用了配置類方式去做,我們就只能通過 Annotationconfig 上下文來獲取容器,通過配置類的class對象加載!
ApplicationContext context = new AnnotationConfigApplicationContext(KuangConfig.Class); //class對象
User getUser =(User)context.getBean( "getUser"); //方法名getUser
System.out.Println(getUser.getName());
}
}
會創建兩個相同對象問題的說明:
彈幕總結 - -> @Bean是相當於< bean>標簽創建的對象,而我們之前學的@Component是通過spring自動創建的這個被注解聲明的對象,所以這里相當於有兩個User對象被創建了。一個是bean標簽創建的(@Bean),一個是通過掃描然后使用@Component,spring自動創建的User對象,所以這里去掉@Bean這些東西,然后開啟掃描。之后在User頭上用@Component即可達到spring自動創建User對象了
//這個也會Spring容器托管,注冊到容器中,因為他本米就是一個@Component
// @Configuration表這是一個配置類,就像我們之前看的beans.xml,類似於<beans>標簽
@Configuration
@componentScan("com.Kuang.pojo") //開啟掃描
//@Import(KuangConfig2.class)
public class KuangConfig {
//注冊一個bean , 就相當於我們之前寫的一個bean 標簽
//這個方法的名字,就相當於bean 標簽中的 id 屬性 ->getUser
//這個方法的返同值,就相當於bean 標簽中的class 屬性 ->User
//@Bean
public User getUser(){
return new User(); //就是返回要注入到bean的對象!
}
}
彈幕評論:ComponentScan、@Component("pojo”) 這兩個注解配合使用
測試類
public class MyTest {
public static void main(String[ ] args) {
//如果完全使用了配置類方式去做,我們就只能通過 Annotationconfig 上下文來獲取容器,通過配置類的class對象加載!
ApplicationContext context = new AnnotationConfigApplicationContext(KuangConfig.Class); //class對象
User getUser =(User)context.getBean( "getUser"); //方法名getUser
System.out.Println(getUser.getName());
}
}
會創建兩個相同對象問題的說明:
彈幕總結 - -> @Bean是相當於< bean>標簽創建的對象,而我們之前學的@Component是通過spring自動創建的這個被注解聲明的對象,所以這里相當於有兩個User對象被創建了。一個是bean標簽創建的(@Bean),一個是通過掃描然后使用@Component,spring自動創建的User對象,所以這里去掉@Bean這些東西,然后開啟掃描。之后在User頭上用@Component即可達到spring自動創建User對象了
10、代理模式
為什么要學代理模式?
因為這個就是SpringAOP的底層!【SpringAOP 和 SpringMVC】
分類:
- 動態代理
- 靜態代理
10.1、靜態代理
代碼步驟:
1、接口
package pojo;
public interface Host {
public void rent();
}
2、真實角色
package pojo;
public class HostMaster implements Host{
public void rent() {
System.out.println("房東要出租房子");
}
}
3、代理角色
package pojo;
public class Proxy {
public Host host;
public Proxy() {
}
public Proxy(Host host) {
super();
this.host = host;
}
public void rent() {
seeHouse();
host.rent();
fee();
sign();
}
//看房
public void seeHouse() {
System.out.println("看房子");
}
//收費
public void fee() {
System.out.println("收中介費");
}
//合同
public void sign() {
System.out.println("簽合同");
}
}
4、客戶端訪問代理角色
package holle4_proxy;
import pojo.Host;
import pojo.HostMaster;
import pojo.Proxy;
public class My {
public static void main(String[] args) {
//房東要出租房子
Host host = new HostMaster();
//中介幫房東出租房子,但也收取一定費用(增加一些房東不做的操作)
Proxy proxy = new Proxy(host);
//看不到房東,但通過代理,還是租到了房子
proxy.rent();
}
}
代碼翻倍:幾十個真實角色就得寫幾十個代理
AOP橫向開發
10.2、動態代理
動態代理和靜態角色一樣,動態代理底層是反射機制
動態代理類是動態生成的,不是我們直接寫好的!
動態代理(兩大類):基於接口,基於類
- 基於接口:JDK的動態代理【使用ing】
- 基於類:cglib
- java字節碼實現:javasisit
了解兩個類
1、Proxy:代理
2、InvocationHandler:調用處理程序
實例:
接口 Host.java
//接口
package pojo2;
public interface Host {
public void rent();
}
接口Host實現類 HostMaster.java
//接口實現類
package pojo2;
public class HostMaster implements Host{
public void rent() {
System.out.println("房東要租房子");
}
}
代理角色的處理程序類 ProxyInvocationHandler.java
package pojo2;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
///用這個類,自動生成代理
public class ProxyInvocationHandler implements InvocationHandler {
// Foo f =(Foo) Proxy.NewProxyInstance(Foo. Class.GetClassLoader(),
// new Class<?>[] { Foo.Class },
// handler);
// 被代理的接口
public HostMaster hostMaster ;
public void setHostMaster(HostMaster hostMaster) {
this.hostMaster = hostMaster;
}
// 得到生成的代理類
public Object getProxy() {
// newProxyInstance() -> 生成代理對象,就不用再寫具體的代理類了
// this.getClass().getClassLoader() -> 找到加載類的位置
// hostMaster.getClass().getInterfaces() -> 代理的具體接口
// this -> 代表了接口InvocationHandler的實現類ProxyInvocationHandler
return Proxy.newProxyInstance(this.getClass().getClassLoader(), hostMaster.getClass().getInterfaces(), this);
// 處理代理實例並返回結果
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
seeHouse();
// 動態代理的本質,就是使用反射機制實現的
// invoke()執行它真正要執行的方法
Object result = method.invoke(hostMaster, args);
fee();
return result;
}
public void seeHouse() {
System.out.println("看房子");
}
public void fee() {
System.out.println("收中介費");
}
}
用戶類 My2.java
package holle4_proxy;
import pojo2.Host;
import pojo2.Host2;
import pojo2.HostMaster;
import pojo2.ProxyInvocationHandler;
public class My2 {
public static void main(String[] args) {
//真實角色
HostMaster hostMaster = new HostMaster();
//代理角色,現在沒有;用代理角色的處理程序來實現Host接口的調用
ProxyInvocationHandler pih = new ProxyInvocationHandler();
//pih -> HostMaster接口類 -> Host接口
pih.setHostMaster(hostMaster);
//獲取newProxyInstance動態生成代理類
Host proxy = (Host) pih.getProxy();
proxy.rent();
}
}
彈幕評論:
什么時候調用invoke方法的?
代理實例調用方法時invoke方法就會被調用,可以debug試試
改為萬能代理類
///用這個類,自動生代理
public class ProxyInvocationHandler implements InvocationHandler {
// Foo f =(Foo) Proxy.NewProxyInstance(Foo. Class.GetClassLoader(),
// new Class<?>[] { Foo.Class },
// handler);
// 被代理的接口
public Object target;
public void setTarget(Object target) {
this.target = target;
}
// 得到生成的代理類 -> 固定的代碼
public Object getProxy() {
// newProxyInstance() -> 生成代理對象,就不用再寫具體的代理類了
// this.getClass().getClassLoader() -> 找到加載類的位置
// hostMaster.getClass().getInterfaces() -> 代理的具體接口
// this -> 代表了接口InvocationHandler的實現類ProxyInvocationHandler
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
}
// 處理代理實例並返回結果
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//前置通知
pre();
//打印日志,通過反射的方式
log(method.getName());
// 動態代理的本質,就是使用反射機制實現的
// invoke()執行它真正要執行的方法
Object result = method.invoke(target, args);
//后置通知
post();
return result;
}
public void pre() {
System.out.println("[前置通知]");
}
public void post() {
System.out.println("[后置通知]");
}
//日志打印
public void log(String msg) {
System.out.println("[Debug]執行了" + msg + "方法");
}
}
測試類:
public class Client {
public static void main(String[] args) {
//真實角色
UserServiceImpl userService = new UserServiceImpl();
//代理角色,不存在
ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler();
//設置要代理的對象
proxyInvocationHandler.setTarget(userService);
//動態生產代理類
UserService proxy = (UserService) proxyInvocationHandler.getProxy();
//執行業務操作
proxy.add();
}
}
11、AOP
11.1、什么是AOP
11.2、AOP在Spring中的使用
提供聲明式事務,允許用戶自定義切面
- 橫切關注點:跨越應用程序多個模塊的方法或功能。即是,與我們業務邏輯無關的,但是我們需要關注的部分,就是橫切關注點。如日志,安全,緩存,事務等等…
- 切面(Aspect):橫切關注點 被模塊化的特殊對象。即,它是一個類。(Log類)
- 通知(Advice):切面必須要完成的工作。即,它是類中的一個方法。(Log類中的方法)
- 目標(Target):被通知對象。(生成的代理類)
- 代理(Proxy):向目標對象應用通知之后創建的對象。(生成的代理類)
- 切入點(PointCut):切面通知執行的”地點”的定義。(最后兩點:在哪個地方執行,比如:method.invoke())
- 連接點(JointPoint):與切入點匹配的執行點。
SpringAOP中,通過Advice定義橫切邏輯,Spring中支持5種類型的Advice:
即AOP在不改變原有代碼的情況下,去增加新的功能。(代理)
11.3、使用Spring實現AOP
【重點】
- 使用AOP織入,需要導入一個依賴包!
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
11.3.1、方法一:使用原生spring接口
springAPI接口實現
需要先導入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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>
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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--注冊bean-->
<bean id="userservice" class="service.UserServiceImpl"/>
<bean id="log" class="log.Log"/>
<bean id="afterLog" class="log.AfterLog"/>
<!--方式一,使用原生Spring API接口-->
<!--配置aop,還需要導入aop約束-->
<aop:config>
<!--切入點:expression:表達式,execution(要執行的位置)-->
<aop:pointcut id="pointcut" expression="execution(* service.UserServiceImpl.*(..))"/>
<!--UserServiceImpl.*(..) -》 UserServiceImpl類下的所以方法(參數)-->
<!--執行環繞增加-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
<!-- 環繞,在id="pointcut"的前后切入 -->
</aop:config>
</beans>
execution(返回類型,類名,方法名(參數))
-> execution(* com.service.*,*(…))
UserService.java
package service;
public interface UserService {
public void add() ;
public void delete() ;
public void query() ;
public void update();
}
UserService 的實現類 UserServiceImp.java
package service;
public class UserServiceImpl implements UserService {
public void add() {
System.out.println("add增");
}
public void delete() {
System.out.println("delete刪");
}
public void update() {
System.out.println("update改");
}
public void query() {
System.out.println("query查");
}
}
前置Log.java
package log;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class Log implements MethodBeforeAdvice {
//method:要執行的目標對象的方法
//args:參數
//target:目標對象
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName()+"的"+method.getName()+"被執行了");
}
}
后置AfterLog.java
package log;
import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;
public class AfterLog implements AfterReturningAdvice {
//returnVaule: 返回值
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("執行了"+method.getName()+"方法,返回值是"+returnValue);
}
}
測試類MyTest5
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import service.UserService;
public class MyTest5 {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//注意:動態代理代理的是接口
UserService userService = (UserService) context.getBean("userservice");
userService.add();
}
}
11.3.2、方法二:自定義類實現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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--注冊bean-->
<bean id="userservice" class="service.UserServiceImpl"/>
<bean id="log" class="log.Log"/>
<bean id="afterLog" class="log.AfterLog"/>
<!-- 方式二,自定義 -->
<bean id="diy" class="diy.DiyPointcut"/>
<aop:config>
<!--自定義切面-->
<aop:aspect ref="diy">
<!--切入點-->
<aop:pointcut id="point" expression="execution(* service.UserServiceImpl.*(..))"/>
<aop:before method="before" pointcut-ref="point"/>
<aop:after method="after" pointcut-ref="point"/>
</aop:aspect>
</aop:config>
</beans>
package diy;
public class DiyPointcut {
public void before(){
System.out.println("插入到前面");
}
public void after(){
System.out.println("插入到后面");
}
}
//測試
public class MyTest5 {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//注意:動態代理代理的是接口
UserService userService = (UserService) context.getBean("userservice");
userService.add();
}
}
11.3.3、方法三:使用注解實現
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 注冊 -->
<bean id="userservice" class="service.UserServiceImpl"/>
<!--方式三,使用注解實現-->
<bean id="diyAnnotation" class="diy.DiyAnnotation"></bean>
<!-- 開啟自動代理
實現方式:默認JDK (proxy-targer-class="fasle")
cglib (proxy-targer-class="true")-->
<aop:aspectj-autoproxy/>
</beans>
DiyAnnotation.java
package diy;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect //標注這個類是一個切面
public class DiyAnnotation {
@Before("execution(* service.UserServiceImpl.*(..))")
public void before(){
System.out.println("=====方法執行前=====");
}
@After("execution(* service.UserServiceImpl.*(..))")
public void after(){
System.out.println("=====方法執行后=====");
}
//在環繞增強中,我們可以給地暖管一個參數,代表我們要獲取切入的點
@Around("execution(* service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("環繞前");
Object proceed = joinPoint.proceed();
System.out.println("環繞后");
}
}
測試
public class MyTest5 {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//注意:動態代理代理的是接口
UserService userService = (UserService) context.getBean("userservice");
userService.add();
}
}
輸出結果:
12、整合mybatis
【提醒】:
本節知識密度大,時間長,需要多看幾遍視頻,加深理解
步驟:
- 導入相關jar
- junit
- mybatis
- mysql數據庫
- spring相關的
- aop織入
- mybatis-spring【new】
- 編寫配置文件
- 測試
mybatis-spring官網:https://mybatis.org/spring/zh/
12.0、回憶Mybatis
mybatis的配置流程:
- 編寫實體類
- 編寫核心配置文件
- 編寫接口
- 編寫Mapper.xmi
- 測試
實例:
- 導入相關jar,pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-study</artifactId>
<groupId>com.melodyhub</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-10-mybatis</artifactId>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<!--<version>5.1.47</version>-->
<version>8.0.19</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<!--Spring操作數據庫的話,需要一個spring-jdbc-->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.3</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>
</dependencies>
<!--在build中配置resources,來防止資源導出失敗的問題-->
<!-- Maven解決靜態資源過濾問題 -->
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<!--<filtering>false</filtering>-->
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<!--<filtering>true</filtering>-->
<filtering>false</filtering>
</resource>
</resources>
</build>
</project>
- 編寫實體類,User.java:
package com.melodyhub.pojo;
import lombok.Data;
@Data
public class User {
private int id;
private String name;
private String pwd;
}
- 編寫核心配置文件,resources/mybatis-config.xml:
<?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 核心配置文件-->
<configuration>
<!--開啟日志-->
<!--<settings>
<setting name="logImpl" value="STDOUT_LOGGING" />
</settings>-->
<!--可以給實體類起別名 -->
<typeAliases>
<package name="com.melodyhub.pojo"/>
</typeAliases>
<!--environments 可配置多個環境-->
<environments default="development">
<!--多個環境,不同id-->
<environment id="development">
<!--事務管理-->
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!--<property name="driver" value="com.mysql.jdbc.Driver"/>-->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&autoReconnect=true"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!--每一個Mapper.xml都需要在MyBatis核心配置文件中注冊!!!-->
<mappers>
<mapper class="com.melodyhub.mapper.UserMapper"/>
<!--<mapper resource="com/melodyhub/mapper/UserMapper.xml"/>-->
</mappers>
</configuration>
- 編寫接口,UserMapper.xml:
package com.melodyhub.mapper;
import com.melodyhub.pojo.User;
import java.util.List;
public interface UserMapper {
public List<User> selectUser();
}
- 編寫接口配置文件,UserMapper.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.melodyhub.mapper.UserMapper">
<!-- 綁定接口 -->
<select id="selectUser" resultType="user">
select * from mybatis.user;
</select>
</mapper>
- 測試,test/java/MyTest.java:
import com.melodyhub.mapper.UserMapper;
import com.melodyhub.pojo.User;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import org.apache.ibatis.io.Resources;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class MyTest {
@Test
public void test() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.selectUser();
for (User user : userList) {
System.out.println(user);
}
}
}
- 結果:
User(id=1, name=狂神, pwd=123456)
User(id=2, name=張三, pwd=abcdef)
User(id=3, name=李四, pwd=987654)
Process finished with exit code 0
12.1、mybatis-spring-方式一
- 編寫數據源配置
sqISessionFactory
sqISessionTemplate
(相當於sqISession
)- 需要給接口加實現類【new】
- 將自己寫的實現類,注入到Spring中
- 測試!
數據源:
-
DataSource
:使用Spring的數幫源替換Mybatis的配置 -
這使用Spring提供的JDBC:
org.springframework.jdbc.datasource
-
其他數據源:
c3p0
、dbcp
、druid
【核心對象】:
sqISessionFactory
在基礎的 MyBatis 用法中,是通過
SqlSessionFactoryBuilder
來創建SqlSessionFactory
的。 而在 MyBatis-Spring 中,則使用SqlSessionFactoryBean
來創建。
sqISessionTemplate
SqlSessionTemplate
是 MyBatis-Spring 的核心。作為SqlSession
的一個實現,這意味着可以使用它無縫代替你代碼中已經在使用的SqlSession
。SqlSessionTemplate
是線程安全的,可以被多個 DAO 或映射器所共享使用。當調用 SQL 方法時(包括由
getMapper()
方法返回的映射器中的方法),SqlSessionTemplate
將會保證使用的SqlSession
與當前 Spring 的事務相關。此外,它管理 session 的生命周期,包含必要的關閉、提交或回滾操作。另外,它也負責將 MyBatis 的異常翻譯成 Spring 中的DataAccessExceptions
。由於模板可以參與到 Spring 的事務管理中,並且由於其是線程安全的,可以供多個映射器類使用,你應該總是用
SqlSessionTemplate
來替換 MyBatis 默認的DefaultSqlSession
實現。在同一應用程序中的不同類之間混雜使用可能會引起數據一致性的問題。可以使用
SqlSessionFactory
作為構造方法的參數來創建SqlSessionTemplate
對象。
先導入jar包,
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-study</artifactId>
<groupId>com.melodyhub</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-10-mybatis</artifactId>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<!--<version>5.1.47</version>-->
<version>8.0.19</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<!--Spring操作數據庫的話,需要一個spring-jdbc-->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.3</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>
</dependencies>
<!--在build中配置resources,來防止資源導出失敗的問題-->
<!-- Maven解決靜態資源過濾問題 -->
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<!--<filtering>false</filtering>-->
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<!--<filtering>true</filtering>-->
<filtering>false</filtering>
</resource>
</resources>
</build>
</project>
關於解決“Maven解決靜態資源過濾問題”詳見:Maven中靜態資源和字節碼文件的過濾
編寫順序:
User -> UserMapper -> UserMapper.xml -> spring-dao.xml -> UserServiceImpl -> applicationContext.xml -> MyTest6
代碼步驟:
pojo實體類 User:
package com.melodyhub.pojo;
import lombok.Data;
@Data
public class User {
private int id;
private String name;
private String pwd;
}
mapper目錄下的 UserMapper、UserMapperImpl、UserMapper.xml
接口UserMapper:
package com.melodyhub.mapper;
import com.melodyhub.pojo.User;
import java.util.List;
public interface UserMapper {
public List<User> selectUser();
}
UserMapperImpl:
package com.melodyhub.mapper;
import com.melodyhub.pojo.User;
import org.mybatis.spring.SqlSessionTemplate;
import java.util.List;
public class UserMapperImpl implements UserMapper {
//我們的所有操作,在原來都使用sqlSession來執行,現在都使用SqlSessionTemplate;
private SqlSessionTemplate sqlSession;
public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}
@Override
public List<User> selectUser() {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
return userMapper.selectUser();
}
}
UserMapper.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.melodyhub.mapper.UserMapper">
<!-- 綁定接口 -->
<select id="selectUser" resultType="user">
select * from mybatis.user;
</select>
</mapper>
resource目錄下的 mybatis-config.xml、spring-dao.xml、applicationContext.xml
mybatis-config.xml:
<?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 核心配置文件-->
<configuration>
<!--開啟日志-->
<!--<settings>
<setting name="logImpl" value="STDOUT_LOGGING" />
</settings>-->
<!--可以給實體類起別名 -->
<typeAliases>
<package name="com.melodyhub.pojo"/>
</typeAliases>
<!--environments 可配置多個環境-->
<!--配置了Spring-Dao.xml后,這塊可以直接注釋掉了:dataSource
<environments default="development">
<!–多個環境,不同id–>
<environment id="development">
<!–事務管理–>
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!–<property name="driver" value="com.mysql.jdbc.Driver"/>–>
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&autoReconnect=true"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>-->
<!--每一個Mapper.xml都需要在MyBatis核心配置文件中注冊!!!-->
<!-- 配置了Spring-Dao.xml后,這塊可以直接注釋掉了:綁定Mybatis配置文件
<mappers>
<mapper class="com.melodyhub.mapper.UserMapper"/>
<!–<mapper resource="com/melodyhub/mapper/UserMapper.xml"/>–>
</mappers>-->
</configuration>
spring-dao.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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--DataSource:使用Spring的數幫源替換Mybatis的配置 其他數據源:c3p0、dbcp、druid
這使用Spring提供的JDBC: org.springframework.jdbc.datasource -->
<!--dataSource -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName"
value="com.mysql.cj.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&autoReconnect=true"/>
<property name="username" value="root" />
<property name="password" value="root" />
</bean>
<!--sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!--綁定 Mybatis 配置文件-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/melodyhub/mapper/*.xml"/>
</bean>
<!-- sqlSessionTemplate 就是之前使用的:sqlsession -->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<!-- 只能使用構造器注入sqlSessionFactory 原因:它沒有set方法-->
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
</beans>
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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--導入其他配置文件-->
<import resource="spring-dao.xml"/>
<!--<import resource="spring-mvc.xml"/>-->
<!--mybatis-spring 方式1-->
<bean id="userMapper" class="com.melodyhub.mapper.UserMapperImpl">
<property name="sqlSession" ref="sqlSession"/>
</bean>
</beans>
測試類:
import com.melodyhub.mapper.UserMapper;
import com.melodyhub.pojo.User;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import org.apache.ibatis.io.Resources;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class MyTest {
@Test
public void test1() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
for (User user : userMapper.selectUser()) {
System.out.println(user);
}
}
}
12.2、mybatis-spring-方式二
SqlSessionDaoSupport
SqlSessionDaoSupport
是一個抽象的支持類,用來為你提供SqlSession
。調用getSqlSession()
方法你會得到一個SqlSessionTemplate
,之后可以用於執行 SQL 方法,就像下面這樣:public class UserDaoImpl extends SqlSessionDaoSupport implements UserDao { public User getUser(String userId) { return getSqlSession().selectOne("org.mybatis.spring.sample.mapper.UserMapper.getUser", userId); } }
在這個類里面,通常更傾向於使用
MapperFactoryBean
,因為它不需要額外的代碼。但是,如果你需要在 DAO 中做其它非 MyBatis 的工作或需要一個非抽象的實現類,那么這個類就很有用了。
SqlSessionDaoSupport
需要通過屬性設置一個sqlSessionFactory
或SqlSessionTemplate
。如果兩個屬性都被設置了,那么SqlSessionFactory
將被忽略。假設類
UserMapperImpl
是SqlSessionDaoSupport
的子類,可以編寫如下的 Spring 配置來執行設置:<bean id="userDao" class="org.mybatis.spring.sample.dao.UserDaoImpl"> <property name="sqlSessionFactory" ref="sqlSessionFactory" /> </bean>
UserServiceImpl2.java:
package com.melodyhub.mapper;
import com.melodyhub.pojo.User;
import org.apache.ibatis.session.SqlSession;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import java.util.List;
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper {
@Override
public List<User> selectUser() {
/*
SqlSession sqlSession = getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
return userMapper.selectUser();
*/
return getSqlSession().getMapper(UserMapper.class).selectUser();
}
}
spring-dao.xml:SqlSessionTemplate
可以不寫了
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--DataSource:使用Spring的數幫源替換Mybatis的配置 其他數據源:c3p0、dbcp、druid
這使用Spring提供的JDBC: org.springframework.jdbc.datasource -->
<!--dataSource -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName"
value="com.mysql.cj.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&autoReconnect=true"/>
<property name="username" value="root" />
<property name="password" value="root" />
</bean>
<!--sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!--綁定 Mybatis 配置文件-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/melodyhub/mapper/*.xml"/>
</bean>
<!-- 方法二:SqlSessionTemplate 可以不寫了-->
</beans>
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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--導入其他配置文件-->
<import resource="spring-dao.xml"/>
<!--<import resource="spring-mvc.xml"/>-->
<!--mybatis-spring 方式-2-->
<bean id="userMapper2" class="com.melodyhub.mapper.UserMapperImpl2">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
</beans>
測試:
public class MyTest6 {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper userMapper = (UserMapper) context.getBean("userMapper2");
for (User user : userMapper.getUser()) {
System.out.println(user);
}
}
}
13. 聲明式事務
- 把一組業務當成一個業務來做;要么都成功,要么都失敗!
- 事務在項目開發中,十分的重要,涉及到數據的一致性問題
- 確保完整性和一致性
事務的ACID原則:
1、原子性
2、隔離性
3、一致性
4、持久性
ACID參考文章:https://www.cnblogs.com/melodyjerry/p/13621129.html
Spring中的事務管理
- 聲明式事務:AOP
- 編程式事務:需要再代碼中,進行事務管理
聲明式事務
先導入jar包
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.4</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.12</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
</dependencies>
<!--在build中配置resources,來防止資源導出失敗的問題-->
<!-- Maven解決靜態資源過濾問題 -->
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
代碼步驟:
pojo實體類 User
package pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
private String name;
private String pwd;
}
mapper目錄下的 UserMapper、UserMapperImpl、UserMapper.xml
接口UserMapper
package mapper;
import java.util.List;
import org.apache.ibatis.annotations.Param;
import pojo.User;
public interface UserMapper {
public List<User> getUser();
public int insertUser(User user);
public int delUser(@Param("id") int id);
}
UserMapperImpl
package mapper;
import pojo.User;
import org.apache.ibatis.session.SqlSession;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import java.util.List;
public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper {
public List<User> getUser() {
User user = new User(5,"你好","ok");
insertUser(user);
delUser(5);
SqlSession sqlSession = getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return mapper.getUser();
//或者return getSqlSession().getMapper(UserMapper.class).getUser();
}
//插入
public int insertUser(User user) {
return getSqlSession().getMapper(UserMapper.class).insertUser(user);
}
//刪除
public int delUser(int id) {
return getSqlSession().getMapper(UserMapper.class).delUser(id);
}
}
UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 綁定接口 -->
<mapper namespace="mapper.UserMapper">
<select id="getUser" resultType="pojo.User">
select * from mybatis.mybatis
</select>
<insert id="insertUser" parameterType="pojo.User" >
insert into mybatis.mybatis (id,name,pwd) values (#{id},#{name},#{pwd})
</insert>
<delete id="delUser" parameterType="_int">
deleteAAAAA from mybatis.mybatis where id = #{id}
<!-- deleteAAAAA是故意寫錯的 -->
</delete>
</mapper>
resource目錄下的 mybatis-config.xml、spring-dao.xml、applicationContext.xml
mybatis-config.xml
<?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 -->
<configuration>
<!--開啟日志-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING" />
</settings>
<!--可以給實體類起別名-->
<typeAliases>
<package name="pojo" />
</typeAliases>
</configuration>
spring-dao.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"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--data source -->
<bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName"
value="com.mysql.cj.jdbc.Driver" />
<property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai"/>
<property name="username" value="root" />
<property name="password" value="root" />
</bean>
<!--sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="datasource" />
<!--綁定 mybatis 配置文件-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>
<!--聲明式事務-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<constructor-arg ref="datasource" />
</bean>
<!--結合aop實現事務織入-->
<!--配置事務的通知類-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--給哪些方法配置事務-->
<!--新東西:配置事務的傳播特性 propagation-->
<tx:attributes>
<tx:method name="add" propagation="REQUIRED"/>
<tx:method name="delete" propagation="REQUIRED"/>
<tx:method name="update" propagation="REQUIRED"/>
<tx:method name="query" read-only="true"/>
<!-- *號包含上面4個方法:
<tx:method name="*" propagation="REQUIRED"/> -->
</tx:attributes>
</tx:advice>
<!--配置事務切入-->
<aop:config>
<aop:pointcut id="txpointcut" expression="execution(* mapper.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txpointcut"/>
</aop:config>
</beans>
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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<import resource="spring-dao.xml" />
<bean id="userMapper" class="mapper.UserMapperImpl">
<property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
</bean>
</beans>
測試類
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import mapper.UserMapper;import pojo.User;
public class MyTest7 {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper userMapper = (UserMapper) context.getBean("userMapper");
for (User user : userMapper.getUser()) {
System.out.println(user);
}
}
}
思考:
為什么需要事務?
- 如果不配置事務,可能存在數據提交不一致的情況下;
- 如果不在spring中去配置聲明式事務,我們就需要在代碼中手動配置事務!
- 事務在項目的開發中非常重要,涉及到數據的一致性和完整性問題!