Spring学习:五、AOP 面向切面编程、Spring与Mybatis整合

news2025/1/16 8:15:08

7. AOP 面向切面编程

7.1 AOP概述

​ AOP英文名为Aspect Oriented Programming,意为面向切面编程,通过预编译方式和运行期间动态代理实现程序功能统一维护的一种技术。AOP是OOP的延续,是Spring框架中的一个重要内容,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

​ 面向切面编程,实现在不修改源代码的情况下给程序动态统一添加额外功能的一种技术,如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZGLWk3gL-1670414166429)(assets/image-20220801200813598.png)]

​ AOP可以拦截指定的方法并且对方法增强,而且无需侵入到业务代码中,使业务与非业务处理逻辑分离,比如Spring的事务,通过事务的注解配置,Spring会自动在业务方法中开启、提交业务,并且在业务处理失败时,执行相应的回滚策略。

7.2 AOP的作用

​ AOP 采取横向抽取机制(动态代理),取代了传统纵向继承机制的重复性代码,其应用主要体现在事务处理、日志管理、权限控制、异常处理等方面。

​ 主要作用是分离功能性需求和非功能性需求,使开发人员可以集中处理某一个关注点或者横切逻辑,减少对业务代码的侵入,增强代码的可读性和可维护性。

​ 简单的说,AOP 的作用就是保证开发者在不修改源代码的前提下,为系统中的业务组件添加某种通用功能。

在这里插入图片描述

7.3 AOP应用场景

应用场景:

  • 事务管理

  • 记录日志

  • 监测性能(统计方法运行时间)

  • 权限控制

  • 缓存

主要目的:

​ 将日志记录,性能统计,安全控制,事务处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。

7.4 Spring AOP的术语

  • **Joinpoint(连接点)😗*所谓连接点是指那些被拦截到的点,在Spring中,这些点指的是方法,因为Spring只支持方法类型的连接点.

  • **Pointcut(切入点)😗*所谓的切入点是指我们要对哪些Joinpoint进行拦截的定义

  • **Advice(通知/增强)😗*所谓通知是指拦截到Joinpoint之后所要做的事情就是通知,通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的事情)

  • **Introduction(引介)😗*引介是一种特殊的通知,在不修改类代码的前提下,Introduction可以在运行期为类动态的添加一些方法或Field

  • Target代理的目标对象

  • **Weaving(织rget(目标):入)😗*是指把增强应用到目标对象来创建的代理对象的过程,Spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入

  • **Proxy(代理)😗*一个类被AOP织入增强后,就产生一个结果代理类

  • **Aspect(切面)😗*是切入点和通知(引介)的结合

建议采用图解方式理解:
在这里插入图片描述

7.5 基于AspectJ的AOP编程

​ Spring AOP通知分类:

通知类型说明
before(前置通知)通知方法在目标方法调用之前执行
after(后置通知)通知方法在目标方法返回或异常后调用
after-returning(返回后通知)通知方法会在目标方法正常执行,返回后调用
after-throwing(抛出异常通知)通知方法会在目标方法抛出异常后调用
around(环绕通知)通知方法会将目标方法包装起来

7.5.1 AspectJ各种通知编程案例(xml方式 )

  1. 导入依赖 spring-aop spring-aspects (整合AspectJ) AspectJ的框架依赖
 <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>5.2.15.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>5.2.15.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.9.6</version>
    </dependency>
  1. 编码

    编写各种通知的日志记录类

    在所有增强方法中,都可以接受JoinPoint类型连接点参数对象,可以获取当前增强是哪个类的哪个方法,还可以获取调用目标方法传递参数

package org.aop.aspectJ.logger;

import java.sql.SQLException;
import java.util.Arrays;

import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
@Slf4j
public class UserBizLogger {
	/**
	 * 前置增强
	 * @param jp
	 */
	public void before(JoinPoint jp){
		log.info("调用"+jp.getTarget()+"的"+jp.getSignature().getName()
				+"方法,传递的参数:"+Arrays.toString(jp.getArgs()));
	}
	
	/**
	 * 后置增强
	 * @param jp
	 * @param result  返回值
	 */
	public void afterReturning(JoinPoint jp,Object result){
		log.info("调用"+jp.getTarget()+"的"+jp.getSignature().getName()
				+"方法,方法的返回值:"+result);
	}
	
	/**
	 * 异常增强
	 * @param jp
	 * @param e 增强的异常类型
	 */
	public void afterThrowing(JoinPoint jp,SQLException e){
		log.error("调用"+jp.getTarget()+"的"+jp.getSignature().getName()
				+"方法发生异常"+e);
	}
	
	/**
	 * 最终增强
	 * @param jp
	 */
	public void after(JoinPoint jp){
		log.info(jp.getSignature().getName()+"方法执行结束");
	}
	
	/**
	 * 环绕增强
	 * @param jp
	 * @throws Throwable
	 */
	public Object aroundLogger(ProceedingJoinPoint jp) throws Throwable{
		log.info(jp.getSignature().getName()+"方法开始执行");
		try {
			//执行目标方法
			Object rs = jp.proceed();
			log.info(jp.getSignature().getName()+"方法正常执行完");
			return rs;
		} catch (SQLException e) {
			log.error("调用"+jp.getTarget()+"的"+jp.getSignature().getName()
					+"方法发生异常"+e);
			throw e;
		}
	}
}

  1. 配置切入点和切面

    引入aop命名空间:

    <?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: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">
    

