Spring学习?这一篇文章就够,史上最全!

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS MySQL,高可用系列 2核4GB
简介: Spring学习?这一篇文章就够,史上最全!



前言

Spring框架:IOC、AOP、JdbcTemplate、事务管理,带你一篇速通。


一、IOC概述及底层原理

1.概述和原理

  • IOC是控制反转的意思。使用对象时候由主动new对象转换成由外部提供对象,此过程中对象的创建权由程序转移到外部,这种思想叫做控制反转。即把对象创建和对象的调用过程交给spring进行管理。
  • 目的:降低耦合度。
  • 底层原理:xml配置,反射,工厂模式。
  • Spring提供IOC容器两种实现方式(两个接口)
    (1)BeanFactory:Spring内部使用的接口,不提倡开发人员使用。特点:加载配置文件时不会创建对象,获取对象时才会创建对象。
    (2)ApplicationContext:BeanFactory的子接口,提供了更多更强大的功能,一般由开发人员使用。特点:加载配置文件时会把配置文件里的对象进行创建。
    (3)核心:Spring提供了一个容器,称为IOC容器,用来充当IOC思想中的外部
    IOC容器负责对象的创建、初始化等一系列工作,被创建或被管理的对象在IOC容器中统称为Bean。
  • ApplicationContext两个常用的实现类:
    (1)FileSystemXmlApplicationContext:绝对路径,从盘符开始算起
    (2)ClassPathXmlApplicationContext:相对路径,从项目的src开始算起
  • IOC实现:依赖注入,即在容器中建立bean与bean之间的依赖关系的整个过程。

二、思路分析

  • IOC管理什么(bean对象)
  • 如何告知IOC去管理bean对象(通过配置文件)
  • 被管理的对象交给IOC容器,如何获取IOC容器?(接口)
  • IOC容器得到后,如何获取bean?(通过ApplicationContext接口的两个实现类的getBean方法获取bean实例创建对象)

三、IOC操作bean管理(基于xml,使用的是IDEA2022.3.3,maven仓库)

首先告诉大家本篇文章的所有使用的spring依赖版本:

<dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>6.0.9</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>6.0.9</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>6.0.9</version>
        </dependency>
        <dependency>
                <groupId>commons-logging</groupId>
                <artifactId>commons-logging</artifactId>
                <version>1.2</version>
            </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.8</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>6.0.9</version>
        </dependency>
    </dependencies>

1.xml实现bean管理

(1)创建对象

项目的resource资源文件夹创建spring的配置文件,然后添加如下代码:

<bean id="book" class="com.dragon.spring5.Book"></bean>
  • id:创建对象的名称
  • class:Book类的路径
  • 创建对象时默认是执行无参构造函数

(2)注入属性

创建对象后,对象内的属性还没有赋值等等,讲诉三种注入属性得到方式:

  • 第一种:set方法注入
    这种方法需要类的属性有对应的set方法,如下:
public class Book {
    private String bname;
    private String bauthor;
    public void setBname(String bname) {
        this.bname = bname;
    }
    public void setBauthor(String bauthor) {
        this.bauthor = bauthor;
    }
    public void testbook(){
        System.out.println(bname+":"+bauthor);
    }
}

然后在spring配置文件中通过property标签进行属性注入,如下:

<bean id="book" class="com.dragon.spring5.Book">
      <property name="bname" value="易筋经"></property>
            <property name="bauthor" value="达摩老祖"></property>
</bean>

然后测试:

<!--加载spring配置文件创建对象-->
  ApplicationContext context=new ClassPathXmlApplicationContext("bean.xml");
  <!--反射-->
  Book book=context.getBean("book", Book.class);
  System.out.println(book);
  book.testbook();

  • 第二种:有参构造方法注入
    提供有参构造方法:
public class Book {
    private String bname;
    private String bauthor;
    public Book(String bname, String bauthor) {
        this.bname = bname;
        this.bauthor = bauthor;
    }
    public void testbook(){
        System.out.println(bname+":"+bauthor);
    }
}

然后在spring配置文件中,如下:

<bean id="book" class="com.dragon.spring5.Book">
          <constructor-arg name="bname" value="易筋经"></constructor-arg>
          <constructor-arg name="bauthor" value="达摩老祖"></constructor-arg>
</bean>

(3)p名称空间注入

set方法、测试的代码跟上诉一样,不再赘诉。

在spring配置文件中添加p名称空间,和配置代码:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       <!--添加p名称空间-->
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="book" class="com.dragon.spring5.Book" p:bname="九阴神功" p:bauthor="无名氏"></bean></beans>

(4)其他注入

1.注入的属性值是null或者有符号,如下:
<bean id="book" class="com.dragon.spring5.Book">
                <property name="bauthor">
                        <null/>
                </property>
                <property name="bname">
                        <value><![CDATA[<<读者>>]]></value>
                </property>
        </bean>
  • 有符号:用<![CDATA[带符号的属性值(包括符号)]]>
  • 空值:< null />

2.注入外部bean,如下:

spring配置文件:

  • ref:引用外部bean的id值
<bean id="userService" class="com.dragon.spring5.service.UserService">
        <property name="userDao" ref="userDaoImpl"></property>
    </bean>
    <bean id="userDaoImpl" class="com.dragon.spring5.dao.UserDaoImpl"></bean>

UserService、UserDao、UserDaoImpl、测试类代码:

package com.dragon.spring5.dao;
public interface UserDao {
    public void update();
}
=============================================================
package com.dragon.spring5.dao;
public class UserDaoImpl implements UserDao{
    @Override
    public void update() {
        System.out.println("dao update.......");
    }
}
=========================================================
package com.dragon.spring5.service;
import com.dragon.spring5.dao.UserDao;
public class UserService {
    private UserDao userDao;
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
    public void add(){
        System.out.println("service add........");
        userDao.update();
    }
}
======================================================
ApplicationContext context=new ClassPathXmlApplicationContext("bean1.xml");
UserService userService=context.getBean("userService", UserService.class);
userService.add();

3.内部bean注入

不通过ref属性,而是通过嵌套一个bean标签实现

spring配置文件:

