https://blog.csdn.net/a745233700/article/details/80959716
https://thinkwon.blog.csdn.net/article/details/104397516
1、是什么是IOC?
控制反转把创建对象的权利交给工厂或者框架,在原来,我们获取对象是采用new的方式,是主动的,现在我们获取对象时,是向工厂或者框架容器要的,有工厂或者框架容器帮我们查找或创建对象,是被动的。这种被动接受的方式获取对象的思想就是控制反转。
2、Spring创建bean的三种方式
- 通过默认构造函数创建
- 通过spring管理静态工厂,使用静态工厂的方法来创建对象
class:静态工厂的全限定类名
factory-method:静态工厂获取对象的静态方法 - 通过spring管理实例工厂,使用实例工厂的方法来创建对象
factory-bean: 实例工厂的id
factory-method: 实例工厂获取对象的方法
3、什么是依赖注入?
依赖注入是spring ioc的具体实现,我们在编写程序时,通过控制反转,把对象的创建交给spring,spring ioc是降低程序间的依赖关系,而不能消除依赖关系,像这种依赖关系,在使用spring之后,就由spring来维护。
例如:我们的业务层调用持久层的方法,spring为我们提供持久层的对象,我们只需要在配置文件描述即可,依赖关系的维护就称为依赖注入。依赖注入的本质就是装配,装配是依赖注入的具体行为。
能注入的数据类型大致分为三类:
- 基本数据类型和String
- 其他bean(在ioc容器的bean)
- 复杂类型/集合类型
依赖注入的三种方式:
- 构造函数注入
- set方法注入
- 接口注入
4、什么是AOP?
AOP即Aspect Oriented Program,面向切面编程,是面向对象编程(OOP)的一种增强模式,可以将项目中与业务无关的,却为业务模块所共同调用的非核心代码封装成(比如事务管理、日志管理、权限控制等等)一个个切面,然后在运行的时候通过动态代理或者CGLib代理的方式织入到核心业务功能中。
5、Spring自动装配Bean的二种方式
5.1 基于xml的自动装配
Spring容器可以在不使用<constructor-arg>
和<property>
元素的情况下自动装配相互协作的bean之间的关系,助于减少编写一个大的基于Spring的应用程序的XML配置的数量使用元素的autowire属性为一个bean定义指定自动装配模式。
在Spring中,我们有4种方式可以装配Bean的属性。
-
byName
通过byName方式自动装配属性时,是在定义Bean的时候,在property标签中设置autowire属性为byName,那么Spring会自动寻找一个与该属性名称相同或id相同的Bean,注入进来。 -
byType
通过byType方式自动注入属性时,是在定义Bean的时候,在property标签中设置autowire属性为byType,那么Spring会自动寻找一个与该属性类型相同的Bean,注入进来。 -
constructor
通过构造器自动注入。在定义Bean时,在bean标签中,设置autowire属性为constructor,那么,Spring会寻找与该Bean的构造函数各个参数类型相匹配的Bean,通过构造函数注入进来。 -
autodetect
自动装配。如果想进行自动装配,但不知道使用哪种类型的自动装配,那么就可以使用autodetect,让容器自己决定。这是通过在定义Bean时,设置bean标签的autowire属性为autodetect来实现的。设置为autodetect时,Spring容器会首先尝试构造器注入,然后尝试按类型注入。
默认情况下,Spring是不进行自动装配的。我们可以在xml中,设置beans标签的default-autowire属性为byName,byType等,来设置所有bean都进行自动装配。
5.2基于注解的自动装配
使用@Autowired、@Resource
5.3基于java配置装配
。。。
6、Spring常用注解
@Component
作用:把资源让spring来管理,相当于在xml中配置一个bean
属性:value:指定 bean 的 id。如果不指定 value 属性,默认 bean 的 id 是当前类的类名。首字母小写。
@Controller、@Service、@Repository
他们三个注解都是针对一个的衍生注解,他们的作用及属性都是一模一样的。
他们只不过是提供了更加明确的语义化。
@Controller:一般用于表现层的注解。
@Service:一般用于业务层的注解。
@Repository:一般用于持久层的注解。
细节:如果注解中有且只有一个属性要赋值时,且名称是 value,value 在赋值是可以不写。
@Autowired
作用:自动按照类型注入。当使用注解注入属性时,set方法可以省略,它只能注入其他bean类型,当该类型有多个对象进行匹配时,使用要注入的对象变量名称作为bean的id在spring容器中查找,找到了也可以注入成功,找不到就报错。
@Qualifier
作用:在按照自动类型注入的基础上,再按照bean的id注入,它在给字段注入时不能单独使用,必须和@Autowired一起使用,但是在给方法参数注入时,可以单独使用。
属性:value:指定 bean 的 id。
@Qualifier的value默认为"" 所以必须指定value,否则报错,找不到该类型的bean
@Primary
注解@Primary代表首要的,当Spring IoC通过一个接口或者抽象类注入对象的时候,由于存在多个实现类或 者具体类,就会犯糊涂,不知道采用哪个类注入为好。注解@Primary则是告诉Spring IoC容器,请优先使用该类注入。
@Resource
如果指定name,则按bean的id查找,找不到报错;
如果指定type,则按类型查找,找不到报错;
如果指定了name和type,则按两者的交集查找,找不到报错;
如果不指定,则将属性名称作为beanId,先按bean的id查找,找不到则通过类型查找,找不到就报错。
@Value
作用:注入基本数据类型和String类型
属性:value用于指定值
@Scope
作用:指定bean的作用范围
属性:value指定范围的值:singleton、prototype、request、session、global session
@PostConstruct
作用:用于指定初始化方法-----与bean的生命周期有关
@PreDestroy
作用:用于指定销毁方法-----与bean的生命周期有关
@Configuration
作用:用于指定当前类是spring的配置类,当创建容器时,会从该类上加载注解。获取容器时,需要使用AnnotationConfigApplicationContext(有@Configuration注解的类.class)
属性:value:指定配置的id
@ComponentScan
作用:用于指定spring在初始化容器要扫描的包。作用和在 spring 的 xml 配置文件中的:
<context:component-scan base-package="com.itheima"/>是一样的。
属性:basePackages:用于指定要扫描的包。和该注解中的 value 属性作用一样。
@Bean
作用:该注解只能使用在方法上,表示使用此方法创建一个对象,并放入spring容器中。
属性:name:给当前@Bean 注解方法创建的对象指定一个名称(即 bean 的 id)。
如果不指定name属性,则@Bean注解标注的方法名为bean的id
@PropertySource
作用:用于加载.properties文件中的配置,可以把连接数据库的信息写到properties 配置文件中,就可以使用此注解指定 properties 配置文件的位置。
属性:value[]:用于指定 properties 文件位置。如果是在类路径下,需要写上 classpath:
@Import
作用:用于引入其他配置类,在引入其他配置类时,其他配置类可以不用再写@Configuration 注解。当然,写上也没问题。
属性:value[]:用于指定其他配置类的字节码。
@Required
作用:@Required 注解应用于 bean 属性的 setter 方法,它表明受影响的 bean 属性在配置时必须放在 XML 配置文件中,否则容器就会抛出一个 BeanInitializationException 异常。
@Condition注解的作用
public class TulingCondition implements Condition {
/**
* 当条件满足时,被@Condition注解标记的类才会被spring处理
* @param context
* @param metadata
* @return true:加载 false:不加载
*/
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
//判断容器中是否有tulingAspect的组件
if(context.getBeanFactory().containsBean("tulingAspect")) {
return true;
}
return false;
}
}
public class MainConfig {
@Bean
public TulingAspect tulingAspect() {
return new TulingAspect();
}
//当切 容器中有tulingAspect的组件,那么tulingLog才会被实例化.
@Bean
@Conditional(value = TulingCondition.class)
public TulingLog tulingLog() {
return new TulingLog();
}
}
7、Spring的通知的类型
xml-aop事务通知的执行顺序(从上到下):
- before:前置通知
- after-returning:返回通知(返回通知和异常通知只有一个会被执行)
- after-throwing:异常通知(返回通知和异常通知只有一个会被执行)
- after:最终通知(用于释放资源,无论是否发生异常都会执行)
注解-aop事务通知的执行顺序(从上到下):
- @Before:前置通知
- @After:后置通知
- @AfterReturning:返回通知(返回通知和异常通知只有一个会被执行)
- @AfterThrowing:异常通知
采用注解的方式,after-returning和after的职责发生了对调。
aop通知的执行顺序:
https://blog.csdn.net/Scoful/article/details/110405493
AOP中连接点、切点、切面的详解:
https://www.cnblogs.com/sandea/p/11175834.html
8、@Transaction失效情况
- Spring AOP技术使用的是动态代理,意味着,静态(static)方法和非public方法,注解@Transactional是失效。
- 自调用。
由于@Transactional的实现原理是AOP,而AOP的实现原理是动态代理,而自己调用自己的过程,并不存在代理对象的调用,这样就不会产生AOP去为我们设置@Transactional配置的参数,这样就出现了自调用注解失效的问题。 - 类没有被spring托管
- 异常被捕获,未抛出
- 没有配置spring的事务管理器
- 事务的传播行为不支持事务运行
- 数据库本身不支持事务
- rollbackFor回滚异常设置不正确
public可重写可被代理(在实现类中重写是强制的)
private和static不能够被重写也不能够被代理
default可重写可代理(在实现类中不强制必须重写,不过不论重写不重写都会主动被代理)
9、Spring事务传播行为
https://blog.csdn.net/Linging_24/article/details/121726188?spm=1001.2014.3001.5501
10、Spring ioc原理:
主要步骤:
- 准备spring的上下文环境,applicationContext
- 解析配置文件,扫描所有注解,生成BeanDefinition
- 对bean进行实例化和初始化
- 把bean放到ioc容器中
详细解析:
- 初始化IOC容器 DefaultListableBeanFactory
- 读取xml配置文件
- 解析配置文件内容注册为BeanDefinition
- 执行BeanFactoryPostProcessor实现类(扩展点)
- 实例化(反射)(根据BeanDefinition实例化Bean)
- 初始化(不是java对象的初始化,是对spring参数的初始化)
6.1 属性填充
6.2 调用Aware结尾接口的方法
6.3 执行postProcessBeforeInitialization方法(扩展点,前置处理,BeanPostProcessor)
6.4 执行@PostConstruct初始化
6.5 检查是否实现InitializingBean接口,以此来调用afterPropertiesSet方法
6.6 执行init-method指定的方法
6.7 执行postProcessAfterInitialization方法(扩展点,后置处理,BeanPostProcessor),aop代理对象的创建在这一步生成 - 完整对象,可以使用
- 关闭容器前
- 执行@PreDestory
- 检查是否实现DisposableBean接口
- 调用destory-method指定的方法
- 容器关闭
步骤4:执行BeanFactoryPostProcessor实现类(扩展点),后置处理器,可以在bean实例化之前对BeanDefinition进行修改,读取BeanDefinition并修改它。例如:可以进行元数据的配置,对xml文件中配置的数据源中的${jdbc.driverName}等进行值的替换。
步骤5:使用反射实例化对象
Constructor ctor = clazz.getDeclareConstructor();
Object obj = ctor.newInstance();
5.1 对于BeanFactory容器,当客户向容器请求一个尚未初始化的bean时,或初始化bean的时候需要注入另一个尚未初始化的依赖时,容器就会调用createBean进行实例化。
5.2 对于ApplicationContext容器,当容器启动结束后,便实例化所有的bean。 容器通过获取BeanDefinition对象中的信息进行实例化。并且这一步仅仅是简单的实例化,并未进行依赖注入。 实例化对象被包装在BeanWrapper对象中,BeanWrapper提供了设置对象属性的接口,从而避免了使用反射机制设置属性。
步骤6:初始化
6.1 属性填充:实例化后的对象被封装在BeanWrapper对象中,并且此时对象仍然是一个原生的状态,并没有进行依赖注入。紧接着,Spring根据BeanDefinition中的信息进行依赖注入。并且通过BeanWrapper提供的设置属性的接口完成依赖注入。
6.2 调用Aware结尾接口的方法:实现以Aware结尾的接口的Bean在实例化之后,可以取得一些相应的资源。例如:1.实现BeanFactoryAware接口的Bean可以在实例化之后取得BeanFactory对象;2.实现ApplicationContextAware接口的Bean在实例化之后可以取得ApplicationContext对象
6.3 和 6.7 所提供的扩展点,它们是BeanPostProcessor接口的两个方法,此时的Bean对象已经被正确的构造,如果此时想要对Bean对象进行一些自定义的处理,就可以实现BeanPostProcessor接口。(处理Bean)
- postProcessBeforeInitialzation( Object bean, String beanName )
当前正在初始化的bean对象会被传递进来,我们就可以对这个bean作任何处理。
这个函数会先于InitialzationBean执行,因此称为前置处理。
所有Aware接口的注入就是在这一步完成的。 - postProcessAfterInitialzation( Object bean, String beanName )
当前正在初始化的bean对象会被传递进来,我们就可以对这个bean作任何处理。
这个函数会在InitialzationBean完成后执行,因此称为后置处理。
实现BeanPostProcessor接口可以实现AOP功能。
6.5 检查是否实现InitializingBean接口,以此来调用afterPropertiesSet方法
bean创建完成,所有属性注入完成后执行
当BeanPostProcessor的前置处理完成后就会进入本阶段。
InitializingBean接口只有一个函数:
- afterPropertiesSet()
这一阶段也可以在bean正式构造完成前增加我们自定义的逻辑,但它与前置处理不同,由于该函数并不会把当前bean对象传进来,因此在这一步没办法处理对象本身,只能增加一些额外的逻辑。
若要使用它,我们需要让bean实现该接口,并把要增加的逻辑写在该函数中。然后Spring会在前置处理完成后检测当前bean是否实现了该接口,并执行afterPropertiesSet函数。
当然,Spring为了降低对客户代码的侵入性,给bean的配置提供了init-method属性,该属性指定了在这一阶段需要执行的函数名。Spring便会在初始化阶段执行我们设置的函数。init-method本质上仍然使用了InitializingBean接口。
步骤8和步骤9和步骤10:
和init-method一样,通过给destroy-method指定函数,就可以在bean销毁前执行指定的逻辑。
spring的Bean生命周期
上述的步骤5,6,7,8,9,10,11就是Bean的生命周期
https://www.cnblogs.com/javazhiyin/p/10905294.html
11、Spring循环依赖
DefaultSingletonBeanRegistry类:
解决循环依赖所需要的三级缓存
// 一级缓存,保存完成的对象 Map<beanName,完成对象(代理对象)>
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** Cache of early singleton objects: bean name to bean instance. */
// 二级缓存,保存半成品对象 Map<BeanName,半成品对象> 临时
//这个半成品对象就是实例化的,但是没有初始化的bean
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
/** Cache of singleton factories: bean name to ObjectFactory. */
// 三级缓存 Map<BeanName, lambda> 临时
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
bean的创建过程及循环依赖的解决:
1.遍历实例化对象
2.getBean()
3.doGetBean()
4.从容器中获取对象getSingleton(beanName)
4.1 从一级缓存中获取对象
4.2 从二级缓存中获取对象
4.3 如果提前暴露对象,从三级缓存中获取lambda表达式,获取提前暴露的对象,添加到二级缓存中
4.4 获取到对象则return;
5.容器中不存在对象,则getSington(beanName,()->{createBean()})
5.1 从一级缓存中获取对象,存在则return;
5.2 不存在则调用getObject(),即createBean()->doCreateBean()创建对象
5.3 并加入一级缓存,移除二、三级缓存(结束)
5.4 return 对象;
6.doCreateBean()
6.1 实例化对象,clazz.newInstant()
6.2 提前暴露对象,三级缓存保存()->{getEarlyBeanReference(对象)}
6.3 移除二级缓存
6.4 属性填充populateBean()
7.populateBean()属性填充
7.1 遍历属性列表
7.2 根据bean名,从容器中获取对象getBean(),步骤2
7.3 获取到对象,则进行属性设置
思考:
0.依赖注入的属性对象是代理对象:
如果不存在循环依赖,则代理对象在beanPostProcessor的后置处理方法中生成
如果存在循环依赖,则代理对象会在三级缓存的ObjectFactory中提前生成,并注入
所以三级缓存的作用是解决在循环依赖下,属性是代理对象的情况。
1.如果没有aop,解决循环依赖,只需要二级缓存即可
2.如果有aop,需要三级缓存,三级缓存解决aop时的Bean的循环依赖问题,我们的Bean是单例,通过BeanName获取的对象只能有一个,aop会生成代理对象,这时就存在代理对象和目标对象两个对象,所以就要用代理对象替代目标对象,这个过程在三级缓存的lambda表达式调用的方法中实现。
1、干掉原来的三级缓存
分析:一级缓存存放已经完成的bean,二级缓存存放已经实例化,但是还没属性注入的bean,这样会导致什么结果呢?无法处理具有AOP性质的循环依赖。
原因:我们从上面的代码中能看到,bean的AOP增强发生的初始化的after处理中和原本三级缓存获取提前引用的地方,如果我们把原来的三级缓存干掉,那么注入的就是原始的bean,无法注入有AOP增强的bean。
2、干掉原来的二级缓存
分析:上面说了必须满足可以实现有AOP增强的Bean的循环依赖注入,那么干脆就留下原来的一级缓存和三级缓存,那这样会有什么后果呢?无法处理多级依赖,举个栗子:A=>B B=>(A,C) C=>A
原因:在B中注入A的时候会调用getObject方法返回代理对象,在C中注入A的时候又会调用getObject方法返回代理对象,这样导致B和C中得到的A的注入不一致,这样显然是不行的,在中间再加一层缓存就可以轻松实现。
如果移除二级缓存,每次从三级缓存中获取,如果对象存在代理的话,会导致对象被重复代理多次。
12、spring中接口的概念
-
BeanDefinition:用于描述Spring Bean的配置信息。
-
BeanDefinitionRegistry:是BeanDefinition容器,所有的Bean配置解析后生成的BeanDefinition对象都会注册到BeanDefinitionRegistry对象中。
Spring提供了扩展机制,允许用户在Spring框架启动时,动态地往BeanDefinitionRegistry容器中注册BeanDefinition对象。 -
BeanFactory:是Spring的Bean工厂,负责Bean的创建及属性注入。它同时是一个Bean容器,Spring框架启动后,会根据BeanDefinition对象创建Bean实例,所有的单例Bean都会注册到BeanFactory容器中。
-
BeanFactoryPostProcessor:是Spring提供的扩展机制,用于在所有的Bean配置信息解析完成后修改Bean工厂信息。postProcessBeanFactory
-
ImportBeanDefinitionRegistrar:是一个接口,该接口的实现类作用于Spring解析Bean的配置阶段,
当解析@Configuration注解时,可以通过ImportBeanDefinitionRegistrar接口的实现类向BeanDefinitionRegistry容器中添加额外的BeanDefinition对象。 -
BeanPostProcessor:Bean的后置处理器,在Bean初始化方法(init-method属性指定的方法或afterPropertiesSet()方法)调用前后,会执行BeanPostProcessor中定义的拦截逻辑。
-
ClassPathBeanDefinitionScanner:是BeanDefinition扫描器,能够对指定包下的Class进行扫描,将Class信息转换为BeanDefinition对象注册到BeanDefinitionRegistry容器中。
-
FactoryBean:是Spring中的工厂Bean,通常用于处理Spring中配置较为复杂或者由动态代理生成的Bean实例。实现了该接口的Bean不能作为普通的Bean使用,而是作为单个对象的工厂。
当我们通过Bean名称获取FactoryBean实例时,获取到的并不是FactoryBean对象本身,而是FactoryBean对象的getObject()方法返回的实例。提供自定义bean实例化的逻辑。 -
InitializingBean:实现此接口的Bean会在初始化时调用其afterPropertiesSet方法来进行bean的逻辑初始化。
-
ApplicationContextAware:Spring在创建该Bean时会调用setapplicationContext方法注入上下文
-
BeanNameAware:创建是调用setBeanName方法
-
DisposableBean:DisposableBean接口和InitializingBean接口一样,为bean提供了释放资源方法的方式,它只包括destroy方法,凡是继承该接口的类,在bean被销毁之前都会执行destory()方法。
-
BeanDefinitionRegisterPostProcessor:
这个接口继承了BeanFactoryPostProcessor. 从名字上来看, 这个接口是BeanDefinitionRegistry的后处理器,我们先介绍下BeanDefinitionRegistry. BeanDefinitionRegistry是用来注册BeanDefinition的. BeanDefinition就是Bean的配置元数据或Bean的描述信息, 比如Bean的属性值, 构造方法的参数值等. 上面的BeanFactory的BeanDefinition也是由它注册的. BeanDefinitionRegistryPostProcessor是BeanFactoryPostProcessor的扩展, 允许在BeanFactoryPostProcessor被调用之前对BeanDefinition做一些操作, 尤其是它可以注册BeanFactoryPostProcessor的BeanDefinition. 它提供了一个方法postProcessBeanDefinitionRegistry(), 这个方法被调用的时候, 所有的BeanDefinition已经被加载了, 但是所有的Bean还没被创建. 注意: 所有的Bean生成都有个顺序: 定义 --> 创建 --> 初始化. BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry和postProcessBeanFactory方法在Bean被定义但还没被创建的时候执行.
-
执行顺序:加载顺序为PriorityOrdered -> Ordered -> 都没实现
-
MergedBeanDefinitionPostProcessor
-
ApplicationListener
ApplicationListener可以监听某个事件event
通过实现这个接口,传入一个泛型事件,在run方法中就可以监听这个事件,从而做出一定的逻辑
比如在等所有bean加载完之后执行某些操作
public class SystemListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
if (event.getApplicationContext().getParent() == null) {
System.out.println("do something");
}
}
}
Spring内置事件
1、ContextRefreshedEvent
ApplicationContext 被初始化或刷新时,该事件被发布。这也可以在 ConfigurableApplicationContext接口中使用 refresh() 方法来发生。此处的初始化是指:所有的Bean被成功装载,后处理Bean被检测并激活,所有Singleton Bean 被预实例化,ApplicationContext容器已就绪可用
2、ContextStartedEvent
当使用 ConfigurableApplicationContext (ApplicationContext子接口)接口中的 start() 方法启动 ApplicationContext 时,该事件被发布。你可以调查你的数据库,或者你可以在接受到这个事件后重启任何停止的应用程序
3、ContextStoppedEvent
当使用 ConfigurableApplicationContext 接口中的 stop() 停止 ApplicationContext 时,发布这个事件。你可以在接受到这个事件后做必要的清理的工作
4、ContextClosedEvent
当使用 ConfigurableApplicationContext 接口中的 close() 方法关闭 ApplicationContext 时,该事件被发布。一个已关闭的上下文到达生命周期末端;它不能被刷新或重启
5、RequestHandledEvent
这是一个 web-specific 事件,告诉所有 bean HTTP 请求已经被服务。只能应用于使用DispatcherServlet的Web应用。在使用Spring作为前端的MVC控制器时,当Spring处理用户请求结束后,系统会自动触发该事件
6、自定义监听器,继承ApplicationEvent
13、spring加载类到ioc容器中,有哪些方式?
- @Import(xxx.class)
- @Import(MyImportSelector.class)
- @Import({MyImportBeanDefinitionRegistrar.class})
- @Bean导入
- @ComponentScan 注意:使用时扫描其他包路径时,当前包扫描路径也得加上
- 实现Factory接口
- 在resource下创建META-INF/spring.factories,其中编写org.springframework.boot.autoconfigure.EnableAutoConfiguration=xxx
14、spring中使用了哪些设计模式
- 工厂模式:Spring使用工厂模式通过BeanFactory、ApplicationContext创建bean对象。
- 代理模式:Spring Aop功能
- 单例模式:Spring中的Bean默认是单例
- 模板方法:Spring中jdbcTemplate、hibernateTemplate等以Template结尾的对象数据库操作的类,它们就使用到了模板方法
- 策略模式:项目连接多个数据库,根据不同需要访问不同数据库。
- 观察者模式:Spring事件驱动模型就是典型的观察者模式的应用
- 适配器模式:Spring Aop的增强或通知(Advice)使用到了适配器模式,SpringMVC中也使用了适配器模式适配Controller。
15、spring对循环依赖的处理有三种情况:
- 构造器的循环依赖:这种依赖spring是处理不了的,直接排除BeanCurrentlyInCreationException异常。
- 单例模式下的setter循环依赖:通过三级缓存处理循环依赖
- 非单例循环依赖:无法处理