Spring-Bean的生命周期概述

Bean的生命周期概述

入门使用的Spring代码:

ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
UserService userService = (UserService) context.getBean("userService");
userService.test();

思考问题:

1、第一行代码都做了哪些事情?

2、第二行代码getBean()如何实现的?返回的UserService对象和new创建的对象有什么区别?

在新版的Spring MVCSpring Boot的底层主要用的都是AnnotationConfigApplicationContext

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

UserService userService = (UserService) context.getBean("userService");
userService.test();

注意:AppConfig.classspring.xml的本质是一样的,都表示Spring的配置,类比两种写法:

spring.xml中的内容为:


    
AppConfig中的内容为:
@ComponentScan("com.gax")
public class AppConfig {
    @Bean
    public UserService userService(){
    	return new UserService();
    }
}

目前spring.xml这种方式已经很少见了;更多的是使用Spring MVC或者Spring Boot,这两种也是基于spring.xml这种方式,在内部创建一个ApplicationContext,但是也有区别:

1、Spring MVC创建的是XmlWebApplicationContext,和ClassPathXmlApplicationContext类似,都是基于XML配置的

2、Spring Boot创建的是AnnotationConfigApplicationContext

后续重点学习研究的是AnnotationConfigApplicationContext这一种

Spring中是如何创建一个对象?

AnnotationConfigApplicationContext可以简单理解为创建Java对象,当调用context.getBean("userService")方法时创建一个对象。那么问题来了,方法内部如何知道"userService"对应的是UserService类呢?

可以分析出来AnnotationConfigApplicationContext的构造方法做的一些事情:

1、解析AppConfig.class,得到扫描路径

2、遍历扫描路径下的所有Java类,如果类上面有@Component、@Service等注解,那么Spring就会把这个类记录下来,存放在一个Map里,比如像Map,Spring源码中叫BeanDefinitionMap

3、Spring根据规则生成当前类的beanName,作为存入Map时的key,当前类作为存入Map时的value(Spring中的beanName默认是类名首字母小写)

Bean的创建过程

Spring到底是如何来创建一个Bean的呢,也就是Bean创建的生命周期。大致流程如下:

1、利用该类的构造方法来实例化得到一个对象(有多个构造方法时,Spring会做出选择,推断构造方法

2、得到一个对象后,Spring会判断该对象中是否存在被@Autowired注解了的属性,把这些属性找出来并由Spring进行赋值,依赖注入

3、依赖注入后,Spring会判断该对象是否实现了BeanNameAware接口、BeanClassLoaderAware接口、BeanFactoryAware接口,如果实现了,就表示当前对象必须实现该接口中所定义的setBeanName()、setBeanClassLoader()、 setBeanFactory()方法,那Spring就会调用这些方法并传入相应的参数,Aware回调

4、Aware回调后,Spring会判断该对象中是否存在某个方法被@PostConstruct注解了,如果存在,Spring会调用当前对象的此方法,初始化前

5、Spring会判断该对象是否实现了InitializingBean接口,如果实现了,就表示当前对象必须实现该接口中的afterPropertiesSet()方法,那Spring就会调用当前对象中的afterPropertiesSet()方法,初始化

6、Spring会判断当前对象需不需要进行AOP,如果不需要那么Bean就创建完了,如果需要进行AOP,则会进行动态代理并生成一个代理对象做为Bean,初始化后

注意:不用AOP时,Bean就是构造方法创建的普通对象;需要AOP时,Bean是代理类实例化的对象

Bean对象创建完成后,如果是单例Bean就放在Map里,下次可以直接从Map中直接获取(Spring源码中Map就是单例池);如果是原型Bean就不用放在Map里,下次重新创建

推断构造方法

选择构造方法的判断逻辑:

1、只有一个构造方法,无论是否有参,Spring都会选择这个

2、有多个构造方法时,Spring会选择无参的构造方法(无参本身就是一种默认)

没有无参构造方法,且构造方法上面都没有@Autowired注解时报错,Spring不知道选择哪一个

没有无参构造方法,但是某个构造方法上面添加了@Autowired注解,Spring选择这一个构造方法

注意:Spring选择有参构造方法时,Spring会先根据参数类型,类型相同再根据参数名字去找Bean对象,类型和名字都找不到就报错

推断构造方法:确定用哪个构造方法,确定入参的Bean对象

AOP大致流程

判断当前Bean对象需不需要进行AOP:

1、找出所有的切面Bean

2、遍历切面中的每个方法,看是否写了@Before、@After等注解

3、如果写了,则判断所对应的Pointcut是否和当前Bean对象的类匹配

4、如果匹配则表示当前Bean对象有匹配的的Pointcut,需要进行AOP

利用cglib进行AOP的大致流程:(cglib说白了就是父子类)

1、生成代理类XXServiceProxy,代理类继承XXService

2、代理类中重写了父类的方法

3、代理类中有一个target属性,该属性的值为被代理对象

(这里的被代理对象:是推断构造方法实例化的对象,完成了依赖注入、初始化等)

4、代理类中的方法被执行时,先执行before的切面逻辑,再执行target.目标方法

注意:XXServiceProxy是代理类,它实例化出来的是代理对象;target是被代理对象

思考:代理对象的属性是null,没有进行依赖注入,为什么?

没有必要,大部分情况用不到,需要用的话也可以通过target获取

Spring事务

在某个方法上加了@Transactional注解后,就表示该方法在调用时会开启Spring事务,而这个方法所在的类所对应的Bean对象会是该类的代理对象。

1、判断当前执行的方法是否存在@Transactional注解

2、如果存在,则利用事务管理器(TransactionMananger)新建一个数据库连接

3、修改数据库连接的autocommit为false

4、执行target.目标方法,业务逻辑的sql

5、执行完之后没有异常则提交,否则回滚

事务失效的场景分析:

1、调用目标方法的是否为代理类,使用代理类调用时才有切面的逻辑

2、是否使用的同一个事务管理器(或者说是否同一个数据库连接)

你可能感兴趣的:(java,spring)