<bean id="emp" class="com.dragon.spring5.bean.Emp">
            <property name="enanme" value="lucy"></property>
            <property name="gender" value="女"></property>
            <property name="dept">
                <bean id="dept" class="com.dragon.spring5.bean.Dept">
                    <property name="dname" value="保安部门"></property>
                </bean>
            </property>
        </bean>

Emp、Dept、测试类代码:

package com.dragon.spring5.bean;
public class Emp {
    private String enanme;
    private String gender;
    private Dept dept;
    public Dept getDept() {
        return dept;
    }
    public void setDept(Dept dept) {
        this.dept = dept;
    }
    public void setEnanme(String enanme) {
        this.enanme = enanme;
    }
    public void setGender(String gender) {
        this.gender = gender;
    }
    public void add(){
        System.out.println(enanme+"::"+gender+"::"+dept);
    }
}
===================================================================
package com.dragon.spring5.bean;
public class Dept {
    private String dname;
    public void setDname(String dname) {
        this.dname = dname;
    }
    @Override
    public String toString() {
        return "Dept{" +
                "dname='" + dname + '\'' +
                '}';
    }
}
==========================================================================
ApplicationContext context1=new ClassPathXmlApplicationContext("bean2.xml");
        Emp emp=context1.getBean("emp", Emp.class);
        emp.add();

4.级联赋值

写法一:也就是上面所说的外部bean,通过ref属性来获取外部bean

写法二:

emp类中有ename和dept两个属性,其中dept有dname属性,写法二需要emp提供dept属性的get方法。

<bean id="emp" class="com.dragon.spring5.bean.Emp">
                    <property name="enanme" value="john"></property>
                    <property name="gender" value="男"></property>
                    <!--写法一-->
                    <property name="dept" ref="dept"></property>
                    <!--写法二-->
                    <property name="dept.dname" value="技术部"></property>
                </bean>
                <bean id="dept" class="com.dragon.spring5.bean.Dept">
                    <property name="dname" value="财务部"></property>
                </bean>
5.注入集合

stu类:

package com.dragon.spring5.collectiontype;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class Stu {
    private String[] courses;
    private List<String> list;
    private Map<String,String> maps;
    private Set<String> sets;
    private List<Course> courselist;
    public void setCourselist(List<Course> courselist) {
        this.courselist = courselist;
    }
    public void setSets(Set<String> sets) {
        this.sets = sets;
    }
    public void setCourses(String[] courses) {
        this.courses = courses;
    }
    public void setList(List<String> list) {
        this.list = list;
    }
    public void setMaps(Map<String, String> maps) {
        this.maps = maps;
    }
    public void test(){
        System.out.println(Arrays.toString(courses));
        System.out.println(list);
        System.out.println(maps);
        System.out.println(sets);
        System.out.println(courselist);
    }
}

Course类:

package com.dragon.spring5.collectiontype;
public class Course {
    private String cname;
    public void setCname(String cname) {
        this.cname = cname;
    }
    @Override
    public String toString() {
        return "Course{" +
                "cname='" + cname + '\'' +
                '}';
    }
}

spring配置文件:

<bean id="stu" class="com.dragon.spring5.collectiontype.Stu">
        <property name="courses">
            <array>
                <value>java课程</value>
                <value>数据库课程</value>
            </array>
        </property>
        <property name="list">
            <list>
                <value>张三</value>
                <value>小三</value>
            </list>
        </property>
        <property name="maps">
            <map>
                <entry key="JAAVA" value="java"></entry>
                <entry key="PHP" value="php"></entry>
            </map>
        </property>
        <property name="sets">
            <set>
                <value>MySQL</value>
                <value>Redis</value>
            </set>
        </property>
        <!--外部bean注入-->
        <property name="courselist">
            <list>
                <ref bean="course1"></ref>
                <ref bean="course2"></ref>
            </list>
        </property>
</bean>
<bean id="course2" class="com.dragon.spring5.collectiontype.Course">
       <property name="cname" value="MyBatis框架"></property>
</bean>

测试:

ApplicationContext context=new ClassPathXmlApplicationContext("bean3.xml");
        Stu stu=context.getBean("stu", Stu.class);
        stu.test();

集合提取出来注入:

用util标签(注意util标签怎么引入):

Book类:

package com.dragon.spring5.collectiontype;
import java.util.List;
public class Book {
    private List<String> list;
    public void setList(List<String> list) {
        this.list = list;
    }
    public void test(){
        System.out.println(list);
    }
}
<?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:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                          http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
        <util:list id="booklist">
            <value>易筋经</value>
            <value>九阳神功</value>
            <value>九阴真经</value>
        </util:list>
        <bean id="book" class="com.dragon.spring5.collectiontype.Book" scope="prototype">
            <property name="list" ref="booklist"></property>
        </bean>
</beans>

(5)FactoryBean(工厂bean)

普通bean在配置文件中定义的bean类型就是返回类型。而工厂bean在配置文件定义的bean类型可以和返回类型不一样。

下面的MyBean类中实现FactoryBean接口,重写getObject,getObjectType方法。

这里把getObject重写成返回Course对象的方法。

package com.dragon.spring5.factorybean;
import com.dragon.spring5.collectiontype.Course;
import org.springframework.beans.factory.FactoryBean;
public class MyBean implements FactoryBean<Course> {
    @Override
    public Course getObject() throws Exception {
        Course course=new Course();
        course.setCname("abc");
        return course;
    }
    @Override
    public Class<?> getObjectType() {
        return null;
    }
    @Override
    public boolean isSingleton() {
        return FactoryBean.super.isSingleton();
    }
}

spring配置文件:

<bean id="mybean" class="com.dragon.spring5.factorybean.MyBean"></bean>

测试:

注意这里实例化对象不是MyBean类型,是上面getObject方法返回的对象类型。

ApplicationContext context=new ClassPathXmlApplicationContext("bean5.xml");
        Course course=context.getBean("mybean", Course.class);
        System.out.println(course);

2.bean的作用域

  • 在Spring中,默认情况下bean创建的是单实例对象:
