Spring基础

1.Spring容器机制

容器是Spring实现功能的基础,所有配置过的类被纳入容器,通过容器对象管理bean。

1.1 BeanFactory接口

最基本的容器接口,定义创建和管理bean的方法

1.2 ApplicationContext接口

建立在BeanFactory接口之上

1.3 容器启动过程

  • BeanDifinition的Resource定位: 容器需要找到具体的Resource(XML或注解),定位由ResouceLoader统一的Resource接口完成
  • BeanDifinition载入: 将配置信息转化为容器内部的数据结构(BeanDifinition)
  • BeanDifinition注册:将其注册到容器,通过HashMap持有BeanDifinition数据,这个过程调用BeanDifinitionRegisty接口实现。

1.4 BeanFactory和ApplicationContext

  • BeanFactory:是Spring里面最底层的接口,包含了各种Bean的定义,读取bean配置文档,管理bean的加载、实例化,控制bean的生命周期,维护bean之间的依赖关系。
  • ApplicationContext是BeanFactory的子接口,除了提供BeanFactory所具有的功能外,还提供了更完整的框架功能:
    继承MessageSource,因此支持国际化。
    统一的资源文件访问方式。
    提供在监听器中注册bean的事件。
    同时加载多个配置文件。
    载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层。

1.4.1 加载方式

  • BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化。
  • ApplicationContext,它是在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误,这样有利于检查所依赖属性是否注入。

1.4.2 创建方式

  • BeanFactory通常以编程的方式被创建
  • ApplicationContext还能以声明的方式创建,如使用ContextLoader。

1.4.3 注册方式

BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,
但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册。

1.4.4 关系

image

  • BeanFactory:理解为就是个 HashMap,Key 是 BeanName,Value 是 Bean 实例。
  • ApplicationContext:高级容器,定义了一个 refresh 方法,用于刷新整个容器,即重新加载/刷新所有的 bean。依赖而不是继承低级容器

2.AOP

1.介绍

Aspect Oriented Programming 面向切面编程,将程序中的相同业务逻辑进行横向隔离,将重复的业务逻辑抽取到一个独立的模块。

  • Aspect(切面):横切关注点的模块化,包含切点和通知

  • Join point(连接点):程序执行过程中能够插入切面的点

  • Pointcut(切点):定义在哪些连接点上应用通知

  • Advice(通知):切面在特定连接点上执行的代码
    Before advice:在方法执行前执行
    After returning advice:在方法成功执行后执行
    After throwing advice:在方法抛出异常后执行
    After advice:在方法执行后执行(无论是否成功)
    Around advice:环绕方法执行,可以控制方法的执行

  • Target object(目标对象):被一个或多个切面所通知的对象

  • Proxy(代理):AOP框架创建的对象,用于实现切面合约

  • Weaving(织入):将切面与其他对象连接以创建被通知对象的过程

2.实现机制

2.1 JDK动态代理

JDK代理只能为接口创建代理实例
使用 Java 原生的 java.lang.reflect.Proxy 类

  • LoginService接口
public interface LoginService {
    public void login();
}
  • LoginServiceImpl
public class LoginServiceImpl implements LoginService{

    @Override
    public void login() {
        System.out.println("Login");
    }
}
  • PerformHandler类
public class PerformHandler implements InvocationHandler {
    private Object target;

    public PerformHandler(Object target){
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //增强的方法
        System.out.println("start");
        //执行被代理类的原方法
        Object invoke = method.invoke(target, args);
        System.out.println("end");
        return invoke;
    }
}
  • Test类
@Test
public void fun(){
    LoginService loginService = new LoginServiceImpl();
    //通过Proxy生成代理对象
    PerformHandler performHandler = new PerformHandler(loginService);
    loginService = (LoginService) Proxy.newProxyInstance(loginService.getClass().getClassLoader(),
            loginService.getClass().getInterfaces(),performHandler);
    loginService.login();
}

