Spring框架
spring的概述:
- spring是什么:
- Spring是一个轻量级控制反转(IoC)和面向切面(AOP)的容器框架
- spring两大核心:
- IOC(Inverse Of Control):控制反转
- AOP(Aspect Oriented Programming):面向切面编程
- spring的发展历程和优势:
- 发展历程
- 优势
- 方便解耦,简化开发
- AOP编程的支持
- 声明式事务的支撑
- 方便程序的测试
- 方便集成各种优秀框架
- 降低JavaEE API的使用难度
- spring体系结构:
- 扩展:spring学习线路
- 现代化的Java开发:基于spring的开发!
- SpringBoot
- 一个快速开发的脚手架
- 基于SpringBoot可以快速的开发单个微服务
- 约定大于配置
- SpringCloud
- SpringClound是基于SpringBoot实现的
- 学习SpringBoot的前提,需要完全掌握Spring及SpringMvc
IOC(控制反转)
- IOC理论推导:
- 没有使用spring时的业务实现
- UserDao接口
- 没有使用spring时的业务实现
public interface UserDao {
void getUser();
}
2. UserDaoImpl实现类
public class UserDaoImpl implements UserDao{
public void getUser() {
System.out.println("默认获取用户的数据");
}
}
3. UserService业务接口
public interface UserService {
void getUser();
}
4. UserServiceImpl业务实现类
package com.cc.service;
import com.cc.dao.UserDao;
import com.cc.dao.UserDaoImpl;
public class UserServiceImpl implements UserService{
private UserDao userDao = new UserDaoImpl();//固定了程序的执行逻辑,如果需求变更则需要修改这行代码
public void getUser() {
userDao.getUser();
}
}
5. 用户调用
public class MyTest {
public static void main(String[] args) {
//用户实际调用的是业务层,dao层他们不需要接触
UserService userService = new UserServiceImpl();
userService.getUser();
}
}
* 在这样的业务中,用户的需求可能会影响我们原来的代码,需要根据需求去修改源代码,如果程序代码量特别大,那修改一次的代价十分昂贵
- 解决方案
- 使用一个Set接口实现
public class UserServiceImpl implements UserService{
private UserDao userDao;
//利用set实现值的动态注入
public UserServiceImpl setUserDao(UserDao userDao) {
this.userDao = userDao;
return this;
}
public void getUser() {
userDao.getUser();
}
}
- 两种方式的区别
- 之前,程序数主动创建对象的,控制权在程序员手上
- 使用了set注入后,程序不在具有主动性,而是变成了被动的接收对象
- 这种思想从本质上解决了问题,程序员不再去管理对象的创建了,大大降低了系统的耦合性,可以更加专注在业务的实现上。这就是IOC的原型。
- IOC本质
-
IOC:控制反转,是一种设计思想,DI:依赖注入是实现IOC的一种方式,也有人认为DI只是IOC的另一种说法,在没有IOC的程序中,我们使用面向对象编程,对象的创建与对象之间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,而控制反转后是将对象的创建转移给第三方。所以控制反转就是:获得依赖对象的方式反转了
-
控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IOC容器,其实现方式就是依赖注入(DI)
HelloSpring
- 第一个spring程序
- 步骤
- 创建一个Hello类
public class Hello {
private String str;
public String getStr() {
return str;
}
public Hello setStr(String str) {
this.str = str;
return this;
}
@Override
public String toString() {
return "Hello{" +
"str='" + str + '\'' +
'}';
}
}
<?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">
<!--
使用spring来创建对象,在spring中这些都称为Bean
Hello hello = new Hello();
id = 变量名
class = new 的对象
property 相当于给对象中的属性设置一个值
name = 属性名
value = 值
-->
<bean id="hello" class="com.cc.pojo.Hello">
<property name="str" value="Spring"></property>
</bean>
</beans>
public class MyTest {
public static void main(String[] args) {
//获取spring的上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
//现在的对象都由Spring管理了,我们要使用的话,直接从里面取出来就可以了
Hello hello = (Hello) context.getBean("hello");
System.out.println(hello.toString());
}
}
- 思考问题
- Hello对象是谁创建的?
- Hello对象是Spring创建的
- Hello对象的属性是怎么设置的?
- 是由Spring容器设置的
- Hello对象是谁创建的?
- 这个过程就叫做控制反转
- 控制:谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建的,使用spring之后,对象是由spring来创建的
- 反转:程序本身不创建对象,而变成被动的接收对象
- 依赖注入:就是利用set方法来注入的
- IOC是一种编程思想,由主动的创建变成被动的接收
- 当使用的IOC思想之后,我们就再也不用在程进行改动了,要实现不同的操作,只需要在xml配置文件中进行修改,所谓的IOC,就是:对象由Spring来创建、管理、装配!
-
IOC创建对象的方式
- 使用无参构造创建对象(默认方式)
- 使用有参构造创建对象
- 参数索引赋值:
<!--第一种:索引赋值--> <bean id="user" class="com.cc.pojo.User"> <constructor-arg index="0" value="张三"></constructor-arg> </bean>- 参数类型赋值
<!--第二种:通过参数类型进行创建,不建议使用;因为当有多个参数类型一样时就会出现问题--> <bean id="user" class="com.cc.pojo.User"> <constructor-arg type="java.lang.String" value="张三"></constructor-arg> </bean>- 通过参数名赋值
<!--第三种:直接通过参数名进行赋值--> <bean id="user" class="com.cc.pojo.User"> <constructor-arg name="name" value="张三"/> </bean>
- 总结:在配置文件加载的时候,容器中管理的对象就已经初始化了,需要使用哪个直接get就行了
-
Spring配置
- 别名:
<!--别名:如果添加了别名,我们也可以通过别名来获取到这个对象--> <alias name="user" alias="user2"></alias>- Bean的配置
<!-- id:bean的唯一标识符,也就是相当于我们学的对象名 class:bean对象所对应的全限定类名:包名+类名 name:也是别名,而且name 可以同时起多个别名,可以通过很多分隔符分隔 --> <bean id="user" class="com.cc.pojo.User" name="user3 user4,user5"> <constructor-arg name="name" value="李四"></constructor-arg> </bean>- import
- import一般用于团队开发使用,他可以将多个配置文件导入合并为一个
- 假设项目中有多个beans.xml,就可以用import合并为一个总的applicationContext.xml
- 便于团队开发协作
- import一般用于团队开发使用,他可以将多个配置文件导入合并为一个
DI(依赖注入)
-
构造器注入
-
set方式注入
- 依赖注入:本质是Set注入
- 依赖:bean对象的创建依赖于容器
- 注入:bean对象中的所有属性,由容器来注入
- 环境搭建
- 复杂类型
public class Address { private String address; public String getAddress() { return address; } public Address setAddress(String address) { this.address = address; return this; } }- 真实测试对象
public class Student { private String name; private Address address; private String[] books; private List<String> hobbys; private Map<String,String> card; private Set<String> games; private String wife; private Properties info; }- 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="student" class="com.cc.pojo.Student"> <!--第一种:普通值注入,value--> <property name="name" value="张三"/> </bean> </beans>4.测试类
public class MyTest { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); Student student = (Student) context.getBean("student"); System.out.println(student.getName()); System.out.println(student.getAddress()); } }- 完善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="address" class="com.cc.pojo.Address"> <property name="address" value="北京"/> </bean> <bean id="student" class="com.cc.pojo.Student"> <!--第一种:普通值注入,value--> <property name="name" value="张三"/> <!--第二种:Bean注入,ref--> <property name="address" ref="address"/> <!--数组Array--> <property name="books"> <array> <value>红楼梦</value> <value>西游记</value> <value>水浒传</value> <value>三国志</value> </array> </property> <!--List--> <property name="hobbys"> <list> <value>敲代码</value> <value>听歌</value> <value>看电影</value> </list> </property> <!--Map--> <property name="card"> <map> <entry key="身份证" value="1000222000112010"/> <entry key="银行卡" value="1245455645464455"/> </map> </property> <!--Set--> <property name="games"> <set> <value>LOL</value> <value>COC</value> <value>OW</value> </set> </property> <!--null--> <property name="wife"> <null/> </property> <!--properties--> <property name="info"> <props> <prop key="driver">Driver</prop> <prop key="url">www.spring.io</prop> <prop key="username">张三</prop> <prop key="password">123456</prop> </props> </property> </bean> </beans> - 依赖注入:本质是Set注入
-
拓展方式注入
- p 命名空间
- c 命名空间 *注意:p命名和c命名不能直接使用,需要导入xml约束(官网)
xmlns:p="http://www.springframework.org/schema/:p xmlns:c="http://www.springframework.org/schema/:c -
bean的作用域
xml <bean id="student" class="com.cc.pojo.Student" scope="singleton">
2. 原型模式:每次从容器中get的时候都会产生一个新对象
xml <bean id="student" class="com.cc.pojo.Student" scope="prototype">
3. 其余的request,session,application这些只能在web开发中使用到
Bean的自动装配
-
自动装配是spring满足bean依赖的一种方式
-
Spring会在上下文中自动寻找,并自动给bean装配属性
-
在Spring中有三种自动装配的方式
-
在xml中显式的配置
-
在java中显式的配置
-
隐式的自动装配bean
-
-
xml中配置
- ByName自动装配
2.Bytype自动装配<!--autowire="byName":根据类中的属性名自动装配, 会自动在容器上下文中查找和自己对象set后的值对应的bean的id,找到了就自动装配--> <bean id="person" class="com.cc.pojo.Person" autowire="byName"> <property name="name" value="张三"></property> </bean><!-- autowire="byType":会自动在容器上下文中查找和自己对象属性类型相同的bean,找到了就自动装配 --> <bean id="person" class="com.cc.pojo.Person" autowire="byType"> <property name="name" value="张三"></property> </bean>- 注意:
- byname的时候,需要保证所有的bean的id值唯一,并且这个bean需要和自动注入的属性的set方法后的值一致
- bytype的时候,需要保证所有的bean的class值唯一,并且这个bean需要和自动注入的属性的类型一致
- 注意:
- ByName自动装配
-
使用注解实现自动装配
- jdk1.5支持的注解,spring2.5支持注解
- 要使用注解须知:
- 导入约束 context
- 配置注解的支持 context:annotation-config/
<?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>3.导入注解
- @Autowired:直接在属性上使用即可,也可以在set方法上使用, 在属性上使用此注解后实体类中可以不用编写set方法,前提是这个属性在IOC(Spring)容器中存在,且符合名字byname
public class Person { private String name; @Autowired private Cat cat; @Autowired private Dog dog; }@Nullable 字段标识了这个注解,说明这个字段可以为null- @Autowired源码
public @interface Autowired { boolean required() default true; }//如果显式的配置了Autowired注解的属性required值为false,则表示这个值可以为null,默认为true @Autowired(required = false)- 如果@Autowired自动装配的环境比较复杂,自动装配无法通过一个注解@Autowired完成的时候,我们可以使用@Qualifier(value="xxx")来配合@Autowired配置
public class Person { private String name; //如果显式的配置了Autowired注解的属性required值为false,则表示这个值可以为null,默认为true @Autowired(required = true) @Qualifier("cat111") private Cat cat; @Autowired @Qualifier("dog111") private Dog dog; }- @Resource
public class Person { private String name; @Resource(name = "cat111") private Cat cat; @Resource private Dog dog; }- 小结:
- @Resource 和 @Autowired 的区别:
- 都是用来配置自动装配的,都可以放在属性字段上
- @Autowired 通过bytype的方式实现,环境复杂的话和@Qualifier配套使用
- @Resource 默认通过byname的方式实现,如果找不到名字,则通过bytype方式实现,环境复杂可以直接定义属性
name的值为bean的id,相当于是@Autowired和@Qualifier的结合体(不是spring的注解)
Spring注解开发
- 注意:在Spring4之后,要使用注解开发,必须要保证aop的包导入了, 使用注解需要导入context约束,添加注解的支持
- bean
//等价于<bean id="user" class="com.cc.pojo.User"/>
//@Component 组件,放在类上,说明这个类被Spring管理了,相当于bean
@Component
public class User {
public String name = "张三";
}
- 属性如何注入
//等价于<bean id="user" class="com.cc.pojo.User"/>
//@Component 组件,放在类上,说明这个类被Spring管理了,相当于bean
@Component
public class User {
//相当于<property name="name" value="张三"/>
@Value("张三")
public String name;
}
@Value("张三")也可放在name的set方法前
- 衍生的注解
- @Component有几个衍生注解,我们在web开发中,会按照mvc三层架构分层
- dao (@Repository)
- service (@Service)
- controller (@Controller)
- 这四个注解是等价的,都是代表将某个类注册到Spring中,装配Bean
- 自动装配配置
- @Autowired
- @Qualifier
- @ Nullable
- @ Resource
- 作用域
- @Scope
- singleton 单例
- prototype 原型
- 小结
- xml 和 注解
- xml :更加万能,适用于任何场合,维护简单方便
- 注解:不是自己的类使用不了,维护相对复杂
- xml 和 注解的最佳实践
- xml用来管理bean
- 注解只负责完成属性的注入
- 在使用的过程中只需要注意一个问题:要让注解生效,就必须开启注解的支持
<!--指定要扫描的包,这个包下的注解就会生效--> <context:component-scan base-package="com.cc"/> <!--配置支持注解--> <context:annotation-config/>
javaConfig方式配置Spring
- 使用JavaConfig的方式配置Spring,就完全不需要再使用xml配置了,全权交给Java来做
- javaConfig:
- Spring的一个子项目,在Spring4之后他成为了一个核心功能
- 实体类
//@Component的意思就是说明这个类被Spring接管了,注册到了容器中 @Component public class User { private String name; public String getName() { return name; } //属性注入值 @Value("张三") public User setName(String name) { this.name = name; return this; } @Override public String toString() { return "User{" + "name='" + name + '\'' + '}'; } }- 配置类
*测试类//代表这是一个配置类,相当于之前使用的beans.xml(applicationConfig.xml) @Configuration//这个也会被Spring容器托管,注册到容器中,因为他本来就是一个@Component @ComponentScan("com.cc")//显式的配置扫描包 @Import(CcConfig2.class)//合并两个配置类 public class CcConfig { //注册一个Bean,相当于之前的bean标签 //方法名相当于bean标签的id属性 //返回值相当于之前bean标签的class属性 @Bean public User user(){ return new User();//返回的是要注入的对象 } }public class MyTest { public static void main(String[] args) { //如果完全使用配置类来做,我们就只能通过AnnotationConfig上下文来获取容器,通过配置类的class对象加载 ApplicationContext context = new AnnotationConfigApplicationContext(CcConfig.class); User user = (User) context.getBean("user"); System.out.println(user.getName()); } }
代理模式
-
为什么要学习代理模式?
- 因为代理模式是SpringAop的底层
-
代理模式的分类:
- 静态代理
- 动态代理
-
静态代理:
- 角色分析:
- 抽象角色:一般会使用接口或者抽象类来解决
- 真实角色:被代理的角色
- 代理角色:代理真实角色,代理真实角色后,我们一般会做一些附属操作
- 客户:访问代理对象的人
- 代理模式的好处:
- 可以使真实角色的操作更加纯粹,不用去关注一些公共的业务
- 公共业务就交给代理角色,实现了业务的分工
- 公共业务发生扩展的时候方便集中管理
- 缺点:
- 一个真实角色就会产生一个代理角色,代码量会翻倍,开发效率会变低
- 角色分析:
-
动态代理:
- 动态代理和静态代理角色一样
- 动态代理类是动态生成的,不是我们直接写好的
- 动态代理分为两大类:基于接口的动态代理,基于类的动态代理
- 基于接口--->JDK动态代理(常用)
- 基于类 --->cglib
- java字节码实现--->javasist
- 需要了解两个类:Proxy:代理 , InvocationHandler:调用处理程序
- 动态代理的好处:
- 可以使真实角色的操作更加纯粹,不用去关注一些公共的业务
- 公共业务就交给代理角色,实现了业务的分工
- 公共业务发生扩展的时候方便集中管理
- 一个动态代理类代理的是一个接口,一般就是对应的一类业务
- 一个动态代理类可以代理多个类,只要实现了他同一个接口即可
AOP(面向切面编程)
- AOP:面向切面编程,通过预编译的方式和运行期动态代理实现程序功能的统一维护的一种技术,AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个核心内容,是函数式编程的一种衍生泛型,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
-
AOP在Spring中的作用
- **提供声明式事务:允许用户自定义切面
-
Spring中实现AOP
- 使用AOP前需要导入一个依赖包
<dependencies> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.13</version> </dependency> </dependencies>-
实现方式一:Spring的API接口
- 前置日志
public class AfterLog implements AfterReturningAdvice { //returnValue:返回值 public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println("执行了"+method.getName()+"返回结果为"+returnValue); } }- 后置日志
public class Log implements MethodBeforeAdvice { //method:要执行的目标对象的方法 //args:参数 //target:目标对象 public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了"); } }- xml配置
<!--方式一:使用Spring API接口--> <!--配置Aop,需要导入aop的约束--> <aop:config> <!--切入点,expression:表达式 execution(要执行的位置)--> <aop:pointcut id="pointcut" expression="execution(* com.cc.service.UserServiceImpl.*(..))"/> <!--执行环绕增加--> <aop:advisor advice-ref="log" pointcut-ref="pointcut"/> <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/> </aop:config> -
实现方式二:自定义类实现AOP(主要是切面定义)
- 自定义的类
public class DiyPointcut { public void before(){ System.out.println("=====方法执行前====="); } public void after(){ System.out.println("=====方法执行后====="); } }- xml配置
<!--方式二:自定义类--> <bean id="diy" class="com.cc.diy.DiyPointcut"></bean> <aop:config> <!--自定义切面,ref指定要引用的类--> <aop:aspect ref="diy"> <!--切入点--> <aop:pointcut id="point" expression="execution(* com.cc.service.UserServiceImpl.*(..))"/> <aop:before method="before" pointcut-ref="point"/> <aop:after method="after" pointcut-ref="point"/> </aop:aspect> </aop:config> -
实现方式三:注解实现
- xml配置
<!--方式三:注解--> <bean id="annotationPointcut" class="com.cc.diy.AnnotationPointcut"/> <!--开启注解支持--> <aop:aspectj-autoproxy/>- 切面类
/** *使用注解方式实现AOP */ @Aspect//标注这个类是一个切面 public class AnnotationPointcut { @Before("execution(* com.cc.service.UserServiceImpl.*(..)))") public void before(){ System.out.println("=====方法执行前====="); } @After("execution(* com.cc.service.UserServiceImpl.*(..)))") public void after(){ System.out.println("=====方法执行后====="); } }
整合Mybatis
- 步骤
-
导入相关jar包
- junit
- mybatis
- mysql
- spring相关
- aop织入
- mybatis-spring
- mybatis-spring会将mybatis代码无缝的整合到spring中,他将允许mybatis参与到spring的事务管理之中,创建映射器mapper和sqlSession并注入到bean中。
-
编写配置文件
- 配置数据源代替掉mybatis的配置
<!--DataSource:使用Spring的数据源替换mybatis的配置 --> <bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/> <property name="username" value="root"/> <property name="password" value="root"/> </bean>- 通过数据源注入SqlSessionFactory对象
<!--sqlSessionFactory对象--> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="datasource"/> <!--绑定mybatis配置文件--> <property name="configLocation" value="classpath:mybatis-config.xml"/> <property name="mapperLocations" value="classpath:com/cc/mapper/*.xml"/> </bean>- 通过SqlSessionFactory对象注入SqlSessionTemplate对象
<!--sqlSessionTemplate对象--> <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"> <!--只能使用构造器注入sqlSessionFactory,因为他没有set方法--> <constructor-arg index="0" ref="sqlSessionFactory"/> </bean>- 需要加一个Mapper的实现类
public class UserMapperImpl implements UserMapper{ //在mybatis中所有的操作都使用sqlSession来执行,现在都使用sqlSessionTemplate private SqlSessionTemplate sqlSession; public UserMapperImpl setSqlSession(SqlSessionTemplate sqlSession) { this.sqlSession = sqlSession; return this; } public List<User> findUser() { UserMapper mapper = sqlSession.getMapper(UserMapper.class); return mapper.findUser(); } }- 将实现类注入到Spring中即可
<!--实现类--> <bean id="userMapper" class="com.cc.mapper.UserMapperImpl"> <property name="sqlSession" ref="sqlSession"/> </bean> -
测试
@Test public void test() throws IOException { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); UserMapper userMapper = context.getBean("userMapper", UserMapper.class); for (User user : userMapper.findUser()) { System.out.println(user); } }
声明式事务
- 事务:
- A:原子性:要么都成功,要么都失败
- C:一致性:事务开始前和结束后数据库的一致性没有被破坏
- I:隔离性:防止事务并发执行时破坏数据的一致
- 四个隔离级别:
- 读未提交
- 读提交
- 可重复读
- 串行化
- 四个隔离级别:
- D:持久性:事务一旦处理结束,对数据的修改就是永久的
- Spring中的事务管理
- 声明式事务:AOP
<!--结合AOP实现事务的织入-->
<!--配置事务通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--给哪些方法配置事务-->
<tx:attributes>
<!--配置事务的传播特性 propagation-->
<!--REQUIRED:支持当前事务,如果没有事务,就新建一个事务,这是最常见的选择,也是spring的默认选择-->
<tx:method name="insertUser" propagation="REQUIRED"/>
<tx:method name="deleteUser"/>
<tx:method name="findUser" read-only="true"/><!--只读-->
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<!--配置事务切入-->
<aop:config>
<!--配置切入点-->
<aop:pointcut id="txPointcut" expression="execution(* com.cc.mapper.*.*(..))"/>
<!--织入事务-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>
- 编程式事务:需要在代码中进行事务的管理
- try catch当出现异常时回滚事务
- 为什么需要事务?
- 事务在项目的开发中十分重要,涉及到数据的一致性和完整性问题
- 如果不配置事务,可能存在数据提交不一致的情况下,导致数据不一致
- 如果不在spring中配置配置声明式事务,就需要在代码中手动配置编程式事务