Spring

Spring

容器

一种轻量级java框架,用于整合其他框架

  • 核心:控制反转IOC面向切面编程AOP

  • 主要作用:解耦合

  • 特点:

    • 轻量级

      由20多个很小的jar包组成

      对代码无污染,不需要按照特定结构构建项目

    • 面向接口

      项目可扩展性,可维护性高

      接口不关心实现类类型,接口指向实现类,切换实现类即可切换整个功能

    • AOP 面向前面编程

      将公共的通用的重复的代码单独开发,在需要时返回

      底层原理是动态代理

    • 整合其他框架

      是其他第三方框架更容易使用

IOC 控制反转

是一种设计模式

将对象的创建和依赖关系的管理从代码中转移到容器spring

依赖注入DI实现IOC

容器 spring 负责管理对象之间的依赖关系。

spring通过XML配置文件注解来描述对象之间的依赖关系,并负责创建和管理这些对象,

开发者只需在配置文件或代码中声明对象及其依赖关系,Spring容器会根据配置来创建对象并完成依赖注入

这样,目标对象只需关注自身的业务逻辑,而不需要关心如何获取和创建所需的依赖对象

反转

由容器spring创建对象和依赖注入,容器掌握控制权

正转:程序员创建对象和依赖注入,程序员掌握控制权

三层架构项目

使用三层架构进行用户数据插入

非spring接管:界面层,业务逻辑层,数据访问层(模拟)

  • 实体类 org.example.pojo

    ​ Users (javabean)

    ​ {id,name,age}

  • 数据访问层 **org.example.dao ** 或 org.example.mapper

    • UsersMapper.java (接口)
    public interface usersMapper {
        int insert(users u);
    }
    
    • UsersMapperImpl.java (实现类)
    public class usersMapperImpl implements usersMapper {
        @Override
        public int insert(users u) {
            //模拟实现数据库插入的具体做法(sql)
            System.out.println(u.getUname()+"创建成功");
            return 1;
        }
    }
    

    原来需要写与UsersMapper同名映射文件UsersMapper.xml,并放到target同一地方

    但是这里还没讲到spring整合mybaitis

    所以模拟的建了个UsersMapperImpl.java

  • 业务逻辑层 org.example.service

    • UsersService.java (接口)
    public interface usersService {
        //增加用户
        int insert(users u);
    }
    
    • UsersServiceImpl.java (实现类)
    public class usersServiceImpl implements usersService {
        //在所有业务逻辑层都有 数据访问层对象
        private usersMapper usersMapper; //=new usersMapperImpl()程序做
    
        //加setter()交给spring做
        public void setUsersMapper(usersMapper usersMapper) {
            this.usersMapper = usersMapper;
        }
        @Override
        public int insert(users u) {
            //...可以进行复杂业务
            return usersMapper.insert(u);
        }
    }
    
  • 界面层 org.example.controller

    UsersController.java

    public class usersController {
        //所有界面层都有业务逻辑层对象
        private usersService usersService;  //new usersServiceImpl();程序员做
    
        //加setter()交给spring接管
        public void setUsersService(usersService usersService) {
            this.usersService = usersService;
        }
    
        //界面层功能实现,对外提供访问功能
        public int insert(users u){
            return usersService.insert(u);
        }
    }
    

XML方式实现ioc

  • 在pom.xml中引入spring依赖,并配置资源目录的路径,文件类型和名称

    <!--spring-->
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>6.0.11</version>
    </dependency>
    
    1. 标签:这个标签是 Maven POM 文件的顶级元素,用于指定构建相关的信息和配置
    2. 标签:这个标签定义了一个资源目录,可以包含多个 子标签,用于指定要包含的资源文件类型和名称模式
    3. 标签:这个标签用于指定资源目录的路径
    4. 标签:这个标签指定了要包含的文件类型和名称模式
<build>
  <resources>
    <resource>
      <directory>src/main/resources</directory>
      <includes>
        <include>**/*.xml</include>
        <include>**/*.properties</include>
      </includes>
    </resource>
  </resources>
</build>
  • 在main是resources中创建spring的核心配置文件applicationContext.xml

  • 在applicationContext.xml里创建学生对象spring创建

    <!--创建学生对象 = student s = new student();-->
    <bean id="s" class="org.example.pojo.student"></bean>
    

    id:创建的对象名称;

    class:创建的对象类型,底层通过反射构建对象

    程序员自己创建:

    在这里插入图片描述

  • 创建容器对象并启动

    ApplicationContext 容器对象名

    =new ClassPathXmlApplicationContext("核心配置文件")

    容器启动时,对象自动被创建

    //创建spring容器对象并启动
    ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
    
  • 从容器spring中取对象

    容器对象名.getBean("对象名")

    取出的对象是object类型

    //从spring中取对象
    student s= (student) ac.getBean("s");
    System.out.println(s);
    
  • 给创建的对象赋值

    引用类型可以用autowire来自动检索,参数:

    1. byName:相当于注解里的 @Autowired+@Qualifier(“对象名称”)

    2. byType:相当于注解里的 @Autowired

    • setter注入

      value:简单类型注入

      ref:引用类型注入

      必须提供无参构造setter方法才能使用被spring接管

      <!--创建爱好对象-->
      <bean id="h" class="org.example.pojo.hobby">
          <property name="food" value="apple" ></property>
          <property name="color" value="orange" />
      </bean>
      
      <!--创建学生对象-->
      <bean id="s" class="org.example.pojo.student">
          <property name="name" value="aaa"/>
          <property name="age" value="22"/>
          <property name="hobby" ref="h"/>
      </bean>
      

      在中如果是

    • 构造方法注入

      1. 使用构造方法参数名称注入值
      2. 使用构造方法参数下标注入值
      3. 使用默认构造方法参数顺序注入值
      <!--创建学生对象-->
      <bean id="s1" class="org.example.pojo.student">
          <constructor-arg name="name" value="aaa"/>  <!--使用构造方法名称注入-->
          <constructor-arg index="1" value="21"/>     <!--使用构造方法参数索引注入-->
          <constructor-arg ref="h"/>                  <!--使用构造方法默认顺序注入-->
      </bean>
      
      <!--创建爱好对象-->
      <bean id="h" class="org.example.pojo.hobby">
          <property name="food" value="apple" ></property>
          <property name="color" value="orange" />
      </bean>
      
  • 测试

    @Test
    public void test_student_constructor() {
        ApplicationContext ac=new ClassPathXmlApplicationContext("student_constructor/applicationContext.xml");
        Student s= (Student) ac.getBean("s");
        System.out.println(s);
    }
    

