存于一级缓存,为了解决循环依赖问题。
纯净态,对象仅仅被实例化,由BeanFactory创建实例。
单例Map,也就是单例池,也就是一级缓存,处于成熟态的bean会加入到单例池中。
简述springioc加载bean的过程。
bean形态的转变过程:
概念态>定义态>纯净态>成熟态
1、创建ioc容器ApplicationContext,bean被加载到BeanDefinition,用户后续创建对象,比如id、classs、lazy、scope这些都会加载进BeanDefinition中。
2、BeanFactory创建bean的实例,存入一级缓存,用于解决后续的循环依赖问题。
3、BeanFactory为实例注入属性,对象加入到单例池(一个Map),也就是加入到一级缓存。
注意,创建的对象是单例的。
@Bean相当于xml的<bean>
bean在初始化和销毁的时候,分别由各自的回调方法。
条件:
1、手动创建的bean,也就是类。
2、配置类。包括@Configuration和和@Bean
3、启动项目的时候,手动创建容器AnnotationConfigApplicationContext,通过手动创建的容器加载bean,模拟ApplicationContext加载bean。
4、关闭容器,applicaitonContext.close(),加载过的bean会销毁。
注意,在bean初始化(通过容器获取bean的时候会初始化bean)和销毁(关闭容器)的时候,我们可以在这两个节点分别自定义两个回调方法用户处理我们需要的业务。
bean的类中别出现@Component、@Service、@Configuration,会直接导致配置类不生效,这也正说明了,配置类@Configuration的执行优先级高于bean类中的@Component,bean类后续被容器再次创建从而覆盖了配置类中设置的处理逻辑。
bean中的类被配置类使用,由配置类通过容器创建对象。
@Componet、@Configuration、@Service的意思就是告诉ApplicationContext创建对象。
某个类使用了@Componet,说明这个类无法配置初始化和销毁的回调方法。
简述bean生命周期中的回调方法的几种实现方法以及这几种方法的执行顺序。
bean在初始化和销毁,这两个状态的时候,可以执行回调方法。
有三种方式实现回调
1、注解。@PostConstruct初始化(容器加载bean的时候会初始化bean)的回调,@PreDestroy销毁(容器close的时候会销毁所有加载的bean)时的回调。
2、接口。InitializingBean和Disposiable分别override afterPropertiesSet()和destroy()
3、通过注解的属性指定回调方法@Bean。等同于使用xml的<bean>方式创建对象,同样会触发回调方法。属性分别为initMethod和destroyMethod,值是回调方法名,注意,类中若添加了@Componet,那么在该类被加载的时候,是没办法加载回调方法的,写了也没有用。
执行顺序:注解>接口>通过注解的属性指定回调方法。
说说循环依赖。
@Component表示class BeanA是容器需要创建的bean。容器就是用来创建bean的。
创建bean包括:实例化和属性注入。
注入属性时,解析autowire发现需要注入BeanB,此时到容器中去寻找beanB,发现容器中没有,故容器实例beanB并注入属性,解析autowire发现beanA,故到容器中找beanA,找不到beanA,容器创建beanA,如此循环,却无法创建beanA和beanB的现象。
容器中的对象必须完成实例化和属性注入,否则对象无法加入容器。
autowire就是用来为属性注入容器中必须存在的bean。
Spring如何解决bean的循环依赖?
使用三级缓存,也就是三个map。
缓存和map关系匪然。map用来做缓存。
A的实例,A的bean,A的动态代理。
函数接口,参数是A的实例和A的名称,方法实现的逻辑是创建aop。调用函数接口创建的是A的动态代理。
循环依赖:A依赖B,B依赖A
多重循环依赖:A依赖B和C,B和C又分别依赖A。
二级缓存的作用:多重循环依赖的情况下,避免A的代理被第二次创建。A的代理只需要一个。从二级缓存中获取到A的代理后,就不会去三级缓存中调用函数接口再次创建A的动态代理了。
属性赋值操作,赋值的对象可以是代理或者bean,唯独不能是实例。在A初始化之后,就会创建A的动态代理,没有循环依赖的对象是在初始化后创建代理。
在A的属性赋值时候创建A的动态代理,否在B属性赋值时,赋的是A的实例而不是A的动态代理,这是有问题的。
多例bean每次使用都要重新创建,既然每次都重新创建,就没有必要用缓存,况且多例bean没有使用缓存一说。
至少有一个缓存,存储了早期对象,A依赖B,创建B的时候发现依赖A,此时从缓存中拿到A的早起对象,就可以解决循环依赖问题,缓存是循环的一个出口。B拿到A的早起对象就可以初始化,从而A就可以注入B属性。
多例@Scope(“”prototype)
属性的注入方式有两种,@Autowired(属性注入的方式),构造函数的方式注入,构造方法中的参数是当前类依赖的另外一个类。
spring解决循环依赖的方法:三级缓存
一级可以解决,多线程环境下,获取不到完整bean问题。
二级缓存也可以解决循环依赖问题,有多重循环依赖下,重复创建代理的问题。
多例可以解决循环依赖么?无法解决,多例没有用到缓存,故无法解决循环依赖。
构造方法注入属性的循环依赖问题怎么解决?在构造方法上使用注解@Lazy。实例化A的时候,创建的是B的代理,创建B的代理的时候,又需要A的实例,此时调用的是A的无参构造方法。在bean实际调用方法的时候,才会创建。
context.getBean(A)>context.getBean(A).getB,此时创建B的代理>context.getBean(A).getB.xxx调用的就是B的方法。
jdk动态代理:被代理的目标类没有实现接口。
cglib动态代理:被代理的目标类实现了接口。