likes
comments
collection
share

Spring 相关知识点

作者站长头像
站长
· 阅读数 56

本文主要整理 Spring 相关知识点,包括 Spring bean 的生命周期,Spring 事务的原理,Srping 事务的实现方式等等

一、Spring Bean 的生命周期

@service
public class TestService implements ApplicationContextAware, InitializingBean {

    @Autowired
    private TestInfo testInfo;
    
    private TestDAO testDao;
    
    public TestService(TestDAO testDao) {
        this testDao = testDao;
    }
    
    @PostConstruct
    public void test() {
        System.out.println("init exexute test function");
    }
    
    @Override  
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {  
  
    }
    
    @Override  
    public void afterPropertiesSet() throws Exception {  
        System.out.println("init after test function");
    }
    
}

1、推断构造方法

在一个 Bean 中,如果使用了构造方法,Spring 要去推断构造方法,为什么要推断构造方法,假如一个 Bean 中,使用了多个构造方法,那么 Spring 应该使用哪个构造方法?

  • 如果类中只有一个构造方法,那么会 spring 会使用这一个构造方法
  • 如果类中有多个构造方法,如果两个里面有一个使用 @Autowired 注解,那么 Spring 就会使用这个构造方。
  • 如果两个都没有使用 @Autowired 注解,如果存在无参的构造方法,那么会使用无参的构造方法
  • 如果两个都没有使用 @Autowired 注解,并且不存在无参的构造方法,那么在运行时会报错
  • 如果两个都加了 @Autowired 注解,那么 Spring 会报错

2、实例化

通过构造方法得到一个对象,如果选择无参的构造方法,那么对象中的属性 testDao 是没有值的

3、填充属性

实例化一个对象之后,Spring 就会进行依赖注入,比如在上面代码中,会判断加了 @Autowired 注解或者 @Resource 注解的属性,利用返回找到这些属性的 Bean 对象,然后赋值给这些属性。

4、处理 Aware 回调

填充属性之后,会进一步判断当前的类中是否定义了 Aware 回调,比如上面实现了 ApplicationContextAware, 会将当前的 Spring 容器传给当前的方法,可以利用容器做些其他的事情。

5、初始化

初始化分为初始化前、初始化中和初始化后。

初始化前,处理 @PostConstruct 接口。如果被加载的类中有使用 @PostConstruct 注解修饰的方法,那么 spring 会自动执行这个方法。

初始化中,处理 InitializingBean 接口,如果被加载的类中有实现 InitializingBean 的接口,那么会实现对应方法 - afterPropertiesSet。

执行的先后顺序:setApplicationContext() -> @PostConstruct 修饰的方法 -> 执行实现 InitializingBean 接口对应的方法

初始化后,进行 AOP。前面的过程构造的对象(经过依赖注入,回调,经过初始化前,初始化中执行的方法)生成一个代理对行,AOP 的结果就是返回一个代理对象。

6、容器关闭时,会对 Bean 进行销毁

二、Spring 事务管理

1、Spring 事务传播机制

Spring 事务传播是指在两个事务方法中,其中一个方法调用另外一个方法,这个时候希望另外一个方法应该如何去执行事务逻辑,具体代码如下所示:

public class testTraction{
    
    @Traction
    public void testA() {
        testB();
    }
    
    public void testB() {
        
    }
}

共有七种事务传播机制:

Spring 相关知识点

spring 事务实现是使用 AOP 进行实现的,但是事务的本质还是使用数据库的链接,比如新创建一个事务,本质还是新打开一个数据库链接,并且基于新的数据库链接进行提交以及回滚事务。

2、声明式事务与编程式事务

声明式编程事务使用 @Transactional 来进行管理。示例如下:

@Service
public class TransactionDemo {

    @Transactional
    public void declarativeUpdate() {
        updateOperation1();
        updateOperation2();
    }
}

这样的写法相当于执行 declarativeUpdate() 方法前使用 BEGIN 开启事务,在方法调用结束后使用 COMMIT 进行提交事务。也可以将 @Transactional 放在类上,这样默认对象中的 public 方法都开启了事务。

相比之下,使用编程式事务管理相对来说比较复杂一些,编程式事务管理是指通过代码来控制事务,使用 TransactionTemplate 来实现。示例如下所示:

@Service
public class TransactionDemo {
    
    @Autowired
    private TransactionTemplate transactionTemplate;

    public void programmaticUpdate() {
        // 这里也可以使用Lambda表达式
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                updateOperation1();
                updateOperation2();
            }
        });
    }
}

两种方式的对比如下:

Spring 相关知识点

3、事务失效条件

方法内部调用
@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    //@Transactional
    public void add(UserModel userModel) {
        // do something...
        updateStatus(userModel);
    }

    @Transactional
    public void updateStatus(UserModel userModel) {
        doSameThing();
    }
}

这是一个非常常见的错误,在方法内部进行调用,但是我们都知道,spring 事务实现方式是基于 AOP 进行实现的,上面的代码逻辑相当于是调用了 this.updateStatus(userModel)。这时候 this 并不是代理之后的对象,自然而然就会失效。

解决的方法如下:

  • 新增一个类进行方法调用
  • 在本类中注入自己
  • 使用 AopContext.currentProxy() 进行调用
使用 private 进行修饰

会判断方法是否使用 private 进行修饰,如果使用 private 进行修饰,则会直接返回 null,即代表不支持事务

方法使用 final 进行修饰

如果一个事务方法被定义成了final,也会导致事务不生效,这是因为通过cglib是通过生成子类来方式生成代理类。如果方法被定义为final,则意味着该方法无法被重写,无法添加事务功能。

未被 spring 进行管理

如果在类注视中没有使用 @Component、@Service、@Repository 等,则事务将不会生效

多线程调用

在多线程之间调用,会导致不同的线程获取不同的链接,这两个方法就会在不同的事务中。

转载自:https://juejin.cn/post/7383017171179372598
评论
请登录