基于xml三层架构项目

  • 在pom.xml中导入依赖

    <!--添加spring依赖-->
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>6.0.11</version>
    </dependency>
    
  • 在resources中创建spring核心配置文件来接管

    • 自下而上创建(实现类)对象接口不行

    • 创建的对象若有成员变量必须提供setter无参构造(自动创建)

    <!--创建数据访问层对象-->
    <bean id="UMapper" class="org.example.dao.usersMapperImpl"/>
    
    <!--创建业务逻辑层对象-->
    <bean id="UService" class="org.example.service.usersServiceImpl">
        <property name="usersMapper" ref="UMapper"/>
    </bean>
    
    <!--创建界面层对象-->
    <bean id="UController" class="org.example.controller.usersController">
        <property name="usersService" ref="UService"/>
    </bean>
    
  • 给对应被spring创建的对象添加setter方法

    dao层没有创建变量不需要setter()

    • usersServiceImpl

      //spring接管:(必须有setter 和 无参构造/默认有/ )
      public void setUsersMapper(org.example.dao.usersMapper usersMapper) {
          this.usersMapper = usersMapper;
      }
      
    • usersController

      //想要被spring接管必须提供 setter() 和 无参构造/默认有/
      public void setUsersService(org.example.service.usersService usersService) {
          this.usersService = usersService;
      }
      
  • 测试

    //启动容器
    ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
    //取出对象(调用controller,其他自动嵌套调用)
    usersController uController = (usersController) ac.getBean("UController");
    //测试功能(增加)
    int a=uController.insert(new Users(10001,"aaa",22));
    

注解方式实现ioc

也称DI,是基于ioc思想的一种实现技术

创建哪个对象在哪个对象头上放对应注解,

在核心配置文件中加入包扫描就ok

  • 创建对象注解

    • @Component :可以创建任何类型对象

      创建的对象名默认为类名的驼峰命名法.

      也可以指定对象名称@Component(“自定义名称”)

    • @Controller:专门用来创建控制器对象(servlet),接收用户请求并且返回给客户端

      //界面层
      @Controller
      public class UsersController {
          //所有界面层都有业务逻辑层对象
          @Autowired
          private UsersService usersService;//程序员做=new UsersServiceImpl();
          //界面层功能实现,对外提供访问功能
          public int insert(Users u){
              return usersService.insert(u);
          }
      }
      
    • @Service:专门用来创建业务逻辑层对象,负责向下访问数据访问层,处理结果返回给界面层

      //业务逻辑层实现类
      @Service
      public class UsersServiceImpl implements UsersService {
          //在所有业务逻辑层都有 数据访问层对象
          @Autowired
          private UsersMapper usersMapper;//程序员做=new UsersMapperImpl();
          @Override
          public int insert(Users u) {
              //...可以进行复杂业务
              return usersMapper.insert(u);
          }
      }
      
    • @Repository:创建数据访问层对象,负责数据库中增删改查操作

      //数据访问层实现类
      @Repository
      public class UsersMapperImpl implements UsersMapper {
          @Override
          public int insert(Users u) {
              //模拟实现数据库插入的具体做法(sql)
              System.out.println(u.getUname()+"创建成功");
              return 1;
          }
      }
      
  • 普通类型依赖注入注解

    @Value:给简单类型(8钟基本类型 + String)注入

  • 引用类型依赖注入注解

    • @Autowired:使用类型注入值,

      从整个Bean工厂搜索同源类型对象进行注入

      同源类型:

      1. 被注入的类型和注入的类型是相同类型

      2. 被注入的类型()和注入的类型()是父子类

        按类型注入有多个可注入对象(父子类),

        此时按名称进行二次筛选选中与被注入对象名称相同的对象注入

      3. 被注入的类型(接口)和注入的类型(接口实现类)是接口和接口实现类类型

    • @Autowired+@Qualifier("对象名称"):使用名称注入值,从整个Bean工厂搜索同源类型对象进行注入

Studen类

@Component("stu") //交给spring创建对象,容器启动时创建.指定对象名字stu
public class Student {
    @Value("小明") //普通类型赋值
    private String name;
    @Value("21")
    private int age;
    
    //@Autowired //引用类型赋值 自动扫描包下被创建的对应类型(Hobby)
    //private Hobby hobby;
    
    @Autowired
    @Qualifier("ho") //引用类型赋值 自动扫描包下被创建的对应名称("ho")
    private Hobby hobby;
    ...
}

Hobby类

@Component("ho") //注解实现让spring创建对象,对象名ho
public class Hobby {
    @Value("cookie")
    private String food;
    @Value("red")
    private String color;
    ...
}

spring核心配置文件

<!--添加包扫描
创建容器时自动启动,调用无参构造-->
<context:component-scan base-package="org.example.pojo.comment"></context:component-scan>

扫描"org.example.pojo.comment",

Student和Hobby类都在此包下

测试

基于注解三层架构项目

  • 三层

    pojo:User{uid,uname,uage}

    • dao

      UsersMapper

      UsersMapperImpl

      //数据访问层实现类
      @Repository
      public class UsersMapperImpl implements UsersMapper {
          @Override
          public int insert(Users u) {
              //模拟实现数据库插入的具体做法(sql)
              System.out.println(u.getUname()+"创建成功");
              return 1;
          }
      }
      
    • service

      UsersService

      UsersServiceImpl

      //业务逻辑层实现类
      @Service
      public class UsersServiceImpl implements UsersService {
          //在所有业务逻辑层都有 数据访问层对象
          @Autowired
          private UsersMapper usersMapper;//程序员做=new UsersMapperImpl();
          @Override
          public int insert(Users u) {
              //...可以进行复杂业务
              return usersMapper.insert(u);
          }
      }
      
    • controller

      UsersController

      //界面层
      @Controller
      public class UsersController {
          //所有界面层都有业务逻辑层对象
          @Autowired
          private UsersService usersService;//程序员做=new UsersServiceImpl();
          //界面层功能实现,对外提供访问功能
          public int insert(Users u){
              return usersService.insert(u);
          }
      }
      
  • applicationContext中添加包扫描

    <!--单个包扫描-->
    <context:component-scan base-package="org.example.controller"/>
    <context:component-scan base-package="org.example.service"/>
    <context:component-scan base-package="org.example.dao"/>
    
    <!--扫根包,不推荐-->
    <context:component-scan base-package="org.example"/>
    
  • 测试

    @Test
    public void test_insert(){
        //创建并启动容器
        ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
        //取出userscontroller对象
        UsersController usersController = (UsersController) ac.getBean("usersController");
        //插入
        usersController.insert(new Users(10001,"user1",22));
    }
    

