1、Spring
spring是一个轻量级、非入侵式的控制反转(IOC)和面向切面(AOP)的容器框架.
-
SpringBoot
- 一个快速开发的脚手架
- 基于SpringBoot可以快速的开发单个微服务
- 约定大于配置
-
SpringCloud
控制反转 IoC
IoC是一种设计思想,而DI(依赖注入)只是实现 IOC的其中一种方法。
IoC是一种通过描述(XML或者注解)并通过第三方去生产或获取特定对象的方式,在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection)
Hello,Spring!
- 控制:谁来控制对象的创建!传统的应用程序的对象是由程序本身控制创建的,使用Spring后,对象是由Spring来创建的
- 反转:程序本身不创建对象,而变成被冻的接收对象
- 依赖注入:就是利用set方法进行注入
- IOC是一种编程思想,由主动的编程变为被动的接收。对象由Spring来创建、管理、装配!
Pojo:
package com.shen.pojo;
import lombok.Data;
@Data
public class Hello {
private String str;
}
Beans.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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="hello" class="com.shen.pojo.Hello">
<property name="str" value="Hello,spring!"></property>
</bean>
</beans>
Test:
import com.shen.pojo.Hello;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Hello hello = (Hello)context.getBean("hello");
System.out.println(hello.toString());
}
}
IoC例子2:
ServiceImpl:
package com.shen.service;
import com.shen.dao.UserDao;
import lombok.Data;
@Data
public class UserServiceImpl implements UserService{
private UserDao userDao;
@Override
public void printDaoInfo() {
userDao.printInfo();
}
}
beans.xml:
<bean id="mysqlDao" class="com.shen.dao.UserDaoMysqlImpl"></bean>
<bean id="oracleDao" class="com.shen.dao.UserDaoOracleImpl"></bean>
<bean id="service" class="com.shen.service.UserServiceImpl">
<property name="userDao" ref="oracleDao"></property>
</bean>
test:
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserServiceImpl service = (UserServiceImpl)context.getBean("service");
service.printDaoInfo();
}
}
使用配置的好处在于,当需要修改set注入的内容的时候,不需要重启服务器,因为没有改动源代码,改的是xml配置!!而且解耦了,如果要增加新的实现,只需要写新的实现,并且替换配置。
IoC创建对象方式
-
默认走的是无参构造 -
可以使用有参构造
<bean id="service" class="com.shen.service.UserServiceImpl">
<constructor-arg type="java.lang.String" value="有参构造的bean"></constructor-arg>
</bean>
缺点在于 如果有两个参数都是同类型,就不行了,所以不建议使用此法。(如果非要用的话,可以按顺序赋值)
<constructor-arg index="0" value="有参构造的bean"></constructor-arg>
<constructor-arg name="str" value="直接通过参数名赋值"></constructor-arg>
2、Spring配置
-
别名:alias,和bean是同一级别的标签。name,在bean标签内,也是别名,而且name可以取多个别名(用逗号、空格、分号进行分割)。 -
import,和bean是同一级别的标签。一般用于团队开发使用,可以将多个配置文件导入合并为一个。 <import resource="beans2.xml"/>
注:使用import导入的时候,如果遇到同名的东西,后面的会覆盖前面的(因为按照从上到下的顺序进行导入合并) -
scope 在bean标签内,可以设置是否为单例模式(默认单例)
3、依赖注入
3.1 构造器注入
上面写过了
也可以用c命名空间
3.2 set方式注入(重点)
也可以用p命名空间
- 依赖:bean对象的创建依赖于spring这个容器
- 注入:bean对象中的所有属性,由容器注入
<bean id="student" class="com.shen.pojo.Student" name="stu">
<property name="name" value="孟特"></property>
<property name="id" value="4"></property>
<property name="address" ref="add"></property>
<property name="books">
<array>
<value>《西游记》</value>
<value>《三国演义》</value>
</array>
</property>
<property name="hobbies">
<list>
<value>弹吉他</value>
<value>写代码</value>
</list>
</property>
<property name="scopes">
<map>
<entry key="语文" value="90"></entry>
<entry key="数学" value="100"></entry>
</map>
</property>
<property name="info">
<props>
<prop key="学号">201920085211015</prop>
<prop key="sex">男</prop>
</props>
</property>
</bean>
<bean id="address" class="com.shen.pojo.Address" name="add">
<constructor-arg name="addressName" value="梆子井4503"></constructor-arg>
<property name="concludes">
<set>
<value>北京市</value>
<value>朝阳区</value>
<value></value>
<value>null</value>
<null></null>
<value>三里屯</value>
</set>
</property>
</bean>
3.3 拓展方式注入
p命名空间、c命名空间
需要引入第三方xml约束:
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
3.4 Bean scope (作用域)
-
Singleton 单例
-
prototype 原型
- 每次从容器中get的时候都会产生一个新的对象,其实这就是原型模式
下面的这些只有在web中才能用 -
request -
session -
websocket -
Application
4、Bean的自动装配(autowired)
spring不仅可以手动依赖,还可以实现自动依赖!
spring会在上下文中自动寻找,并且给bean装配属性
有三种装配的方式
- 在xml中显示配置
- 在java中显示配置
- 隐式的自动配置
自动装配的实现方法:
-
byName:会自动在容器上下文中查找,自己对象set方法后面的值对应的beanid ? 比如,当前bean对象有一个方法叫setCat,就会自动寻找一个id叫做的cat的bean对象,把它装配进去 -
byType:会自动在容器上下文中查找,与自己对象属性类型相同的bean
5、使用注解进行自动装配
要使用注解须知:
- 导入约束
- 配置注解的支持
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org.schema/context/spring-context.xsd">
<context:annotation-config/>
</beans>
例:
<bean id="dog" class="com.shen.pojo.Dog" >
<property name="name" value="wang~"></property>
</bean>
<bean id="cat" class="com.shen.pojo.Cat" >
<property name="name" value="miao~"></property>
</bean>
<bean id="people" class="com.shen.pojo.People" />
public class People {
@Autowired
private Dog dog;
@Autowired
private Cat cat;
private String name;
@Override
public String toString() {
return "People{" +
"dog=" + dog +
", cat=" + cat +
", name='" + name + '\'' +
'}';
}
}
我们发现:没有set方法,仅使用注解,也可以成功注入!!这是为什么呢?
因为使用注解的时候,不是通过set注入,而是通过反射直接获取了变量名以后进行注入
小结
@Autowired既可以直接在属性上使用,也可以在set方式上使用。
@Autowired(required = false) 可以为null
科普
@Nullable 字段标记了这个注解,则这个字段可以为null而不报错!
如果自动装配环境比较复杂,无法通过一个注解完成的时候(比如一个类有多个bean实例),我们可以在@Autowired后面接一个@Qualifier(value = “dog111”) 来指定唯一的bean对象。
也可以不使用两个注解,而只使用@Resource(name = “xxx”),这个是java自带的注解,先通过名字匹配byName,不唯一的话再通过类型匹配byType。都找不到就报错了。
6、使用注解开发
在Spring4之后,要使用注解开发,必须保证aop的包已经导入
-
bean package com.shen.pojo;
import lombok.Data;
import org.springframework.stereotype.Component;
@Data
@Component
public class User {
private String name;
}
-
属性如何注入 @Component
public class User {
@Value("沈航冉")
private String name;
}
-
衍生的注解 @Component有几个衍生注解,在我们的web开发中,会按照mvc三层架构分层
-
自动装配 -
作用域 @Scope(“prototype”) 采用原型模式(默认为singleton) -
小结
- xml更加万能,适用于任何场合,维护方便简单
- 注解 不是自己的类不能用,维护相对复杂
最佳实践:xml用来管理bean,注解只负责使用的注入。 使用注解记得需要开启注解的支持。
7、使用Java的方式配置Spring
现在我们不使用Spring的xml配置了,全权交给java来做。
JavaConfig是Spring的一个子项目,在Spring4之后,它成为了一个核心功能。
@Data
@Component
public class User {
@Value("沈阳的沈")
private String name;
}
@Configuration
public class ShenConfig {
@Bean
public User getUser(){
return new User();
}
}
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(ShenConfig.class);
User getUser = context.getBean("getUser", User.class);
System.out.println(getUser);
}
}
8、代理模式
和装饰器模式的语法完全一样,只是语意不一样!
SpringAOP的底层就是代理模式.
8.1、静态代理
代码步骤:
- 公共接口
- 真实角色
- 代理角色
- 客户端访问代理角色
和装饰器模式完全相同。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aW48FLto-1625239701232)(/Users/shenhangran/Desktop/学习笔记/我的/Spring笔记.assets/image-20210629162314168.png)]
原来是纵向开发,在不改动原有DAO层代码的时候,想给DAO层加一个日志功能,就可以采取和Service层并列的新建一个代理模式的类来完成,这就是横向开发。
8.2、动态代理
动态代理的代理类是动态生成的,不是我们直接写好的。
动态代理分为两大类:
需要了解两个类:Proxy(代理),InvocationHandler(调用处理程序)
例子:
用来生成代理类(对象)的类,详情看注释:
package shen;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyInvocationHandler implements InvocationHandler {
private Rent rent;
public void setRent(Rent rent) {
this.rent = rent;
}
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object invoke = method.invoke(rent, args);
seeHouse();
fare();
return invoke;
}
public void seeHouse(){
System.out.println("中介带用户看房子");
}
public void fare(){
System.out.println("中介收你中介费");
}
}
代码步骤:
- 设置要代理的类是谁,给它写一个set方法
- 利用要代理的类,生成得到代理类
- 处理代理实例,返回结果(添砖加瓦)
客户这样使用:
public class Client {
public static void main(String[] args) {
Host host = new Host();
ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler();
proxyInvocationHandler.setRent(host);
Rent proxy = (Rent)proxyInvocationHandler.getProxy();
proxy.rrrrent();
}
}
于是,我们发现了,上面写的ProxyInvocationHandler类==可以当做一个工具类了,因为它可以代理任何一个类呀!==比如要增加日志功能,只需要自顶一个log方法,并放到invoke方法里使用就可以了!
public void log(String msg){
System.out.println("[Debug]执行了"+msg+"方法");
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object invoke = method.invoke(rent, args);
log(method.getName());
return invoke;
}
通过method.getName()反射可以获取方法名呀!
动态代理的好处:
- 可以使真实角色的操作更加纯粹!不用关注一些公共的业务(比如log)
- 公共业务可以交给代理角色,实现了业务的分工!
- 公共业务发生扩展的时候,方便集中管理!
- 一个动态代理类代理的是一个接口,一般就是对应的一类业务
- 一个动态代理类可以代理多个类,只要实现了同一个接口就行,复用成本低。
使用spring实现aop,需要导入一个依赖包
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
8.3 使用spring的aop
方式1:使用原生API接口
写一个接口和一个要被代理的类,最终目的是给它增加日志功能。
public class UserServiceImpl implements UserService{
@Override
public void add() {
System.out.println("使用了add方法");
}
@Override
public void delete() {
System.out.println("delete");
}
@Override
public void update() {
System.out.println("使用了update方法");
}
@Override
public void query() {
System.out.println("使用了query方法");
}
}
使用原生API接口,定义在某方法执行前,我们要做的事情
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class Log implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println(o.getClass().getName()+"的"+method.getName()+"被执行啦!");
}
}
使用xml配置aop,定义切入点(在哪里执行log方法),并且定义执行环绕增加(log方法是谁)
<bean id="userService" class="com.shen.UserServiceImpl"/>
<bean id="log" class="com.shen.log.Log"/>
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* com.shen.UserServiceImpl.*(..))"/>
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
</aop:config>
测试
注意,我们动态代理代理的是接口,而不是具体实现类,所以如果写成具体实现类会报错!
import com.shen.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
UserService userservice = context.getBean("userService", UserService.class);
userservice.delete();
}
}
方式2:使用自定义类实现AOP
package com.shen.diy;
public class DiyPointCut {
public void before(){
System.out.println("===方法执行前===");
}
public void after(){
System.out.println("===方法执行后===");
}
}
将自定义类当做切面,配置到aop中
<bean id="diy" class="com.shen.diy.DiyPointCut"/>
<aop:config>
<aop:aspect ref="diy">
<aop:pointcut id="pointcut" expression="execution(* com.shen.UserServiceImpl.*(..))"/>
<aop:before method="before" pointcut-ref="pointcut"/>
<aop:after method="after" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
很明显,第一种方法功能要强大的多。因为第一种用到了反射。
方式3:使用注解实现
自定义切面类,在上面使用注解
补充:环绕增强,详见代码。
package com.shen.diy;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class AnnotationPointCut {
@Before("execution(* com.shen.UserServiceImpl.*(..))")
public void before(){
System.out.println("注解实现的方式执行之前");
}
}
@Around("execution(* com.shen.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("===环绕前===");
Signature signature = proceedingJoinPoint.getSignature();
System.out.println("签名:"+signature);
Object proceed = proceedingJoinPoint.proceed();
System.out.println("===环绕后===");
}
然后将这个类配置到xml中,成为bean对象。最后,开启注解支持
<bean id="annotationPointCut" class="com.shen.diy.AnnotationPointCut"/>
<aop:aspectj-autoproxy/>
结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IUg3Pl4g-1625239701234)(/Users/shenhangran/Desktop/学习笔记/我的/Spring笔记.assets/image-20210630231832789.png)]
8.4 名词总结
- 横切关注点:与业务逻辑无关的,我们要实现的功能的概述。比如“日志、安全、缓存等等”
- 切面(aspect):横切关注点被模块化的特殊对象。即,一个类
- 通知(advice):切面要完成的工作。即,一个方法
- 目标(target):被通知对象。即,被代理的类对象。
- 代理(proxy):向目标对象应用通知后创建的对象。即,使用动态代理模式创建出的代理类对象。
- 切入点(pointcut):切面通知执行的“地点”的定义。即,在哪里执行“通知”这个方法。
- 连接点(jointpoint):与切入点匹配的执行点。
9、整合Mybatis
步骤:
-
导入相关jar包
- junit
- mybatis
- mysql数据库
- spring相关的
- aop织入
- mybatis-spring
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.25</version>
</dependency>
<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.8</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.7</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.2</version>
</dependency>
</dependencies>
-
编写配置文件 -
测试
整合步骤:
将spring的配置和mybatis的配置都整合到spring中,首先需要创建一个spring-dao.xml
其实本质就是把mybatis里的datasource、sqlSessionFactory和sqlSession交给spring托管
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/smbms?useSSL=TRUE&useUnicode=TRUE&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="jiawensili1029"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="datasource"/>
<property name="configLocation" value="classpath:mybatis-config2.xml"/>
<property name="mapperLocations" value="classpath:com/shen/mapper/*.xml"/>
</bean>
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
<bean id="userMapper" class="com.shen.mapper.UserMapperImpl">
<property name="sqlSession" ref="sqlSession"/>
</bean>
</beans>
其中,userMapper是自定义的一个接口的实现类,它的作用就是,把sqlSession注入进来,然后使用它!而它本身又成为一个bean对象,方便我们spring来托管。
package com.shen.mapper;
import com.shen.pojo.User;
import org.mybatis.spring.SqlSessionTemplate;
import java.util.List;
public class UserMapperImpl implements UserMapper{
private SqlSessionTemplate sqlSession;
public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}
@Override
public List<User> getAllUser() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return mapper.getAllUser();
}
}
在配置文件中,我们注意下面这段:
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="datasource"/>
<property name="configLocation" value="classpath:mybatis-config2.xml"/>
<property name="mapperLocations" value="classpath:com/shen/mapper/*.xml"/>
</bean>
其中,mapperLocations包含了所有mapper.xml,比如:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"htto://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.shen.mapper.UserMapper">
<select id="getAllUser" resultType="user">
select * from user_test
</select>
</mapper>
我们的sql语句就是在这里写的!
而configLocation则是原来mybatis的配置文件。
由于原来mybatis的配置文件有很多内容已经集合到spring-dao.xml里了,所以现在的mybatis配置文件只剩下“别名”和“设置”这两部分内容!
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"htto://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<typeAliases>
<typeAlias type="com.shen.pojo.User" alias="user"/>
</typeAliases>
</configuration>
如果没有别名的话,写sql会比较麻烦,而设置里我们可以添加其他的设置,比如日志。
此外,由于除了userMapper这个bean对象以外,其他都是固定的,因此我们可以上面一部分内容单独拿出来固定在一个xml里,再新建一个总的application.xml,把上面那个xml通过resource的形式引入,这样就不会显得乱了!(解耦了)
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<import resource="spring-dao.xml"/>
<bean id="userMapper" class="com.shen.mapper.UserMapperImpl">
<property name="sqlSession" ref="sqlSession"/>
</bean>
</beans>
注:sqlSessionTemplate是线程安全的
10、声明式事务
@Override
public List<User> mixFixed() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.addUser(new User("shr", "shrshrshr", "沈航冉"));
mapper.deleteUser("shr");
List<User> allUser = mapper.getAllUser();
return allUser;
}
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
UserMapperImpl userMapper = context.getBean("userMapper", UserMapperImpl.class);
List<User> allUser = userMapper.mixFixed();
for(User user:allUser){
System.out.println(user);
}
}
经过测试,我们明明把好几个任务混合起来,当做了一个“新的任务”,它是一个新的原子性,应该当做一个事务来处理,但我们这么跑下来,虽然程序会报错(我们故意写错了delete的sql语句),但是还是成功插入了一组数据,说明这个方法并不是满足ACID原则的,说明它没有完成事务的功能!
为此,我们要使用声明式事务。
补充:
- 声明式事务:AOP
- 编程式事务:需要在代码中进行事务的管理(使用try、catch、rollback等)
声明式事务需要在spring的配置文件里做,本质就是spring帮我们写好了一个事务管理器。
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="datasource"/>
</bean>
切入点处的方法已经被tx实现了,所以我们导入tx依赖,就可以写成下面这样.
tx的作用就是实现了事务处理的一些方法,我们要把这些方法配置到使用的地方(切入点)
tx这一套东西相当于是一个切面(理解为一个bean对象,所以可以使用advice-ref来增加执行环绕)
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="add" propagation="REQUIRED"/>
<tx:method name="delete" propagation="REQUIRED"/>
<tx:method name="query" read-only="true"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="txpointcut" expression="execution(* com.shen.mapper.*.xml.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txpointcut"/>
</aop:config>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-prPOjvAk-1625239701235)(/Users/shenhangran/Desktop/学习笔记/我的/Spring笔记.assets/image-20210702205923362.png)]
经测试,发现执行mixFixed方法还是把东西插进去了,找到原因:
-
因为mixFixed方法并没有在mapper中注册,而只是在UserMapperImpl里写明了它是把好几个事情混在一起的方法,我们的配置事务作用域并没有包含UserMapperImpl,而只包含了所有的mapper底下的所有的xml里的东西。 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lQLRfdyP-1625239701236)(/Users/shenhangran/Desktop/学习笔记/我的/Spring笔记.assets/image-20210702213239187.png)] -
解决方案:
-
改为mapper底下所有文件,而不只是xml文件
<aop:config>
<aop:pointcut id="txpointcut" expression="execution(* com.shen.mapper.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txpointcut"/>
</aop:config>
-
使用的地方,统一为UserMapper.class,而不是UserMapperImpl.class [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Elvyeevm-1625239701237)(/Users/shenhangran/Desktop/学习笔记/我的/Spring笔记.assets/image-20210702214034558.png)] 不然会报错: Bean named ‘xxx’ is expected to be of type ‘xxx’ but was actually of type 'com.sun.proxy.$Proxy14 -
tx中配置的name必须与方法名相同 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UAZW157X-1625239701238)(/Users/shenhangran/Desktop/学习笔记/我的/Spring笔记.assets/image-20210702214300647.png)]
经测试,此处注释掉mixFixed会导致失败.
当然,也可使直接使用*来匹配所有.
|