ApplicationContext context1=new ClassPathXmlApplicationContext("bean4.xml");
        Book book1=context1.getBean("book", Book.class);
        Book book2=context1.getBean("book", Book.class);

可以看出地址一样

  • bean有个属性是scope,可以通过设置成singleton或prototype来决定其是单实例还是多实例(1)singleton:默认值,表示单实例对象。加载配置文件时就会创建单实例对象。
    (2)prototype:表示多实例对象。不是在加载配置文件时创建对象,在调用getBean方法时创建多实例对象。
<bean id="book" class="com.dragon.spring5.collectiontype.Book" scope="prototype">
            <property name="list" ref="booklist"></property>
</bean>

  • scope的值还可以是request、session(大家应该知道这两个值的作用域吧)

3.bean的生命周期

(1)通过构造器创建 bean 实例(无参数构造)

(2)为 bean 的属性设置值和对其他 bean 引用(调用 set 方法)

(3)把 bean 实例传递 bean 后置处理器的方法 postProcessBeforeInitialization

(4)调用 bean 的初始化的方法(需要进行配置初始化的方法)

(5)把 bean 实例传递 bean 后置处理器的方法 postProcessAfterInitialization

(6)bean 可以使用了(对象获取到了)

(7)当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)

下面提供大家测试需要用的类,方便大家理解:

Orders类:

package com.dragon.spring5.bean;
public class Orders {
    private String oname;
    public Orders() {
        System.out.println("第一步 执行无参数构造创建bean实例");
    }
    public void setOname(String oname) {
        this.oname = oname;
        System.out.println("第二步 调用set方法设置属性值");
    }
    public void initMethod(){
        System.out.println("第三步 执行初始化方法");
    }
    public void destoryMethod(){
        System.out.println("第五步 bean销毁的方法");
    }
}

后置处理器:

package com.dragon.spring5.bean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class MyBeanPost implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("在初始化之前执行的方法");
        return bean;
    }
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("在初始化之后执行的方法");
        return bean;
    }
}

测试:

package com.dragon.spring5.testDemo;
import com.dragon.spring5.bean.Orders;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class test3 {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext("bean6.xml");
        Orders orders=context.getBean("orders", Orders.class);
        System.out.println("第四步 获取创建beam实例对象");
        System.out.println(orders);
        context.close();
    }
}

spring配置文件:

</bean>
<!--        后置处理器-->
        <bean id="myBeanPost" class="com.dragon.spring5.bean.MyBeanPost"></bean>

4.xml自动配置属性值

bean标签属性autowire两个常用值:

(1)byName:根据属性名称注入,注入值bean的id值和类属性名称一样

(2)byType:根据属性类型注入

spring配置文件:

(Emp类中有dept属性)

<bean id="emp" class="com.dragon.spring5.autowrite.Emp">
                <property name="dept" ref="dept"></property>
</bean>
<bean id="dept" class="com.dragon.spring5.autowrite.Dept"></bean>

正常情况下属性值需要用property标签注入。

而用autowire,可以自动注入

<bean id="emp" class="com.dragon.spring5.autowrite.Emp" autowire="byName"></bean>
        <bean id="dept" class="com.dragon.spring5.autowrite.Dept"></bean>

5.外部属性文件操作bean

这里用连接数据库举例:

在resource文件夹内jdbc属性文件(jdbc.properties)

pro.driverClass=com.mysql.jdbc.Driver
pro.url=jdbc:mysql://localhost:3306/?(这个根据自己数据库配置)
pro.username=root
pro.password=root

spring配置文件(注意context配置,这里使用的< context:property-placeholder/ >标签引入属性文件,其中classpath就是配置属性文件的全称):

<?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:property-placeholder location="classpath:jdbc.properties"/>
        <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
                <property name="driverClassName" value="${pro.driverClass}"></property>
                <property name="url" value="${pro.url}"></property>
                <property name="username" value="${pro.username}"></property>
                <property name="password" value="${pro.password}"></property>
        </bean>
</beans>

四、注解实现bean管理

1.基于注解创建对象

spring提供四种创建对象的注解(下面四个注解说的一般使用在哪层是约定俗成的,其实效果都一样,都可以混用):

  • @Component
  • @Service:一般用于业务逻辑或Service层
  • @Controller:一般用于web层
  • @ Repository:一般用于Dao层
    步骤:
    (1)引入依赖(开头已告知所有maven依赖库)
    (2)开启组件扫描:扫描base-package包下所有有注解的类并为其创建对象
<context:component-scan base-package="com.dragon.spring5_1"></context:component-scan>

代表扫描com.dragon.spring5_1下的所有文件,找寻有注解的文件,然后创建实例。

(2)创建类并在类上创建对象注解

@Service(value = "userService")
public class UserService {
    public void add(){
        System.out.println("service add.......");
    }
}

测试:

ApplicationContext context=new ClassPathXmlApplicationContext("bean9.xml");
        UserService userService=context.getBean("userService",UserService.class);
        System.out.println(userService);
        userService.add();

2.组件扫描细节

还有两种用法:

设置不扫描的注解
      <context:component-scan base-package="com.dragon.spring5_1">
                <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Component"/>
        </context:component-scan>
    设置扫描的注解
        <context:component-scan base-package="com.dragon.spring5_1" use-default-filters="false">
                <context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/>
        </context:component-scan>

3.基于注解进行属性注入

  • @Autowired:根据属性类型自动装配
  • @Qualifier:根据属性名称自动装配,需要和@Autowired一起使用
    当遇到一个接口有很多实现类时,只通过@Autowire是无法完成自动装配的,所以需要再使用@Qualifier通过名称来锁定某个类
  • @Resource:可以根据类型注入,也可以根据名称注入(不常用)
  • @Value:注入属性值
    实例:
    UserDao、UserDaoImpl、UserService类:
