感謝狂神,講解清晰,以下是原視頻【狂神說Java】Spring5最新完整教程IDEA版通俗易懂的筆記
簡介
- 2002年interface21是雛形
- 2004年3月24日發(fā)布1.0正式版
- 本身是大雜燴,方便JavaEE的開發(fā)
- SSH :Struct2 + Spring + Hibernate
- SSM : SpringMVC + Spring + Mybatis!
官網(wǎng):Spring | Why Spring?
下載:repo.spring.io
源代碼:GitHub - spring-projects/spring-framework: Spring Framework
- Spring web和Spring jdbc的maven依賴:
<!-- https://mvnrepository.com/artifact/org.springframework/spring-web -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.2.19.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.19.RELEASE</version>
</dependency>
優(yōu)點(diǎn)
- 輕量級、非入侵式、開源的框架(容器)
- 控制反轉(zhuǎn)(IOC, inversion of control)、面向切面編程(AOP, aspect oriented programming)
- 支持事務(wù)的處理,對框架整合的支持
總結(jié):是一個(gè)輕量級的控制反轉(zhuǎn)、面向切面編程的框架。
組成及擴(kuò)展
組成
擴(kuò)展
-
Spring Boot
- 一個(gè)腳手架
- 可以快速開發(fā)單個(gè)微服務(wù)
- 約定大于配置
-
Spring Cloud
- 基于Spring Boot實(shí)現(xiàn)的
學(xué)習(xí)SpringBoot的前提是Spring和SpringMVC,承上啟下。
弊端:發(fā)展久了,大雜燴導(dǎo)致配置繁瑣,”配置地獄“。
IOC理論推導(dǎo)
1、UserDao 接口
2、UserDaoImpl 實(shí)現(xiàn)類
3、UserService 業(yè)務(wù)接口
4、UserServiceImpl 業(yè)務(wù)實(shí)現(xiàn)類
用戶需求會影響原來的代碼,代碼量大,修改一次的成本代價(jià)大。
private UserDao userDao;
// 利用set進(jìn)行動態(tài)實(shí)現(xiàn)值的注入
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
之前由程序員來指定,現(xiàn)在由使用者指定。(不需要修改原本的程序,即控制反轉(zhuǎn):主動創(chuàng)建對象--->被動的接受對象)
IOC本質(zhì)
IOC是一種設(shè)計(jì)思想,DI(Dependency Injection,依賴注入)是實(shí)現(xiàn)IOC的一種方法。對象的創(chuàng)建由程序自己控制,控制反轉(zhuǎn)之后將對象的創(chuàng)建轉(zhuǎn)移給第三方。
采用XML方式配置Bean的時(shí)候,Bean的定義信息是和實(shí)現(xiàn)分離的,而采用注解的方式可以把兩者合為一體。Bean的定義信息直接以注解的形式定義在實(shí)現(xiàn)類中,從而達(dá)到了零配置的目的。
控制反轉(zhuǎn)是一種通過描述(XML或注解)并通過第三方去生產(chǎn)或獲取特定對象的方式。在Spring中實(shí)現(xiàn)控制反轉(zhuǎn)的是IOC容器,其實(shí)現(xiàn)方式是依賴注入(DI)。
IOC創(chuàng)建對象
無參構(gòu)造
<bean id = "user" class="com.pojo.User">
<property name="name" value="KyoDante"/>
</bean>
有參構(gòu)造的三種方式:下標(biāo)、類型(不推薦)、參數(shù)名都可以實(shí)現(xiàn)。
<!--index-->
<bean id="user" class="com.pojo.User">
<constructor-arg index="0" value="KyoDante"/>
</bean>
<!--type-->
<bean id="user" class="com.pojo.User">
<constructor-arg type="java.lang.String" value="KyoDante"/>
</bean>
<!--arg name-->
<bean id="user" class="com.pojo.User">
<constructor-arg name="name" value="KyoDante"/>
</bean>
Spring配置說明
別名(Alias)
<alias name="user" alias="aaaaa"/>
Bean配置
id是bean的唯一標(biāo)識;
class是bean的對應(yīng)的類:包名+類名;
name也是別名,而且可以同時(shí)取多個(gè)別名(用逗號或者空格分割)。
<bean id="user" class="com.pojo.User" name="bbbbb,ccccc">
<constructor-arg name="name" value="KyoDante"/>
</bean>
Import
一般是多個(gè)beans.xml合并為一個(gè)。注意:重名的bean是后一個(gè)覆蓋前一個(gè)。
依賴注入(DI)
構(gòu)造器注入
之前的部分就是構(gòu)造器
Set方式注入(重點(diǎn))
- 依賴注入:Set注入
- 依賴:bean對象的創(chuàng)建依賴于容器
- 注入:bean對象中的所有屬性,由容器來注入
【環(huán)境搭建】
- 復(fù)雜類型
public class Address {
private String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
- 真實(shí)測試對象
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.pojo.Student">
<property name="name" value="KyoDante"/>
</bean>
</beans>
- 注入
這樣是空串,String(""),而不是null。
<property name="wife" value=""/>
想要null則是下面的方式。
<?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="address" class="com.pojo.Address">
<property name="address" value="潮汕"/>
</bean>
<bean id="student" class="com.pojo.Student">
<property name="name" value="KyoDante"/>
<property name="address" ref="address"/>
<property name="books">
<array>
<value>紅樓夢</value>
<value>水滸傳</value>
<value>三國演義</value>
<value>西游記</value>
</array>
</property>
<property name="hobbies">
<list>
<value>聽歌</value>
<value>看電影</value>
<value>看書學(xué)習(xí)</value>
</list>
</property>
<property name="card">
<map>
<entry key="身份證" value="777777777"/>
<entry key="銀行卡" value="888888888"/>
</map>
</property>
<property name="games">
<set>
<value>DMC</value>
<value>DNF</value>
<value>LOL</value>
</set>
</property>
<property name="wife">
<null/>
</property>
<property name="info">
<props>
<prop key="性別">男</prop>
<prop key="姓名">KyoDante</prop>
</props>
</property>
</bean>
</beans>
擴(kuò)展方式注入
p空間和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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user" class="com.pojo.User" p:age="18" p:name="KyoDante">
</bean>
<bean id="user2" class="com.pojo.User" c:age="20" c:name="Dante">
</bean>
</beans>
bean的默認(rèn)scope(作用域)是singleton,可以調(diào)為prototye.
<bean id="user2" class="com.pojo.User" c:age="20" c:name="Dante" scope="prototype">
</bean>
web開發(fā)分別有request、session等作用域,分別生命周期為單次HTTP request、HTTP session等。
自動裝配(autowire):
- 可以節(jié)省一部分的xml語句,自動注入字段。
- byName:要保證bean的id唯一,且屬性和set方法的值一致!
- byType:要保證所有bean的class唯一,且bean需要和自動注入的屬性的類型一致!
<bean id="dog" class="com.pojo.Dog"/>
<bean id="cat" class="com.pojo.Cat"/>
<bean id="people" class="com.pojo.People" autowire="byType">
<property name="name" value="KyoDante"/>
<!-- <property name="cat" ref="cat"/>-->
<!-- <property name="dog" ref="dog"/>-->
</bean>
若使用注解,則需要配置beans.xml文件里面的context,打開annotation-config。
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"
<context:annotation-config/>
通常使用Autowired(如果設(shè)置了required=false,則可以為空對象)和Qualifier(當(dāng)id和class找不到名字的時(shí)候,用value來指定名字)搭配使用。
@Autowired(required = false)
@Qualifier(value = "dog222")
private Dog dog;
- @Autowired 通過byType的方式實(shí)現(xiàn)。
- 還有@Resource可以了解。(高版本JDK似乎沒有了)先byName到byType。
- 都可以放在屬性字段上。
注解
@Nullable 學(xué)會這個(gè)注解,說明這個(gè)字段可以為null。
注意:Spring4之后,要使用注解,需要確保aop的包導(dǎo)入了。同時(shí)配置context和annotation-config
使用<context:component-scan/>來掃描應(yīng)用注解的包。
<?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.pojo"/>
<context:annotation-config/>
</beans>
// 等價(jià)于 <bean id="user" class="com.pojo.User" />
@Component
public class User {
// 等價(jià)于 <property name="name" value="KyoDante"/>
@Value("KyoDante")
public String name;
}
@Component在Web開發(fā)以MVC延申:
- dao [@Repository]
- service [@Service]
- controller [@Controller]
//等價(jià)于<bean id="user" class="com.pojo.User" scope="prototype"/>
@Scope("prototype")
public class User {
總結(jié):
- xml相對萬能,維護(hù)相對簡單。
- 注解,不是自己的類用不了,注解維護(hù)困難。
- 最佳實(shí)踐:xml管理beans,注解管理屬性注入。
- 注意導(dǎo)入和配置。
用JavaConfig來配置Spring
- 直接@Bean!
public class Config {
@Bean
public User getUser(){
return new User();
}
}
使用@Bean的話,最后獲取使用getBean("getUser");
- @Configuration+@Conponent+@ConponentScan
@Configuration
@ComponentScan("com")
public class Config {
}
@Component
public class User {
@Value("KyoDante")
private String name;
public User() {
System.out.println("no arg");
}
public User(String name) {
System.out.println("a arg");
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
掃描包的話,最終獲取使用getBean("user");
- 聯(lián)合兩個(gè)配置(把Config2注入)
@Import(Config2.class)
public class Config {
}
同時(shí)使用時(shí),如果用getBean("getUser");會出現(xiàn)兩次構(gòu)造函數(shù),一次是因?yàn)镾pring的單例,另一次是因?yàn)檎{(diào)用getUser()方法里面new了一個(gè)實(shí)例,這樣會打破單例模式。
- SpringBoot里隨處可見注解!
代理
代理模式(SpringAOP底層)
代理:比如房產(chǎn)中介、婚姻中介。
- 靜態(tài)代理
- 抽象角色 使用抽象類或接口解決
public interface Rent {
void rent();
}
- 真實(shí)角色 被代理的角色
public class Renter implements Rent{
@Override
public void rent() {
System.out.println("房東要出租啦");
}
}
- 代理角色 代理真實(shí)角色,代理真實(shí)角色后,一般會做附屬操作
public class Proxy {
private Renter renter;
public Proxy() {
}
public Proxy(Renter renter) {
this.renter = renter;
}
public void seeHouse() {
System.out.println("帶你看房");
}
public void signContract() {
System.out.println("簽合同");
}
public void fee() {
System.out.println("收中介費(fèi)");
}
public void rent() {
seeHouse();
signContract();
fee();
renter.rent();
}
}
- 客戶 訪問代理對象的人(需要隔著中介進(jìn)行租房)
public class Client {
public static void main(String[] args) {
Renter renter = new Renter();
Proxy proxy = new Proxy(renter);
// renter.rent();
proxy.rent();
}
}
好處:
- 真實(shí)角色操作純粹,不用管公共服務(wù)
- 業(yè)務(wù)分工,公共服務(wù)給了代理完成
- 公共服務(wù)擴(kuò)展的時(shí)候方便
缺點(diǎn):
- 一個(gè)真實(shí)角色就會有一個(gè)代理,代碼量增多
盡量不動原本的業(yè)務(wù)代碼,所以在原本的實(shí)現(xiàn)上面加一層代理,可以在代理之上添加新的功能。
public interface UserService {
void add();
void delete();
void update();
void select();
}
public class UserServiceImp 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 UserServiceProxy {
private UserService userService;
public void setUserService(UserService userService) {
this.userService = userService;
}
public void log(String msg) {
System.out.println("[Debug]添加了:"+msg+"方法");
}
public void add() {
log("add");
userService.add();
}
public void delete() {
log("delete");
System.out.println("刪除");
}
public void update() {
log("update");
System.out.println("改變");
}
public void select() {
log("select");
System.out.println("查找");
}
}
public class Client {
public static void main(String[] args) {
UserServiceImp userServiceImp = new UserServiceImp();
userServiceImp.add();
// 用代理模式添加了一個(gè)日志功能。
UserServiceProxy userServiceProxy = new UserServiceProxy();
userServiceProxy.setUserService(userServiceImp);
userServiceProxy.add();
}
}
(AOP的原理:代理)
(實(shí)現(xiàn)一些Service)
| |
->Service->Controller->User
動態(tài)代理
動態(tài)代理和靜態(tài)代理角色一樣
動態(tài)生成
-
基于接口和類的動態(tài)代理
- 基于接口-JDK動態(tài)代理【在這里使用】
- 基于類-cglib
- java字節(jié)碼實(shí)現(xiàn)-javassist
需要了解兩個(gè)類:Proxy:代理、InvocationHandler:調(diào)用處理程序
public class ProxyInvocationHandler implements InvocationHandler {
// 目標(biāo)被代理接口
Object target;
// 設(shè)置被代理接口
public void setTarget(Object target) {
this.target = target;
}
public Object getProxy() {
return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
@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 msg) {
// System.out.println(msg);
// }
}
public class TestCases {
public static void main(String[] args) {
ProxyInvocationHandler pih = new ProxyInvocationHandler();
Renter renter = new Renter();
pih.setTarget(renter);
Rent rentProxy = (Rent) pih.getProxy();
rentProxy.rent();
UserServiceImp userServiceImp = new UserServiceImp();
pih.setTarget(userServiceImp);
UserService userProxy = (UserService) pih.getProxy();
userProxy.add();
userProxy.delete();
}
}
動態(tài)代理好處
- 和靜態(tài)代理一樣的好處
- 一個(gè)動態(tài)代理可以代理一個(gè)接口,一般對應(yīng)一種業(yè)務(wù)
- 一個(gè)動態(tài)代理可以代理多個(gè)類,只要實(shí)現(xiàn)了同一個(gè)接口即可。(動態(tài)性見上例即可有所體會。)
建議:返回去學(xué)習(xí)注解和反射的相關(guān)課程
AOP
Aspect Oriented Programming(AOP):面向切面編程,通過預(yù)編譯方式和運(yùn)行期動態(tài)代理實(shí)現(xiàn)程序功能的統(tǒng)一維護(hù)的一種技術(shù)。AOP是OOP的延續(xù),是軟件開發(fā)中的一個(gè)熱點(diǎn),也是Spring框架中的一個(gè)重要內(nèi)容,是函數(shù)式編程的一種衍生泛型。利用AOP可以對業(yè)務(wù)邏輯的各個(gè)部分進(jìn)行隔離,從而使得業(yè)務(wù)邏輯各部分之間的耦合度降低,提高程序的可重用性,同時(shí)提高了開發(fā)效率。
AOP在Spring中的作用
- 提供聲明式事務(wù),允許用戶自定義切面
- 橫切關(guān)注點(diǎn):跨越應(yīng)用程序多個(gè)模塊的方法或功能。即是,與我們業(yè)務(wù)邏輯無關(guān)的,但是我們需要關(guān)注的部分,就是橫切關(guān)注點(diǎn)。如:日志,安全,緩存,事務(wù)等等。
- 切面(ASPECT):橫切關(guān)注點(diǎn) 被模塊化的 特殊對象。即,它是一個(gè)類。
- 通知(Advice):卻面必須要完成的工作。即,它是類中的一個(gè)方法。
- 目標(biāo)(Target):被通知對象。
- 代理(Proxy):向目標(biāo)對象應(yīng)用通知之后創(chuàng)建的對象。
- 切入點(diǎn)(PointCut):切面通知執(zhí)行的“地點(diǎn)”的定義。
- 連接點(diǎn)(JointPoint):與切入點(diǎn)匹配的執(zhí)行點(diǎn)。
SpringAOP中,通過Advice定義橫切邏輯,Spring中支持5種類型的Advice:
即不改變原本代碼的情況下,添加功能。
實(shí)現(xiàn)方式一(SpringAOP的API)
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
</dependency>
需要配置切入的店,以及通知的內(nèi)容
<?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="service.UserServiceImp"/>
<bean id="log" class="log.Log"/>
<bean id="afterLog" class="log.AfterLog"/>
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* service.UserServiceImp.*(..))"/>
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
</beans>
public class Log implements MethodBeforeAdvice {
// o:被代理的類
// method:被執(zhí)行方法
// objects:參數(shù)
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println(o.getClass().getName()+"的"+method.getName()+"被執(zhí)行了");
}
}
public class AfterLog implements AfterReturningAdvice {
// o:返回值
// method:被執(zhí)行方法
@Override
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println("執(zhí)行了"+method.getName()+"返回結(jié)果為"+o);
}
}
public class TestCases {
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
// 代理的是接口
UserService userService = context.getBean("userService", UserService.class);
userService.add();
}
}
實(shí)現(xiàn)方式二(自定義)
public class MyAspect {
public void before() {
System.out.println("before+++");
}
public void after() {
System.out.println("after+++");
}
}
<bean id="myAspect" class="aspects.MyAspect"/>
<aop:config>
<aop:aspect ref="myAspect">
<aop:pointcut id="point" expression="execution(* service.UserServiceImp.*(..))"/>
<aop:before method="before" pointcut-ref="point"/>
<aop:after method="after" pointcut-ref="point"/>
</aop:aspect>
</aop:config>
實(shí)現(xiàn)方式三(注解實(shí)現(xiàn)AOP)
<bean id="annotationAspect" class="aspects.AnnotationAspect"/>
<!-- proxy-target-class="false" jdk -->
<!-- proxy-target-class="true" cglib -->
<aop:aspectj-autoproxy/>
@Aspect // 標(biāo)注為切面
public class AnnotationAspect {
@Before("execution(* service.UserServiceImp.*(..))")
public void before() {
System.out.println("方法執(zhí)行前");
}
@After("execution(* service.UserServiceImp.*(..))")
public void after() {
System.out.println("方法執(zhí)行后");
}
@Around("execution(* service.UserServiceImp.*(..))")
public void around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("環(huán)繞前");
Object proceed = pjp.proceed();
System.out.println("環(huán)繞后");
System.out.println(pjp.getSignature());
System.out.println(proceed);
}
}
MyBatis
整合MyBatis
- 導(dǎo)包(連接mysql,mybatis, spring-mybatis,spring-jdbc,aspectj等包)
<dependencies>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.7</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.19.RELEASE</version>
</dependency>
</dependencies>
- 使用mybatis:
- 實(shí)體類
- 核心配置
- 接口
- Mapper
原本的mybatis-config-old.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>
<typeAliases>
<package name="com.pojo"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<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=UTC"/>
<property name="username" value="賬號"/>
<property name="password" value="密碼"/>
</dataSource>
</environment>
</environments>
<!--Mapper.XML-->
<mappers>
<mapper resource="com/mapper/UserMapper.xml"/>
</mappers>
</configuration>
UserMapper.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.mapper.UserMapper">
<!--select-->
<select id="getUsers" resultType="User">
select * from mybatis.user;
</select>
</mapper>
轉(zhuǎn)換成spring-mybatis,需要讓spring接管mybatis的一些配置。具體如下:
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>
<typeAliases>
<package name="com.pojo"/>
</typeAliases>
</configuration>
spring-config.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">
<!-- DataSource:use spring's datasource to replace mybatis' settings (c3p0 dbcp druid) -->
<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=UTC"/>
<property name="username" value="root"/>
<property name="password" value="XIAOLIkyo54123414"/>
</bean>
<bean id = "sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/mapper/*.xml"/>
</bean>
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
<bean id="userMapper" class="com.mapper.UserMapperImp">
<property name="sqlSession" ref="sqlSession"/>
</bean>
</beans>
具體的實(shí)現(xiàn)類:
public class UserMapperImp implements UserMapper {
SqlSession sqlSession;
public void setSqlSession(SqlSession sqlSession) {
this.sqlSession = sqlSession;
}
@Override
public List<User> getUsers() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return mapper.getUsers();
}
}
- 編寫數(shù)據(jù)源
- SqlSessionFactory
- SqlSessionTemplate
- 給接口加實(shí)現(xiàn)類(Imp)
- 實(shí)現(xiàn)類注入到Spring中
- 測試
test測試了原本的mybatis,test2使用的則是spring-myabtis。
public class TestCases {
@Test
public void test() {
String resource = "mybatis-config-old.xml";
SqlSessionFactory sqlSessionFactory;
try (InputStream inputStream = Resources.getResourceAsStream(resource);){
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> users = mapper.getUsers();
for (User user : users) {
System.out.println(user);
}
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
public void test2() {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
UserMapperImp userMapperImp = context.getBean("userMapper", UserMapperImp.class);
List<User> users = userMapperImp.getUsers();
for (User user : users) {
System.out.println(user);
}
}
}
- 測試(另一種實(shí)現(xiàn)方式,即官方幫我們實(shí)現(xiàn)了方式一,只需要繼承SqlSessionDaoSupport,注入sqlSessionFactory即可)
public class UserMapperImp2 extends SqlSessionDaoSupport implements UserMapper {
@Override
public List<User> getUsers() {
return getSqlSession().getMapper(UserMapper.class).getUsers();
}
}
<bean id="userMapper2" class="com.mapper.UserMapperImp2">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
@Test
public void test3() {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
UserMapper userMapper = context.getBean("userMapper2", UserMapper.class);
List<User> users = userMapper.getUsers();
for (User user : users) {
System.out.println(user);
}
}
事務(wù)回顧
- 回顧事務(wù)
- 一組業(yè)務(wù)當(dāng)成一個(gè)業(yè)務(wù)來做
- 重要,涉及到數(shù)據(jù)的一致性問題
- 確保完整性和一致性
事務(wù)ACID原則:
- Atomic 原子性
- Consistency 一致性
- Isolation 隔離性
- 多個(gè)業(yè)務(wù)可能操作同一個(gè)資源,防止數(shù)據(jù)損壞
- durability 持久性
- 事務(wù)一旦提交,無論系統(tǒng)發(fā)生什么問題,結(jié)果都不會再被影響
Spring中的事務(wù)管理
- 聲明式事務(wù):AOP
- 編程式事務(wù):在代碼中進(jìn)行事務(wù)管理
UserMapper.xml(delete語句寫錯(cuò)為deletes來測試事務(wù))
<?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.mapper.UserMapper">
<!--select-->
<select id="getUsers" resultType="User">
select * from mybatis.user;
</select>
<insert id="addUser" parameterType="User">
insert into mybatis.user (id, name, pwd) values (#{id}, #{name}, #{pwd})
</insert>
<delete id="deleteUser" parameterType="int">
deletes from mybatis.user where id=#{id}
</delete>
</mapper>
public class UserMapperImp extends SqlSessionDaoSupport implements UserMapper{
@Override
public List<User> getUsers() {
UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
mapper.addUser(new User(4, "KyoDante", "1111111"));
mapper.deleteUser(5);
return mapper.getUsers();
}
@Override
public int addUser(User user) {
UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
return mapper.addUser(user);
}
@Override
public int deleteUser(int id) {
UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
return mapper.deleteUser(id);
}
}
spring-config.xml
<!-- set up transactions -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- advice -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- which method -->
<!-- propagation features -->
<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"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="txPointCut" expression="execution(* com.mapper.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut" />
</aop:config>
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
UserMapper mapper = context.getBean("userMapper", UserMapper.class);
List<User> users = mapper.getUsers();
for (User user : users) {
System.out.println(user);
}
}
為什么需要事務(wù)?
- 不配置事務(wù),可能存在數(shù)據(jù)提交不一致的情況,如上例(增加成功,但是刪除失敗的時(shí)候,需要進(jìn)行回滾)
- 如果不用聲明式AOP來配置事務(wù),則需要通過代碼的方式來進(jìn)行。
- 事務(wù)涉及到數(shù)據(jù)的一致性和完整性問題。
總結(jié)回顧
- IOC
- 代理
- AOP