多人开发问题

配置文件拆分

因为可能有多个pojo类,多人操作可能乱

  • 按层拆:

    • controller

    • service

    • dao/mapper

  • 按功能拆

    • users

    • books

配置文件整合

  • 单个导入

    在resource下创建total.xml把所拆分的xml进来

    <import resource="applicationContext_mapper.xml"/>
    <import resource="applicationContext_service.xml"/>
    <import resource="applicationContext_controller.xml"/>
    
  • 批量导入

    <import resource="applicationContext_*.xml"/>
    

AOP 面向切面编程

是一种编程范式

用于将横切关注点从核心业务逻辑中分离出来

并以模块化的方式进行管理

Spring AOP通过代理机制实现切面的织入,

可以在运行时动态地将切面应用到目标对象中。

开发者可以使用注解或XML配置文件来定义切面和切点,以及指定通知的类型和执行顺序

切面

公共的 ,通用的 ,重复的功能

面向切面就是把切面编程提取出来,单独开发.实现业务代码,切面代码分离

在需要调用的方法中通过动态代理的方式织入

如日志记录、性能统计、安全控制、事务处理…

连接点

目标方法

目标方法中要实现目标方法的功能和切面功能

代理的方法

切入点

切入点(pointcut)指定切入位置

多个连接点构成切入点

切入点可以是一个目标方法,可以是一个类中所有方法,可以是某个包下所有类中方法

可用@Before(value=“execution(修饰符 返回类型 包.方法(参数))”)来切入

@Aspect
public class MyAspect{
    @Before(value="execution(* *...s.SomeService.*(..)))
    public void myAspect(){
        sout("前置日志")
    }
}

把myAspect方法切入到返回值任意,任意包中s.SomeService包下的任意方法中

通知

通知(advice)指定切入时机

  • 时机: 前/后/出错时/环绕

目标对象

被操作者

bookserviceimpl,produceserviceimpl…

AOP框架项目

业务: 图书购买

切面: 事务

  • 业务,切面紧耦合

    public class BookServiceImpl {
        public void buy()
        {
            try 
            {
                System.out.println("事务开启...");
                System.out.println("图书购买业务功能...");
                System.out.println("事务提交...");
            } 
            catch (Exception e) {System.out.println("事务回滚..."); }
        }
    }
    
  • 使用子类代理拆分业务,切面

    • BookServiceImpl

      public class BookServiceImpl {
          //在父类中只有图书馆里业务
          public void buy()
          {System.out.println("图书购买...");}
      }
      
    • SubBookServiceImpl (子类)

      public class SubBookServiceImpl extends BookServiceImpl{
          @Override
          public void buy()
          {
              try {
                  //事务开启切面
                  System.out.println("事务开启...");
                  //主业务实现
                  super.buy();
                  //事务提交切面
                  System.out.println("事务提交...");
              } 
              catch (Exception e) {System.out.println("事务回滚...");}
          }
      }
      
  • 使用静态代理拆分业务,切面

    业务和业务接口已拆分;切面和业务紧耦合
    在这里插入图片描述

    • service

      public interface Service 
      {	//规定业务功能
          void buy();
      }
      
    • …serviceimpl(不同对象service实现类)

      public class BookServiceImpl implements Service
      {
          @Override
          public void buy() 
          {System.out.println("图书购买...");}
      }
      
      public class ProductServiceImpl implements Service{
          @Override
          public void buy() 
          {System.out.println("商品购买...");}
      }
      
    • agent

      public class Agent implements Service{
          //设计成员变量的类型为接口,可以灵活切换目标对象
          public Service target;  //服务对象多变
          //构造器注入目标对象
          public Agent(Service target) 
          {this.target = target;}
          @Override
          public void buy() {
              try {
                  //切面
                  System.out.println("事务开启...");
                  //业务
                  target.buy();
                  //切面
                  System.out.println("事务结束...");
              }
              catch (Exception e) 
              {System.out.println("事务回滚..."); }
          }
      }
      

    测试

    public class Test03 {
        @Test
        public void test03(){
            Service agent = new Agent(new BookServiceImpl());
            agent.buy();
            System.out.println("");
            Service agent1 = new Agent(new ProductServiceImpl());
            agent1.buy();
        }
    }
    
  • 使用静态代理拆分业务和业务接口,切面和切面接口

    • 设计思路

    在这里插入图片描述

    • 项目结构

    • 在这里插入图片描述

    • AOP切面接口及实现类

      public interface AOP {
          default void before(){};
          default void after(){};
          default void exception(){};
      }
      
      //日志实现切面接口
      public class LogAop implements AOP{
          @Override
          public void before() {
              System.out.println("前置日志输出...");
          }
      }
      
      //事务实现aop
      public class TransAop implements AOP{
          @Override
          public void before()
          {System.out.println("事务开启...");}
      
          @Override
      
          public void after()
          {System.out.println("事务提交...");}
      
          @Override
          public void exception()
          {System.out.println("事务回滚...");}
      }
      
    • service接口与实现类同上…

    • agent

      public class Agent implements Service {
          //传入目标对象,切面对象
          Service target;
          AOP aop;
          //构造函数传入目标对象,切面对象
          public Agent(Service target,AOP aop) {
              this.target = target;
              this.aop = aop;
          }
      
      
          @Override
          public void buy() {
              try {
                  //切面
                  aop.before();
                  //业务
                  target.buy();
                  //切面
                  aop.after();
              } 
              catch (Exception e) {
                  //切面
                  aop.exception();
              }
          }
      }
      
    • 测试

      可以通过agent构造方法传入不同实现类来实现切面和业务灵活变换

      也可以通过嵌套来实现多切面调用

