万字大章学习Spring

2022年05月15日 阅读数:6
这篇文章主要向大家介绍万字大章学习Spring,主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。

Spring

​ 传统的Java EE在解决企业级应用时会出现诸如开发效率低、开发难度大、实际性能能差等问题。Spring致力于Java EE应用的各类解决方案,不只仅专一于某一层的方案。Spring能够说是企业级应用开发的“一站式”选择。Spring贯穿表现层、业务层、持久层,而且能够和其它已有的框架无缝整合。传统的Java EE开发企业级应用时存在代码层层之间耦合性太高,后期扩展性低等问题。Spring则解决了业务层与其它各层之间的耦合,其特性表现为高内聚、低耦合。java

​ Spring即Spring Framework。是一个开源的、轻量级的企业应用开发框架,可以建立出松耦合、易测试、易维护的Java应用系统。坚持“毫不作重复轮子”的原则,对于已有较好解决方案的领域,Spring毫不作重复性的实现。例如对象持久化和ORM,Spring只对现有JDBC、MyBatis等技术提供支持,使之更加易用,而不是从新作一个实现。框架的主要优点之一就是分层架构,容许自行选择使用哪个组件。mysql

Spring的核心及优势

Spring的核心

​ Spring的核心是控制反转(IOC)和面向切面编程(AOP)。Spring根据代码的功能特色,使用IOC下降业务之间的耦合度。IOC使得主业务在相互调用的过程当中,不用本身建立要使用的对象,而是由Spring容器统一管理,自动“注入”(赋值)。而AOP使系统服务获得最大复用,不用咱们手工将系统级服务“混杂”到主业务逻辑中了,而是由Spring容器完成“织入”。程序员

Spring的优势

  1. 轻量级web

    ​ Spring框架运行占用的资源校,运行效率高。Spring框架使用的jar都比较小,通常都在1M如下或几百kb,核心功能的所需jar总共再3M左右,但这种说法实际上是仁者见仁智者见智,虽然核心功能的jar包小,但当咱们在完整的使用Spring框架作项目时,咱们所要集成的各类jar包也是至关庞大的。spring

  2. 非侵入式sql

    ​ 编写一些业务类时不须要继承Spring特定的类,经过配置完成依赖注入后就可使用,此时,Spring就没有侵入到咱们的业务类代码中。数据库

  3. 针对接口编程——解耦合express

    ​ 这里的耦合是指两个或两个以上的对象经过相互引用而彼此影响甚至结合的现象。Spring提供了IOC控制反转,由容器管理对象,对象的依赖关系。原来在程序代码中的对象建立方式,如今由容器完成,促进了低耦合,即对象之间解耦合。编程

  4. AOP编程的支持设计模式

    ​ AOP(面向切面编程),许多不容易用传统OOP(面向对象编程)实现的功能均可以经过AOP轻松应付在Spring中开发人员能够从繁杂的事务管理代码中解脱出来,经过声明的方式灵活进行事务的管理,提升开发效率和质量。

5.一站式框架

​ Spring自己也提供了数据访问功能和web功能,以及能够很好的管理其它框架。

Spring环境搭建

  1. 建立maven项目。
  2. pom.xml中添加依赖:
<!-- 进行Junit单元测试 -->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>provided</scope>
</dependency>

<!--依赖log4j的日志管理-->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

<!--提供了框架的基本组成部分,包括IOC和DI-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>4.1.6.RELEASE</version>
</dependency>

<!--提供了BeanFactory-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <version>4.1.6.RELEASE</version>
</dependency>

<!--上下文配置对象,提供一个框架式的对象访问方式-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>4.1.6.RELEASE</version>
</dependency>


<!--提供了强大的表达式语言-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-expression</artifactId>
    <version>4.1.6.RELEASE</version>
</dependency>
  1. 在resources下建立Spring的配置文件applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">


</beans>

注意: Schema和DTD的区别和联系

​ (1)联系:都是XML文件的验证器。

​ (2)Schema是DTD的升级版,可扩展性强。在一个xml文件中引入多个xsd文件,xmlns:自定义名称=“路径”;在一个xml文件中只能引入一个dtd文件。

  1. 建立实体类以及将实体类交给sprig管理实现IOC