package com.dragon.spring5_1.dao;
import org.springframework.stereotype.Repository;
public interface UserDao {
    public void add();
}
==================================================
package com.dragon.spring5_1.dao;
import org.springframework.stereotype.Repository;
@Repository(value = "userDaoImpl1")
public class UserDaoImpl implements UserDao{
    @Override
    public void add() {
        System.out.println("dao add......");
    }
}
=========================================================
package com.dragon.spring5_1.service;
import com.dragon.spring5_1.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service(value = "userService")
public class UserService {
    @Value(value = "abc")
    private String name;
    @Autowired
    @Qualifier(value ="userDaoImpl1")
    private UserDao userDao;
    public void add(){
        System.out.println("service add......."+name);
        userDao.add();
    }
}

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"
       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.dragon.spring5_1"></context:component-scan>
</beans>

测试:

ApplicationContext context=new ClassPathXmlApplicationContext("bean9.xml");
        UserService userService=context.getBean("userService",UserService.class);
        System.out.println(userService);
        userService.add();

五、完全注解开发

上面讲诉后,完全注解开发应该是最简洁的。来个实例看一看。

这时要引入配置类来代替spring配置文件,来实现组件扫描不用xml配置,进而实现完全注解开发。

配置类:

package com.dragon.spring5_1.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration//配置类
@ComponentScan(basePackages = {"com.dragon.spring5_1"})//配置扫描的文件
public class SpringConfig {
}

测试(UserDao、UserDaoImpl、UserService还是上面基于注解属性注入例子的):

package com.dragon.spring5_1.testDemo;
import com.dragon.spring5_1.config.SpringConfig;
import com.dragon.spring5_1.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class test2 {
    public static void main(String[] args) {
      //注意new的对象变了,变成了AnnotationConfigApplicationContext
        ApplicationContext context=new AnnotationConfigApplicationContext(SpringConfig.class);
        UserService userService=context.getBean("userService", UserService.class);
        System.out.println(userService);  
        userService.add();
    }
}

六、AOP概述和原理

用的依赖(包括上篇文章讲诉的IOC依赖):

<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>6.0.9</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>6.0.9</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>6.0.9</version>
        </dependency>
        <dependency>
                <groupId>commons-logging</groupId>
                <artifactId>commons-logging</artifactId>
                <version>1.2</version>
            </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.8</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>6.0.9</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>6.0.9</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.19</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>aopalliance</groupId>
            <artifactId>aopalliance</artifactId>
            <version>1.0</version>
        </dependency>
        <dependency>
            <groupId>net.sourceforge.cglib</groupId>
            <artifactId>com.springsource.net.sf.cglib</artifactId>
            <version>2.1.3</version>
        </dependency>

1.概述和原理

  • AOP:面向切面编程。利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。通俗来说就是在不修改代码的情况下添加新的功能。
  • 底层通过动态代理实现:
    (1)有接口情况:使用JDK动态代理,即创建接口实现类的代理对象。
    (2)无接口情况:使用CGLIB动态代理,即创建当前类子类的代理对象。

2.JDK动态代理实例

核心:

  • 通过 java.lang.reflect.Proxy类 的 newProxyInstance方法 创建代理类。
  • newProxyInstance方法

    参数一:类加载器
    参数二:所增强方法所在的类,这个类实现的接口,支持多个接口
    参数三:实现InvocationHandle接口,重写invoke方法来添加新的功能
    模拟代理:
    UserDao、UserDaoImpl类:
package com.dragon.springaop;
public interface UserDao {
    public int add(int a,int b);
    public String update(String id);
}
================================================
package com.dragon.springaop;
public class UserDaoImpl implements UserDao{
    @Override
    public int add(int a,int b) {
        System.out.println("add方法执行了...");
        return a+b;
    }
    @Override
    public String update(String id) {
        System.out.println("update方法执行了...");
        return id;
    }
}

JDKProxy类(通过 java.lang.reflect.Proxy类 的 newProxyInstance方法创建代理类):

package com.dragon.springaop;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
public class JDKProxy {
    public static void main(String[] args) {
        Class[] interfaces={UserDao.class};//创建接口实现类代理对象
        UserDaoImpl userDao=new UserDaoImpl();
        UserDao dao=(UserDao)Proxy.newProxyInstance(JDKProxy.class.getClassLoader(),interfaces,new UserDaoProxy(userDao)) ;
        int result=dao.add(1,2);
        System.out.println("result:"+result);
    }
}
class UserDaoProxy implements InvocationHandler{
    //创建的是谁的代理对象,把谁传递过来
    //有参构造传递
    private Object obj;
    public UserDaoProxy (Object obj){
        this.obj=obj;
    }
  //参数含义:代理对象、方法、参数
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("方法之前执行...."+ method.getName()+":传递参数...."+ Arrays.toString(args));
        Object res=method.invoke(obj,args);
        System.out.println("方法之后执行...."+obj);
        return res;
    }
}

其实JDK动态代理本质就是在原来要增强的方法前面或后面增加一些逻辑处理等,而不修改源代码。从上面运行结果看以看出,add方法执行结果是3,但是在执行add方法执行多一些语句输出,这些就是我们增加的逻辑处理部分。其中method.invoke()就是模拟的原add方法执行,而前后的输出语句是模拟的增强的代码

七、基于AspectJ实现的AOP操作

1.什么是AspectJ

AspectJ不是Spring组成部分,独立AOP框架,一般把AspectJ和Spring框架一起使用进行AOP操作。

2.AOP相关术语

  • 连接点:类中可以被增强的方法,称为连接点。
  • 切入点:实际被增强的方法,称为切入点。
  • 通知:增强的那一部分逻辑代码。通知有多种类型:
    (1)前置通知:增强部分代码在原代码前面。
    (2)后置通知:增强部分代码在原代码后面。
    (3)环绕通知:增强部分代码既有在原代码前面,也有在原代码后面。
    (4)异常通知:原代码发生异常后才会执行。
    (5)最终通知:类似与finally那一部分
  • 切面:指把通知应用到切入点这一个动作。

3.切点表达式

  • 语法:execution([权限修饰符] [返回类型] [类全路径] [方法名称] [参数列表])
  • 例1:对 com.atguigu.dao.BookDao 类里面的 add 进行增强
execution(* com.auguigu.dao.BookDao.add(..))
  • 例2:对 com.atguigu.dao.BookDao 类里面的所有的方法进行增强
execution(* com.atguigu.dao.BookDao.*(..))
  • 例3:对 com.atguigu.dao 包里面所有类,类里面所有方法进行增强