      极其灵活!!!

      在这里插入图片描述

      嵌套传参,实现多切面调用

      外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 使用动态代理对上个版本优化

    动态代理

    • 项目结构
      在这里插入图片描述

    • aop,service同上

    • ProxyFactory

      代理过程,通过getProxy()方法创建代理

      getProxy()方法通过newProxyInstance(ClassLoader l,Class<?>[] i,InvocationHandler h)方法创建代理

      参数InvocationHandler h通过创建InvocationHandler实现类并重写invoke(Object proxy, Method method, Object[] args) throws Throwable来实现代理要做的事

      public class ProxyFactory {
          public static Object getProxy(Object target, AOP aop) {
              return Proxy.newProxyInstance(
                      //类加载器
                      target.getClass().getClassLoader(),
                      //目标类接口
                      target.getClass().getInterfaces(),
                      //InvocationHandler代理功能实现
                      new InvocationHandler() {
                          @Override
                          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                              Object result = null;
                              try {
                                  //切面
                                  aop.before();
                                  //业务
                                  result = method.invoke(target, args);
                                  //切面
                                  aop.after();
                              }
                              catch (IllegalAccessException e) {
                                  //切面
                                  aop.exception();
                              }
                              return result;
                          }
                      }
              );
          }
      
      }
      
    • 测试

      在这里插入图片描述

Spring支持的aop实现

spring原生常用通知:

  • Before通知

    在目标方法被调用前调用

  • After通知

    在目标方法被调用后调用

  • Throws通知

    在目标方法抛出异常时调用

  • Around通知

    拦截目标对象方法时调用

案例

  • Service接口

    package org.example.service;
    //业务接口
    public interface Service {
        //规定业务功能
        void buy(String name);
        //新增有参有返回值方法
        default String show(String name)
        {return null;};
    }
    
  • BookServiceImpl实现接口

    package org.example.service;
    
    
    public class BookServiceImpl implements Service {
        @Override
        public void buy(String name) {
            System.out.println("图书购买"+name);
        }
        @Override
        public String show(String name)
        {
            System.out.println("购买图书"+name);
            return "...";
        }
    
    }
    
    
  • TransAop

    package org.example.aop;
    
    import org.springframework.aop.MethodBeforeAdvice;
    
    import java.lang.reflect.Method;
    import java.text.SimpleDateFormat;
    import java.util.Arrays;
    import java.util.Date;
    
    //事务实现aop的MethodBeforeAdvice接口
    //在目标方法被调用前调用
    public class TransAop implements MethodBeforeAdvice {
    
        @Override
        public void before(Method method, Object[] args, Object target) throws Throwable {
            //打印当前时间
            SimpleDateFormat sf=new SimpleDateFormat("yyyy-MM-dd");
            System.out.println("\n[系统日志]"+sf.format(new Date())+"  "+method.getName()+ Arrays.toString(args));
        }
    }
    
    
  • 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">
    
        <!--创建业务/目标对象-->
        <bean id="bookserviceTarget" class="org.example.service.BookServiceImpl"/>
        <!--创建切面对象-->
        <bean id="transaop" class="org.example.aop.TransAop"/>
    
    
        <!--绑定业务和切面-->
        <bean id="bookservice" class="org.springframework.aop.framework.ProxyFactoryBean">
            <!--配置业务接口-->
            <property name="interfaces" value="org.example.service.Service"/>
    
            <!--配置切面-->
            <property name="interceptorNames">
                <list>
                    <value>transaop</value>
                </list>
            </property>
    
            <!--织入-->
            <property name="target" ref="bookserviceTarget"/>
        </bean>
    
    </beans>
    

    bookservice属于org.springframework.aop.framework.ProxyFactoryBean类

    ProxyFactoryBean是底层封装好的类

    需要配置业务接口,配置切面和织入

    • 配置业务接口
    <property name="interfaces" value="实际业务的接口">
    
    • 配置切面
    <property name="interceptorNames">
        <list>
            <value>实际切面类1</value>
            <value>实际切面类2</value>
        </list>
    </property>
    
    • 织入
    <property name="target" ref="实际业务类"/>
    
  • MyTest测试

    package org.example.test;
    
    import org.example.service.BookServiceImpl;
    import org.example.service.Service;
    import org.junit.jupiter.api.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class MyTest {
        @Test
        public void test() {
            ApplicationContext ac=new ClassPathXmlApplicationContext
                ("applicationContext.xml");
            Service proxy= (Service) ac.getBean("bookservice");
            proxy.buy("朝花夕拾");
        }
    }
    

    结果:

    [系统日志]2024-03-06 buy[朝花夕拾]
    图书购买朝花夕拾

    proxy是jdk动态代理类型

AspectJ框架

是一个优秀的面向切面的框架,提供了大量切面实现

切入点表达式

execution( 访问权限 方法返回值 方法名(参数) 异常类型)

简化后execution( 方法返回值 方法声明(参数) )

  • 符号

    1* :通配符,表任意字符

    2… :表任意参数,或者本路径及其所有子路径

  • execution(public * *(…)) :任意公共方法
  • execution(* set*(…)) :任何一个以set开头的方法
  • execution(* org.example.service.* .*(…)) :任意返回值类型,在org.example.service包下的任意类中任意方法的任意参数
  • execution(* org.example…* .*(…)) :任意返回值类型,在org.example包及其子包下的任意类中任意方法的任意参数
  • execution(* *…service. *. *(…)) :service前可以有任意子包
  • execution(* *.service. *. *(…)) :service前只可以有一个子包

常用通知类型

前置环绕@Before

拦截目标方法,执行切面方法后再执行目标方法

在切面方法中不可以获得目标方法返回值,只能得到目标方法签名如访问权限,返回值,方法名,方法参数

切面规范
  • 访问权限:public

  • 方法返回值:void

  • 方法名自定义

  • 方法参数:空或JoinPoint类型

    可以通过jp.getSignature获得目标方法签名

    jp.getArgs()获得目标方法参数

    @Before(value = "execution(public boolean org.example.demo02.BookServiceImpl.buy(String))")
    public void MyBefore(JoinPoint jp){
        System.out.println("切面中前置通知功能开启...");
        System.out.println("目标方法签名: "+jp.getSignature());
        System.out.println("目标方法参数: "+ Arrays.toString(jp.getArgs()));
    }
    
  • 使用@Before注解来声明切入时机和切入点

    • 参数value:指定切入点表达式
