spring
spring是一个轻量级控制反转和面向切面的容器框架。 https://repo.maven.apache.org/maven2/org/springframework/spring/ 查看最新版本的
简介
-
2002年,首次退出Spring框架的雏形-interface21框架;
-
2004年3月24日,发布了1.0正式版本;
-
Rod Johnson,spring Framework创始人,他是悉尼大学的博士,但他的专业是音乐学;
-
spring理念:使现有的技术更加容易使用,本身就是一个大杂烩,整合了现有的技术框架。
-
SSH:Spring + Struts2 + Hibernate
-
SSM:Spring +SpringMVC + Mybatis
官网:https://spring.io/projects/spring-framework#overview
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.9</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.9</version>
</dependency>
优点
- spring是一个开源的免费框架(容器)
- spring是一个轻量级的、非入侵式的框架
- 控制反转和面向切面
- 支持事务的处理,对框架整合的支持
组成
IOC推导
//最初由程序自己new对象,但随着需求越来越多,dao实例也越来越多,直接new的方式弊端显现,需要改动代码,代码量越大,成本越高
private UserDao userDao = new UserDaoImpl();
public void getUser() {
userDao.getUser();
}
//使用set接口实现,利用set进行动态实现值的注入
private UserDao userDao;
public void setUserDao(UserDao userDao){
this.userDao=userDao;
}
public void getUser() {
userDao.getUser();
}
- 之前,程序是主动创建对象的!控制权在程序员手中
- 使用了set注入后,程序不再具有主动性,而是变成被动的接受对象!
这种思想,从本质上解决问题,程序员不用再去管理对象的创建了。系统的耦合性大大降低,可以更加专注的在业务实现上!这就是IOC的原型!
IOC本质
控制反转IoC(Inversion of Control),是一种思想,DI(依赖注入)实现IoC的一种方式。没有Ioc的程序中,我们使用的是面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建交给第三方,个人认为所谓的控制反转就是:获得依赖对象的方式反转了。
控制反转是一种通过描述(XML或者注解)并通过第三方去生产或获取特定对象的方式。在spring中实现控制反转的是IoC容器,其实现方法就是依赖注入(Dependency Injection)。
springIoc案例
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userDao" class="com.kaka.dao.impl.UserDaoImpl"></bean>
<bean id="userMysqlDao" class="com.kaka.dao.impl.UserMysqlDaoImpl"></bean>
<bean id="userService" class="com.kaka.service.impl.UserServiceImpl">
<property name="userDao" ref="userMysqlDao"></property>
</bean>
</beans>
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserServiceImpl userService = (UserServiceImpl)context.getBean("userService");
userService.getUser();
}
}
如上,交给spring管理之后,甚至代码都不需要改动,将xml文件中的引用改动一下即可实现不同的操作。需要扩展新的dao,只需要新增dao类,注册到xml文件中,修改ref即可。
Ioc的创建对象方式
- 无参构造,默认的方式
<bean id="user" class="com.kaka.pojo.User">
<property name="name" value="kaka" />
</bean>
- 有参构造
<!-- 下标赋值 -->
<bean id="user" class="com.kaka.pojo.User">
<constructor-arg index="0" value="kaka" />
</bean>
<!-- 类型赋值 不推荐,多个参数相同类型的话不可以-->
<bean id="user" class="com.kaka.pojo.User">
<constructor-arg type="java.lang.String" value="kaka" />
</bean>
<!-- 参数名赋值 -->
<bean id="user" class="com.kaka.pojo.User">
<constructor-arg name="name" value="kaka" />
</bean>
spring 配置
<!-- ##别名 -->
<alias name="user" alias="kaka"/>
<!-- ##name也是别名,而且可以通过, 空格 分号取多个别名 -->
<bean id="user" class="com.kaka.pojo.User" name="kaka,jsj,hkjh">
<property name="name" value="kaka" />
</bean>
<!-- 导入其他配置文件 -->
<import resource="beans.xml"/>
依赖注入
构造器注入
参见创建对象方式
set方式注入【*】
@Data
public class Student {
private String name;
private User friend;
private String[] books;
private List<String> hobbys;
private Map<String,String> card;
private Set<String> games;
private Properties info;
}
<bean id="user" class="com.kaka.pojo.User">
<constructor-arg index="0" value="kaka" />
</bean>
<bean id="student" class="com.kaka.pojo.Student">
<!-- 普通方式注入 value -->
<property name="name" value="卡卡罗特"/>
<!-- Bean方式注入 ref -->
<property name="friend" ref="user" />
<!-- Array方式注入 value -->
<property name="books">
<array>
<value>西游记</value>
<value>东游记</value>
<value>三国演义</value>
</array>
</property>
<!-- List方式注入 value -->
<property name="hobbys">
<list>
<value>唱</value>
<value>跳</value>
<value>rap</value>
</list>
</property>
<!-- map方式注入 value -->
<property name="card">
<map>
<entry key="身份证" value="3665568556556" />
</map>
</property>
<!-- Set方式注入 value -->
<property name="games">
<set>
<value>LOL</value>
<value>CS</value>
</set>
</property>
<!-- Properties方式注入 value -->
<property name="info">
<props>
<prop key="学号" >41221214</prop>
<prop key="姓名" >root</prop>
</props>
</property>
<!--null注入 value -->
<property name="wife">
<null />
</property>
</bean>
拓展方式注入
<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:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- p标签注入 须引入p约束 -->
<bean id="user2" class="com.kaka.pojo.User" p:name="kaka" />
<!-- c标签注入 须引入c约束 -->
<bean id="user2" class="com.kaka.pojo.User" c:name="kaka" />
<!-- 上述两种就相当于构造器注入和set注入的简写模式 -->
</beans>
bean的作用域scope
Scope | Description |
---|---|
singleton | (Default) Scopes a single bean definition to a single object instance for each Spring IoC container. |
prototype | Scopes a single bean definition to any number of object instances. |
request | Scopes a single bean definition to the lifecycle of a single HTTP request. That is, each HTTP request has its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring ApplicationContext . |
session | Scopes a single bean definition to the lifecycle of an HTTP Session . Only valid in the context of a web-aware Spring ApplicationContext . |
application | Scopes a single bean definition to the lifecycle of a ServletContext . Only valid in the context of a web-aware Spring ApplicationContext . |
websocket | Scopes a single bean definition to the lifecycle of a WebSocket . Only valid in the context of a web-aware Spring ApplicationContext . |
- 单例模式-spring默认的机制;
<bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>
- 原型模式:每次从容器中get的时候,都会产生一个新的对象;
<bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/>
Bean的自动装配
- 自动装配是Spring满足bean依赖一种方式
- spring会在上下文中自动寻找,自动给bean装配属性
在spring中有两种方式装配:
- 在xml中显示配置
<bean id="student2" class="com.kaka.pojo.Student" autowire="byName"/>
##其中,有以下几个参数
<xsd:enumeration value="default"/>
<xsd:enumeration value="no"/>
<xsd:enumeration value="byName"/> #名称要对应
<xsd:enumeration value="byType"/> #类型全局唯一
<xsd:enumeration value="constructor"/>
byName:需要保证所有bean的id唯一,并且id和自动注入的属性的set方法的值一致;
byName:需要保证所有bean的class唯一,并且和自动注入的属性的类型值一致;
- 在java中显示配置(使用注解)
<?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">
<!-- 开启注解 导入aop的包-->
<context:annotation-config/>
<context:component-scan base-package="com.kaka" />
<bean id="user" class="com.kaka.pojo.User" />
<bean id="student" class="com.kaka.pojo.Student" />
</beans>
@Autowired
private User user;
@Autowired(required = false) //后面加参数,false表示可以为null,不是必输项
//可以配合 @Qualifier("user1")使用
//可以直接在属性上和set方法上使用,属性上使用之后set方法也可以不要
//@Autowired和@Resource区别
1、@Autowired注解是Spring提供的,而@Resource注解是J2EE本身提供的
2、@Autowird注解默认通过byType方式注入,而@Resource注解默认通过byName方式注入
3、@Autowired注解注入的对象需要在IOC容器中存在,否则需要加上属性required=false,表示忽略当前要注入的bean,如果有直接注入,没有跳过,不会报错
@Resource还有两个重要的属性:name和type,用来显式指定byName和byType方式注入
代理模式
为什么要代理模式?因为这就是SpringAOP的底层
静态代理
角色分析:
- 抽象角色:一般会使用接口或者抽象类
- 真实角色:被代理的角色
- 代理角色:代理真实角色,代理后会做一些附属操作
- 客户:访问代理对象
好处:
- 可以使真实角色的操作更加纯粹,不用关注一些公共的业务!
- 公共业务交给代理角色!实现业务上的分工!
- 公共业务发生扩展的时候,方便集中管理
缺点:
- 一个真实角色就会产生一个代理角色,代码量会翻倍,开发效率变低!!!!
动态代理
- 动态代理和静态代理角色是一样的
- 动态代理的代理类是动态生成的,不是由我们直接写好的
- 动态代理分为两类:
- 基于接口的-------JDK动态代理(以此为例子)
- 基于类的----------cglib
- Java字节码实现--------javasist ===》jboss服务器
//接口
public interface RentO {
void rent();
}
//被代理类
public class Rent implements RentO{
public void rent(){
System.out.println("我有房出租!!!");
}
}
//动态代理类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口
private Object obj;
public void setObject(Object obj) {
this.obj = obj;
}
//生成得到代理对象
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),obj.getClass().getInterfaces(),this);
}
//处理代理实例,并返回结果
public Object invoke(Object proxy, Method method, Object[] args)
//动态代理的本质就是通过反射机制实现
Object result = method.invoke(obj, args);
return result;
}
}
//调用
Rent rent = new Rent();
//代理处理类创建实例
ProxyInvocationHandler proxy = new ProxyInvocationHandler();
//设置被代理对象
proxy.setObject(rent);
//生成代理对象
RentO proxyRent = (RentO)proxy.getProxy();
//代理对象执行业务
proxyRent.rent();
AOP
AOP在srping中的作用
提供声明式事务;允许用户自定义切面
- 横切关注点:跨越应用程序多个模块的方法或功能。与业务逻辑无关的,但是需要我们关注的部分就是横切关注点,譬如日志、安全、缓存、事务等;
- 切面(ASPECT):横切关注点被模块化的特殊对象,也就是说它是一个类;
- 通知(ADVICE):切面必须要完成的工作,类中的一个方法;
- 目标:被通知对象;
- 代理(Proxy):项目对象应用通知之后创建的对象;
- 切入点(pointcut):切面通知 执行的地点定义;
- 连接点(joinpoint):与切入点匹配的执行点;
使用spring实现AOP
【重点】使用AOP织入,需要导入一个依赖包
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.9.1</version>
</dependency>
方式一:使用spring的API接口【springAIP的接口实现】
public class BeforeLog implements MethodBeforeAdvice {
//method 要执行的目标对象的方法
//args 参数
//target 目标对象
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName()+"开始执行["+method.getName()+"]方法!");
}
}
public class AfterLog implements AfterReturningAdvice {
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName()+"的["+method.getName()+"]方法执行完毕,返回值为:"+returnValue);
}
}
public interface UserService {
void add();
void delete();
void update();
void list();
}
public class UserServiceImpl implements UserService {
public void add() {
System.out.println("新增了一个用户");
}
public void delete() {
System.out.println("删除了一个用户");
}
public void update() {
System.out.println("更新了一个用户");
}
public void list() {
System.out.println("查询了一个用户");
}
}
<!-- 注册bean-->
<bean id="userService" class="com.kaka.service.UserServiceImpl"/>
<bean id="beforeLog" class="com.kaka.aop.BeforeLog"/>
<bean id="afterLog" class="com.kaka.aop.AfterLog"/>
<!-- 配置aop,需导入aop约束-->
<aop:config>
<!-- 切入点 表达式:execution(要执行的位置! * * * * *)-->
<aop:pointcut id="pointcut" expression="execution(* com.kaka.service.UserServiceImpl.*(..))"/>
<!-- 执行增强-->
<aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
测试结果
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//动态代理的是接口,注意点
UserService userService = context.getBean("userService", UserService.class);//这里只能用接口,,用实现类会报错
userService.add();
----------------------------------------------------------------------------
com.kaka.service.UserServiceImpl开始执行[add]方法!
新增了一个用户
com.kaka.service.UserServiceImpl的[add]方法执行完毕,返回值为:null
Process finished with exit code 0
方式二:自定义类实现【主要是切面的定义】
public class DiyPointcut {
public void before(){
System.out.println("===========方法执行前================");
}
public void after(){
System.out.println("===========方法执行后================");
}
}
public interface UserService {
void add();
void delete();
void update();
void list();
}
public class UserServiceImpl implements UserService {
public void add() {
System.out.println("新增了一个用户");
}
public void delete() {
System.out.println("删除了一个用户");
}
public void update() {
System.out.println("更新了一个用户");
}
public void list() {
System.out.println("查询了一个用户");
}
}
<bean id="diyPointcut" class="com.kaka.zdy.DiyPointcut"/>
<aop:config>
<aop:aspect ref="diyPointcut">
<aop:pointcut id="point" expression="execution(* com.kaka.service.UserServiceImpl.*(..))"/>
<aop:before method="before" pointcut-ref="point"/>
<aop:after method="after" pointcut-ref="point"/>
</aop:aspect>
</aop:config>
测试
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//动态代理的是接口,注意点
UserService userService = context.getBean("userService", UserService.class);//这里只能用接口,,用实现类会报错
userService.add();
----------------------------------------------------------------------------
===========方法执行前================
新增了一个用户
===========方法执行后================
方式三:注解实现
<!---开启注解支持---->
<context:annotation-config/>
<context:component-scan base-package="com.kaka" />
<aop:aspectj-autoproxy/>
@Aspect
@Component
public class AnnotationPointcut {
@Before("execution(* com.kaka..*.*(..))")
public void before(){
System.out.println("卧槽无情");
}
@Around("execution(* com.kaka..*.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("环绕前");
System.out.println(jp.getSignature());//获得签名
jp.proceed();
System.out.println("环绕后");
}
}
测试结果
com.kaka.service.UserServiceImpl开始执行[add]方法!
===========方法执行前================
环绕前
void com.kaka.service.UserService.add()
卧槽无情
新增了一个用户
环绕后
===========方法执行h后================
com.kaka.service.UserServiceImpl的[add]方法执行完毕,返回值为:null
注意:
<!-- 代理方式 :默认 JDK动态代理 proxy-target-class="false",cglib:proxy-target-class="true"-->
<aop:aspectj-autoproxy proxy-target-class="false"/>
执行顺序可以参考上面的执行结果
#springApi > 自定义切面 > 注解
#before > 环绕
整合mybatis
原始的使用mybatis
导入依赖
- junit
- mybatis
- mysql数据库
- spring相关的
- aop织入
- mybatis-spring
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.9</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.9</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.9</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.9.1</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.7</version>
</dependency>
配置文件
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<package name="com.kaka.pojo"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!--- 新版本驱动发生变化 com.mysql.jdbc.Driver已弃用 -->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!--- 注册mapper.xml -->
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
</configuration>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.kaka.mapper.UserMapper">
<select id="getAll" resultType="com.kaka.pojo.User">
select * from `user`
</select>
</mapper>
public interface UserMapper {
public List<User> getAll();
}
//测试
String resource = "mybatis-config.xml";
InputStream in = Resources.getResourceAsStream(resource);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
SqlSession session = factory.openSession(true);//自动提交事务
UserMapper mapper = session.getMapper(UserMapper.class);
List<User> all = mapper.getAll();
System.out.println(all);
整合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: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/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- DataSource :使用spring的数据源替换mybatis的配置,C3p0 dbcp druid -->
<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/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<!-- sqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!-- 绑定配置文件 -->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>
<!-- SqlSessionTemplate就是我们使用的sqlSession -->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<!-- 只能使用构造器注入,因为源码中没有set方法-->
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
<bean id="userMapper" class="com.kaka.mapper.UserMapperTemplate">
<property name="sqlSession" ref="sqlSession"/>
</bean>
</beans>
public class UserMapperTemplate implements UserMapper{
//SqlSessionTemplate完全可以替换SqlSession 而且是线程安全的
private SqlSessionTemplate sqlSession;
public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}
public List<User> getAll() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> all = mapper.getAll();
return all;
}
}
//测试
ApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml");
UserMapperTemplate userMapper = context.getBean("userMapper", UserMapperTemplate.class);
System.out.println(userMapper.getAll());
整合mybatis方式二
public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper {
public List<User> getAll() {
return getSqlSession().getMapper(UserMapper.class).getAll();
}
}
<bean id="userMapper2" class="com.kaka.mapper.UserMapperImpl">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
事务
- 要么都成功,要么都失败
- 事务在项目开发中,十分的重要,涉及到数据的一致性问题,不能马虎!
- 确保完整性和一致性
事务的ACID原则
- 原子性
- 一致性
- 隔离性
- 多个业务可能操作同一个资源,防止数据损坏
- 持久性
- 事务一旦提交,无论系统发生了什么问题,结果都不会再被影响,被持久化的写到存储器中!
spring事务
- 声明式事务:aop横切进去
- 编程式事务:利用注解细粒度手动开启事务
<!-- 配置声明式事务 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="add" propagation="REQUIRED"/>
<tx:method name="update" propagation="REQUIRED"/>
<tx:method name="delete" propagation="REQUIRED"/>
<tx:method name="query" read-only="true"/>
<tx:method name="find" read-only="true"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="txPointcut" expression="execution(* com.kaka..*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>
事务的传播特性
1: PROPAGATION_REQUIRED
加入当前正要执行的事务不在另外一个事务里,那么就起一个新的事务
比如说,ServiceB.methodB的事务级别定义为PROPAGATION_REQUIRED, 那么由于执行ServiceA.methodA的时候,
ServiceA.methodA已经起了事务,这时调用ServiceB.methodB,ServiceB.methodB看到自己已经运行在ServiceA.methodA
的事务内部,就不再起新的事务。而假如ServiceA.methodA运行的时候发现自己没有在事务中,他就会为自己分配一个事务。
这样,在ServiceA.methodA或者在ServiceB.methodB内的任何地方出现异常,事务都会被回滚。即使ServiceB.methodB的事务已经被
提交,但是ServiceA.methodA在接下来fail要回滚,ServiceB.methodB也要回滚
2: PROPAGATION_SUPPORTS
如果当前在事务中,即以事务的形式运行,如果当前不再一个事务中,那么就以非事务的形式运行
3: PROPAGATION_MANDATORY
必须在一个事务中运行。也就是说,他只能被一个父事务调用。否则,他就要抛出异常
4: PROPAGATION_REQUIRES_NEW
这个就比较绕口了。 比如我们设计ServiceA.methodA的事务级别为PROPAGATION_REQUIRED,ServiceB.methodB的事务级别为PROPAGATION_REQUIRES_NEW,
那么当执行到ServiceB.methodB的时候,ServiceA.methodA所在的事务就会挂起,ServiceB.methodB会起一个新的事务,等待ServiceB.methodB的事务完成以后,
他才继续执行。他与PROPAGATION_REQUIRED 的事务区别在于事务的回滚程度了。因为ServiceB.methodB是新起一个事务,那么就是存在
两个不同的事务。如果ServiceB.methodB已经提交,那么ServiceA.methodA失败回滚,ServiceB.methodB是不会回滚的。如果ServiceB.methodB失败回滚,
如果他抛出的异常被ServiceA.methodA捕获,ServiceA.methodA事务仍然可能提交。
5: PROPAGATION_NOT_SUPPORTED
当前不支持事务。比如ServiceA.methodA的事务级别是PROPAGATION_REQUIRED ,而ServiceB.methodB的事务级别是PROPAGATION_NOT_SUPPORTED ,
那么当执行到ServiceB.methodB时,ServiceA.methodA的事务挂起,而他以非事务的状态运行完,再继续ServiceA.methodA的事务。
6: PROPAGATION_NEVER
不能在事务中运行。假设ServiceA.methodA的事务级别是PROPAGATION_REQUIRED, 而ServiceB.methodB的事务级别是PROPAGATION_NEVER ,
那么ServiceB.methodB就要抛出异常了。
7: PROPAGATION_NESTED
理解Nested的关键是savepoint。他与PROPAGATION_REQUIRES_NEW的区别是,PROPAGATION_REQUIRES_NEW另起一个事务,将会与他的父事务相互独立,
而Nested的事务和他的父事务是相依的,他的提交是要等和他的父事务一块提交的。也就是说,如果父事务最后回滚,他也要回滚的。
而Nested事务的好处是他有一个savepoint。
*****************************************
ServiceA {
/**
* 事务属性配置为 PROPAGATION_REQUIRED
*/
void methodA() {
try {
//savepoint
ServiceB.methodB(); //PROPAGATION_NESTED 级别
} catch (SomeException) {
// 执行其他业务, 如 ServiceC.methodC();
}
}
}
********************************************
也就是说ServiceB.methodB失败回滚,那么ServiceA.methodA也会回滚到savepoint点上,ServiceA.methodA可以选择另外一个分支,比如
ServiceC.methodC,继续执行,来尝试完成自己的事务。
但是这个事务并没有在EJB标准中定义。
Spring常用注解
@Autowired:自动装配,通过type name
@Qualifier:和autowired联合使用,作用跟@Resource类似
@Nullable 字段标记这个注解表示这个字段可以为null
@Component:表明组件,下面是该注解的三个衍生注解,都表示将类注册到spring
@Repository
@Service
@Controller
----@Configuration 表明这是个配置类,相当于一个xml
----@Bean 修饰方法,返回值交给spring管理
----@ComponentScan("com.kaka") 包扫描
----@Import(secondConfig.class) 引入其他配置类
@Scope("prototype") 作用域
以上内容如有错误之处,欢迎指正!!!
学习源为:https://www.bilibili.com/video/BV1WE411d7Dv?p=28&spm_id_from=333.880.my_history.page.click