execution(* com.atguigu.dao.*.* (..))

3.基于注解方式实现

(1)实例:

User:

注解方式创建User对象user

package com.dragon.springaop.anno;
import org.springframework.stereotype.Component;
@Component
public class User {
    public void add(){
        System.out.println("add....");
    }
}

UserProxy类(先忽略@Order注解):

注解方式创建UserProxy对象userProxy

@Aspect定义UserProxy为切面类(即指把通知应用到切入点的类)

package com.dragon.springaop.anno;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.junit.jupiter.api.Order;
import org.springframework.stereotype.Component;
@Component
@Aspect//定义切面类
@Order(3)
public class UserProxy {
    @Before(value = "execution(* com.dragon.springaop.anno.User.add(..))")
    public void before(){
        System.out.println("before.....");
    }
    @AfterReturning(value = "execution(* com.dragon.springaop.anno.User.add(..))")
    public void afterReturning(){
        System.out.println("afterReturning....");
    }
    @After(value = "execution(* com.dragon.springaop.anno.User.add(..))")
    public void after(){
        System.out.println("after....");
    }
    @AfterThrowing(value = "execution(* com.dragon.springaop.anno.User.add(..))")
    public void afterThrowing(){
        System.out.println("afterThrowing....");
    }
    @Around(value = "execution(* com.dragon.springaop.anno.User.add(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint)throws Throwable{
        System.out.println("环绕之前....");
            proceedingJoinPoint.proceed();
        System.out.println("环绕之后....");
    }
}

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"
       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
                                http://www.springframework.org/schema/aop  http://www.springframework.org/schema/aop/spring-aop.xsd">
         <!--开启组件扫描-->
        <context:component-scan base-package="com.dragon.springaop.anno"/>
        <!--开启AspectJ生成代理对象-->
        <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

测试:

ApplicationContext context=new ClassPathXmlApplicationContext("beanaop1.xml");
        User user=context.getBean("user", User.class);
        user.add();

可以看出Befoe(前置通知,在add前运行)、AfterRetruning(后置通知或返回通知,在add返回值后运行)、AfterThrowing(异常通知,在add出现异常后,这个没有运行,因为没异常,大家在add内添加 int i=1/0 测试一下,不在演示)、After(最终通知,在add执行完后)、Around(环绕通知,在add前和后)的运行时期。

(2)切入点提取

上面的UserProxy 类中有很多重复的切入点表达式,可以进行公共提取

@Pointcut(value = "execution(* com.dragon.springaop.anno.User.add(..))")
public void pointDemo(){    
}
//前置通知
@Before(value="pointDemo()")
public void before(){
    System.out.println("before....");
}

(3)设置增强类优先级

上面的实例只有UserProxy一个增强类,当有多个增强类时,可以使用@Order(数值)设置优先级执行。(数字越小优先级越高)

在上诉例子中多创建一个PeopleProxy类,设置@Order:

package com.dragon.springaop.anno;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.junit.jupiter.api.Order;
import org.springframework.stereotype.Component;
@Component
@Aspect
@Order(1)
public class PersionProxy {
    @Before(value = "execution(* com.dragon.springaop.anno.User.add(..))")
    public void persionBefore(){
        System.out.println("persionBefore.....");
    }
}

上面的UserProxy类中我设置的是Order(3),所以PeopleProxylei类先运行。

(4)完全注解开发

AOPConfig类:

package com.dragon.springaop.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@ComponentScan(basePackages = {"com.dragon.springaop"})
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AopConfig {
}

Book、BookProxy:

package com.dragon.springaop.aopxml;
import org.springframework.stereotype.Component;
@Component
public class Book {
    public void buy(){
        System.out.println("buy......");
    }
}
========================================
package com.dragon.springaop.aopxml;
public class BookProxy {
    public void before(){
        System.out.println("before....");
    }
}

测试:

ApplicationContext context=new AnnotationConfigApplicationContext(AopConfig.class);
        Book book=context.getBean("book", Book.class);
        book.buy();

4.基于xml配置文件实现(了解)

Book、BookProxy:

package com.dragon.springaop.aopxml;
import org.springframework.stereotype.Component;
public class Book {
    public void buy(){
        System.out.println("buy......");
    }
}
========================================
package com.dragon.springaop.aopxml;
public class BookProxy {
    public void before(){
        System.out.println("before....");
    }
}

spring配置文件:

<bean id="book" class="com.dragon.springaop.aopxml.Book"></bean>
    <bean id="bookProxy" class="com.dragon.springaop.aopxml.BookProxy"></bean>
<!--    配置aop增强-->
    <aop:config>
<!--        切入点-->
        <aop:pointcut id="p" expression="execution(* com.dragon.springaop.aopxml.Book.buy(..))"/>
<!--        配置切面-->
        <aop:aspect ref="bookProxy">
<!--            增强作用在具体的方法上-->
            <aop:before method="before" pointcut-ref="p"></aop:before>
        </aop:aspect>
    </aop:config>

测试:

ApplicationContext context=new ClassPathXmlApplicationContext("beanaop2.xml");
        Book book=context.getBean("book", Book.class);
        book.buy();

八、JdbcTemplate

1.相关依赖(AOP、IOC、JdbcTemplate都有)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.example</groupId>
    <artifactId>spring</artifactId>
    <version>1.0-SNAPSHOT</version>
    <properties>
        <maven.compiler.source>19</maven.compiler.source>
        <maven.compiler.target>19</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>5.9.2</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>6.0.9</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>6.0.9</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>6.0.9</version>
        </dependency>
        <dependency>
                <groupId>commons-logging</groupId>
                <artifactId>commons-logging</artifactId>
                <version>1.2</version>
            </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.16</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>6.0.9</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>6.0.9</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.19</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>aopalliance</groupId>
            <artifactId>aopalliance</artifactId>
            <version>1.0</version>
        </dependency>
        <dependency>
            <groupId>net.sourceforge.cglib</groupId>
            <artifactId>com.springsource.net.sf.cglib</artifactId>
            <version>2.1.3</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.33</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>6.0.9</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>6.0.9</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>6.0.9</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>compile</scope>
        </dependency>
            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjweaver</artifactId>
                <version>1.9.19</version>
            </dependency>
    </dependencies>