创建步骤
  1. 创建业务/目标接口及实现类

    Service接口

    package org.example.demo01;
    //业务接口
    public interface Service {
        //规定业务功能
        boolean buy(String name);
    }
    

    目标实现类BookServiceImpl

    package org.example.demo01;
    
    //目标对象:业务功能具体实现
    //业务和业务接口分离
    
    public class BookServiceImpl implements Service {
        @Override
        public boolean buy(String name) {
            System.out.println("购买图书"+name);
            return true;
        }
    
    }
    
  2. 创建切面类,实现切面方法

    切面MyAspect

    package org.example.demo01;
    
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    
    //切面类,包含各种切面方法
    @Aspect     //交给aspectj框架识别管理
    public class MyAspect {
        //所有切面功能都是由切面方法实现的
        //可以将各种切面方法定义在此切面类中
    
        //public String show(String name)
        //前置切面方法,用注解指定切面方法前切位置
        @Before(value = "execution(public boolean org.example.demo01.BookServiceImpl.buy(String))")
        public void MyBefore(){
            System.out.println("切面中前置通知功能开启...");
        }
    }
    

    @Before的value指定在切入点前执行

  3. 在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" xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <!--创建业务对象-->
        <bean id="bookservice" class="org.example.demo01.BookServiceImpl"/>
        <!--创建切面对象-->
        <bean id="myaspect" class="org.example.demo01.MyAspect"/>
    
        <!--绑定-->
        <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    </beans>
    

    < aop:aspectj-autoproxy>< /aop:aspectj-autoproxy>

    简简单单实现切面和具体业务的绑定

  4. 测试

    @Test
    public void test(){
        ApplicationContext ac=new ClassPathXmlApplicationContext("demo01/applicationContext.xml");
        //取出代理对象
        Service service =(Service) ac.getBean("bookservice");
        service.buy("king");
    }
    

    < aop:aspectj-autoproxy>< /aop:aspectj-autoproxy>

    默认jdk动态代理,代理对象用接口接口可指向所有实现类

    < aop:aspectj-autoproxy proxy-target-class=“true”>< /aop:aspectj-autoproxy>

    设置为cglib子类代理,代理对象可用接口和实现类

    • 最好用接口
  • 用注解绑定

    • 在目标实现类上加@Service 或@Conponent

    • 在切面类上加@Conponent

    • applicationContext.xml

      <!--基于注解访问要包扫描-->
      <context:component-scan base-package="org.example.demo02"></context:component-scan>
      
      <!--绑定-->
      <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
      
    • 测试

      @Test
      public void test02(){
          ApplicationContext ac=new ClassPathXmlApplicationContext("demo02/applicationContext.xml");
          //取出代理对象
          org.example.demo02.Service service = (org.example.demo02.Service) ac.getBean("bookServiceImpl");
          service.buy("king");
      }
      

后置环绕@AfterReturning

在目标方法执行后再执行切面方法

可以接收目标方法返回值,可改变引用类型返回值

切面规范
  • 访问权限:public

  • 方法返回值:void

  • 方法名自定义

  • 方法参数:目标方法返回值,若目标方法无返回值则无参

    如果目标方法返回值是8种基本类型或String ==> 后置环绕不可改变返回值

    如果目标方法返回值是引用类型 ==> 后置环绕可改变返回值

  • 使用@AfterRturning注解表明后置通知

    • 参数value:指定切入点表达式
    • 参数returning:指定目标方法返回值名称,名称必须与切面方法参数名称一致
创建步骤
  • 接口

    public interface Service {
        //规定业务功能
        Book buy(String name,int price);
    }
    
  • 实现类

    @org.springframework.stereotype.Service
    public class BookServiceImpl implements Service {
        @Override
        public Book buy(String name,int price) {
            System.out.println("购买图书"+name);
            Book book=new Book(name,price);
            return book;
        }
    }
    
  • 接口

    //切面类,包含各种切面方法
    @Component  //交给spring容器创建对象
    @Aspect     //交给aspectj框架识别管理
    public class MyAspect {
        //所有切面功能都是由切面方法实现的
        //可以将各种切面方法定义在此切面类中
    
        @AfterReturning(value = "execution( * org.example.demo03.BookServiceImpl.buy(..))",returning = "arg")
        public void MyBefore(Object arg){
            System.out.println("切面中后置通知功能开启...");
            if(arg!=null){
                if(arg instanceof Book){
                    Book book=(Book) arg;
                    book.setName("《后来的后来》");
                }
            }
        }
    }
    
  • applicationContext.xml

    <!--基于注解访问要包扫描-->
    <context:component-scan base-package="org.example.demo03"></context:component-scan>
    
    <!--绑定-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    
  • 测试

    @Test
    public void test03(){
        ApplicationContext ac=new ClassPathXmlApplicationContext("demo03/applicationContext.xml");
        //代理对象
        Service service=(Service)ac.getBean("bookServiceImpl");
        Book returning= service.buy("《后来》",100);
        System.out.println(returning);
    }
    

    购买图书《后来》
    切面中后置通知功能开启…
    Book{name=‘《后来的后来》’, price=100}

环绕通知@Around

拦截目标方法,在目标方法前后增强功能的通知

以目标方法为参数,返回值就是目标方法返回值

也就是说可轻易改变目标方法返回值

切面规范
  • 访问权限public
  • 返回值就是目标方法返回值
  • 方法名称自定义
  • 参数为目标方法
  • 回避异常
  • 使用@Around注解声明是环绕通知
    • 参数value:指定切入点表达式