在这里插入图片描述

配置:

<!-- 配置增强方法所在的Bean -->
	<bean id="userBizLogger" class="org.aop.aspectJ.logger.UserBizLogger"></bean>
	
	<!-- 配置AspectJ的AOP -->
	<aop:config>
		<aop:pointcut expression="execution( * org.aop.service..*.*(..))" id="pointcut"/>
		<!-- 引用增强方法的bean -->
		<aop:aspect ref="userBizLogger">
			<!-- 
				前置增强, method: 前置增强的方法
			 -->
			<aop:before method="before" pointcut-ref="pointcut"/>
			<!-- 
				后置增强
					returning: 返回值,对应方法参数
			 -->
			<aop:after-returning method="afterReturning" pointcut-ref="pointcut" returning="result"/>
			<!-- 
				异常增强
					throwing: 异常的类型, 对应方法的异常参数
			 -->
			<aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="e"/>
			<!-- 
				最终增强
			 -->
			<aop:after method="after" pointcut-ref="pointcut"/>
			<!-- 
				环绕增强
			 -->
			<aop:around method="aroundLogger" pointcut-ref="pointcut"/>
		</aop:aspect>
	</aop:config>

切入点说明

execution(修饰符? 返回值 方法名(参数) 异常?)

  • execution(* *(…)) 匹配所有spring管理对象所有方法, 第一个*任意返回值 ,第二个*任意方法, … 任意参数

  • execution(* org.suke.spring…*.*(…) ):匹配org.suke.spring包及其子包所有类的所有方法

  • execution(* org.suke.spring.*.*(…)) 匹配org.suke.spring包中所有对象所有方法

  • execution(* org.suke.spring.UserService.s*(…)) 匹配org.suke.spring包UserService中s开头方法

  • execute( public * addUser(entity.User)) 匹配addUser方法,返回值任意,参数为entity包User对象

  • execute( public void *(entity.User)) 匹配返回值为void,参数为entity包User的所有方法

  • execute( public void addUser(…)) 匹配返回值为void,参数任意的addUser方法

测试效果:
在这里插入图片描述

7.5.2 AspectJ AOP注解编程

编写增强类:

package org.aop.aspectJ.logger;

import java.sql.SQLException;
import java.util.Arrays;

import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect  //把该类定义为切面类
public class UserBizLogger {
	private static final Logger log =Logger.getLogger(UserBizLogger.class);
	
	/**
	 * 前置增强
	 * @param jp
	 */
	@Before("execution( * org.aop.service..*.*(..))")  //使用@Before 定义该方法为前置增强
	public void before(JoinPoint jp){
		log.info("调用"+jp.getTarget()+"的"+jp.getSignature().getName()
				+"方法,传递的参数:"+Arrays.toString(jp.getArgs()));
	}
	
	/**
	 * 后置增强
	 * @param jp
	 * @param result  返回值
	 */
	//使用@AfterReturning 定义该方法为后置增强方法
	@AfterReturning(pointcut="execution( * org.aop.service..*.*(..))",returning="result")
	public void afterReturning(JoinPoint jp,Object result){
		log.info("调用"+jp.getTarget()+"的"+jp.getSignature().getName()
				+"方法,方法的返回值:"+result);
	}
	
	/**
	 * 异常增强
	 * @param jp
	 * @param e 增强的异常类型
	 */
	//使用@AfterThrowing 定义该方法为异常增强
	@AfterThrowing(pointcut="execution( * org.aop.service..*.*(..))",throwing="e")
	public void afterThrowing(JoinPoint jp,SQLException e){
		log.error("调用"+jp.getTarget()+"的"+jp.getSignature().getName()
				+"方法发生异常"+e);
	}
	
	/**
	 * 最终增强
	 * @param jp
	 */
	//使用@After 定义该方法为最终增强
	@After("execution( * org.aop.service..*.*(..))")
	public void after(JoinPoint jp){
		log.info(jp.getSignature().getName()+"方法执行结束");
	}
	
	/**
	 * 环绕增强
	 * @param jp
	 * @throws Throwable
	 */
	//使用@Around 定义该方法为环绕增强
	@Around("execution( * org.aop.service..*.*(..))")
	public Object aroundLogger(ProceedingJoinPoint jp) throws Throwable{
		log.info(jp.getSignature().getName()+"方法开始执行");
		try {
			//执行目标方法
			Object rs = jp.proceed();
			log.info(jp.getSignature().getName()+"方法正常执行完");
			return rs;
		} catch (SQLException e) {
			log.error("调用"+jp.getTarget()+"的"+jp.getSignature().getName()
					+"方法发生异常"+e);
			throw e;
		}
	}
}

配置:

<!-- 配置增强方法所在的Bean -->
<bean id="userBizLogger" class="org.aop.aspectJ.logger.UserBizLogger"></bean>
<!-- 启动对于AspectJ的注解支持 -->
<aop:aspectj-autoproxy/>