</project>

2.代码实战

(1)方法讲解

  • 增删改:
int update(String sql, Object... args);
  • 查询(返回某个值):
T queryForObject(String sql,Class<T> requiredType);
  • 查询(返回某个对象):
T queryForObject(String sql,Class<T> requiredType);
  • 查询(返回集合):
List<T> query(String sql,RowMapper<T> rowMapper,Object... args);
  • 批量增删改:
int[] batchUpdate(String sql,List<Object[]> batchArgs);

(2)代码实例

实体层——Book实体类:

package com.dragon.jdbc.entity;
public class Book {
    private String bookId;
    private String bookname;
    private String bstatus;
    public String getBookId() {
        return bookId;
    }
    public String getBookname() {
        return bookname;
    }
    public String getBstatus() {
        return bstatus;
    }
    public void setBookId(String bookId) {
        this.bookId = bookId;
    }
    public void setBookname(String bookname) {
        this.bookname = bookname;
    }
    public void setBstatus(String bstatus) {
        this.bstatus = bstatus;
    }
    @Override
    public String toString() {
        return "Book{" +
                "bookId='" + bookId + '\'' +
                ", bookname='" + bookname + '\'' +
                ", bstatus='" + bstatus + '\'' +
                '}';
    }
}

Dao层——BookDao类:

package com.dragon.jdbc.dao;
import com.dragon.jdbc.entity.Book;
import java.util.List;
public interface BookDao {
    public void add(Book book);//添加
    public void update(Book book);//修改更新
    public void delete(String id);//删除
    public int selectCount();//查找数量,返回int类型
    public Book findBookInfo(String id);//根据id查找某本书,返回对象
    public List<Book> findAllBook();//查找数据库内所有对象,返回集合
    public void bathAddBook(List<Object[]> bathArgs);//批量添加
    public void bathUpdateBook(List<Object[]> bathArgs);//批量修改
    public void bathDeleteBook(List<Object[]> bathArgs);//批量删除
}

BookDao实现类BookDaoImpl:

package com.dragon.jdbc.dao;
import com.dragon.jdbc.entity.Book;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import java.util.Arrays;
import java.util.List;
@Repository
public class BookDaoImpl implements BookDao{
    @Autowired
    private JdbcTemplate jdbcTemplate;
    @Override
    public void add(Book book) {
        String sql="insert into t_book values(?,?,?)";
        Object[] args={book.getBookId(),book.getBookname(),book.getBstatus()};
        int update=jdbcTemplate.update(sql,args);
        System.out.println(update);
    }
    @Override
    public void update(Book book) {
        String sql="update t_book set bookname=?,bstatus=? where book_id=?";
        Object[] args={book.getBookname(),book.getBstatus(),book.getBookId()};
        int update=jdbcTemplate.update(sql,args);
        System.out.println(update);
    }
    @Override
    public void delete(String id) {
        String sql="delete from t_book where book_id=?";
        int update=jdbcTemplate.update(sql,id);
        System.out.println(update);
    }
    @Override
    public int selectCount() {
        String sql="select count(*) from t_book";
         Integer count=jdbcTemplate.queryForObject(sql,Integer.class);
        return count;
    }
    @Override
    public Book findBookInfo(String id) {
        String sql="select * from t_book where book_id=?";
        Book book= jdbcTemplate.queryForObject(sql,new BeanPropertyRowMapper<Book>(Book.class),id);
        return book;
    }
    @Override
    public List<Book> findAllBook() {
        String sql="select * from t_book";
        List<Book> bookList=jdbcTemplate.query(sql,new BeanPropertyRowMapper<Book>(Book.class));
        return bookList;
    }
    @Override
    public void bathAddBook(List<Object[]> bathArgs) {
        String sql="insert into t_book values(?,?,?)";
        int[] ints=jdbcTemplate.batchUpdate(sql,bathArgs);
        System.out.println(Arrays.toString(ints));
    }
    @Override
    public void bathUpdateBook(List<Object[]> bathArgs) {
        String sql="update t_book set bookname=?,bstatus=? where book_id=?";
        int[] ints=jdbcTemplate.batchUpdate(sql,bathArgs);
        System.out.println(Arrays.toString(ints));
    }
    @Override
    public void bathDeleteBook(List<Object[]> bathArgs) {
        String sql="delete from t_book where book_id=?";
        int[] ints=jdbcTemplate.batchUpdate(sql,bathArgs);
        System.out.println(Arrays.toString(ints));
    }
}

service层——BookService:

package com.dragon.jdbc.service;
import com.dragon.jdbc.dao.BookDao;
import com.dragon.jdbc.entity.Book;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class BookService {
    @Autowired
    private BookDao bookDao;
    public void addBook(Book book){
        bookDao.add(book);
    }
    public void updateBook(Book book){
        bookDao.update(book);
    }
    public void deleteBook(String id){
        bookDao.delete(id);
    }
    public int findCount(){
       return bookDao.selectCount();
    }
    public Book finOne(String id){
        return bookDao.findBookInfo(id);
    }
    public List<Book> findAll(){
        return bookDao.findAllBook();
    }
    public void bathAdd(List<Object[]> bathArgs){
        bookDao.bathAddBook(bathArgs);
    }
    public void bathUpdate(List<Object[]> bathArgs){
        bookDao.bathUpdateBook(bathArgs);
    }
    public void bathDelete(List<Object[]> bathArgs){
        bookDao.bathDeleteBook(bathArgs);
    }
}

数据库连接配置——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"
       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:property-placeholder location="classpath:jdbc.properties"/>
        <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
                <property name="driverClassName" value="${pro.driverClass}"></property>
                <property name="url" value="${pro.url}"></property>
                <property name="username" value="${pro.username}"></property>
                <property name="password" value="${pro.password}"></property>
        </bean>
        <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
                <property name="dataSource" ref="dataSource"></property>
        </bean>
        <context:component-scan base-package="com.dragon.jdbc"></context:component-scan>