public class User {
   
   
    private Integer id;
    private String name;
    private Integer age;
    private String sex;
    
    //get set 构造 tostring
}
<?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="user1" class="com.cwd.bean.User">

    </bean>

</beans>
  1. 测试
@Test
public void test1() {
   
   
    //1.加载配置文件
    ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");

    //2.得到User对象
    User user = (User) app.getBean("user1");
    user.setId(001);
    user.setName("张三");
    user.setAge(18);
    user.setSex("男");

    System.out.println(user);
}

Spring IOC

  1. IOC实现的好处:实现了代码间的解耦。

  2. 控制反转:

    (1)控制:建立对象的过程。

    (2)反转:建立对象的操做自己是程序员本身完成的,如今是反转给Spring进行建立。

Spring IOC建立对象的方式

  1. 使用无参构造
public class User {
   
   
    private Integer id;
    private String name;
    private Integer age;
    private String sex;

    public User() {
   
   

    }
}
<!--
        id:惟一标识
        class:当前建立对象的全局限定名
-->
<bean id="user1" class="com.cwd.bean.User">

</bean>
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = (User) app.getBean("user1");//这就是使用无参构造
  1. 使用有参构造
public class User {
   
   
    private Integer id;
    private String name;
    private Integer age;
    private String sex;

    public User(Integer id, String name, Integer age, String sex) {
   
   
        this.id = id;
        this.name = name;
        this.age = age;
        this.sex = sex;
    }
}
<!--使用有参构造建立对象-->
<bean id="user2" class="com.cwd.bean.User">
    <!--
        name值:构造方法中参数名相同;
        index值:构造方法中参数的位置,从0开始;
        value值:通常为简单类型赋值;
        ref:通常为引用类型赋值;
        type:值类型
    -->
    <constructor-arg name="id" value="003"/>
    <constructor-arg name="name" value="王五"/>
    <constructor-arg name="age" value="20"/>
    <constructor-arg name="sex" value=""/>
</bean>
@Test
public void test2() {
   
   
    ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
    User user = (User) app.getBean("user2");
    System.out.println(user);
}
  1. 使用工厂模式

    工厂模式属于设计模式的一种,使用工厂设计模式能够达到对象的统一管理,能够批量的产生某一类对象。

    实现方式

    (1)使用实例工厂类+无参

    //建立一个实例工厂
    public class UsersFactory {
         
         
        public User getInstance() {
         
         
            return new User(001,"张三",21,"女");
        }
    }
    
    <!--
        使用工厂模式建立对象
        1.建立工厂对象
        2.根据工厂对象,建立user对象,factory-bean是工厂对象,factory-method是工厂对象方法
    -->
    <bean id="userFactory" class="com.cwd.factory.UsersFactory"/>
    <bean id="user3" factory-bean="userFactory" factory-method="getInstance"/>
    

    (2)使用静态工厂类+无参

    public class UsersFactory {
         
         
        public static User getInstance() {
         
         
            return new User(004,"李四",31,"女");
        }
    }
    
    <!--
        使用静态工厂模式+无参
        直接指定哪一个工厂的哪一个方法生成对应的bean对象
    -->
    <bean id="user4" class="com.cwd.factory.UsersFactory" factory-method="getInstance"/>
    

    (3)使用实例工厂类+有参

    public class UsersFactory {
         
         
        public User getInstance(User user) {
         
         
            return user;
        }
    }
    
    <!--使用实例工厂类+有参-->
    <bean id="userFactory" class="com.cwd.factory.UsersFactory"/>
    <bean id="user5" factory-bean="userFactory" factory-method="getInstance">
        <constructor-arg name="user" ref="user1"/>
    </bean>
    

    (4)使用静态工厂类+有参

    public class UsersFactory {
         
         
        public static User getInstance(User user) {
         
         
            return user;
        }
    }
    
    <!--
        使用静态工厂类+有参
    -->
    <bean id="user6" class="com.cwd.factory.UsersFactory" factory-method="getInstance">
        <constructor-arg name="user" ref="user2"/>
    </bean>
    

Spring DI注入

DI:全称Dependency Injection,依赖注入。