8. Spring与Mybatis整合

​ 由于Spring框架提供了对象的管理,切面编程等非常实用的功能,如果把mybatis的对象交给Spring容器进行解偶合管理,不仅能大大增强系统的灵活性,便于功能扩展,还能通过Spring提供的服务简化编码,减小开发工作量,提高开发效率.实现整合的主要工作就是把Mybatis中的对象配置到Spring容器中,交给Spring来管理.比如说Mapper对象,以及业务层的事物管理。

8.1 基于xml的配置方式

  1. 导入相关依赖

    <?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-webdemo</artifactId>
      <version>1.0-SNAPSHOT</version>
      <packaging>war</packaging>
    
      <name>spring-webdemo Maven Webapp</name>
      <!-- FIXME change it to the project's website -->
      <url>http://www.example.com</url>
    
      <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <spring.version>5.2.15.RELEASE</spring.version>
      </properties>
    
      <dependencies>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
          <version>${spring.version}</version>
        </dependency>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-test</artifactId>
          <version>${spring.version}</version>
        </dependency>
        <!--spring-web-->
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-web</artifactId>
          <version>${spring.version}</version>
        </dependency>
        <!-- servlet-api -->
        <dependency>
          <groupId>javax.servlet</groupId>
          <artifactId>javax.servlet-api</artifactId>
          <version>3.1.0</version>
          <scope>provided</scope>
        </dependency>
        <!-- jsp-api -->
        <dependency>
          <groupId>javax.servlet.jsp</groupId>
          <artifactId>jsp-api</artifactId>
          <version>2.1</version>
          <scope>provided</scope>
        </dependency>
        <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>4.12</version>
          <scope>test</scope>
        </dependency>
    
        <!--log4j-->
        <dependency>
          <groupId>org.slf4j</groupId>
          <artifactId>slf4j-log4j12</artifactId>
          <version>1.7.36</version>
        </dependency>
    
        <dependency>
          <groupId>org.projectlombok</groupId>
          <artifactId>lombok</artifactId>
          <version>1.18.24</version>
        </dependency>
    
        <dependency>
          <groupId>javax.inject</groupId>
          <artifactId>javax.inject</artifactId>
          <version>1</version>
        </dependency>
    
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-aop</artifactId>
          <version>5.3.21</version>
        </dependency>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-aspects</artifactId>
          <version>5.3.21</version>
        </dependency>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-jdbc</artifactId>
          <version>5.3.21</version>
        </dependency>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-tx</artifactId>
          <version>5.3.21</version>
        </dependency>
        <dependency>
          <groupId>org.mybatis</groupId>
          <artifactId>mybatis</artifactId>
          <version>3.5.7</version>
        </dependency>
        <dependency>
          <groupId>org.mybatis</groupId>
          <artifactId>mybatis-spring</artifactId>
          <version>2.0.6</version>
        </dependency>
        <dependency>
          <groupId>mysql</groupId>
          <artifactId>mysql-connector-java</artifactId>
          <version>8.0.29</version>
        </dependency>
        <dependency>
          <groupId>com.mchange</groupId>
          <artifactId>c3p0</artifactId>
          <version>0.9.5.2</version>
        </dependency>
    
        <dependency>
          <groupId>org.aspectj</groupId>
          <artifactId>aspectjweaver</artifactId>
          <version>1.9.6</version>
        </dependency>
        <dependency>
          <groupId>com.alibaba</groupId>
          <artifactId>fastjson</artifactId>
          <version>1.2.75</version>
        </dependency>
      </dependencies>
    
    
      <build>
        <finalName>spring-webdemo</finalName>
        <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
          <plugins>
            <plugin>
              <artifactId>maven-clean-plugin</artifactId>
              <version>3.1.0</version>
            </plugin>
            <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
            <plugin>
              <artifactId>maven-resources-plugin</artifactId>
              <version>3.0.2</version>
            </plugin>
            <plugin>
              <artifactId>maven-compiler-plugin</artifactId>
              <version>3.8.0</version>
            </plugin>
            <plugin>
              <artifactId>maven-surefire-plugin</artifactId>
              <version>2.22.1</version>
            </plugin>
            <plugin>
              <artifactId>maven-war-plugin</artifactId>
              <version>3.2.2</version>
            </plugin>
            <plugin>
              <artifactId>maven-install-plugin</artifactId>
              <version>2.5.2</version>
            </plugin>
            <plugin>
              <artifactId>maven-deploy-plugin</artifactId>
              <version>2.8.2</version>
            </plugin>
          </plugins>
        </pluginManagement>
      </build>
    </project>
    
    1. 编写User实体类和UserMapper接口,以及UserService,UserServlet

      package com.suke.pojo;
      import lombok.Data;
      @Data
      public class User {
          private Integer id;
          private String name;
          private String gender;
          private Integer age;
          private String address;
          private String email;
          private String qq;
          private String photo;
      
      }
      
      package com.suke.mapper;
      
      import com.suke.pojo.User;
      
      public interface UserMapper {
          public void insert(User user);
          public void delete(int id);
          public User selectById(int id);
      }
      
      package com.suke.service;
      
      import com.suke.pojo.User;
      
      public interface UserService {
          public void addUser(User user);
      
          public void deleteUser(Integer uid);
      
          public User queryById(Integer uid);
      }
      
      
      package com.suke.service.impl;
      
      import com.suke.mapper.UserMapper;
      import com.suke.pojo.User;
      import com.suke.service.UserService;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.context.annotation.Scope;
      import org.springframework.stereotype.Service;
      
      @Service("userService")
      public class UserServiceImpl implements UserService {
          @Autowired
          private UserMapper userMapper;
      
          @Override
          public void addUser(User user) {
              userMapper.insert(user);
          }
      
          @Override
          public void deleteUser(Integer uid) {
              if(uid == null){
                 throw new RuntimeException("id不能为空!");
              }
              userMapper.delete(uid);
          }
      
          @Override
          public User queryById(Integer uid) {
              if(uid == null){
                  throw new RuntimeException("id不能为空!");
              }
              return userMapper.selectById(uid);
          }
      }
      
      package com.suke.web;
      
      import com.alibaba.fastjson.JSON;
      import com.suke.pojo.User;
      import com.suke.service.UserService;
      import org.springframework.context.ApplicationContext;
      import org.springframework.web.context.WebApplicationContext;
      import org.springframework.web.context.support.WebApplicationContextUtils;
      
      import javax.servlet.ServletException;
      import javax.servlet.annotation.WebServlet;
      import javax.servlet.http.HttpServlet;
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      import java.io.IOException;
      import java.io.PrintWriter;
      
      @WebServlet(name = "UserServlet",value = "/userServlet")
      public class UserServlet extends HttpServlet {
          protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
      
          }
      
          protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
              ApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
      
              UserService userService = applicationContext.getBean("userService", UserService.class);
              int id = Integer.parseInt(request.getParameter("id"));
              User user = userService.queryById(id);
              response.setContentType("application/json;charset=UTF-8");
              PrintWriter out = response.getWriter();
              String json = JSON.toJSONString(user);
              out.print(json);
              out.flush();
              out.close();
      
          }
      }
      
      1. 在src/main/resource导入spring,log4j等配置文件

