万字大章学习Spring

Spring

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

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

Spring的核心及优点

Spring的核心

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

Spring的优点
  1. 轻量级

    ​ Spring框架运行占用的资源校,运行效率高。Spring框架使用的jar都比较小,一般都在1M以下或几百kb,核心功能的所需jar总共再3M左右,但这种说法其实是仁者见仁智者见智,虽然核心功能的jar包小,但当我们在完整的使用Spring框架做项目时,我们所要集成的各种jar包也是相当庞大的。

  2. 非侵入式

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

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

    ​ 这里的耦合是指两个或两个以上的对象通过相互引用而彼此影响甚至结合的现象。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;

}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值