利用DI进行解决

  1. 为何使用DI(依赖注入)?

    做用:给对象中的全局属性进行赋值的操做。

  2. 依赖注入是什么?

    依赖:一个类在另外一个类中做为全局属性时称做依赖。

    注入:经过Spring容器为本身的属性注入一个值。

  3. DI的意义?

    能够解除类与类之间的耦合度,并为对象中的全局对象进行赋值。

  4. 如何理解DI和IOC。

    Spring帮助建立对象的过程就叫作IOC,建立对象时给对象的全局对象赋值的操做叫作DI。

DI注入的方式

准备两个类:

public class User {
   
   
    private Integer id;
    private String name;
    private Integer age;
    private String sex;

    public User() {
   
   

    }

    public User(Integer id, String name, Integer age, String sex) {
   
   
        this.id = id;
        this.name = name;
        this.age = age;
        this.sex = sex;
    }
  
  	//get,set,toString()方法
}

public class Student {
   
   
    private Integer stuid;
    private String stuname;
    private Integer stuage;
    private User user;

    public Student() {
   
   

    }

    public Student(Integer stuid, String stuname, Integer stuage, User user) {
   
   
        this.stuid = stuid;
        this.stuname = stuname;
        this.stuage = stuage;
        this.user = user;
    }
  
  	//get,set,toString()方法
}
构造完成注入
<!--
    使用有参构造完成DI操做
    1.建立User对象;
    2.建立Student对象
-->
<bean id="user" class="com.cwd.bean.User">
    <constructor-arg name="id" value="003"/>
    <constructor-arg name="name" value="王五"/>
    <constructor-arg name="age" value="20"/>
    <constructor-arg name="sex" value=""/>
</bean>


<bean id="student" class="com.cwd.bean.Student">
    <constructor-arg name="stuid" value="001"/>
    <constructor-arg name="stuname" value="每天"/>
    <constructor-arg name="stuage" value="15"/>
    <constructor-arg name="user" ref="user"/>
</bean>
属性注入
<!--
    使用属性注入方式,要求属性有set方法
-->
<bean id="student1" class="com.cwd.bean.Student">
    <property name="stuid" value="002"/>
    <property name="stuname" value="晶晶"/>
    <property name="stuage" value="17"/>
    <property name="user" ref="user"/>
</bean>

不一样数据类型的注入方式

(1)String或基本类型

<property name="stuname" value="晶晶"/>

(2)对象类型

<property name="name" ref="name"/>

(3)属性是数组,能够和list相互替换

<property name="num">
		<array>
			<value>1</value>
			<value>2</value>
		</array>
</property>

(4)属性是List集合时,能够和array相互替换

<property name="num">
		<list>
			<value>1</value>
			<value>2</value>
		</list>
</property>

(5)属性是set集合

<property name="num">
		<set>
			<value>1</value>
			<value>2</value>
		</set>
</property>

(6)属性是map集合

<property name="map">
		<map>
		
			<entry>
				<key>
					<value></value>
				</key>
				<value></value>
			</entry>
			
			<entry>
				<key>
					<value></value>
				</key>
				<value></value>
			</entry>
			
		</map>
</property>
自动注入

​ 配置自动注入有两种方法:一是全局配置,一是局部配置。

​ 全局配置是在标签中配置default-autowrie=“XXX”。

​ 局部单独配置是对每个bean单独设置注入方式,单独配置是在中配置autowrie=“XXX”。

autowrie有五种取值:
(1)no:Spring不进行自动注入。
(2)byName:在Spring容器中查找与id与引用类型属性名相同的bean自动注入。
(3)byTye:在Spring容器中查找与id与引用类型数据类型相同的bean自动注入,多个类型相同的bean的id则报错。
(4)constrcutor:先使用byname进行,再根据bytye进行匹配。
(5)default:看全局default-autowrie属性的值。

Spring AOP

​ 面向切面编程,使系统服务获得最大复用,不用咱们手工将系统级服务“混杂”到主业务逻辑中了,而是由Spring容器完成“织入”。按照软件重构的思想,OOP是经过抽象相同的代码到父类(纵向抽取),但没法经过抽象父类消除重复性的横切代码,AOP就是为了解决将分散在各个业务逻辑代码中的相同代码经过横向切割的方式抽取到一个独立的模块。