</beans>

properties配置文件——jdbc.properties:

需要自行修改数据库名称(我的是user_db)

pro.driverClass=com.mysql.cj.jdbc.Driver
pro.url=jdbc:mysql://localhost:3306/user_db?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&autoReconnect=true&useSSL=false
pro.username=root
pro.password=root

测试类:

package com.dragon.jdbc.test;
import com.dragon.jdbc.entity.Book;
import com.dragon.jdbc.service.BookService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.ArrayList;
import java.util.List;
public class test1 {
    public static void main(String[] args) {
        ApplicationContext context=new ClassPathXmlApplicationContext("bean8.xml");
        BookService bookService=context.getBean("bookService",BookService.class);
//        Book book=new Book();
//        book.setBookId("1");
//        book.setBookname("java");
//        book.setBstatus("a");
//        bookService.addBook(book);
//        Book book=new Book();
//        book.setBookId("1");
//        book.setBookname("javaup");
//        book.setBstatus("a");
//        bookService.updateBook(book);
//        bookService.deleteBook("1");
//        System.out.println( bookService.findCount());
//        Book book=bookService.finOne("1");
//        System.out.println(book);
//        System.out.println(bookService.findAll());
//        List<Object[]> bathArgs=new ArrayList<>();
//        Object[] o1={"3","java","j"};
//        Object[] o2={"4","c++","c"};
//        Object[] o3={"5","MySql","m"};
//        bathArgs.add(o1);
//        bathArgs.add(o2);
//        bathArgs.add(o3);
//        bookService.bathAdd(bathArgs);
//        List<Object[]> bathArgs=new ArrayList<>();
//        Object[] o1={"java001","j","3"};
//        Object[] o2={"c++002","c","4"};
//        Object[] o3={"MySql003","m","5"};
//        bathArgs.add(o1);
//        bathArgs.add(o2);
//        bathArgs.add(o3);
//        bookService.bathUpdate(bathArgs);
        List<Object[]> bathArgs=new ArrayList<>();
        Object[] o1={"3"};
        Object[] o2={"4"};
        bathArgs.add(o1);
        bathArgs.add(o2);
        bookService.bathDelete(bathArgs);
    }
}

九、事务及其参数含义

1.事务的四个特性

  • 原子性
  • 一致性
  • 隔离性
  • 持久性。

2.事务的传播行为(propagation)

Spring定义了7种传播行为:

传播属性 描述
REQUIRED 如果有事务在运行,当前的方法就在这个事务内运行,否则,就启动一个新的事务,并在自己的事务内运行
REQUIRED_NEW 当前的方法必须启动新事务,并在它自己的事务内运行,如果有事务正在运行,应该将它挂起
SUPPORTS 如果有事务在运行,当前的方法就在这个事务内运行,否则它可以不运行在事务中
NOT_SUPPORTED 当前方法不应该运行在事务中,如果有运行的事务,将它挂起
MANDATORY 当前的方法不应该运行在事务中,如果有运行的事务,就抛出异常
NESTED 如果有事务在运行,当前的方法就应该在这个事务的嵌套事务内运行,否则,就启动一个新的事务,并在它自己的事务内运行

这里只图解介绍一个,其他类推

3.事务隔离性

  • 脏读:一个未提交事务读取到另一个未提交事务的数据
    例:事务A读取到事务B修改后的数据,但是读取后事务B回滚了,此时A读取的是修改后的数据,但是修改撤销了。
  • 不可重复读:一个未提交的事务读取到另一个提交事务修改数据
    例:事务A和事务B读取同一个数据,但是事务B在读取后进行修改,然后提交,提交后事务A又读取这个数据,此时读取的是修改后的,跟上次读取的不一样。
  • 幻读(虚读):一个未提交的事务读取到另一个提交事务添加数据

4.事务的隔离级别(ioslation)

5.timeout(超时)

事务在一定时间内进行提交,如果不提交会进行回滚,默认值是-1,设置时间以秒为单位进行计算。

6.readOnly(是否只读)

读:查询,写:增删改

默认值是false,表示可以增删改查,设置true后只能查询。

7.rollbackFor(回滚)

设置出现哪些异常进行事务回滚。

8.noRollbackFor(不回滚)

设置出现哪些异常不进行事务回滚。

十、事务管理

Spring事务管理提供了一个接口,叫做事务管理器,这个接口针对不同的框架提供不同的实现类。

1.事务管理的两种形式

  • 编程式事务管理
    例:
try{
            //开启事务
            //进行业务操作
            userDao.reduceMoney();
            //模拟异常
            int i=10/0;
            userDao.addMoney();
            //没出现异常,事务提交
        }catch (Exception e){
            //异常,事务回滚
        }
  • 声明式事务管理(AOP原理)
    例:
@Service
@Transactional(timeout = -1,propagation = Propagation.REQUIRED,isolation = Isolation.READ_COMMITTED)
public class UserService{
    @Autowired
    private UserDao userDao;
    public void accountMoney(){
        userDao.reduceMoney();
        int i= 1 / 0;
        userDao.addMoney();
    }
}

2.注解实现声明式事务管理

就是上述声明式管理的例子,这里补充一下全部代码:

================userDao====================
package com.dragon.shiwu.dao;
public interface UserDao {
    public void addMoney();
    public void reduceMoney();
}
==============userDaoImpl===================
package com.dragon.shiwu.dao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
@Repository
public class UserDaoImpl implements UserDao{
    @Autowired
    private JdbcTemplate jdbcTemplate;
    @Override
    public void addMoney() {
        String sql="update t_account set money=money + ? where username = ?";
        jdbcTemplate.update(sql,100,"mary");
    }
    @Override
    public void reduceMoney() {
        String sql="update t_account set money=money-? where username=?";
        jdbcTemplate.update(sql,100,"lucy");
    }
}
==============userService============================
package com.dragon.shiwu.service;
import com.dragon.shiwu.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service
@Transactional(timeout = -1,propagation = Propagation.REQUIRED,isolation = Isolation.READ_COMMITTED)
public class UserService{
    @Autowired
    private UserDao userDao;
    public void accountMoney(){
        userDao.reduceMoney();
        int i= 1 / 0;
        userDao.addMoney();
    }
}
============Spring配置文件===========================(注意这里引入了tx命名空间和)
<?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:tx="http://www.springframework.org/schema/tx"
       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
                                http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
        <context:property-placeholder location="classpath:jdbc.properties"/>
        <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
                <property name="driverClassName" value="${pro.driverClass}"></property>
                <property name="url" value="${pro.url}"></property>
                <property name="username" value="${pro.username}"></property>
                <property name="password" value="${pro.password}"></property>
        </bean>