2.2 CGLib动态代理

CGLib动态代理为类创建实例 底层字节码技术 继承的方式实现
依靠Enhancer类创建代理实例 MethodInterceptor接口织入方法

  • CglibProxy
public class CglibProxy implements MethodInterceptor {

    private Enhancer enhancer = new Enhancer();

    //生成代理对象
    public Object getProxy(Class clazz){
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        return enhancer.create();
    }
    
    //回调方法 拦截目标类所有方法调用
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("start");
        Object invoke = methodProxy.invokeSuper(o, objects);
        System.out.println("end");
        return invoke;
    }
}
  • Test类
@Test
public void funG(){
    CglibProxy cglibProxy = new CglibProxy();
    //创建代理对象
    LoginServiceImpl loginService = (LoginServiceImpl) cglibProxy.getProxy(LoginServiceImpl.class);
    loginService.login();
}

3.实现方法

3.1 XML

  • spring.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"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
            https://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop.xsd
            ">
 <bean name="loginService" class="service.LoginServiceImpl"></bean>
 <bean name = "xmlAdvice" class="service.XmlAdvice"></bean>
<!--   Spring Aop-->
   <aop:config>
<!--      切点-->
      <aop:pointcut id="pointcut" expression="execution(* service.LoginServiceImpl.*(..))"/>
<!--      切面-->
      <aop:aspect ref="xmlAdvice">
<!--         前置-->
         <aop:before method="before" pointcut-ref="pointcut"></aop:before>
<!--         返回-->
         <aop:after-returning method="afterReturning" pointcut-ref="pointcut"></aop:after-returning>
<!--         环绕-->
         <aop:around method="around" pointcut-ref="pointcut"></aop:around>
<!--         异常-->
         <aop:after-throwing method="afterException" pointcut-ref="pointcut"></aop:after-throwing>
<!--         后置-->
         <aop:after method="after" pointcut-ref="pointcut"></aop:after>
      </aop:aspect>
   </aop:config>
</beans>
  • Test类
@Test
public void funXml(){
    ApplicationContext context = new ClassPathXmlApplicationContext("config/spring1.xml");
    LoginService loginService = context.getBean("loginService", LoginService.class);
    loginService.login();
}

3.2 注解

  • Spring.xml 修改
    <bean name="annoAdvice" class="service.AnnoAdvice"></bean>
<!--    自动动态代理-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

 <bean name="loginService" class="service.LoginServiceImpl"></bean>
  • AnnoAdvice类
@Aspect
public class AnnoAdvice {
    //切点
    @Pointcut("execution(* service.LoginServiceImpl.*(..))")
    public void pointcut(){

    }
    @Before("pointcut()")
    public void before(){
        System.out.println("before");
    }

    //后置通知 异常不执行
    @AfterReturning("pointcut()")
    public void afterReturning(){
        System.out.println("afterReturning");
    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint point) throws Throwable{
        System.out.println("around start");
        Object object = point.proceed();
        System.out.println("around end");
        return object;
    }

    @AfterThrowing("pointcut()")
    public void afterException(){
        System.out.println("Exception");
    }

    @After("pointcut()")
    public void after(){
        System.out.println("after");
    }
}

4.应用

4.1 性能监控

  • Service1
public class Service1 {
    public void service() throws Exception{
        System.out.println("service");
        Thread.sleep(1000);
    }
}
  • Record类:记录执行信息
public class Record {
    private String className;
    private String methodName;
    private Date recordTime;
    private Long expendTime;

    public String getClassName() {
        return className;
    }

    public void setClassName(String className) {
        this.className = className;
    }

    public String getMethodName() {
        return methodName;
    }

    public void setMethodName(String methodName) {
        this.methodName = methodName;
    }

    public Date getRecordTime() {
        return recordTime;
    }

    public void setRecordTime(Date recordTime) {
        this.recordTime = recordTime;
    }

    public Long getExpendTime() {
        return expendTime;
    }