在这里插入图片描述

log4j.properties

log4j.rootCategory=DEBUG, stdout

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %t %c{2}:%L - %m%n
  1. 配置SqlSessionFactory

    ​ 使用Spring整合Mybatis,首先应将诸如JDBC DataSource 或者MyBatis的SqlSessionFactory等数据访问资源以Bean的形式定义在Spring容器中,交由Spring容器进行管理.

​ 1)把jdbc的参数配置到一个properties的文件,便于修改:

db.properties

jdbc.driverClass=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
jdbc.user=root
jdbc.password=123

#当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3
jdbc.acquireIncrement=5
#初始化时获取的连接数,取值应在minPoolSize与maxPoolSize之间。Default: 3
jdbc.initialPoolSize=5
#连接池中保留的最小连接数。
jdbc.minPoolSize=3
#连接池中保留的最大连接数。Default: 15
jdbc.maxPoolSize=10

​ 因为我们在spring的配置文件需要配置大量的bean,为了方便管理,我们把spring的配置文件拆分成几个文件: applicationnContext-mapper.xml和applicationnContext-tx.xml, 然后在applicationnContext.xml使用import导入上面两个配置文件:

在这里插入图片描述

<import resource="applicationContext-mapper.xml"/>
<import resource="applicationContext-tx.xml"/>

然后在 applicationnContext-mapper.xml配置与mybatis相关的配置:

<?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: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">


    <!--加载properties
      如果是多个properties文件, 文件路径与文件路径使用逗号分割   classpath:db.properties,classpath:log4j.properties
    -->
    <context:property-placeholder location="classpath:db.properties" file-encoding="UTF-8"/>
      <!--配置一个C3p0数据源-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!--数据库四大参数-->
        <property name="driverClass" value="${jdbc.driverClass}"/>
        <property name="jdbcUrl" value="${jdbc.url}"/>
        <property name="user" value="${jdbc.user}"/>
        <property name="password" value="${jdbc.password}"/>
        <!--连接池相关的参数-->
        <!--初始连接数-->
        <property name="initialPoolSize" value="${jdbc.initialPoolSize}"/>
        <!--增量, 每次创建几个连接-->
        <property name="acquireIncrement" value="${jdbc.acquireIncrement}"/>
        <!--最大连接数-->
        <property name="maxPoolSize" value="${jdbc.maxPoolSize}"/>
        <!--最小连接数 默认值等于初始连接数-->
        <property name="minPoolSize" value="${jdbc.minPoolSize}"/>
    </bean>

    <!--配置SqlSessionFactoryBean-->
    <bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--注入数据源-->
        <property name="dataSource" ref="dataSource"/>
        <!--加载mybatis全局配置文件-->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <!--配置别名-->
        <property name="typeAliasesPackage" value="com.suke.pojo"/>
        <!--加载sql映射文件  * 任意  一定要注意: classpath*: 如果不写, 后面*无效-->
        <property name="mapperLocations" value="classpath*:mapper/*Mapper.xml"/>
    </bean>


    <!--配置mapper扫描器 扫描指定包下所有的Mapper接口
       创建Mapper接口代理对象, sqlSession.getMapper()并且保存到Spring容器-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!--扫描那个包下Mapper接口-->
         <property name="basePackage" value="com.suke.mapper"/>
        <!--注入SqlSessionFactory-->
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean"/>
    </bean>

