# spring-learn **Repository Path**: hrbu2023/spring-learn ## Basic Information - **Project Name**: spring-learn - **Description**: spring-ioc、AOP学习 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-03-10 - **Last Updated**: 2026-03-13 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Spring Core Spring使得创建Java企业级应用变得轻松。它提供了在企业环境中使用Java语言所需的一切功能,支持Groovy和Kotlin作为JVM上的替代语言,并具备根据应用需求灵活构建多种架构的能力。自Spring Framework 6.0起,Spring要求Java 17+版本 IOC 容器( Inversion of Control ): 控制翻转: 类与类之间的依赖关系,由工厂(容器)管理,被依赖的对象什么时候创建、怎么创建都交给Spring 工厂 - Controller - Service - Dao(Mapper) public class UserController{ private IUserService userService = new UserService - list - save - update } ## 实例 项目名称-- spring-01-ioc-xml ![image-20260310085152357](assets/image-20260310085152357.png) 在父项目中使用 dependencyManagement 统一定义 依赖的版本号方便统一修改管理 ```xml 4.0.0 com.neuedu.sm spring-learn 1.0 pom spring-learn http://maven.apache.org spring-01-ioc-xml UTF-8 5.13.4 org.junit.jupiter junit-jupiter-api ${junit.version} test ``` 在父项目中 添加Spring的版本,然后在 01-ioc-xml项目中真正的添加依赖(不需要版本号) ### 父项目 ```xml 4.0.0 com.neuedu.sm spring-learn 1.0 pom spring-learn http://maven.apache.org spring-01-ioc-xml UTF-8 5.13.4 6.2.16 org.junit.jupiter junit-jupiter-api ${junit.version} test org.springframework spring-context ${spring.version} org.springframework spring-beans ${spring.version} org.springframework spring-core ${spring.version} org.springframework spring-expression ${spring.version} ``` ### 子项目添加具体的依赖 ![image-20260310090802522](assets/image-20260310090802522.png) 使用UserService 依赖 UserDao 说明 IOC的关系 - IUserService : service层的接口 - UserServiceImpl : 实现IUserService 的实现类 - IUserDao : dao层的接口 - UserDaoImpl : 实现IUserDao 的实现类 创建工厂: 管理 IUserService 实例、IUserDao 从工厂中获取一个UserService 调用里面的方法 ### 编写代码 实现使用工厂管理 UserService IUserService ```java package com.neuedu.sm.service; public interface IUserService { /** * 查询用户列表 */ public void list(); } ``` UserServiceImpl ```java package com.neuedu.sm.service.impl; import com.neuedu.sm.service.IUserService; public class UserServiceImpl implements IUserService { @Override public void list() { System.out.println("查询用户信息"); } } ``` ### 使用XML定义工厂 ```xml ``` ### 测试 从容器中获取对象(UserService) 使用XML文件的形式构造容器(工厂),使用ClassPathXmlApplicationContext 加载 xml 构建工厂 ![image-20260310092722735](assets/image-20260310092722735.png) ### 使用工厂 ``` package com.neuedu.sm; import com.neuedu.sm.service.IUserService; import org.junit.jupiter.api.Test; import org.springframework.context.support.ClassPathXmlApplicationContext; public class UseBeanFactory { @Test public void test(){ //1 构造工厂 // "spring-ioc.xml" 配置文件 // context 工厂对象 : 作用 管理对象的全部生命周期(创建、销毁......) ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-ioc.xml"); //2 从工厂中获取对象 IUserService userService = context.getBean(IUserService.class); System.out.println("userService = " + userService); //3 调用对象 list方法 userService.list(); } } ``` ## 依赖注入(构造器子注入) 使用IOC容器管理Dao - IUserDao - UserDaoImpl IUserDao ```java package com.neuedu.sm.dao; public interface IUserDao { public void selectList(); } ``` UserDaoImpl ```java package com.neuedu.sm.dao.impl; import com.neuedu.sm.dao.IUserDao; public class UserDaoImpl implements IUserDao { @Override public void selectList() { System.out.println("UserDaoImpl.selectList 实现"); } } ``` 使用容器管理 ```xml ``` 维护依赖关系 UserService 需要使用UserDao 在容器配置文件中 给UserService 设置一个UserDao ![image-20260310095943537](assets/image-20260310095943537.png) ```xml ``` ## 依赖注入(设值注入 setter) 使用IOC容器管理Dao - IRoleService - RoleServiceImp; - IRoleDao - RoleDaoImpl ### IRoleService ```java package com.neuedu.sm.service; public interface IRoleService { /** * 查询角色列表 */ public void list(); } ``` ### RoleServiceImp 注意需要给注入的属性添加setter属性 ```java package com.neuedu.sm.service.impl; import com.neuedu.sm.dao.IRoleDao; import com.neuedu.sm.dao.IUserDao; import com.neuedu.sm.service.IRoleService; import com.neuedu.sm.service.IUserService; public class RoleServiceImpl implements IRoleService { private IRoleDao roleDao; @Override public void list() { System.out.println("service 查询查询角色 信息"); roleDao.selectList(); } public IRoleDao getRoleDao() { return roleDao; } public void setRoleDao(IRoleDao roleDao) { this.roleDao = roleDao; } } ``` ### IRoleDao(Dao层) ```java package com.neuedu.sm.dao; public interface IRoleDao { public void selectList(); } ``` ### RoleDaoImpl ```java package com.neuedu.sm.dao.impl; import com.neuedu.sm.dao.IRoleDao; import com.neuedu.sm.dao.IUserDao; public class RoleDaoImpl implements IRoleDao { @Override public void selectList() { System.out.println("RoleDaoImpl.selectList 实现"); } } ``` ### 声明依赖 ```xml ``` ![image-20260310101247381](assets/image-20260310101247381.png) ### 单元测试(UseBeanFactory) ```java @Test public void testSetterDI(){ //1 构造工厂 // "spring-ioc.xml" 配置文件 // context 工厂对象 : 作用 管理对象的全部生命周期(创建、销毁......) ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-ioc.xml"); //2 从工厂中获取对象 IRoleService roleService = context.getBean(IRoleService.class); //3 调用对象 list方法 roleService.list(); } ``` # 各种输入的属性类型 在类中注入常见的数据类型 int、String double、float long、array List Map Properties ## 可以被依赖注入的一个Bean类型 ```java package com.neuedu.sm.dao; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; public class DeptDao { private String url; private Integer maxConnection; private double salary; private String[] names; private List users; private Set ages; private Map personInfo; private Properties jdbcInfo; //setter public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public Integer getMaxConnection() { return maxConnection; } public void setMaxConnection(Integer maxConnection) { this.maxConnection = maxConnection; } public double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } public String[] getNames() { return names; } public void setNames(String[] names) { this.names = names; } public List getUsers() { return users; } public void setUsers(List users) { this.users = users; } public Set getAges() { return ages; } public void setAges(Set ages) { this.ages = ages; } public Map getPersonInfo() { return personInfo; } public void setPersonInfo(Map personInfo) { this.personInfo = personInfo; } public Properties getJdbcInfo() { return jdbcInfo; } public void setJdbcInfo(Properties jdbcInfo) { this.jdbcInfo = jdbcInfo; } } ``` ## 在容器中声明(管理)Bean对象 ![image-20260310103658897](assets/image-20260310103658897.png) ## 使用 ```xml 张三 李四 王五 王五 张飞 刘备 李逵 李逵 100 25 33 33 jdbc:mysql://localhost:3306/his?chartxxxxx=utf-8 com.mysql.jdbc.cj.Driver root root ``` ```java package com.neuedu.sm.dao; import org.junit.jupiter.api.Test; import org.springframework.context.support.ClassPathXmlApplicationContext; import static org.junit.jupiter.api.Assertions.*; class DeptDaoTest { /** * * DI dependency Injection 依赖注入 */ @Test public void testDI() { //获取容器 工厂 ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-ioc.xml"); //获取DeptDao // DeptDao deptDao = (DeptDao) context.getBean("deptDao"); // DeptDao deptDao = context.getBean("deptDao", DeptDao.class); DeptDao deptDao = context.getBean(DeptDao.class); System.out.println(deptDao); System.out.println("deptDao.getNames().length = " + deptDao.getNames().length); } } ``` ## 控制台打印 ```ini DeptDao{url='jdbc:mysql://localhost:3306/his?chartxxxxx=utf-8', maxConnection=100, salary=500.02, names=[张三, 李四, 王五, 王五], users=[张飞, 刘备, 李逵, 李逵], ages=[100, 25, 33], personInfo={name=张三, age=25, sex=男}, jdbcInfo={jdbc.password=root, jdbc.user=root, jdbc.url=jdbc:mysql://localhost:3306/his?chartxxxxx=utf-8, jdbc.driver=com.mysql.jdbc.cj.Driver}} deptDao.getNames().length = 4 Process finished with exit code 0 ``` # 使用注解的形式声明Bean - 在xml中使用 包扫描的方式声明Bean ```xml ``` 在需要声明Bean的类上面使用 @Component注解 ```java package com.neuedu.sm.service; import org.springframework.stereotype.Component; /** * 当包扫描的时候 需要在容器中注册Bean * 作用同 */ @Component public class UserService { public void list() { System.out.println("UserService.list--Service层"); } } ``` 各种注解的含义 除了@Component这个泛指组件的注解外,Spring还提供了与@Component功能相同的三个语义化注解。 1. @Service 业务层组件 2. @Controller 控制层组件 3. @Repository 数据层组件 修改上面代码,使用@Repository 和 @Service 替换 dao 与 service 组件上的注解。 # 使用Autowired注入属性 基于注解的依赖 使用@Autowired注解,`@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})` ## 构造方法注入 ```java package com.neuedu.sm.controller; import com.neuedu.sm.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; @Controller public class UserController { private UserService userService; /** * 构造器的方式注入 * @param userService */ @Autowired public UserController(UserService userService) { this.userService = userService; } public void queryList() { System.out.println("UserController.queryList --Controller层"); userService.list(); } } ``` ## 属性注入 ```java package com.neuedu.sm.service; import com.neuedu.sm.dao.UserDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.stereotype.Controller; import org.springframework.stereotype.Service; /** * 当包扫描的时候 需要在容器中注册Bean * 作用同 */ @Service public class UserService { /** * 属性注入 */ @Autowired private UserDao userDao; public void list() { System.out.println("UserService.list--Service层"); userDao.selectList(); } /* public UserDao getUserDao() { return userDao; } public void setUserDao(UserDao userDao) { this.userDao = userDao; }*/ } ``` ```java package com.neuedu.sm.dao; import org.springframework.stereotype.Repository; @Repository public class UserDao { public void selectList() { System.out.println("UserDao.selectList 实现"); } } ``` 存在同一个类型 由多个实例 使用注解 Qualifier 按名称查找, ![image-20260310115754053](assets/image-20260310115754053.png) # Spring的测试框架 使用的框架 是 **Junit5** 在编写代码过程中经常的会测试 Bean,代码的逻辑是使用@BeforeEach的注解,获取容器,然后在从容器中获取Bean(Controller、Service、Dao等) 可以使用 Spring-test框架对 Junit进行扩展(自动启动容器-Bean工厂 ),然他默认加载某一个配置文件,可以使用@Autowired从容器中获取Bean ## 引入依赖 - Junit-4 - junit-5 - Spring-test 在父项目的pom.xml中声明依赖的版本 ```xml 4.0.0 com.neuedu.sm spring-learn 1.0 pom spring-learn http://maven.apache.org spring-01-ioc-xml spring-02-ico-xml-component-scan spring-03-ico-xml-component-scan-DI UTF-8 4.13.2 5.13.4 6.2.16 org.junit.jupiter junit-jupiter-api ${junit.version} test junit junit ${junit4.version} test org.springframework spring-context ${spring.version} org.springframework spring-beans ${spring.version} org.springframework spring-core ${spring.version} org.springframework spring-expression ${spring.version} org.springframework spring-test test ${spring.version} ``` ## 创建一个子项目 spring-04-ioc-xml-scan-autowired-test-framework 子项目的依赖 ```xml 4.0.0 com.neuedu.sm spring-learn 1.0 spring-04-ioc-xml-scan-autowired-test-framework jar spring-04-ioc-xml-scan-autowired-test-framework http://maven.apache.org UTF-8 org.springframework spring-context org.springframework spring-test org.junit.jupiter junit-jupiter-api junit junit ``` ## 使用03的代码(Junit-5) 在单元测试类上添加 `@SpringJUnitConfig(locations = "classpath:spirng-ioc-scan-di.xml")` 会自动的注册Bean工厂,可以在当前的测试类中使用@Autowired从容器中获取 Bean ```java package com.neuedu.sm.controller; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; //使用Spring-test 对 Junit的扩展 @SpringJUnitConfig(locations = "classpath:spirng-ioc-scan-di.xml") class UserControllerJunit5Test { @Autowired UserController userController; @Test void queryList() { userController.queryList(); } } ``` ## Junit4的支持 ```java package com.neuedu.sm.controller; //import org.junit.jupiter.api.Test; 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; //使用Spring-test 对 Junit的扩展 //@SpringJUnitConfig(locations = "classpath:spirng-ioc-scan-di.xml") //RunWith 对 Junit进行扩展 //@RunWith(value=SpringJUnit4ClassRunner.class) //@ContextConfiguration(locations = "classpath:spirng-ioc-scan-di.xml") //@ContextConfiguration(value = "classpath:spirng-ioc-scan-di.xml") /** * 使用Junit4 */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:spirng-ioc-scan-di.xml") public class UserControllerJunit4Test { @Autowired UserController userController; @Test public void queryList() { userController.queryList(); } } ``` # Java配置类 ## 普通的Bean声明 ```java public class UserController { public void queryList(){ System.out.println("UserController.queryList --Controller层"); } } ``` ## 使用配置类的方式注册Bean(替代配置文件) ```java package com.neuedu.sm; import com.neuedu.sm.controller.UserController; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; /** * 相当于配置文件 * * Configuration */ public class SpirngIocConfiguration { /** * @Bean 相当与 * * @Bean 一般用于注册第三方的 包(拿不到源码 ) * @return */ @Bean public UserController userController() { return new UserController(); } } ``` ## 使用@ComponentScan注解进行包扫描 ![image-20260310152102427](assets/image-20260310152102427.png) # 使用@Bean注册第三方类 在容器中注册 Mybatis的SQLSessionFactory - 添加依赖 - 父项目中版本控制 - 子项目中添加依赖(不需要版本号) ## 父项目的pom.xml ```xml 4.0.0 com.neuedu.sm spring-learn 1.0 pom spring-learn http://maven.apache.org spring-01-ioc-xml spring-02-ioc-xml-component-scan spring-03-ioc-xml-component-scan-DI spring-04-ioc-xml-scan-autowired-test-framework spring-05-javaconfig UTF-8 4.13.2 5.13.4 6.2.16 8.2.0 3.5.19 org.junit.jupiter junit-jupiter-api ${junit.version} test junit junit ${junit4.version} test org.springframework spring-context ${spring.version} org.springframework spring-beans ${spring.version} org.springframework spring-core ${spring.version} org.springframework spring-expression ${spring.version} org.springframework spring-test test ${spring.version} com.mysql mysql-connector-j ${mysql.version} org.mybatis mybatis ${mybatis.version} ``` ## 子项目添加依赖 ```xml 4.0.0 com.neuedu.sm spring-learn 1.0 spring-05-javaconfig jar spring-05-javaconfig http://maven.apache.org UTF-8 org.springframework spring-context org.springframework spring-test org.junit.jupiter junit-jupiter-api com.mysql mysql-connector-j org.mybatis mybatis ``` # Bean的作用域 | Scope | Description | | :---------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [singleton](https://docs.spring.io/spring-framework/reference/6.2/core/beans/factory-scopes.html#beans-factory-scopes-singleton) | (默认)将单个 Bean 定义映射到每个 Spring IoC 的单个对象实例 容器。 | | prototype | 将单个豆定义作用域为任意数量的对象实例。 | | request | 将单个 bean 定义作用域到单个 HTTP 请求的生命周期。也就是说, 每个HTTP请求都有自己由单个豆子创建的豆子实例 定义。仅在具备网页功能的 Spring 环境中有效。`ApplicationContext` | | session | 将单个豆定义作用域到HTTP的生命周期。仅在 一个具备网络感知的春季环境。`Session``ApplicationContext` | | application | 用单一豆子定义范围到一个的生命周期。仅在 一个具备网络感知的春季环境。`ServletContext``ApplicationContext` | | [websocket](https://docs.spring.io/spring-framework/reference/6.2/web/websocket/stomp/scope.html) | 用单一豆子定义范围到一个的生命周期。仅在 网络感知春季的背景 `WebSocket``ApplicationContext`. | # AOP 面相切面编程 ## 代理机制 Spring AOP使用了两种代理机制:一种是基于JDK的动态代理;另一种是基于CGLib的动态代理。之所以需要两种代理机制,是因为JDK本身只提供接口的代理,而不支持类的代理。 ### JDK动态代理 Spring的AOP的默认实现就是采用jdk的动态代理机制实现的。 自Java1.3以后,Java提供了动态代理技术,允许开发者在运行期创建接口的代理实例。 ### CGLib代理 JDK只能为接口创建代理实例,对于那些没有通过接口定义业务方法的类,可以通过CGLib创建代理实例。 CGLib采用底层字节码技术,可以为一个类创建子类,并在子类中采用方法拦截技术拦截所有父类方法的调用,这时可以顺势织入横切逻辑。 代理对象的伪代码 ```java package com.neuedu.sm.service; public class UserService { public void save() { //方法之前 开启 事务 System.out.println("保存用户数据"); // 提交事务 //回滚实物 } } ``` ## 代理对象 ```java package com.neuedu.sm.service; public class UserServiceProxy extends UserService { @Override public void save() { System.out.println("开启事务"); super.save(); System.out.println("提交事务"); } public static void main(String[] args) { UserService userService = new UserServiceProxy(); userService.save(); } } ``` ![image-20260311103543751](assets/image-20260311103543751.png) 可以通过AOP的机制 实现动态代理 ### 4.3.1. 切面(Aspect) 切面是切点和通知组成,通知和切点共同定义了切面的全部内容即:它是什么,在何时何处完成其功能; 举例:将pointcut和Advice配置整合的过程切面 ### 4.3.2. 连接点(Joinpoint) 连接点是在应用执行过程中能够插入切面的一个点,Spring仅支持方法的连接点,即仅能在方法调用前,方法调用后,方法抛出异常时及方法调用前后插入切面代码。 . 举例:如果在insert之前执行扩展(增强、通知),简单理解insert这个方法可以称之为连接点。 连接点: 1 Com.neuedu.service.StuServiceImpl.insert() 连接点: 2 Com.neuedu.service.UserServiceImpl.insert() ### 4.3.3. 切点(Pointcut) ``` Com.neuedu.service.*Impl.insert() ``` ### 4.3.4. 通知(Advice)、增强 切面的工作被成为通知,定义了切面是什么及何时使用。除了描述切面要完成的工作,通知还解决了何时执行这个工作的问题,它应该在某个方法被调用之前?之后?等。 举例:额外定义一个方法想要在连接点上进行扩展的功能 #### 4.3.4.1. Spring切面可以应用5种类型的通知: 前置通知(Before)在目标方法被调用之前调用通知功能; 后置通知(After)在目标方法被完成之后调用通知功能,不关心方法的输出是什么; 环绕通知(Around advice)通知包裹了目标方法,在目标方法调用之前和之后执行自定义的行为; 异常通知(After-throwing)在目标方法抛出异常后调用通知; 返回通知(After-returning)在目标方法成功执行之后调用通知; ### 4.3.5. 目标对象(Target) 通知逻辑的织入目标类。如果没有AOP,那么目标业务类需要自己实现所有的逻辑,在AOP的帮助下,目标类只需要实现那些非横切逻辑的程序逻辑,而比如事务管理等这些横切逻辑就可以使用AOP动态织入特定的连接点上。 举例:两个Service即是目标对象 ### 4.3.6. 引入(Introduction) 引介是一种特殊的通知,为类添加一些属性和方法。这样,即使一个业务类原本没有实现某个接口,通过AOP的引介功能,也可以动态地为该业务类添加接口的实现逻辑,使业务类成为这个接口的实现类。 几乎由框架完成,我们可以暂时透明 ### 4.3.7. 代理(Proxy) 一个类被AOP织入通知后,就产生一个结果类,它是融合了原类和通知逻辑的代理类。根据不同的代理方式,代理类既可能是和原类具有相同接口的类,也可能就是原类的子类,所以可以采用与调用原类相同的方式调用代理类。 举例:IOC容器创建的两个Service类型对象 ### 4.3.8. 织入(Weaving) 织入是将通知添加到目标类的具体连接点上的过程.AOP就像一台织布机,将目标类,通知或引介编织到一起。 AOP有三种织入方式: ①编译期织入:切面在目标类编译时被织入,需要特殊的编译器; ②类装载期织入:切面在目标类加载到JVM时被织入,需要特殊的类装载器; ③动态代理织入:切面在应用运行的某个时刻呗织入,AOP容器会为目标对象动态创建一个代理对象; ## 面相切面编程(AOP) 需要编写的代码 - 目标对象( UserService、RoleService 各种Bean) - 通知: 在目标对象方法基础上添加的功能(属性、加方法 加代码 ) - 编写切面 - 切点 - 通知 - 使用目标对象--- 从容器中获取-- 由容器调用AOP (织入) 代理对象 # AOP案例 定义 UseService、RoleService 包含 save功能, > 需求 需要给 save功能 执行之前 添加一个开启事务的功能 - 创建项目 spring-07-aop-xml - 添加依赖 spirng-context(core、bean、express、context)、spirng-aop 、aspectjweaver - 编写 UseService、RoleService 子项目中需要的依赖 ```xml 4.0.0 com.neuedu.sm spring-learn 1.0 spring-07-aop-xml jar spring-07-aop-xml http://maven.apache.org UTF-8 org.springframework spring-context ${spring.version} org.springframework spring-aop org.aspectj aspectjweaver org.springframework spring-test test org.junit.jupiter junit-jupiter-api ${junit.version} test ``` ## 编写AOP基础环境代码 - UseService、RoleService @Service - 配置文件 spring-aop 使用包扫描 component-scan ```xml ``` 编写单元测试 获取UserService、RoleService测试save功能 ```java package com.neuedu.sm; import com.neuedu.sm.service.UserService; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; @SpringJUnitConfig(locations = "classpath:spring-aop.xml") public class UserServiceTest { @Autowired UserService userService; @Test public void test() { userService.save(); } } ``` 测试结果 ![image-20260311110059653](assets/image-20260311110059653.png) ## 配置AOP(代完成) **前置通知(Before)在目标方法被调用之前调用通知功能;** 后置通知(After)在目标方法被完成之后调用通知功能,不关心方法的输出是什么; 环绕通知(Around advice)通知包裹了目标方法,在目标方法调用之前和之后执行自定义的行为; 异常通知(After-throwing)在目标方法抛出异常后调用通知; 返回通知(After-returning)在目标方法成功执行之后调用通知 ### 编写通知方法(Before) 在容器中注册Bean(通知类) ````java package com.neuedu.sm.service.advice; import org.springframework.stereotype.Component; @Component public class MyAdvice { public void before() { System.out.println("在service方法之前执行"); System.out.println("在service方法之前执行"); System.out.println("在service方法之前执行"); System.out.println("在service方法之前执行"); } } ```` ## 配置切面(使用XML) ```xml ``` ### 测试代码 ```java package com.neuedu.sm; import com.neuedu.sm.service.UserService; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; @SpringJUnitConfig(locations = "classpath:spring-aop.xml") public class UserServiceTest { @Autowired UserService userService; @Test public void test() { userService.save(); } } ``` ![image-20260311112657235](assets/image-20260311112657235.png) ## 其他通知类型 基础的Bean代码 ```java package com.neuedu.sm.service; import org.springframework.stereotype.Service; @Service public class RoleService { public void save() { System.out.println("RoleService.save --保存[角色 ]"); } public void queryList() { System.out.println("RoleService.queryList --查询角色"); } public void update(boolean success) throws Exception { System.out.println("RoleService.update --更新角色"); if (!success) { throw new Exception("产生错误了"); } } public Object getById(boolean success) throws Exception { System.out.println("RoleService.queryById --查询对象"); if (!success) { throw new Exception("产生错误了"); } return "查询对象-Role "; } } ``` ```java package com.neuedu.sm.service; import org.springframework.stereotype.Service; @Service public class UserService { public void save() { System.out.println("UserService.save --保存用户"); } public void queryList() { System.out.println("UserService.queryList --查询用户"); } /** * 可能产生异常的方法 * @param success * @throws Exception */ public void update(boolean success) throws Exception { System.out.println("UserService.update --更新用户"); if (!success) { throw new Exception("产生错误了"); } } public Object getById(boolean success) throws Exception { System.out.println("UserService.queryById --查询对象"); if (!success) { throw new Exception("产生错误了"); } return "查询对象-User "; } } ``` ### 配置通知 ```xml ``` ### 测试代码 ```java package com.neuedu.sm; import com.neuedu.sm.service.RoleService; import com.neuedu.sm.service.UserService; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; @SpringJUnitConfig(locations = "classpath:spring-aop.xml") public class RoleServiceTest { @Autowired RoleService roleService; @Test public void test() { roleService.save(); } @Test public void testQuery() { roleService.queryList(); } @Test public void testUpdate() throws Exception { // boolean success = true; boolean success = false; roleService.update(success); } @Test public void testQueryById() throws Exception { // boolean success = true; boolean success = false; roleService.getById(success); } } ``` ## 使用xml注解扫描+AOP注解 将spring-07-aop-xml 复制一份 spring-08-aop-xml-annotation 将xml中 aop:config标签的配置 换成注解 ### 使用配置类 ```java package com.neuedu.sm.service.advice; import org.aopalliance.intercept.Joinpoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; @Component @Aspect public class MyAdvice { /** * * 使用XML的形式注册切点 * * * * */ @Pointcut("execution(* com.neuedu.sm.service.*.save(..) )") public void serviceSave() { } @Pointcut("execution(* com.neuedu.spring.service.*.query*(..) )") public void serviceQuery() { } @Pointcut("execution(* com.neuedu.spring.service.*.update*(..) )") public void serviceUpdate() { } @Pointcut("execution(* com.neuedu.spring.service.*.getById(..))") public void serviceGetById() { } /** * 前置通知 @Before("serviceSave()") * */ @Before("serviceSave()") public void before() { System.out.println("1在service方法之前执行"); System.out.println("1在service方法之前执行"); System.out.println("1在service方法之前执行"); } /** * 前置通知 @After("serviceSave()") * */ @After("serviceSave()") public void afterAdvice() { System.out.println("2在service方法之后执行"); System.out.println("2在service方法之后执行"); System.out.println("2在service方法之后执行"); } /** * 环绕通知 @Around("serviceQuery()") * * @param pjp */ @Around("serviceQuery()") public void around(ProceedingJoinPoint pjp) { // 执行方法 System.out.println("环绕通知 1 "); try { Object target = pjp.getTarget(); System.out.println("target = " + target); Object[] args = pjp.getArgs(); Signature signature = pjp.getSignature(); System.out.println("signature = " + signature); Object aThis = pjp.getThis(); //System.out.println("aThis = " + aThis); pjp.proceed(); //原始目标的方法 //提交事务 } catch (Throwable e) { //回滚事务 throw new RuntimeException(e); } System.out.println("环绕通知 2 "); } /** * 异常通知 @AfterThrowing(pointcut = "serviceUpdate()",throwing = "ex") * * @param ex */ @AfterThrowing(pointcut = "serviceUpdate()", throwing = "ex") public void cathException(Exception ex) { System.out.println("异常通知 " + ex.getMessage()); System.out.println("把异常写入到数据库中。。。。"); } /** * 返回后通知 @AfterReturning("serviceGetById()") * */ @AfterReturning("serviceGetById()") public void returnAdvice() { System.out.println("返回之后执行"); } } ``` ![image-20260311135815764](assets/image-20260311135815764.png) ## 使用Java配置类管理AOP 新建项目spring-09-aop-javaconfig ### 配置类 ```java package com.neuedu.sm; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; /** * ComponentScan 默认扫描 当前配置所在的包 和子包 */ @Configuration //@ComponentScan("com.neuedu.sm") @ComponentScan() @EnableAspectJAutoProxy public class App { } ``` ### 测试类 ````java package com.neuedu.sm; import com.neuedu.sm.service.RoleService; import com.neuedu.sm.service.UserService; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; @SpringJUnitConfig(classes = App.class) public class RoleServiceTest { @Autowired RoleService roleService; @Test public void test() { roleService.save(); } @Test public void testQuery() { roleService.queryList(); } @Test public void testUpdate() throws Exception { // boolean success = true; boolean success = false; roleService.update(success); } @Test public void testQueryById() throws Exception { //boolean success = true; boolean success = false; roleService.getById(success); } } ```` ```java package com.neuedu.sm; import com.neuedu.sm.service.UserService; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; @SpringJUnitConfig(classes = App.class) public class UserServiceTest { @Autowired UserService userService; @Test public void test() { userService.save(); } @Test public void testQuery() { userService.queryList(); } } ``` # 代理模式 实现动态代理有两种形式: 1. jdk动态代理:根据目标类接口获取代理类实现规则,生成代理对象。这个代理对象,也是目标类接口的一个实现类。 2. cglib动态代理:根据目标类本身获取代理类实现规则,生成代理对象。这个代理对象,也是目标类的一个子类。 (如果目标类为final,则不能使用CGLib实现动态代理) SpringAOP可以自动在jdk动态代理和CGLib动态代理之间进行切换,规则如下: 1. 如果目标对象实现了接口,采用jdk动态代理实现aop。 2. 如果目标对象没有实现接口,采用CGLib动态代理实现aop。 3. 如果目标对象实现了接口,但仍然想要使用CGLIB实现aop,可以手动进行配置。 ![image-20260311142112760](assets/image-20260311142112760.png) ![image-20260311142954125](assets/image-20260311142954125.png) # 整合MyBatis 整合的思路 将SQLSessionFactory、Mapper(dao)由 Spring的IOC工厂托管,需要用的时候 @Autowired 自动从容器中获取。 - spring(xml+包扫描注册Bean)+MyBatis (mybaits-spring.jar : 写好了 创建SQLSessionFactory的代码) - spring(配置类)+MyBatis (mybaits-spring.jar : 写好了 创建SQLSessionFactory的代码) ## 基于xml的Spirng整合MyBatis - 创建spring的ioc项目 - 添加依赖 - spring-context 、Spring-aop 、aspectjweaver 、spring-test、 - spring-jdbc、 - junit、 Mybaits、 - MYSQL驱动 、druidDatasource 、 - mybatis-spring 整合的包 - 创建一个配置文件spring-mybaits.xml - 编写一个Bean @Service UserService 类 - 测试 - 从容器中获取 Userservice - 添加 MyBaits - 编写 SQLMapConfig.xml - 基于数据库生成PO、Mapper.xml、Mapper.java - 在IOC容器中配置SQLSessionFactory、 - 将Mapper的创建过程 `session.getMapper(XXXXMapper.class); 交给Spring IOC - 测试 - 从IOC容器中拿到Mapper并测试 ## spring-10-spring-xml-mybaits-configxml ### 1 添加依赖 ```xml 4.0.0 com.neuedu.spring spring-learn 1.0 spring-10-spring-xml-mybaits-configxml jar spring-10-spring-xml-mybaits-configxml http://maven.apache.org UTF-8 org.springframework spring-context ${spring.version} org.springframework spring-aop org.springframework spring-test org.springframework spring-jdbc org.aspectj aspectjweaver org.junit.jupiter junit-jupiter-api ${junit.version} com.mysql mysql-connector-j com.alibaba druid org.mybatis mybatis org.mybatis mybatis-spring org.projectlombok lombok ``` ### 2 ioc的配置文件 spring-mybatis.xml 基础包 : **com.neuedu.sm** - po - mapper - service - controller ```xml ``` ### 3 编写测试的Bean(Userservice) ```java package com.neuedu.sm.service; import org.springframework.stereotype.Service; @Service public class UserService{ public void list(){ System.out.println("UserService.list--Service层"); } } ``` ### 4 使用单元测试 获取Bean 测试IOC ```java package com.neuedu.sm.service; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; import static org.junit.jupiter.api.Assertions.*; @SpringJUnitConfig(locations = "classpath:spring-mybatis.xml") class UserServiceTest { @Autowired private UserService userService; @Test void list() { userService.list(); } } ``` ![image-20260312085703044](assets/image-20260312085703044.png) ### 5 添加Mybaits的配置文件 #### SQLMapconfig.xml ```xml ``` #### jdbc.properties ```properties jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/assica?characterEncoding=utf-8 jdbc.username=root jdbc.password=root ``` ### 6 生成po、mapper DBMS: MySQL (ver. 8.0.30) Case sensitivity: plain=lower, delimited=lower Driver: MySQL Connector/J (ver. mysql-connector-j-8.2.0 (Revision: 06a1f724497fd81c6a659131fda822c9e5085b6c), JDBC4.2) Ping: 61 ms SSL: yes ![image-20260312092350896](assets/image-20260312092350896.png) ![image-20260312092411862](assets/image-20260312092411862.png) ![image-20260312092500978](assets/image-20260312092500978.png)\ ![image-20260312092544378](assets/image-20260312092544378.png) ![image-20260312092629437](assets/image-20260312092629437.png) ### 7 让容器管理SQLSessionFactory Mapper - mybatis-spring 父项目 家版本号,子项目直接依赖(不需要版本号) ```xml org.mybatis mybatis-spring 4.0.0 compile ``` ##### 在IOC配置文件中注册 SqlSessionFactory 、数据源 ```xml ``` ##### 测试SqlSessionFactory ```java package com.neuedu.sm.service; import org.apache.ibatis.session.SqlSessionFactory; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; @SpringJUnitConfig(locations = "classpath:spring-mybatis.xml") public class SqlSessionFactoryTest { @Autowired private SqlSessionFactory sqlSessionFactory; @Test public void testSqlSessionFactory() { System.out.println(sqlSessionFactory); } } ``` #### 使用MapperBean 在 容器中注册 Mapper的的代理对象 ![image-20260312103130217](assets/image-20260312103130217.png) ```xml ``` ```java package com.neuedu.sm.mapper; import com.neuedu.sm.po.Users; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; import static org.junit.jupiter.api.Assertions.*; @SpringJUnitConfig(locations = "classpath:spring-mybatis.xml") class UsersMapperTest { @Autowired private UsersMapper usersMapper; @Test void selectByPrimaryKey() { long id = 1L; Users users = usersMapper.selectByPrimaryKey(id); System.out.println("users = " + users); } } ``` #### 使用 spring 加载 jdbc.properties属性文件 ```xml ``` ![image-20260312103854286](assets/image-20260312103854286.png) ### 批量注册Mapper到IOC工厂 - 使用MapperScannerConfigurer 批量注册Mapper到IOC - 给sessionFactory 注入配置项,代替SqlMapConfig配置文件 ```xml ``` ## 使用事务 编写了一个需要事务的方法(常见的方式 是在 service层上添加事务 查询(select) 、更新(update、insert、delete) ) ```java package com.neuedu.sm.service; import com.neuedu.sm.mapper.UsersMapper; import com.neuedu.sm.po.Users; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class UserService{ @Autowired private UsersMapper usersMapper; /** * 保存的功能 * @param * @return */ public boolean update(boolean error){ Users user1 = usersMapper.selectByPrimaryKey(1L); Users user2 = usersMapper.selectByPrimaryKey(2L); user1.setEmail("+100"); int insert = usersMapper.updateByPrimaryKey(user1); if(error){ throw new RuntimeException("过程出错了"); } user2.setEmail("-100"); insert += usersMapper.updateByPrimaryKey(user2); return insert + insert == 2 ; } public void list(){ System.out.println("UserService.list--Service层"); } } ``` ```java package com.neuedu.sm.service; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; import static org.junit.jupiter.api.Assertions.*; @SpringJUnitConfig(locations = "classpath:spring-mybatis.xml") class UserServiceTest { @Autowired private UserService userService; @Test void list() { userService.list(); } @Test void update() { boolean error = true; boolean success = userService.update(error); System.out.println("success = " + success); } } ``` 当boolean error的值为true的时候 出现一个 第一个账户 有“+100” 标记,但是第二个账户因为代码报错而没有执行 ![1773294575302](assets/1773294575302.png) ### 使用spirng-tx中支持的事务管理器 配置aop切面,实现事务的功能 - 配置事务管理(相当于是通知)TxManager(DataSourceTransactionManager) - 给事务管理器配置切面 - 在什么方法上添加通知(开启事务、提交事务 ) 在spring-mybaits.xml中添加事务配置 ```xml ``` ![1773295230081](assets/1773295230081.png) 再次测试发现 UserService的 update功能室有事务的 事务的传播特性 **注解中的 propagation 属性,可以设置事务传播行为。属性值为:** 1. **REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,就加入到这个事务中。这是最常见的选择。** 2. **SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。** 3. **MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常。** 4. **REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。** 5. **NOT_SUPPORTED:以非事务方式执行操作,如果存在事务,就把当前事务挂起。** 6. **NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。** 事务的隔离特性 1️⃣ Read uncommitted (读未提交):最低级别,任何情况都无法保证。   第一隔离级别怕回滚,因为它能读取到别的事务未提交的数据。如果当前事务将这个数据当成真正的数据,那么如果别的事务回滚,那么就会数据错误,导致脏读。 2️⃣ Read committed (读已提交):可避免脏读的发生。 第二个隔离级别怕提交,因为提交会导致不可重复读。 3️⃣ Repeatable read (可重复读):可避免脏读、不可重复读的发生。*MySQL默认隔离级别 4️⃣ Serializable (串行化):可避免脏读、不可重复读、幻读的发生 ——————————————— 为啥Oracle默认隔离级别是Read committer 为啥MySQL默认隔离级别是Repeatable read ### 使用Transactional注解替代 切面的xml配置 详见 spring-13-spring-xml-mybaits-tx-annotation 项目 在 12的基础上,修改 Service添加注解,并将spring配置文件中的切面配置去掉, 添加注解驱动 ```java package com.neuedu.sm.service; import com.neuedu.sm.mapper.UsersMapper; import com.neuedu.sm.po.Users; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @Service public class UserService{ @Autowired private UsersMapper usersMapper; /** * 保存的功能 * @param * @return */ @Transactional(propagation = Propagation.REQUIRED,isolation= Isolation.DEFAULT,rollbackFor = Exception.class) public boolean update(boolean error) throws Exception { Users user1 = usersMapper.selectByPrimaryKey(1L); Users user2 = usersMapper.selectByPrimaryKey(2L); user1.setEmail("+100"); int insert = usersMapper.updateByPrimaryKey(user1); if(error){ throw new Exception("过程出错了"); } user2.setEmail("-100"); insert += usersMapper.updateByPrimaryKey(user2); return insert + insert == 2 ; } public void list(){ System.out.println("UserService.list--Service层"); } } ``` ```xml ``` ## 使用配置类替代spring的配置文件 参考 spring-14-spring-mybaits-javaconfig-all Java配置类 ```java package com.neuedu.sm; import com.alibaba.druid.pool.DruidDataSource; import org.apache.ibatis.logging.stdout.StdOutImpl; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.mapper.MapperScannerConfigurer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.TransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement; import javax.sql.DataSource; @Configuration //扫描包 注册组件 @ComponentScan //启动事务 @EnableTransactionManagement public class SpringApp { /** * 配置数据源 * @return */ @Bean public DataSource dataSource(){ DruidDataSource dataSource = new DruidDataSource(); dataSource.setUrl("jdbc:mysql://localhost:3306/assica?characterEncoding=utf-8"); dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); dataSource.setUsername("root"); dataSource.setPassword("root"); return dataSource; } /** * 配置SQLSessionFactory * @param dataSource * @return * @throws Exception */ //@Bean("sqlSessionFactory") @Bean public SqlSessionFactory sqlSessionFactory( DataSource dataSource) throws Exception { SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(dataSource); org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration(); configuration.setLogImpl(StdOutImpl.class); sqlSessionFactoryBean.setConfiguration(configuration); return sqlSessionFactoryBean.getObject(); } //注册Mapper @Bean public MapperScannerConfigurer mapperScannerConfigurer(){ MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer(); mapperScannerConfigurer.setBasePackage("com.neuedu.sm.mapper"); //mapperScannerConfigurer.setSqlSessionTemplateBeanName("sqlSessionFactory"); mapperScannerConfigurer.setSqlSessionFactoryBeanName("sqlSessionFactory"); return mapperScannerConfigurer; } //注册事务管理器到IOC容器 @Bean public TransactionManager txManager( DataSource dataSource){ TransactionManager txmanager = new DataSourceTransactionManager(dataSource); return txmanager; } } ``` 测试类 ```java package com.neuedu.sm.service; import com.neuedu.sm.SpringApp; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; import static org.junit.jupiter.api.Assertions.*; @SpringJUnitConfig(classes = SpringApp.class) class UserServiceTest { @Autowired private UserService userService; @Test void list() { userService.list(); } @Test void update() throws Exception { //boolean error = true; boolean error = false; boolean success = userService.update(error); System.out.println("success = " + success); } } ``` 可以发现事务生效了 ### 使用@MapperScan注解 在Spring配置类中我们声明了 MapperScannerConfigurer 用于注册 Mapper 到IOC容器中, - 可以通过在XXXMapper接口上添加@Mapper 实现单个Mapper的注册 - 可以在配置类上添加@MapperScan("com.neuedu.sm.mapper") 用于替代 MapperScannerConfigurer 实现批量注册 - 最终的配置类如下:(后面使用的SpirngBoot就是类似如下的写法) ```java package com.neuedu.sm; import com.alibaba.druid.pool.DruidDataSource; import org.apache.ibatis.logging.stdout.StdOutImpl; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.annotation.MapperScan; import org.mybatis.spring.mapper.MapperScannerConfigurer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.TransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement; import javax.sql.DataSource; @Configuration //扫描包 注册组件 @ComponentScan //启动事务 @EnableTransactionManagement //相当于 MapperScannerConfigurer 的功能, 自动注册Mapper接口代理对象到IOC容器 @MapperScan("com.neuedu.sm.mapper") public class SpringApp { /** * 配置数据源 * @return */ @Bean public DataSource dataSource(){ DruidDataSource dataSource = new DruidDataSource(); dataSource.setUrl("jdbc:mysql://localhost:3306/assica?characterEncoding=utf-8"); dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); dataSource.setUsername("root"); dataSource.setPassword("root"); return dataSource; } /** * 配置SQLSessionFactory * @param dataSource * @return * @throws Exception */ //@Bean("sqlSessionFactory") @Bean public SqlSessionFactory sqlSessionFactory( DataSource dataSource) throws Exception { SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(dataSource); org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration(); configuration.setLogImpl(StdOutImpl.class); sqlSessionFactoryBean.setConfiguration(configuration); return sqlSessionFactoryBean.getObject(); } /** * * 注册Mapper * @return */ //@Bean //public MapperScannerConfigurer mapperScannerConfigurer(){ // MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer(); // mapperScannerConfigurer.setBasePackage("com.neuedu.sm.mapper"); // // 错误的 : mapperScannerConfigurer.setSqlSessionTemplateBeanName("sqlSessionFactory"); // mapperScannerConfigurer.setSqlSessionFactoryBeanName("sqlSessionFactory"); // return mapperScannerConfigurer; //} //注册事务管理器到IOC容器 @Bean public TransactionManager txManager( DataSource dataSource){ TransactionManager txmanager = new DataSourceTransactionManager(dataSource); return txmanager; } } ```