创建步骤
  • 接口

    public interface Service {
        //规定业务功能
        Book buy(String name,int price);
    }
    
  • 实现类

    @org.springframework.stereotype.Service
    public class BookServiceImpl implements Service {
        @Override
        public Book buy(String name,int price) {
            System.out.println("购买图书"+name);
            Book book=new Book(name,price);
            return book;
        }
    }
    
  • 切面

    //切面类,包含各种切面方法
    @Component  //交给spring容器创建对象
    @Aspect     //交给aspectj框架识别管理
    public class MyAspect {
        //所有切面功能都是由切面方法实现的
        //可以将各种切面方法定义在此切面类中
    
        @Around(value = "execution( * org.example.demo04.BookServiceImpl.buy(..))")
        public Object MyBefore(ProceedingJoinPoint pjp) throws Throwable //回避异常
        {
            //前切功能实现
            System.out.println("环绕通知前置功能开启...");
            //目标方法调用
            Book book= (Book) pjp.proceed(pjp.getArgs());
            book.setPrice(666);
            //后切功能实现
            System.out.println("环绕通知后置功能开启...");
            return book;
        }
    }
    
  • applicationContext.xml

    <!--基于注解访问要包扫描-->
    <context:component-scan base-package="org.example.demo04"></context:component-scan>
    
    <!--绑定-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    
  • 测试

    @Test
    public void test04(){
        ApplicationContext ac=new ClassPathXmlApplicationContext("demo04/applicationContext.xml");
        Service service = (Service) ac.getBean("bookServiceImpl");
        Book returning= service.buy("《环绕》",233);
        System.out.println(returning);
    }
    

    目标方法返回值被更改

最终通知@After

无论目标方法是否正常执行,最终代码都会被执行

善后工作,类似资源释放

切面规范
  • 访问权限:public

  • 方法返回值:void

  • 方法名自定义

  • 方法参数:空或JoinPoint类型

  • 使用@After注解表明是最终通知

    参数value:指定切入点表达式

创建步骤
  • 接口

    public interface Service {
        //规定业务功能
        Book buy(String name,int price);
    }
    
  • 实现类

    @org.springframework.stereotype.Service
    public class BookServiceImpl implements Service {
        @Override
        public Book buy(String name,int price) {
            System.out.println("购买图书"+name);
            Book book=new Book(name,price);
            System.out.println(1/0);
            return book;
        }
    }
    

    增加除0异常

  • 切面

    @After(value = "execution( * org.example.demo05.BookServiceImpl.buy(..))")
        public void MyBefore()
        {
            System.out.println("最终通知功能开启...");
        }
    
  • applicationContext.xml

    <!--基于注解访问要包扫描-->
    <context:component-scan base-package="org.example.demo05"></context:component-scan>
    
    <!--绑定-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    
  • 测试

    @Test
    public void test05(){
        ApplicationContext ac=new ClassPathXmlApplicationContext("demo05/applicationContext.xml");
        Service service = (Service) ac.getBean("bookServiceImpl");
        Book returning= service.buy("《最终》",233);
        System.out.println(returning);
    }
    

    即使有除零异常也正常执行

定义切入点@Pointcut

如果把前置,后置,环绕,最终都加上.最后顺序如下

在这里插入图片描述

如果多个切面切到同一个切入点,可以使用别名简化

使用@Pointcut注解创建一个空方法,此方法名称就是别名

@Pointcut(value = "execution( * org.example.demo06.BookServiceImpl.buy(..))")
public void MyCut(){}

创建空方法后,其他切面切入点只需要输入空方法名

@After(value = "MyCut()")
public void MyAfter()
{
    System.out.println("最终通知功能开启...");
}

Spring集成MyBatis

SM整合步骤

  1. 建表

  2. 创项目

  3. pom.xml(复制)

  4. 添加MyBatis模板(Mybatis-config.xml和…Mapper.xml文件)

  5. 添加Mybatis-config.xml Mybatis核心配置文件

  6. 拷贝jdbc.properties属性文件到resources目录

    jdbc.properties通常用于存储与数据库连接相关的配置信息,包括数据库的URL,用户名,密码等

    jdbc.driverClassName=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql:///mydatabase
    jdbc.username=myusername
    jdbc.password=mypassword
    
  7. 添加applicationContext_mapper.xml

  8. 添加applicationContext_service.xml

  9. 添加实体类

  10. 添加…Mapper接口和…Mapper.xml文件

  11. 添加…service接口和…serviceImpl实现类

  12. 添加测试类

pom.xml

<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_MyBatis</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>Spring_MyBatis</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <!--单元测试-->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>

    <!--aspectj-->
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>

    <!--spring核心依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>

    <!--spring事务-->
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-tx -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>

    <!--mysql驱动-->
    <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.47</version>
    </dependency>

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

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.3.2</version>
    </dependency>

    <!--spring-jdbc-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>

    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.9.19</version>
    </dependency>

    <!--mybatis-spring集成-->
    <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>2.0.6</version>
    </dependency>

    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.12</version>
    </dependency>

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


  <build>
    <resources>
      <resource>
        <directory>src/main/resources</directory>
        <includes>
          <include>**/*.properties</include>
          <include>**/*.xml</include>
        </includes>
      </resource>
      <resource>
        <directory>src/main/java</directory>
        <!--包括目录下的.properties和.xml文件都会被扫描到-->
        <includes>
          <include>**/*.properties</include>
          <include>**/*.xml</include>
        </includes>
      </resource>
    </resources>
  </build>
</project>

添加模板

打开:设置–>文件和代码模板–> + -->添加Mybatis-config.xml模板

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!--类型别名-->
    <typeAliases>
        <package name="org.example.pojo"/><!--包扫描-->
    </typeAliases>

    <!--环境配置-->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <!--数据库连接信息-->
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql:///!!!数据库名"/>
                <property name="username" value="root"/>
                <property name="password" value="1234567890"/>
            </dataSource>
        </environment>
    </environments>
    <!--映射名称空间:为了将映射文件与对应的接口或类进行关联,以便 MyBatis 在运行时能够正确地找到和执行相应的 SQL。-->
    <mappers>
        <!--包扫描-->
        <package name="org.example.mapper"/>
    </mappers>
</configuration>