    public void setExpendTime(Long expendTime) {
        this.expendTime = expendTime;
    }

    @Override
    public String toString() {
        return "Record{" +
                "className='" + className + '\'' +
                ", methodName='" + methodName + '\'' +
                ", recordTime=" + recordTime +
                ", expendTime=" + expendTime +
                '}';
    }
}
  • RecordAspect类
@Aspect
public class RecordAspect {
    @Pointcut("execution(* applicationAop.Service* .*(..))")
    public void record(){

    }

    @Around("record()")
    public Object recordTime(ProceedingJoinPoint point) throws Throwable{
        String className = point.getTarget().getClass().getName();
        String methodName = point.getSignature().getName();
        //方法执行时间
        long startTime = System.currentTimeMillis();
        Object result = point.proceed();
        long time = System.currentTimeMillis() - startTime;

        Record record = new Record();
        record.setExpendTime(time);
        record.setClassName(className);
        record.setMethodName(methodName);
        record.setRecordTime(new Date());
        System.out.println(record);
        return result;
    }
}
  • xml配置
<bean name="service1" class="applicationAop.Service1"></bean>
<bean name="recordAspect" class="applicationAop.RecordAspect"></bean>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
  • Test类
 @Test
public void test() throws Exception{
    ApplicationContext context = new ClassPathXmlApplicationContext("config/spring1.xml");
    Service1 service1 = context.getBean("service1",Service1.class);
    service1.service();
}

4.2 异常监控

  • MyException类
public class MyException extends Exception{
    private static final long serialVersionUID = 1l;
    private String msg;

    public MyException(String msg) {
        super();
        this.msg = msg;
    }

    public String getMsg() {
        return msg;
    }
}
  • Service2
public class Service2 {
    public void service() throws Exception{
        System.out.println("service2");
        if(true){
            throw new MyException("true");
        } else {
            System.out.println(false);
        }
    }
}
  • Message类
public class Message {
    private String className;
    private String methodName;
    private Date recordTime;
    private String exceptionMsg;

    public String getClassName() {
        return className;
    }

    public void setClassName(String className) {
        this.className = className;
    }

    public String getMethodName() {
        return methodName;
    }

    public void setMethodName(String methodName) {
        this.methodName = methodName;
    }

    public Date getRecordTime() {
        return recordTime;
    }

    public void setRecordTime(Date recordTime) {
        this.recordTime = recordTime;
    }

    public String getExceptionMsg() {
        return exceptionMsg;
    }

    public void setExceptionMsg(String exceptionMsg) {
        this.exceptionMsg = exceptionMsg;
    }

    @Override
    public String toString() {
        return "Message{" +
                "className='" + className + '\'' +
                ", methodName='" + methodName + '\'' +
                ", recordTime=" + recordTime +
                ", exceptionMsg='" + exceptionMsg + '\'' +
                '}';
    }
}
  • MessageAspect
@Aspect
public class MessageAspect {
    @Pointcut("execution(* applicationAop.Service* .*(..))")
    public void exceptionMsg(){

    }

    @Around("exceptionMsg()")
    public Object msgMethod(ProceedingJoinPoint point) throws Throwable{
        String className = point.getTarget().getClass().getName();
        String methodName = point.getSignature().getName();

        try {
            return point.proceed();
        }catch (MyException e){
            Message message = new Message();
            message.setClassName(className);
            message.setMethodName(methodName);
            message.setRecordTime(new Date());
            message.setExceptionMsg(e.getMsg());
            System.out.println(message);
        }


        return null;
    }
}
  • xml配置
<bean name="service2" class="applicationAop.Service2"></bean>
<bean name="mmessageAspect" class="applicationAop.MessageAspect"></bean>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

-Test类

@Test
public void test1() throws Exception{
    ApplicationContext context = new ClassPathXmlApplicationContext("config/spring1.xml");
    Service2 service2 = context.getBean("service2",Service2.class);
    service2.service();
}