​ 0.加强:向各个程序内部注入一些逻辑代码从而加强原有程序的功能。

​ 1.链接点(JoinPoint):类中能够被加强的方法,这个方法就就被称为链接点,切记链接点并非必定会被加强。

​ 2.切入点(Pointcut):类中实际被加强的方法。

​ 3.通知(Advice):指一个切面在特定的链接点要作的事情,简单来讲就是“加强”。能够分为方法执行前通知,方法执行后通知,环绕通知等等。

前置通知:方法执行前执行;
后置通知:方法执行后执行;
环绕通知:先后都执行;
异常通知:出异常时通知;
最终通知:如return后执行。

​ 4.切面(Aspect):把通知添加到切入点的过程就叫切面。

​ 5.目标(Target):代理的目标对象,即要加强的方法所在的类。

​ 6.代理(Proxy):向目标对象应用通知以后建立的代理对象。

代理模式实现AOP

什么是代理模式?
  1. 代理模式

    未解决某一类问题而产生的设计模式,分为静态代理和动态代理(JDK动态代理和CGLIB动态代理)。

  2. 代理模式的角色

    (1)抽象对象(抽象父类或接口):须要完成的功能。

    (2)被代理对象:隐藏起来的对象。

    (3)代理对象:暴露给其余人的对象,访问被代理对象经过代理对象进行访问。

  3. 代理模式的优势

    (1)被代理对象只须要完成好本身的业务便可。

    (2)加强了代码的可扩展性。

  4. 代理模式案例

    (1)抽象对象:租房

    (2)被代理对象:房东

    (3)代理对象:中介

  5. 实现静态代理的步骤

    (1)建立抽象对象: zufang接口

    public interface ZuFang {
         
         
        void zuFang() throws Exception;
    }
    

    (2)建立被代理对象

    public class FangDong implements ZuFang {
         
         
    
        @Override
        public void zuFang() throws Exception {
         
         
            System.out.println("出租房屋IFS写字楼89层");
        }
    }
    

    (3)建立代理对象

    public class FangDong implements ZuFang {
         
         
    
        @Override
        public void zuFang() throws Exception {
         
         
            System.out.println("出租房屋IFS写字楼89层");
        }
    }
    

    (4)调用

    public class ZuKe {
        //程序入口^o^
        public static void main(String[] args) {
            ZhongJie zhongJie = new ZhongJie();
            try {
                zhongJie.zuFang();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
动态代理

​ JDK动态代理和CGLib动态代理,第一个能够代理实现接口的类,第二个能够代理拥有父类的类。

​ 动态代理的底层是根据反射来实现的,只要给到代理对象产传递被代理的对象,就能够直接调用被代理对象中的方法。代理类能够代理任意类型的对象,被代理对象必须实现指定的借口。

基于AspectJ实现AOP

基于AspectJ的xml配置实现, 全部的配置都在spring.xml文件中进行。

  1. 导入实现AOP的AspectJ的jar
<!--Spring实现AOP是依靠Aspects框架实现-->
<!--Aspects相关jar-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.2.2.RELEASE</version>
</dependency>
  1. 建立一个加强功能的类
import org.aspectj.lang.ProceedingJoinPoint;
//通知(Advice):在链接点要作的事情
public class Aop {
   
   

    public void doLog() {
   
   
        System.out.println("=====保存日志=====");
    }

    public void commit() {
   
   
        System.out.println("=====提交事务=====");
    }

    public void around(ProceedingJoinPoint proceedingJoinPoint) {
   
   
        System.out.println("======方法前通知======");
        try {
   
   
            proceedingJoinPoint.proceed();//调用本身的方法
        } catch (Throwable throwable) {
   
   
            throwable.printStackTrace();
        }
        System.out.println("======方法后通知======");
    }

    public void throwable(Throwable throwable) {
   
   
        System.out.println("======出异常了======");
        System.out.println(throwable.getMessage());
    }
}
  1. 将装有加强功能的类交给spring容器管理
<bean name="aop" class="com.cwd.aopAJ.AOP"/>
  1. 配置切面(Aspect)

    先准备一个被加强的类,即目标(Target)

    //目标(Target):代理的目标对象,即要加强的类
    public class Target {
         
         
        /*
        链接点(Joinpoint),能够被加强的方法
        切入点(pointcut),实际被加强的方法,被加强了
        */
        public void pointCut() {
         
         
            System.out.println("这是一个保存的操做!!!");
            return;
        }
    }
    
  2. 将通知添加到切入点。

    须要引入命名空间
    xmlns:aop="http://www.springframework.org/schema/aop"
    
    <bean name="target" class="com.cwd.aopAJ.Target"/>
    <!--织入-->
    <aop:config>
        <!--
        配置切入点
        execution表达式 前*表示返回值 saveUser(..)表示要加强的方法 ..表示参数
        -->
    
        <aop:pointcut id="pointCut" expression="execution(* com.cwd.aopAJ.Target.pointCut(..))"/>
    
        <!--配置通知 ref中引用的是通知类的id-->
        <aop:aspect ref="aop">
    
            <!--前置通知-->
            <aop:before method="doLog" pointcut-ref="pointCut"/>
    
        </aop:aspect>
    </aop:config>
    
  3. 测试

    @Test
    public void test1() {
         
         
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        Target target = (Target) app.getBean("target");
        target.pointCut();
    }
    

AspectJ实现五种通知类型

  1. 前置通知
<!--织入-->
<aop:config>
    <aop:pointcut id="pointCut" expression="execution(* com.cwd.spring4pro.demo.Target.pointCut(..))"/>
    <aop:aspect ref="aop">
        <!--前置通知-->
        <aop:before method="doLog" pointcut-ref="pointCut"/>
    </aop:aspect>
</aop:config>
  1. 后置通知
<!--织入-->
<aop:config>
    <aop:pointcut id="pointCut" expression="execution(* com.cwd.spring4pro.demo.Target.pointCut(..))"/>
    <aop:aspect ref="aop">
        <!--后置通知-->
        <aop:after method="commit" pointcut-ref="pointCut"/>
    </aop:aspect>
</aop:config>
  1. 环绕通知
<!--织入-->
<aop:config>
    <aop:pointcut id="pointCut" expression="execution(* com.cwd.spring4pro.demo.Target.pointCut(..))"/>
    <aop:aspect ref="aop">
        <!--环绕通知-->
        <aop:around method="around" pointcut-ref="pointCut"/>
    </aop:aspect>
</aop:config>
  1. 异常通知
<!--织入-->
<aop:config>
    <aop:pointcut id="pointCut" expression="execution(* com.cwd.spring4pro.demo.Target.pointCut(..))"/>
    <aop:aspect ref="aop">
        <!--异常通知-->
        <aop:after-throwing method="throwable" pointcut-ref="pointCut" throwing="throwable"/>
    </aop:aspect>
</aop:config>

  1. 最终通知
<!--织入-->
<aop:config>
    <aop:pointcut id="pointCut" expression="execution(* com.cwd.spring4pro.demo.Target.pointCut(..))"/>
    <aop:aspect ref="aop">
        <!--最终通知-->
        <aop:after-returning method="commit" pointcut-ref="pointCut"/>
    </aop:aspect>
</aop:config>

Spring中的经常使用注解

  1. @Component 建立类对象, 至关于配置< bean/>

    bean的ID默认为类名首字母小写, 也能够指定ID, 例如@Component(value = “user”)

  2. @Service与@Component功能相同

    写在Service层的类上

  3. @Repository与@Component功能相同

    写在数据访问层, dao/mapper层

  4. @Controller与@Component功能相同

    写在控制层类上

  5. @Resource 不须要写属性对象的get/set

    java中的注解, 默认按照名称注入, 若是没有1名称对象, 按照byType注入

  6. @Autowired 不须要写属性对象的get/set

    Spring的注解, 默认按照byte的方式注入

  7. @Value() 获取properties文件的内容

  8. @Pointcut() 定义切点

  9. @Aspect() 定义切面类

  10. @Before() 前置通知

  11. @After() 后置通知

  12. @AfterReturning() 后置通知, 必须在切点正确执行

  13. @AfterThrowing() 异常通知

  14. @Around() 环绕通知

注意:使用注解必定要在文件中声明注解扫描

<!--开启Spring注解扫描,须要注意context的约束,完善beans标签的属性-->
<context:component-scan base-package="须要扫描的java类的包名"> </context:component-scan>

<!--开启aop注解标签-->
<aop:aspectj-autoproxy/>

Spring事务

​ 事务能够看作是数据库的若干操做组成的一个单元。

​ 咱们在开发企业级应用时,用户的一个操做实际倒是对数据读写多步操做的结合。因为数据操做在顺序执行中,任何一步都有可能发生异常,致使后续的操做没法完成,此时因为业务逻辑所有完成,以前操做的数据并不可靠,须要在这种状况下进行回退(回滚),将数据恢复到用户操做以前。

​ 事务的做用就是为了保证用户的每个操做都是可靠的,事务的每一步操做都必须完成,只要发生异常就回退到事务未开始操做的状态,这些操做要么所有完成,要么所有取消,从而保证数据知足一致性的要求。

Spring事务特征

  1. 原子性(Atomicity):强调事务的不可分割性。

  2. 一致性(Consistency):事务的执行先后数据完整性保持一致。

  3. 隔离性(Isolation):一个事务在执行中不会受到其它事务的影响。

  4. 持久性(Durability):事务一旦结束,数据变化会持久到数据库。

Spring事务管理形式

  1. 编程式事务,这类事务管理形式在项目中不多使用,这种方式须要注入一个事务管理对象TransactionTemplate,而后再咱们的代码中须要提交事务或回滚事务时由咱们本身写代码实现。

  2. 声明式事务:这类事务管理创建在AOP的基础上,本质式对方法先后进行切面式的拦截,因此声明式事务是方法级别的。声明式事务管理方式有两种:分别是基于xml配置实现和基于注解的方式实现。

Spring事务管理的实现

​ 因为事务是数据库的操做集合,所以咱们对事务的管理都是基于数据库源操做的。

  1. 配置数据库源

(1)在pom.xml中导入Spring-jdbc的相关jar包,这里咱们使用阿里的德鲁伊数据库源

<!--Spring管理的JDBC-->
<!-- spring-jdbc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.2.2.RELEASE</version>
</dependency>

<!--这里以MySql为例,故导入MySql驱动包-->
<!-- mysql-connector-java -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.16</version>
</dependency>

<!-- 阿里数据源 数据库连接池-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.10</version>
</dependency>

(2)在Spring文件中配置数据库源,将数据库源交给spring进行管理

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       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/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--开启Spring注解扫描-->
    <context:component-scan base-package="com.cwd.spring6pro.demo1"> </context:component-scan>

    
    <!--配置DruidDataSource交给spring容器管理,spring管理数据库连接(数据源)-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis_db?characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai"></property>
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
        <property name="username" value="root"></property>
        <property name="password" value="root"></property>
    </bean>

</beans>
  1. 配置事务管理器

Spring针对不一样的dao框架,提供了不一样的实现类,JDBC和MyBtais的事务管理实现类是DataSourceTransactionManager。

配置spring事务管理器,并注入数据源。

<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<property name="dataSource" ref="dataSource"></property>
</bean>
  1. 基于xml配置实现

(1)配置事务传播行为

<!--配置事务的传播行为-->
<tx:advice id="txadvice" transaction-manager="dataSourceTransactionManager">
    <tx:attributes>
        <!--
        name:全部save开头的方法
        propagation:事务传播行为
        -->
    	<tx:method name="save*" propagation="REQUIRED"/>
    </tx:attributes>
</tx:advice>

(2)基于aop为不一样的类和方法开启事务管理

<!--织入-->
<aop:config>
    <!--链接点-->
    <aop:pointcut expression="execution(* com.cwd.spring6pro.demo1.dao.UserDao.*(..))" id="allmethod"/>
    <!--
    advice-ref:引入配置好的事务传播行为
    pointcut-ref:须要事务管理的类或方法
    -->
    <aop:advisor advice-ref="txadvice" pointcut-ref="allmethod"/>
</aop:config>
  1. 基于注解方式实现

(1)在配置好事务管理器后,开启注解事务管理。

<!--开始注解事务管理-->
<tx:annotation-driven transaction-manager="dataSourceTransactionManager"></tx:annotation-driven>

(2)在service层中经过注解控制事务

import com.cwd.spring6pro.demo1.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service(value = "userService")
@Transactional(propagation = Propagation.REQUIRED)
//@Transactional注解若是使用在类名上,那么这个类的全部方法都在事务中运行,也能够在方法上
public class UserService {
   
   

    @Autowired//注入注解
    private UserDao userDao;

    public void UserSave() {
   
   
        userDao.saveUser();
    }

    public void saveAdd() {
   
   
        userDao.save();
        userDao.add();
    }

}

Spring事务传播行为

​ 即然是传播,那么至少有两个东西才能够产生传播。单体是不存在传播的。事务传播行为(Propagation Behavior)指的就是当一个事务方法被另外一个事务方法调用时,这个事务方法应该如何继续进行。事务传播行为是Spring框架独有的事务加强特性,它不属于事务的实际提供方行为,即不属于数据库行为。

​ 举个例子,方法A事务调用方法B事务时,方法B是在方法A的事务中运行呢?仍是本身新开一个事务运行呢?这就由方法B的事务传播行为决定。

七种传播行为

  1. PROPAGATION_REQUIRED
    指定的方法必须在事务内执行,若当前存在事务,就加入到当前事务中,若当前没有事务,则自行建立一个新的事务,这种传播行为是最多见的,也是Spring框架默认的传播行为。

  2. PROPAGATION_SUPPORTS
    若是方法A调用方法B时,方法B配置了此传播行为,方法A有事务时,方法B就加入方法A的事务,若是方法A没有事务,方法B就以非事务的方式执行。

  3. PROPAGATION_REQUIRES_NEW
    老是新建一个事务,若是当前已经存在了一个事务,就把当前事务挂起,直到新建事务的结束。

  4. PROPAGATION_MANDATORY
    老是加入到当前事务,若是当前没有事务,就会抛出异常

  5. PROPAGATION_NOT_SUPPORTED
    老是以非事务的方式执行操做,若是当前存在一个事务,就把当前事务挂起。

  6. PROPAGATION_NEVER
    老是以非事务的方式执行操做,若是当前存在一个事务,就抛出异常。

  7. PROPAGATION_NESTED
    若是当前存在事务,则在嵌套的事务内执行。若是当前没有事务,就自行建立一个事务。

Spring集成MyBatis

​ Spring集成MyBtais的核心操做就是将SqlSessionFactory交给Spring进行管理,并由Spring管理对dao接口的代理实现。

  1. 导入MyBatis的相关jar

Spring结合MyBatis插件包

<!--mybatis-->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.4.2</version>
</dependency>

<!--spring-mybatis-->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>1.3.1</version>
</dependency>
  1. 配置SqlSessionFactory

因为上面文章中事务那一部分已经配置好了数据库源以下所示,所以这里咱们就直接使用。

(1)数据库源

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       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/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--开启Spring注解扫描-->
    <context:component-scan base-package="com.cwd.spring6pro.demo1"> </context:component-scan>

    
    <!--配置DruidDataSource交给spring容器管理,spring管理数据库连接(数据源)-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis_db?characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai"></property>
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
        <property name="username" value="root"></property>
        <property name="password" value="root"></property>
    </bean>

</beans>

(2)配置SqlSessionFactory

<!--spring管理SqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <!--注入数据库源-->
    <property name="dataSource" ref="dataSource"></property>
    <!--导入mybatis全局配置文件-->
    <property name="configLocation" value="MyBatisConfig.xml"></property>
    <!--扫描全部的映射文件-->
    <property name="mapperLocations" value="mapper/*Mapper.xml"> </property>
</bean>

  1. 指定生成代理接口
<!--扫描Dao中的全部接口并生成接口的代理对象-->
<bean id="mapperFactory" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.cwd.springmybatis"></property>
    <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>
  1. 使用

咱们此时就能够在Service中直接注入Dao的代理对象,此接口由Spring代理实现。

package com.cwd.ssm.service;

import com.cwd.ssm.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional
public class UserService {
   
   
  
    @Autowired
    UserDao userDao;

}