<!--创建JdbcTemplate对象-->
        <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <!--注入数据库连接池-->
                <property name="dataSource" ref="dataSource"></property>
        </bean>
        <context:component-scan base-package="com.dragon.shiwu"></context:component-scan>
<!--创建事务管理器-->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
                <property name="dataSource" ref="dataSource"></property>
        </bean>
<!--        开启事务注解-->
        <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
</beans>

运行前:

运行后:

3.xml实现声明式事务管理

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:tx="http://www.springframework.org/schema/tx"
       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/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">
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${pro.driverClass}"></property>
        <property name="url" value="${pro.url}"></property>
        <property name="username" value="${pro.username}"></property>
        <property name="password" value="${pro.password}"></property>
    </bean>
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
<!--    开启组件扫描-->
    <context:component-scan base-package="com.dragon.shiwu"></context:component-scan>
    <!--配置事务通知-->
    <tx:advice id="txadvice">
        <tx:attributes>
            <!--配置事务参数-->
            <tx:method name="accountMoney" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>
<!--    配置切入点和切面-->
    <aop:config>
<!--        配置切入点-->
        <aop:pointcut id="pt" expression="execution(* com.dragon.shiwu.service.UserService.*(..))"/>
<!--        配置切面-->
        <aop:advisor advice-ref="txadvice" pointcut-ref="pt"></aop:advisor>
    </aop:config>
</beans>

4.完全注解开发

TxConfig类:

package com.dragon.shiwu.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
@Configuration//配置类
@ComponentScan(basePackages = "com.dragon.shiwu")//组件扫描
@EnableTransactionManagement//开启事务
public class TxConfig {
    //创建数据库连接池
    @Bean
    public DruidDataSource getDruidDataSource(){
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        druidDataSource.setUrl("jdbc:mysql://localhost:3306/user_db");
        druidDataSource.setUsername("root");
        druidDataSource.setPassword("root");
        return druidDataSource;
    }
    //创建JdbcTemplate对象
    @Bean
    public JdbcTemplate getJdbcTemplate(DataSource dataSource){
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }
    //创建事务管理器
    @Bean
    public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource){
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource);
        return transactionManager;
    }
}

测试类:

package com.dragon.shiwu.test;
import com.dragon.shiwu.config.TxConfig;
import com.dragon.shiwu.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class test2 {
    public static void main(String[] args) {
        ApplicationContext context=new AnnotationConfigApplicationContext(TxConfig.class);
        UserService userService = context.getBean("userService",UserService.class);
        userService.accountMoney();
    }
}

总结

以上就是Spring的全部详细讲解。

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
2天前
|
前端开发 Java 开发者
Spring生态学习路径与源码深度探讨
【11月更文挑战第13天】Spring框架作为Java企业级开发中的核心框架,其丰富的生态系统和强大的功能吸引了无数开发者的关注。学习Spring生态不仅仅是掌握Spring Framework本身,更需要深入理解其周边组件和工具,以及源码的底层实现逻辑。本文将从Spring生态的学习路径入手,详细探讨如何系统地学习Spring,并深入解析各个重点的底层实现逻辑。
21 9
|
22天前
|
前端开发 Java 数据库
SpringBoot学习
【10月更文挑战第7天】Spring学习
33 9
|
23天前
|
XML Java 数据格式
Spring学习
【10月更文挑战第6天】Spring学习
19 1
|
27天前
|
Java 测试技术 开发者
springboot学习四:Spring Boot profile多环境配置、devtools热部署
这篇文章主要介绍了如何在Spring Boot中进行多环境配置以及如何整合DevTools实现热部署,以提高开发效率。
52 2
|
27天前
|
前端开发 Java 程序员
springboot 学习十五:Spring Boot 优雅的集成Swagger2、Knife4j
这篇文章是关于如何在Spring Boot项目中集成Swagger2和Knife4j来生成和美化API接口文档的详细教程。
48 1
|
27天前
|
Java API Spring
springboot学习七:Spring Boot2.x 拦截器基础入门&实战项目场景实现
这篇文章是关于Spring Boot 2.x中拦截器的入门教程和实战项目场景实现的详细指南。
19 0
springboot学习七:Spring Boot2.x 拦截器基础入门&实战项目场景实现
|
27天前
|
Java API Spring
springboot学习六:Spring Boot2.x 过滤器基础入门&实战项目场景实现
这篇文章是关于Spring Boot 2.x中过滤器的基础知识和实战项目应用的教程。
21 0
springboot学习六:Spring Boot2.x 过滤器基础入门&实战项目场景实现
|
28天前
|
Java 关系型数据库 MySQL
springboot学习五:springboot整合Mybatis 连接 mysql数据库
这篇文章是关于如何使用Spring Boot整合MyBatis来连接MySQL数据库,并进行基本的增删改查操作的教程。
46 0
springboot学习五:springboot整合Mybatis 连接 mysql数据库
|
28天前
|
Java 关系型数据库 MySQL
springboot学习四:springboot链接mysql数据库,使用JdbcTemplate 操作mysql
这篇文章是关于如何使用Spring Boot框架通过JdbcTemplate操作MySQL数据库的教程。
20 0
springboot学习四:springboot链接mysql数据库,使用JdbcTemplate 操作mysql
|
27天前
|
Java Spring
springboot 学习十一:Spring Boot 优雅的集成 Lombok
这篇文章是关于如何在Spring Boot项目中集成Lombok,以简化JavaBean的编写,避免冗余代码,并提供了相关的配置步骤和常用注解的介绍。
76 0
下一篇
无影云桌面