3.IOC

IOC:控制反转,对象控制权由代码交给容器,负责创建对象,管理对象(通过依赖注入(DI),装配对象,配置对象,并且管理这些对象的整个生命周期。
DI:依赖注入,为控制反转提供实现方法。

1.DI方式

  • 构造器注入
  • 属性注入
  • 接口注入(废弃)

1.1 构造器注入

在被注入的类声明一个构造方法(有参或无参),Spring通过反射调用构造方法,进而创建对象

1.2 属性注入

在被注入类声明set方法,通过参数注入

  • Student
public class Student {
    public Student(String msg) {
        this.msg = msg;
    }

    public Student(){

    }
    private String msg;

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    @Override
    public String toString() {
        return "Student{" +
                "msg='" + msg + '\'' +
                '}';
    }
}
  • spring.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"
       xmlns:p="http://www.springframework.org/schema/p">

    <bean id="student" class="entity.Student">
<!--        1.构造器注入属性值
                通过构造器-->
        <constructor-arg name="msg" value="MSG"></constructor-arg>
<!--        2.属性注入
                通过set方法-->
        <property name="msg">
            <value>LWX</value>
        </property>
<!--        3.接口注入-->
    </bean>
</beans>
  • Test类
@Test
public void fun(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("config/spring.xml");
    Student student = applicationContext.getBean("student",Student.class);
    System.out.println(student.getMsg());
}

2.DI方式

2.1 注入集合

  • Mix实体
public class Mix {
    private List<String> list;
    private Map<String, String> map;
    private String[] arr;

    public List<String> getList() {
        return list;
    }

    public void setList(List<String> list) {
        this.list = list;
    }

    public Map<String, String> getMap() {
        return map;
    }

    public void setMap(Map<String, String> map) {
        this.map = map;
    }

    public String[] getArr() {
        return arr;
    }

    public void setArr(String[] arr) {
        this.arr = arr;
    }

    @Override
    public String toString() {
        return "Mix{" +
                "list=" + list +
                ", map=" + map +
                ", arr=" + Arrays.toString(arr) +
                '}';
    }
}
  • spring.xml
<bean id="mix" class="entity.Mix">
  <property name="list">
      <list>
          <value>list1</value>
          <value>list2</value>
      </list>
  </property>

  <property name="map">
      <map>
          <entry key="key1" value="map1"></entry>
          <entry key="key2" value="map2"></entry>
      </map>
  </property>

  <property name="arr">
      <array>
          <value>arr1</value>
          <value>arr2</value>
      </array>
  </property>
</bean>

2.2 注入对象属性

利用ref元素

  • Clazz实体
public class Clazz {
    private List<Student> students;

    public List<Student> getStudents() {
        return students;
    }

    public void setStudents(List<Student> students) {
        this.students = students;
    }

    @Override
    public String toString() {
        return "Clazz{" +
                "students=" + students +
                '}';
    }
}
  • spring.xml
 <bean id="clazz" class="entity.Clazz">
<!--        第一种-->
    <property name="students">
        <list>
            <ref bean="student"></ref>
        </list>
    </property>
<!--        第二种-->
    <property name="students" ref="student"></property>
</bean>

2.3 P命名空间注入

上述实体

<!--    p命名空间-->
    <bean id="clazz" class="entity.Clazz" p:students-ref="student"></bean>

2.3 SpEL注入

Spring Expression Language 表达式语言 为Bean属性进行动态赋值 通过bean的id引用bean 调用对象方法或属性

  • Person实体
public class Person {
    private String msg;

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    @Override
    public String toString() {
        return "Person{" +
                "msg='" + msg + '\'' +
                '}';
    }
}
  • spring.xml
<!--    SpEL注入-->
    <bean id="person" class="entity.Person">
        <property name="msg" value="#{student.msg}"></property>
    </bean>

4.Bean

4.1 配置

4.2 作用域

容器创建bean实例时可以指定其作用域,默认singleton

4.3 生命周期

  1. 实例化 Instantiation : createBeanInstance()
  • InstantiationAwareBeanPostProcessor接口的postProcessBeforeInstantiation和postProcessAfterInstantiation
  1. 属性赋值 Populate : populateBean()
  2. 初始化 Initialization : initializeBean()
  • 如果实现了xxxAware 接口,通过不同类型的Aware接口拿到Spring容器的资源
  • 如果实现了BeanPostProcessor接口,则会回调该接口的postProcessBeforeInitialzation 和postProcessAfterInitialization 方法
  • 如果配置了init-method 方法,则会执行init-method 配置的方法
  1. 销毁 Destruction
  • 容器关闭后,如果Bean实现了DisposableBean 接口,则会回调该接口的destroy 方法
  • 如果配置了destroy-method 方法,则会执行destroy-method 配置的方法

bean创建,初始化,销毁的过程。

  • 初始化之前:init-method属性或实现initializingBean接口
  • 销毁之前:destroy-method属性或实现DisposableBean接口
  • Bean01实体
public class Bean01 implements DisposableBean {

    public Bean01(){

    }

    private String value;

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    public void init(){
        System.out.println("INIT");
    }

    public void close(){
        System.out.println("CLOSE");
    }

    @Override
    public void destroy() throws Exception {

    }
}
  • spring.xml
<bean id="bean01" class="entity.Bean01" init-method="init" destroy-method="close">
    <property name="value" value="MyValue"></property>
</bean>

4.4 注解

  • spring.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"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
            https://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop.xsd
            ">

   <context:component-scan base-package="entity"></context:component-scan>
</beans>
  • stu实体类
@Component("stu")
public class Stu {
    public Stu(String msg) {
        this.msg = msg;
    }

    public Stu(){

    }
    @Value("MSG")
    private String msg;

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    @Override
    public String toString() {
        return "Student{" +
                "msg='" + msg + '\'' +
                '}';
    }
}
  • Sch实体类
@Component("sch")
@Scope(scopeName = "singleton")
public class Sch {
    @Autowired
    @Qualifier("stu")//注入该实例
    private Stu stu;

    @PostConstruct
    public void init(){
        System.out.println("INIT");
    }

    @PreDestroy
    public void close(){
        System.out.println("CLOSE");
    }
}

5.事务

5.1 分类

  • 编程式事务:在代码中显式地开启、提交或回滚事务
  • 声明式事务:通过配置来实现的,不需要在代码中显式地管理事务。是通过 AOP 实现的。Spring 提供了两种声明式事务的方式:基于 XML配置和基于注解配置

5.2 架构

5.2.1 俩种模式

  • 策略模式
    PlatformTransactionManager 基类,实现了 事务执行设计到的三个核心方法,去实现它
    TransactionAspectSupport 中, 会有一个determineTransactionManager 方法, 动态的从IOC容器,加载 注册了的PlatformTransactionManager 实现类
  • 动态代理模式
    动态代理有JDK的动态代理、 Aspectj的动态代理,SpringAOP动态代理
    image
    SpringAOP实现的大致思路:
    1.配置获取Advisor (顾问):拦截器+AOP匹配过滤器,生成Advisor
    2.生成代理:根据Advisor生成代理对象,会生成JdkDynamicAopProxy或CglibAopProxy
    3.执行代理:代理类执行代理时,从Advisor取出拦截器,生成MethodInvocation(连接点)并执行代理
    过程。

5.3 事务传播机制

Propagation 属性

  • REQUIRED(Spring默认的事务传播类型):如果当前没有事务,则自己新建一个事务,如果当前存在事务,则加入这个事务
    a1 b1 b2 回滚
@Transactional(propagation = Propagation.REQUIRED)
public void testMain(){
    A(a1);  //调用A入参a1
    testB();    //调用testB
}
@Transactional(propagation = Propagation.REQUIRED)
public void testB(){
    B(b1);  //调用B入参b1
    throw Exception;     //发生异常抛出
    B(b2);  //调用B入参b2
}
  • REQUIRES_NEW:每次执行都会创建新事务,并同时将上下文中的事务挂起,执行完当前线程后再恢复上下文中事务。
    a1 回滚 b1 b2 成功 (B是新事物 和A独立)
@Transactional(propagation = Propagation.REQUIRED)
public void testMain(){
    A(a1);  //调用A入参a1
    testB();    //调用testB
    throw Exception;     //发生异常抛出
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void testB(){
    B(b1);  //调用B入参b1
    B(b2);  //调用B入参b2
}
  • SUPPORTS:如果上下文中存在事务则加入当前事务,如果没有事务则以非事务方式执行。
    a1 b1 成功 b2 失败 (A无事务,B按非事务执行,如果A有事务,B也有事务)
public void testMain(){
    A(a1);  //调用A入参a1
    testB();    //调用testB
}
@Transactional(propagation = Propagation.SUPPORTS)
public void testB(){
    B(b1);  //调用B入参b1
    throw Exception;     //发生异常抛出
    B(b2);  //调用B入参b2
}
  • NOT_SUPPORTED:当上下文中有事务则挂起当前事务,执行完当前逻辑后再恢复上下文事务。
    a1 b2 失败 b1 成功 (B按无事务执行 b1成功 b2失败 A有事务 a1失败)
@Transactional(propagation = Propagation.REQUIRED)
public void testMain(){
    A(a1);  //调用A入参a1
    testB();    //调用testB
}
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void testB(){
    B(b1);  //调用B入参b1
    throw Exception;     //发生异常抛出
    B(b2);  //调用B入参b2
}
  • MANDATORY:该传播级别要求上下文中必须存在事务,否则抛出异常。
    a1成功 b1 b2失败 (B无事务,抛异常)
public void testMain(){
    A(a1);  //调用A入参a1
    testB();    //调用testB
}
@Transactional(propagation = Propagation.MANDATORY)
public void testB(){
    B(b1);  //调用B入参b1
    throw Exception;     //发生异常抛出
    B(b2);  //调用B入参b2
}
  • NEVER:该传播级别要求上下文中不能存在事务,否则抛出异常。
    B直接抛出事务异常 A回滚 都失败
@Transactional(propagation = Propagation.REQUIRED)
public void testMain(){
    A(a1);  //调用A入参a1
    testB();    //调用testB
}
@Transactional(propagation = Propagation.NEVER)
public void testB(){
    B(b1);  //调用B入参b1
    B(b2);  //调用B入参b2
}
  • NESTED:嵌套事务,如果上下文中存在事务则嵌套执行,如果不存在则新建事务
    都回滚
@Transactional(propagation = Propagation.REQUIRED)
public void testMain(){
    A(a1);  //调用A入参a1
    testB();    //调用testB
    throw Exception;     //发生异常抛出
}
@Transactional(propagation = Propagation.NESTED)
public void testB(){
    B(b1);  //调用B入参b1
    B(b2);  //调用B入参b2
}

5.4 事务异常处理

spring事务只对RuntimeException奏效,同一个Service类中的方法相互调用需要使用注入的对象来调用,不要直接使用this.方法名来调用,this.方法名调用是对象内部方法调用,不会通过Spring代理,也就是事务不会起作用,通过aop来获取异常,如果手动捕获则不会回滚

  • 正常回滚
@PostMapping("/test")
@Transactional
public void test() {
    transPlatformAccessMapper.insertTest();
    int i = 1/0;
}
  • 不会回滚
@PostMapping("/test")
@Transactional
public void test() {
    try{
        transPlatformAccessMapper.insertTest();
        int i = 1/0;
    }catch (Exception e){
        e.printStackTrace();
    }

}

5.4.1 手动回滚

  • 回滚点
@PostMapping("/test")
    @Transactional
    public void test() {
        //设置回滚点,只回滚以下异常
        Object savePoint = TransactionAspectSupport.currentTransactionStatus().createSavepoint();
        try{
            transPlatformAccessMapper.insertTest();
            int i = 1/0;
        }catch (Exception e){
            e.printStackTrace();
            //手工回滚到回滚点
            TransactionAspectSupport.currentTransactionStatus().rollbackToSavepoint(savePoint);
        }
    }
  • 全部回滚
@PostMapping("/test")
@Transactional
public void test() {
    try{
        transPlatformAccessMapper.insertTest();
        int i = 1/0;
    }catch (Exception e){
        e.printStackTrace();
        // 全部回滚
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    }
}

5.5 @Tranasctional

  • 当它被用于类上时,它表示该类中所有的方法都将被包含在同一个事务中。当它被用于方法上时,它表示该方法将被包含在一个新的事务中。
  • 只能用在public 方法上,如果用在protected或者private的方法上,不会报错,但是该注解不会生效。
  • 默认情况下,@Transactional注解只能回滚 非检查型异常,具体为RuntimeException及其子类和Error子类
  • 如果需要对检查型异常(Checked Exception)进行回滚,可以使用rollbackFor 属性来定义回滚的异常类型,使用 propagation 属性定义事务的传播行为。
@Transactional(rollbackFor = Exception.class,propagation =Propagation.REQUIRED)
  • 不能回滚被try{}catch() 捕获的异常。
  • 只能对在被Spring 容器扫描到的类下的方法生效。

6.循环依赖

首先,Spring 解决循环依赖有两个前提条件:

  1. 不全是构造器方式的循环依赖
  2. 必须是单例
    本质上解决循环依赖的问题就是三级缓存,通过三级缓存提前拿到未初始化的对象。

第三级缓存:用来保存一个对象工厂,提供一个匿名内部类,用于创建二级缓存中的对象
第二级缓存:用来保存实例化完成,但是未初始化完成的对象
第一级缓存:用来保存实例化、初始化都完成的对象

image

5.1 AB相互依赖

A对象的创建过程:
1.创建对象A,实例化的时候把A对象工厂放入三级缓存
2.A注入属性时,发现依赖B,转而去实例化B
image
3.同样创建对象B,注入属性时发现依赖A,依次从一级到三级缓存查询A,从三级缓存通过对象工厂拿到A,把A放入二级缓存,同时删除三级缓存中的A,
然后,B已经实例化并且初始化完成,把B放入一级缓存。
image
4.接着继续创建A,顺利从一级缓存拿到实例化且初始化完成的B对象,A对象创建也完成,删除二级缓存中的A,同时把A放入一级缓存
5.最后,一级缓存中保存着实例化、初始化都完成的A、B对象
image
因此,由于把实例化和初始化的流程分开了,所以如果都是用构造器的话,就没法分离这个操作,所以都是构造器的话就无法解决循环依赖的问题了。

5.2 二级缓存

不可以,主要是为了生成代理对象。
因为三级缓存中放的是生成具体对象的匿名内部类,他可以生成代理对象,也可以是普通的实例对象。
使用三级缓存主要是为了保证不管什么时候使用的都是一个对象。
假设只有二级缓存的情况,往二级缓存中放的显示一个普通的Bean对象,在BeanPostProcessor 去生
成代理对象之后,覆盖掉二级缓存中的普通Bean对象,那么多线程环境下可能取到的对象就不一致
了。
image

6.注解

6.1 @Autowired @Resource

@Autowired可用于:构造函数、成员变量、Setter方法
@Autowired默认是按照类型装配注入的,默认情况下它要求依赖对象必须存在(可以设置它required属性为false)。
@Resource默认是按照名称来装配注入的,只有当找不到与名称匹配的bean才会按照类型来装配注入。

posted @ 2022-07-02 10:57  lwx_R  阅读(32)  评论(0)    收藏  举报