添加XXXMapper.xml模板

    <!--多条件查询-->
    <select id="selectBySomething" resultType="s">
        <!--通过name,和manager查询-->
        select * from s where name like #{name} and manager=#{manager}
    </select>

    <!--插入-->
    <insert id="add" useGeneratedKeys="true" keyColumn="id">
        insert into s (name,manager,score,c,t)
        values (#{name},#{manager},#{score},#{c},#{t})
    </insert>

    <!--修改全部-->
    <update id="updateAll">
        update s set
            name=#{name},
            manager=#{manager},
            score=#{score},
            c=#{c},
            t=#{t}
        where id=#{id}
    </update>

    <!--删除对应id的记录-->
    <delete id="deleteById">
        delete from s where id=#{id}
    </delete>

Mybatis-config.xml

被注释部分被spring的applicationContext_mapper.xml接管

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!--灰色部分被spring的applicationContext_mapper.xml接管-->

<!--    &lt;!&ndash;读取属性文件中数据库配置&ndash;&gt;-->
<!--    <properties resource="db.properties"></properties>-->
    <!--设置日志输出语句,显示响应操作的sql语句-->
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
<!--    &lt;!&ndash;类型别名&ndash;&gt;-->
<!--    <typeAliases>-->
<!--        <package name="org.example.pojo"/>&lt;!&ndash;包扫描&ndash;&gt;-->
<!--    </typeAliases>-->

<!--    &lt;!&ndash;环境配置&ndash;&gt;-->
<!--    <environments default="development">-->
<!--        <environment id="development">-->
<!--            <transactionManager type="JDBC"/>-->
<!--            <dataSource type="POOLED">-->
<!--                &lt;!&ndash;数据库连接信息&ndash;&gt;-->
<!--                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>-->
<!--                <property name="url" value="jdbc:mysql:///ssm"/>-->
<!--                <property name="username" value="root"/>-->
<!--                <property name="password" value="1234567890"/>-->
<!--            </dataSource>-->
<!--        </environment>-->
<!--    </environments>-->
    <!--映射名称空间:为了将映射文件与对应的接口或类进行关联,以便 MyBatis 在运行时能够正确地找到和执行相应的 SQL。-->
<!--    <mappers>-->
<!--        &lt;!&ndash;包扫描&ndash;&gt;-->
<!--        <package name="org.example.mapper"/>-->
<!--    </mappers>-->
</configuration>

jdbc.properties

jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///ssm
jdbc.username=root
jdbc.password=1234567890

applicationContext_mapper.xml

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

    <!--读取属性文件jdbc.properties-->
    <context:property-placeholder location="jdbc.properties"></context:property-placeholder>
    <!--创建数据源-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driverClassName}"></property>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
    <!--配置SqlSessionFactoryBean类-->
    <bean class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--配置数据源-->
        <property name="dataSource" ref="dataSource"></property><!--引用上方dataSource-->
        <!--配置Mybatis核心配置文件-->
        <!--spring的核心配置文件还不能完全接管mybatis核心配置文件功能-->
        <property name="configLocation" value="mybatis-config.xml"></property>
        <!--注册实体类别名-->
        <property name="typeAliasesPackage" value="org.example.pojo"></property>
    </bean>

    <!--注册mapper.xml文件-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="org.example.mapper"></property>
    </bean>
</beans>

applicationContext_service.xml

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

    <!--sm是基于注解的开发,要添加包扫描-->
    <context:component-scan base-package="org.example.service.impl"></context:component-scan>
    <!--事务处理-->
</beans>

实体类

创建uid,uname,upass组成的javabean

Mapper

UMapper.xml

