依赖倒置原则: 依赖倒置的核心是面向接口编程
- 定义:高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。
- 问题由来:类A直接依赖类B,假如要将类A改为依赖类C,则必须通过修改类A的代码来达成。这种场景下,类A一般是高层模块,负责复杂的业务逻辑;类B和类C是低层模块,负责基本的原子操作;假如修改类A,会给程序带来不必要的风险。
- 解决方案:将类A修改为依赖接口I,类B和类C各自实现接口I,类A通过接口I间接与类B或者类C发生联系,则会大大降低修改类A的几率。
控制反转(Inversion of Control):
是依赖倒置原则的一种代码设计的思路。具体采用的方法就是所谓的依赖注入(Dependency Injection)。
IoC Contain:
- 避免写大量的new来创建对象,将对象间的依赖关系,配置化。
- 创建实例的时候不需要了解具体的细节。
Spring Bean生产总体流程:
Spring 是一个大的 Bean 工厂,创建Bean时要考虑如下问题:
- Bean定义。根据配置文件或者注解加载Bean定义到BeanDefinitionMap。
- 作用域。单例作用域或者原型作用域,单例的话需要全局实例化一次,原型每次创建都需要重新实例化。
- 依赖关系。一个 Bean 如果有依赖,我们需要初始化依赖,然后进行关联。如果多个 Bean 之间存在着循环依赖,A 依赖 B,B 依赖 C,C 又依赖 A,需要解这种循环依赖问题。
Bean 定义:
-
基于XML
<bean id="userService" class="com.javhl.***.UserService" init-method="init" destory-method="destory"/>
-
基于注解
@Component:当对组件的层次难以定位的时候使用这个注解 @Controller:表示控制层的组件 @Service:表示业务逻辑层的组件 @Repository:表示数据访问层的组件
-
基于JavaConfig
@Configuration public class JavaConfigBeanTest { @Bean public BeanTest beanTest(){ BeanTest beanTest = new BeanTest(); beanTest.setTestField("I am a beanTest"); return beanTest; } }
作用域:
循环依赖问题:
-
循环依赖根据注入方式分成两种类型:
- 构造器循环依赖。依赖的对象是通过构造器传入的,发生在实例化 Bean 的时候。
- 设值循环依赖。依赖的对象是通过 setter 方法传入的,对象已经实例化,发生属性填充和依赖注入的时候。
- 如果是构造器循环依赖,本质上是无法解决的。比如我们调用 A 的构造器,发现依赖 B,于是去调用 B 的构造器进行实例化,发现又依赖 C,于是调用 C 的构造器去初始化,结果依赖 A,整个形成一个死结, 导致 A 无法创建。如:A { public A(B b){}};B{public B(A a){}},A,B实例化时形成构造器循环依赖
- 如果是设值循环依赖,Spring框架只支持单例下的设值循环依赖。Spring 通过对还在创建过程中的单例,缓存并提前暴露该单例,使得其他实例可以引用该依赖。
-
原型模式和单例构造器循环依赖问题:
- 加载 A,记录 singletonsCurrentlyInCreation = [a],构造依赖 B,开始加载 B。
- 加载 B,记录 singletonsCurrentlyInCreation = [a, b],构造依赖 C,开始加载 C。
- 加载 C,记录 singletonsCurrentlyInCreation = [a, b, c],构造依赖 A,又开始加载 A
- 加载 A,检测到循环依赖,直接抛出异常结束操作
-
单例setter循环依赖解决方法:
protected Object getSingleton(String beanName, boolean allowEarlyReference) { // 查询缓存中是否有创建好的单例 Object singletonObject = this.singletonObjects.get(beanName); // 如果缓存不存在,判断是否正在创建中 if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { // 加锁防止并发 synchronized (this.singletonObjects) { // 从earlySingletonObjects中查询是否有early缓存 singletonObject = this.earlySingletonObjects.get(beanName); // early缓存也不存在,且允许early引用 if (singletonObject == null && allowEarlyReference) { // 从单例工厂Map里查询beanName ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { // singletonFactory存在,则调用getObject方法拿到单例对象 singletonObject = singletonFactory.getObject(); // 将单例对象添加到early缓存中 this.earlySingletonObjects.put(beanName, singletonObject); // 移除单例工厂中对应的singletonFactory this.singletonFactories.remove(beanName); } } } } return (singletonObject != NULL_OBJECT ? singletonObject : null); }
- 采用三级缓存
- singletonObjects(一级缓存),单例缓存,存储已经实例化完成的单例,成品的Bean。
- earlySingletonObjects(二级缓存),提前暴露的单例缓存,这时候的单例刚刚创建完,后期还会注入依赖,存放半成品的Bean。
- singletonFactories(三级缓存),生产单例的工厂的缓存,存的是Bean工厂对象,用来生成半成品的Bean并放入到二级缓存中。用以解决循环依赖。如果Bean存在AOP的话,返回的是AOP的代理对象。为了解决二级缓存中 AOP 生成新对象的问题,Spring 中的解决方案就是提前 AOP。
- 从流程图上看,实际上注入 C的 A 实例,还在填充属性阶段,并没有完全地初始化。等递归回溯回去,A 顺利拿到依赖 B,才会真实地完成 A 的加载。
BeanFactory & FactoryBean:
- BeanFactory: 以Factory结尾,表示它是一个工厂类(接口), 它负责生产和管理bean的一个工厂。在Spring中,BeanFactory是IOC容器的核心接口,它的职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。BeanFactory只是个接口,并不是IOC容器的具体实现,但是Spring容器给出了很多种实现,如DefaultListableBeanFactory、XmlBeanFactory、ApplicationContext等,其中XmlBeanFactory就是常用的一个,该实现将以XML方式描述组成应用的对象及对象间的依赖关系。
- FactoryBean: 一般情况下,Spring通过反射机制利用的class属性指定实现类实例化Bean,在某些情况下,实例化Bean过程比较复杂,如果按照传统的方式,则需要在中提供大量的配置信息。配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。Spring为此提供了一个org.springframework.bean.factory.FactoryBean的工厂类接口,用户可以通过实现该接口定制实例化Bean的逻辑。FactoryBean接口对于Spring框架来说占用重要的地位,Spring自身就提供了70多个FactoryBean的实现。它们隐藏了实例化一些复杂Bean的细节,给上层应用带来了便利。从Spring3.0开始,FactoryBean开始支持泛型,即接口声明改为FactoryBean的形式
- 以Bean结尾,表示它是一个Bean,不同于普通Bean的是:它是实现了FactoryBean接口的Bean,根据该Bean的ID从BeanFactory中获取的实际上是FactoryBean的getObject()返回的对象,而不是FactoryBean本身,如果要获取FactoryBean对象,请在id前面加一个&符号来获取。
public interface BeanFactory {
String FACTORY_BEAN_PREFIX = "&";
Object getBean(String var1) throws BeansException;
<T> T getBean(String var1, Class<T> var2) throws BeansException;
Object getBean(String var1, Object... var2) throws BeansException;
<T> T getBean(Class<T> var1) throws BeansException;
<T> T getBean(Class<T> var1, Object... var2) throws BeansException;
boolean containsBean(String var1);
boolean isSingleton(String var1) throws NoSuchBeanDefinitionException;
boolean isPrototype(String var1) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String var1, ResolvableType var2) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String var1, Class<?> var2) throws NoSuchBeanDefinitionException;
Class<?> getType(String var1) throws NoSuchBeanDefinitionException;
String[] getAliases(String var1);
}
public interface FactoryBean<T> {
T getObject() throws Exception;
Class<?> getObjectType();
boolean isSingleton();
}
//FactoryBean实例
@Component public class FactoryBeanTest implements FactoryBean{
private String type=”a”;
@Override
public Object getObject() throws Exception {
if("a".equals(type)){
return new A();
}else{
return new B();
}
}
@Override public Class getObjectType() {
if("a".equals(type)){
return A.class;
}else{
return B.class;
}
}
@Override public boolean isSingleton() {
return true;
}
}
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("testBeans.xml");
String[] names = applicationContext.getBeanDefinitionNames();
Arrays.stream(names).forEach(e->System.out.println(e));
//此处获取的是类A的实例
Object realBean = applicationContext.getBean("factoryBeanTest");
System.out.println(realBean.getClass().getName());
//此处获取的是FactoryBeanTest的实例
FactoryBeanTest factoryBeanTest = (FactoryBeanTest)
applicationContext.getBean("&factoryBeanTest");
((ClassPathXmlApplicationContext) applicationContext).close();