</beans>

我们导入mybatis的主配置文件,虽然我们不会在该配置文件添加配置,但是还是推荐保留:

mybatis-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration SYSTEM "http://mybatis.org/dtd/mybatis-3-config.dtd" >
<configuration>
 		<!-- 全局的setting配置 -->
 		
 		<!-- 全局的别名配置 -->
	
		<!--映射文件  -->
</configuration>
  1. 编写UserMapper.xml的sql映射文件

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper  
      PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"  
      "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
     <mapper namespace="com.suke.mapper.UserMapper">
        <select id="selectById" resultType="User">
            select * from tb_userinfo where id = #{id}
        </select>
        
        <insert id="insert">
            <selectKey resultType="int" order="AFTER" keyProperty="id">
                SELECT LAST_INSERT_ID()
            </selectKey>
            insert into tb_userinfo(name,gender,age,address,email,qq,photo)
            values(#{name,jdbcType=VARCHAR},#{gender,jdbcType=VARCHAR},#{age,jdbcType=INTEGER},#{address,jdbcType=VARCHAR}
            ,#{email,jdbcType=VARCHAR},#{qq,jdbcType=VARCHAR},#{photo,jdbcType=VARCHAR})
        </insert>
    
        <delete id="delete">
            delete from tb_userinfo where id = #{id}
        </delete>
    
    </mapper>
    
    1. 在applicationContext-tx.xml配置声明式事务管理

      • PlatformTransactionManager 接口是 spring 的事务管理器,它里面提供了我们常用的操作事务的方法。

      在这里插入图片描述

      注意:

      PlatformTransactionManager 是接口类型,不同的 Dao 层技术则有不同的实现类,例如:Dao 层技术是jdbc

      或 mybatis 时:org.springframework.jdbc.datasource.DataSourceTransactionManager

      Dao 层技术是hibernate时:org.springframework.orm.hibernate5.HibernateTransactionManager

      • TransactionDefinition

      TransactionDefinition 是事务的定义信息对象,里面有如下方法:

      在这里插入图片描述

      • TransactionStatus

      ​ TransactionStatus 接口提供的是事务具体的运行状态,方法介绍如下。

      在这里插入图片描述

      基于XML的声明式事务控制

      Spring 的声明式事务顾名思义就是采用声明的方式来处理事务。这里所说的声明,就是指在配置文件中声明,用在 Spring 配置文件中声明式的处理事务来代替代码式的处理事务。

      声明式事务处理的作用

      • 事务管理不侵入开发的组件。具体来说,业务逻辑对象就不会意识到正在事务管理之中,事实上也应该如此,因为事务管理是属于系统层面的服务,而不是业务逻辑的一部分,如果想要改变事务管理策划的话,也只需要在定义文件中重新配置即可

      • 在不需要事务管理的时候,只要在设定文件上修改一下,即可移去事务管理服务,无需改变代码重新编译,这样维护起来极其方便

      声明式事务底层就是AOP

      <?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: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:component-scan base-package="com.suke"/>
      
          <!--配置声明式事务管理-->
          <!--配置一个事务管理器-->
          <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
              <property name="dataSource" ref="dataSource"/>
          </bean>
      
          <!--配置事务通知-->
          <tx:advice id="txAdvice" transaction-manager="txManager">
              <!--配置事务策略: 对业务层的方法进行怎么样事务管理-->
              <tx:attributes>
                  <!-- 对业务层类的具体的方法的设置事务策略
                     name: 业务层类的方法名, 支持通配符 *
                     要求你业务层类的方法必须按照你配置和名字起名
                     查询方法的策略
                       read-only: 是否只读: true只读, false不只读 默认值, 查询效率,对查询方法设置只读,
                                  增删改方法,一定不能设置只读
                       propagation: 事务传播机制:
                          	REQUIRED:默认值,表示如果存在一个事务,则支持当前事务,如果当前没有事务,则开启一个新的事务.  增删改
                          	SUPPORTS:表示如果存在一个事务,则支持当前事务,如果当前没有事务,则按非事务方式执行, 查询
      
                      rollback-for="在那些异常下进行回滚"  默认值 RuntimeException
                                    rollback-for="java.lang.Exception" 所有异常都回滚
      
                      no-rollback-for="那些异常不回滚"
      
                      timeout: 超时 默认 -1 永不超时
                  -->
                  <tx:method name="query*" read-only="true" propagation="SUPPORTS"/>
                  <tx:method name="find*" read-only="true" propagation="SUPPORTS"/>
                  <tx:method name="select*" read-only="true" propagation="SUPPORTS"/>
                  <tx:method name="add*" rollback-for="java.lang.Exception"/>
                  <tx:method name="update*" rollback-for="java.lang.Exception"/>
                  <tx:method name="del*" rollback-for="java.lang.Exception"/>
                  <tx:method name="*" rollback-for="java.lang.Exception"/>
              </tx:attributes>
          </tx:advice>
      
          <!--把事务通知类txAdvice与业务层的类方法进行织入: AOP的技术-->
           <aop:config>
               <!--配置切入点-->
               <aop:pointcut id="mypointcut" expression="execution(* com.suke.service..*.*(..))"/>
               <aop:advisor advice-ref="txAdvice" pointcut-ref="mypointcut"/>
           </aop:config>
      
      
      </beans>
      

      ​ 注意:我们可以通过tx:attributes子标签定制事务属性.事务属性通过tx:method标签进行设置,Spring支持对不同的方法设置不同的事务属性, tx:method标签中的name是必须的用于指定匹配的方法,这里需要对方法名进行约定,可以使用通配符 ( * ).其他属性均可选,用于指定具体的事务机制,这些属性解释如下:

      • propagation:事务传播机制:该属性可选值如下:
        • REQUIRED:默认值,表示如果存在一个事务,则支持当前事务,如果当前没有事务,则开启一个新的事务. 增删改
        • SUPPORTS:表示如果存在一个事务,则支持当前事务,如果当前没有事务,则按非事务方式执行, 查询
        • MANDATORY:表示如果存在一个事务,则支持当前事务,如果当前没有事务,则抛出异常
        • REQUIRES_NEW:表示总是开启一个新的事务,如果当前有一个事务,则将当前事务挂起,开启新事物执行方法.
        • NOT_SUPPORTED:表示总是以非事务方式执行.如果一个事务已经存在,则将这个存在的事务挂起,然后执行方法.
        • NEVER:表示总是以非事务方式执行.如果当前存在一个活动的事务,则抛出异常
        • NESTED:表示如果当前存在一个活动的事务,则创建一个事务作为当前事务的嵌套事务运行,如果没有当前事务,该取值与REQUIRED相同.

      注意:在实际开发中, REQUIRED能够满足大多数的事务需求,可以作为首选的事务传播行为.

      • isolation:事务隔离级别.即当前事务和其他事务的隔离程度,在并发事务处理的情况下需要考虑它的设置,该属性可选的值如下:
        • DEFAULT:默认值,表示使用数据库默认的事务隔离级别
        • READ_UNCOMMITTED:读未提交
        • READ_COMMITTED:读已提交 oracle
        • REPEATABLE_READ: 可重复读 mysql
        • SERIALIZABLE: 串行读
      • timeout:事务的超时时间,允许事务运行的最长时间,以秒作为单位,超过给定的时间自动回滚,防止事务执行时间过长而影响到系统性能.该属性需要底层的实现支持,默认为-1,表示不超时.
      • rollback-for: 回滚控制, 默认是只对RuntimeException异常进行回滚
      • read-only: 是否只读, 默认值:false, 如果是true: 表示只读
      1. 编写测试类

        package com.suke.service;
        
        import com.suke.pojo.User;
        import org.junit.Test;
        import org.junit.runner.RunWith;
        import org.springframework.beans.factory.annotation.Autowired;
        import org.springframework.test.context.ContextConfiguration;
        import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
        
        import static org.junit.Assert.*;
        
        @RunWith(SpringJUnit4ClassRunner.class)
        @ContextConfiguration("classpath:applicationContext.xml")
        public class UserServiceTest {
            @Autowired
            private UserService userService;
        
            @Test
            public void addUser() {
                User user = new User();
                user.setName("李琦");
                user.setGender("男");
                user.setAge(21);
                user.setAddress("北京");
                user.setEmail("liqi@163.com");
                user.setQq("2312311");
                userService.addUser(user);
                System.out.println(user.getId());
        
            }
        
            @Test
            public void deleteUser() {
                userService.deleteUser(6);
            }
        }
        

基于注解的声明式事务控制

  1. 在业务实现类的方法上添加@Transactional注解进行事务控制

    package com.suke.service.impl;
    
    import com.suke.mapper.UserMapper;
    import com.suke.pojo.User;
    import com.suke.service.UserService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Scope;
    import org.springframework.stereotype.Service;
    
    @Service("userService")
    @Transactional
    public class UserServiceImpl implements UserService {
        @Autowired
        private UserMapper userMapper;
    
        @Override
        @Transactional(propagation = Propagation.REQUIRED)
        public void addUser(User user) {
            userMapper.insert(user);
        }
    
        @Override
        @Transactional(propagation = Propagation.REQUIRED)
        public void deleteUser(Integer uid) {
            if(uid == null){
               throw new RuntimeException("id不能为空!");
            }
            userMapper.delete(uid);
        }
    
        @Override
        @Transactional(propagation = Propagation.SUPPORTS,readOnly = true)
        public User queryById(Integer uid) {
            if(uid == null){
                throw new RuntimeException("id不能为空!");
            }
            return userMapper.selectById(uid);
        }
    }
    
  2. 编写 applicationContext.xml 配置文件

    <!--事务的注解驱动-->
    <tx:annotation-driven/>
     <!--配置事务管理器-->
     <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"/>
        </bean>
    

e(“李琦”);
user.setGender(“男”);
user.setAge(21);
user.setAddress(“北京”);
user.setEmail(“liqi@163.com”);
user.setQq(“2312311”);
userService.addUser(user);
System.out.println(user.getId());

         }
     
         @Test
         public void deleteUser() {
             userService.deleteUser(6);
         }
     }
     ```

基于注解的声明式事务控制

  1. 在业务实现类的方法上添加@Transactional注解进行事务控制

    package com.suke.service.impl;
    
    import com.suke.mapper.UserMapper;
    import com.suke.pojo.User;
    import com.suke.service.UserService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Scope;
    import org.springframework.stereotype.Service;
    
    @Service("userService")
    @Transactional
    public class UserServiceImpl implements UserService {
        @Autowired
        private UserMapper userMapper;
    
        @Override
        @Transactional(propagation = Propagation.REQUIRED)
        public void addUser(User user) {
            userMapper.insert(user);
        }
    
        @Override
        @Transactional(propagation = Propagation.REQUIRED)
        public void deleteUser(Integer uid) {
            if(uid == null){
               throw new RuntimeException("id不能为空!");
            }
            userMapper.delete(uid);
        }
    
        @Override
        @Transactional(propagation = Propagation.SUPPORTS,readOnly = true)
        public User queryById(Integer uid) {
            if(uid == null){
                throw new RuntimeException("id不能为空!");
            }
            return userMapper.selectById(uid);
        }
    }
    
  2. 编写 applicationContext.xml 配置文件

    <!--事务的注解驱动-->
    <tx:annotation-driven/>
     <!--配置事务管理器-->
     <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"/>
        </bean>
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/74336.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++不知算法系列之排序从玩转冒泡算法开始

1. 前言 所谓排序&#xff0c;指把数据群体按个体数据的特征按从大到小或从小到大的顺序存放。 排序在应用开发中很常见&#xff0c;如对商品按价格、人气、购买数量等排序&#xff0c;便于使用者快速找到数据。 常见的排序算法分为两大类&#xff1a; 比较类&#xff1a;通…

springboot奖助学金评审系统的设计与实现毕业设计源码031035

奖助学金评审系统的设计与实现 摘 要 科技进步的飞速发展引起人们日常生活的巨大变化&#xff0c;电子信息技术的飞速发展使得电子信息技术的各个领域的应用水平得到普及和应用。信息时代的到来已成为不可阻挡的时尚潮流&#xff0c;人类发展的历史正进入一个新时代。在现实运用…

③【Maven】创建Maven工程,解读核心配置。

个人简介&#xff1a;Java领域新星创作者&#xff1b;阿里云技术博主、星级博主、专家博主&#xff1b;正在Java学习的路上摸爬滚打&#xff0c;记录学习的过程~ 个人主页&#xff1a;.29.的博客 学习社区&#xff1a;进去逛一逛~ 创建Maven工程&#xff0c;解读核心配置。一、…

React render 的原理和触发时机

一、原理 在类组件和函数组件中&#xff0c;render函数的形式是不同的。 在类组件中render函数指的就是render方法&#xff1b;而在函数组件中&#xff0c;指的就是整个函数组件。 class Foo extends React.Component {render() { //类组件中return <h1> Foo </h1&…

CANoe的安装及打开

1. 安装 CANoe分为软件和硬件。硬件是一个盒子,可以连接真实ECU进行剩余仿真测试 软件需要安装使用,可以在Vector官方网站上下载软件包:https://www.vector.com/cn/zh/products/products-a-z/software/canoe/#c4327 我们以CANoe 15版本为例,下载后是压缩包 解压后,双击运…

结构(structure)创建——直接赋值法与使用struct函数创建法。

一些不同类型的数据组合成一个整体&#xff0c;虽然各个属性分别具有不同的数据类型&#xff0c;但是它们之间是密切相关的&#xff0c;结构&#xff08;(Structure&#xff09;类型就是包含一组记录的数据类型。结构类型的变量多种多样&#xff0c;可以是一维数组、二维数组或…

全面梳理Spring Boot框架的日志体系结构

文章目录1. Java 日志概览1.1 总体概览1.2 日志级别1.3 综合对比1.4 最佳实践2. Spring Boot 日志实现2.1 Spring Boot 日志配置2.2 Logback 配置2.3 Log4j 配置之前录过一个视频和大家分享 Spring Boot 日志问题&#xff0c;但是总感觉差点意思&#xff0c;因此松哥打算再通过…

第十八章 SPFA算法以及负环问题(利用dijkstra推导出该算法,超级详细!!)

第十八章 SPFA算法以及负环问题一、dijkstra算法的弊端二、dijkstra算法的优化1、SPFA算法&#xff08;1&#xff09;算法思路&#xff1a;&#xff08;2&#xff09;算法模板&#xff1a;问题&#xff1a;模板&#xff1a;逐行分析&#xff1a;三、SFPA解决负环问题&#xff1…

uni-clould常用笔记

一&#xff0c;云函数 定义&#xff1a; // hellocf云函数index.js入口文件代码 use strict; exports.main async (event, context) > {//event为客户端上传的参数let c event.a event.breturn {sum: c} // 通过return返回结果给客户端 }调用&#xff1a; // 客户端调…

【Linux】su 和 sudo 命令

su 命令 su 命令作用&#xff1a;在已登录的会话中切换到另外一个用户。 1、su root 切换超级用户root角色&#xff0c;但不切换用户环境。需要输入root角色的密码。 2、su - root 切换root角色&#xff0c;并切换用户环境。 sudo 命令 sudo 命令作用&#xff1a;暂时切…

【树莓派不吃灰】命令篇⑩ 记录Linux常用命令

目录1. 命令格式1.1 mount2. 文件处理命令2.1 ls2.2 mkdir2.3 cd2.4 pwd2.5 rmdir2.6 cp2.7 mv2.8 rm2.9 touch2.10 cat、tac2.11 more、less、head、tail2.12 ln3. 权限管理命令3.1 chmod3.2 chown3.3 chgrp3.4 umask4. 文件搜索命令4.1 find4.2 locate4.3 which4.4 whereis4.…

进阶 - Git的标签管理

本篇文章&#xff0c;是基于我自用Windows&#xff08;Win10&#xff09;系统当做示例演示 本地仓库在&#xff1a;E:\test_git_rep 远程仓库是&#xff1a;gitgithub.com:lili40342/test_git_rep.git 描述测试环境的目的&#xff0c;是更好的解释测试过程&#xff0c;以免对你…

elasticsearch-8.5.2快速入门和kibana-8.5.2的使用

一、 安装 官方安装Elasticsearch&#xff0c;和ES可视化工具kibana。安装下载过程略。 二、 启动Elasticsearch。 windows系统&#xff0c;直接进入到如图目录&#xff0c;然后启动elasticsearch.bat&#xff0c;这个就是ES服务。 启动后&#xff0c;我们可以访问https://…

#438 沸腾客厅:从数字藏品到Web3.0,不止于事件营销

点击文末“阅读原文”即可收听本期节目数字藏品是什么&#xff1f;数字藏品是指使用区块链技术&#xff0c;对应特定的作品、艺术品生成的唯一数字凭证&#xff0c;在保护其数字版权的基础上&#xff0c;实现真实可信的数字化发行、购买、收藏和使用。2022年是天津文化中心成立…

「Redis」10 三大缓存问题、分布式锁

笔记整理自【尚硅谷】Redis 6 入门到精通 超详细 教程 Redis——三大缓存问题、分布式锁 1. 三大缓存 缓存穿透 问题描述 key 对应的数据在数据源并不存在&#xff0c;每次针对此 key 的请求从缓存获取不到&#xff0c;请求都会压到数据源&#xff0c;从而可能压垮数据源。 …

Java入门教程(27)——重写和final关键字

文章目录1.重写(override)2.final关键字实例1&#xff1a;修饰变量实例2.修饰方法实例3.修饰类1.重写(override) 什么是重写呢&#xff0c;顾名思义&#xff0c;子类重写父类的方法&#xff0c;可以用自身行为替换父类行为。方法重写需要符合的条件&#xff1a; 方法名、形参列…

【Linux】静动态库的制作和使用

前言 好久不见&#xff0c;甚是想念~ 本篇文章具体以操作为主&#xff0c;介绍在Linux下如何打包动静态库&#xff0c;并且如何使用这些库&#xff0c;同时&#xff0c;简单的阐述一下原理。让我们开始吧~ 上一篇Linux文章传送地址~ 【Linux】基础IO的理解与操作 - fd_柒海啦的…

复合材料专场 | ABAQUS车载四型复合材料气瓶固化过程的数值模拟分析攻略

复合材料气瓶固化的热场本质上可以认为包含两个阶段&#xff0c;复合材料气瓶表面和周围通过与空气的热对流换热&#xff0c;复合材料与内部塑料芯模以及金属接头的传热。在第一个阶段整体温度较低&#xff0c;热量从表面向复合材料层内部流入&#xff0c;此时固化速率很低&…

Python使用Opencv图像处理方法完成手势识别(三)tkinter制作GUI界面

前面对手势识别已经差不多完成。 这一章来制作一个手势识别GUI界面和说一下精确度不够问题所在。 首先是精确度不够的问题&#xff1a; 让手势更规范&#xff0c;手掌张开点。首先应该调节Hsv阈值&#xff0c;因为手掌和环境颜色与我的可能有差异。调整面积&#xff0c;周长阈…

ADI Blackfin DSP处理器-BF533的开发详解7:SPI接口的驱动和应用(含源代码)

硬件准备 ADSP-EDU-BF533&#xff1a;BF533开发板 AD-HP530ICE&#xff1a;ADI DSP仿真器 软件准备 Visual DSP软件 硬件链接 接口功能介绍 SPI 接口是 4 线串口&#xff0c;可以连接 SPIFLASH&#xff0c;SPI 接口的 AD&#xff0c;DA 等等。ADSP-BF533 的 SPI 接口支持主…