<!--名称空间-->
<mapper namespace="org.example.mapper.UMapper">
    <insert id="insert"  >
        insert into users
        values (#{uid},#{uname},#{upass})
    </insert>
</mapper>

UMapper接口

public interface UMapper {
    void insert(Users users);
}

Service

UService接口

public interface UService {
    void insert(Users users);
}

UService实现类

@Service    //交给spring创建对象
public class UServiceImpl implements UService {
    //在spring的applicationContext_mapper.xml中已注册mapper.xml文件
    //@Autowired交给spring获取UMapper.xml中的具体功能
    @Autowired
    UMapper uMapper;
    @Override
    public void insert(Users users) {
        uMapper.insert(users);
    }
}

测试

@Test
public void Utest() {
    //创建容器并启动
    ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext_service.xml");
    //取出UsersServiceImpl
    UService uService = (UService) ac.getBean("UServiceImpl");
    uService.insert(new Users(100,"diva","123"));
}

事务

所有事务必须添加到业务逻辑层

  • 基于注解的事务管理
  • 基于AspectJ的aop配置管理(声明式事务管理)

基于注解的事务管理

在ServiceImpl类上加注解@Transactional

类中所以方法都必须按照注解的设定

  • 在application_service.xml中

    • 添加事务管理器

      <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>
      
  • 在AServiceImpl类上加注解@Transactional

    @Transactional(propagation = Propagation.REQUIRED)
    

    在内部制造错误(1/0)测试

    @Transactional中参数noRollbackForClassName控制出错不回滚错误类型

    如果noRollbackForClassName = “ArithmeticException”

    那么算数错误ArithmeticException不回滚

  • 测试

    @Test
    public void Atest() {
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext_service.xml");
        AService aService= (AService) ac.getBean("AServiceImpl");
        aService.save(new Accounts(202,"lily","账户安全"));
    }
    

    出错回滚,不执行

@Transactional
  • propagation:事务传播特性

  • noRollbackForClassName :指定发生何种异常不回滚,使用异常名称

    noRollbackForClassName = “ArithmeticException”

  • noRollbackFor:指定发生何种异常不回滚,使用异常类型

    noRollbackFor = ArithmeticException.class

  • rollbackForClassName:指定发生何种异常必须回滚,使用异常名称

  • rollbackFor:指定发生何种异常必须回滚,使用异常类型

  • timeout:链接超时设置

    默认值-1永不超时

  • readOnly:允许增删改

    默认是false,如果是查询操作必须是true

  • isolation:数据库的隔离级别

    • isolation.DEFAULT:使用数据库自己的隔离级别

    • 未提交读(read uncommitted):允许脏读,可能读取到其他会话中未提交事务修改的数据

    • 提交读(read committed):只能读已提交的数据.oracle

    • 可重复读(repeated read):务可以多次读取相同的数据,并且在事务执行期间,其他事务对这些数据进行更新也不会影响到当前事务mysql

    • 串行读(serializable):完全串行化读.每次读都要获得表级共享锁,读写相互阻塞

声明式事务

在配置文件中添加配置,整个项目都遵循设定

但若某个实力类增加了@Transactional,则注解优先级更高

要求项目中方法有命名规范

规定不同方法事务

<!--配置事务切面-->
<tx:advice id="myadvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="*select*" read-only="true"/><!--查找read-only="true"-->
        <tx:method name="*find*" read-only="true"/>
        <tx:method name="*search*" read-only="true"/>
        <tx:method name="*get*" read-only="true"/>
        <tx:method name="*insert*" propagation="REQUIRED"/><!--增加propagation="REQUIRED"-->
        <tx:method name="*add*" propagation="REQUIRED"/>
        <tx:method name="*save*" propagation="REQUIRED"/>
        <tx:method name="*update*" propagation="REQUIRED"/>
        <tx:method name="*change*" propagation="REQUIRED"/>
        <tx:method name="*modify*" propagation="REQUIRED"/>
        <tx:method name="*delete*" propagation="REQUIRED"/><!--删-->
        <tx:method name="*remove*" propagation="REQUIRED"/>
        <tx:method name="*drop*" propagation="REQUIRED"/>
        <tx:method name="*clear*" propagation="REQUIRED"/>
        <tx:method name="*" propagation="SUPPORTS"/>
    </tx:attributes>
</tx:advice>

若增加<tx:method name=“save” propagation=“REQUIRED” no-rollback-for=“ArithmeticException”/>

no-rollback-for指定算数异常(ArithmeticException)不回滚

指定事务作用层并绑定

<!--绑定切面和切入点-->
<aop:config>
    <aop:pointcut id="mycut" expression="execution(* org.example.service.*.*(..))"/>
    <aop:advisor advice-ref="myadvice" pointcut-ref="mycut"></aop:advisor>
</aop:config>

测试

@Test
public void testTrans(){
    ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext_trans.xml");
    UService uService= (UService) ac.getBean("UServiceImpl");
    uService.insert(new Users(102,"jack","123"));
}

事务管理器

用来生成相应技术的连接+执行语句对象

技术连接对象提交回滚
jdbcConnectionconnection.commit()connection.rollback()
MybatisSqlSessionsqlSession.commit()sqlSession.rollback()
HibernateSessionsession.commit()session.rollback()

如果使用Mybatis则必须使用DataSourceTransactionManager类完成

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!--因为事务必须关联数据库,所以要配置数据源-->
    <property name="dataSource" ref="dataSource"></property>
</bean>

事务的传播特性

(propagation)多个事务之间的合并互斥等都可以通过设置事务的传播特性解决

  • PROPAGATION_REQUIRED:必被包含事务(增删改查用)
  • PROPAGATION_REQUIRED_NEW:自己新开事务,不管之前是否有事务
  • PROPAGATION_SUPPORTS:支持事务,若外部方法有事务则支持事务,若没有不单开事务
  • PROPAGATION_NEVER:不能运行在事务中(不能在内部),否则抛异常
  • PROPAGATION_NOT_SUPPORTED:不支持事务,允许在非事务的环境
  • PROPAGATION_MANDATORY:必须包在事务中,否则抛异常
  • PROPAGATION_NESTED:嵌套事务

AServiceImpl

public class AServiceImpl implements AService{
    @Autowired
    AMapper aMapper;
    @Override
    public void save(Accounts accounts) {
        aMapper.save(accounts);
        System.out.println("账户增加成功");
        System.out.println(1/0);
    }
}

UServiceImpl

public class UServiceImpl implements UService{
    @Autowired
    UMapper uMapper;
    @Autowired
    AService aService;
    @Override
    public void insert(Users users) {
        uMapper.insert(users);
        System.out.println("用户增加成功");
        //调用账户增加操作
        aService.save(new Accounts(204,"jack","账户安全"));

    }
}

UServiceImpl包含AServiceImpl

UServiceImplAServiceImpl(抛异常)结果
1无事务无事务u=ok,a=ok
2无事务REQUIREDu=ok,a=no
3REQUIRED无事务u=no,a=no
4REQUIREDREQUIREDu=no,a=no
5REQUIREDNOT_SUPPORTEDu=no,a=ok
6REQUIREDSUPPORTEDu=no,a=no
7REQUIREDREQUIRED_NEWu=no,a=no
8REQUIREDNEVERu=no,a异常
      | 回滚                  |

| --------- | ---------- | ------------------- | --------------------- |
| jdbc | Connection | connection.commit() | connection.rollback() |
| Mybatis | SqlSession | sqlSession.commit() | sqlSession.rollback() |
| Hibernate | Session | session.commit() | session.rollback() |

如果使用Mybatis则必须使用DataSourceTransactionManager类完成

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!--因为事务必须关联数据库,所以要配置数据源-->
    <property name="dataSource" ref="dataSource"></property>
</bean>

事务的传播特性

(propagation)多个事务之间的合并互斥等都可以通过设置事务的传播特性解决

  • PROPAGATION_REQUIRED:必被包含事务(增删改查用)
  • PROPAGATION_REQUIRED_NEW:自己新开事务,不管之前是否有事务
  • PROPAGATION_SUPPORTS:支持事务,若外部方法有事务则支持事务,若没有不单开事务
  • PROPAGATION_NEVER:不能运行在事务中(不能在内部),否则抛异常
  • PROPAGATION_NOT_SUPPORTED:不支持事务,允许在非事务的环境
  • PROPAGATION_MANDATORY:必须包在事务中,否则抛异常
  • PROPAGATION_NESTED:嵌套事务

AServiceImpl

public class AServiceImpl implements AService{
    @Autowired
    AMapper aMapper;
    @Override
    public void save(Accounts accounts) {
        aMapper.save(accounts);
        System.out.println("账户增加成功");
        System.out.println(1/0);
    }
}

UServiceImpl

public class UServiceImpl implements UService{
    @Autowired
    UMapper uMapper;
    @Autowired
    AService aService;
    @Override
    public void insert(Users users) {
        uMapper.insert(users);
        System.out.println("用户增加成功");
        //调用账户增加操作
        aService.save(new Accounts(204,"jack","账户安全"));

    }
}

UServiceImpl包含AServiceImpl

UServiceImplAServiceImpl(抛异常)结果
1无事务无事务u=ok,a=ok
2无事务REQUIREDu=ok,a=no
3REQUIRED无事务u=no,a=no
4REQUIREDREQUIREDu=no,a=no
5REQUIREDNOT_SUPPORTEDu=no,a=ok
6REQUIREDSUPPORTEDu=no,a=no
7REQUIREDREQUIRED_NEWu=no,a=no
8REQUIREDNEVERu=no,a异常
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值