Spring筆記

感謝狂神,講解清晰,以下是原視頻【狂神說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)境搭建】

  1. 復(fù)雜類型
public class Address {
    private String address;

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}
  1. 真實(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;
}
  1. 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>
  1. 注入

這樣是空串,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

  1. 導(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>
  1. 使用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&amp;useUnicode=true&amp;characterEncoding=UTF8&amp;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&amp;useUnicode=true&amp;characterEncoding=UTF8&amp;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);
        }
    }
}
  1. 測試(另一種實(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ù)回顧

  1. 回顧事務(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
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 一、 Spring技術(shù)概述1、什么是Spring : Spring是分層的JavaSE/EE full-stack...
    luweicheng24閱讀 737評論 0 1
  • Spring 框架兩大核心機(jī)制(IoC、AOP) IoC(控制反轉(zhuǎn))/ DI(依賴注入) AOP(面向切面編程) ...
    Juntech閱讀 88評論 0 0
  • ssh struts:web層,ValueStack值棧,攔截器 hibernate mydatis: dao層 ...
    持續(xù)進(jìn)步者閱讀 363評論 0 1
  • 1.Spring整體架構(gòu) 1)核心容器(Core Container) Core模塊,主要包含了Spring框架基...
    Sponge1128閱讀 1,096評論 0 1
  • 在生活中,監(jiān)控用電量是一個(gè)很重要的功能,但并不是大多數(shù)家庭重點(diǎn)關(guān)注的問題。軟件系統(tǒng)的一些功能就像家里的電表一樣,這...
    yjaal閱讀 597評論 0 3