依赖倒置原则: 依赖倒置的核心是面向接口编程
1.定义:高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。
2.问题由来:类A直接依赖类B,假如要将类A改为依赖类C,则必须通过修改类A的代码来达成。这种场景下,类A一般是高层模块,负责复杂的业务逻辑;类B和类C是低层模块,负责基本的原子操作;假如修改类A,会给程序带来不必要的风险。
3.解决方案:将类A修改为依赖接口I,类B和类C各自实现接口I,类A通过接口I间接与类B或者类C发生联系,则会大大降低修改类A的几率。
控制反转(Inversion of Control):
是依赖倒置原则的一种代码设计的思路。具体采用的方法就是所谓的依赖注入(Dependency Injection)。
IoC Contain:
- 避免写大量的new来创建对象,将对象间的依赖关系,配置化。
- 创建实例的时候不需要了解具体的细节。
Spring Bean生产总体流程:
Spring 是一个大的 Bean 工厂,创建Bean时要考虑如下问题:
1.Bean定义。根据配置文件或者注解加载Bean定义到BeanDefinitionMap。
2.作用域。单例作用域或者原型作用域,单例的话需要全局实例化一次,原型每次创建都需要重新实例化。
3.依赖关系。一个 Bean 如果有依赖,我们需要初始化依赖,然后进行关联。如果多个 Bean 之间存在着循环依赖,A 依赖 B,B 依赖 C,C 又依赖 A,需要解这种循环依赖问题。
Bean 定义:
- 基于XML
<bean id="userService" class="com.javhl.***.UserService" init-method="init" destory-method="destory"/>
2.基于注解
@Component:当对组件的层次难以定位的时候使用这个注解 @Controller:表示控制层的组件 @Service:表示业务逻辑层的组件 @Repository:表示数据访问层的组件
3.基于JavaConfig
@Configuration public class JavaConfigBeanTest { @Bean public BeanTest beanTest(){ BeanTest beanTest = new BeanTest(); beanTest.setTestField("I am a beanTest"); return beanTest; } }
作用域:
循环依赖问题:
1.循环依赖根据注入方式分成两种类型:
1.构造器循环依赖。依赖的对象是通过构造器传入的,发生在实例化 Bean 的时候。
2.设值循环依赖。依赖的对象是通过 setter 方法传入的,对象已经实例化,发生属性填充和依赖注入的时候。
3. 如果是构造器循环依赖,本质上是无法解决的。比如我们调用 A 的构造器,发现依赖 B,于是去调用 B 的构造器进行实例化,发现又依赖 C,于是调用 C 的构造器去初始化,结果依赖 A,整个形成一个死结, 导致 A 无法创建。如:A { public A(B b){}};B{public B(A a){}},A,B实例化时形成构造器循环依赖
4.如果是设值循环依赖,Spring框架只支持单例下的设值循环依赖。Spring 通过对还在创建过程中的单例,缓存并提前暴露该单例,使得其他实例可以引用该依赖。
2.原型模式和单例构造器循环依赖问题:
1.加载 A,记录 singletonsCurrentlyInCreation = [a],构造依赖 B,开始加载 B。
2.加载 B,记录 singletonsCurrentlyInCreation = [a, b],构造依赖 C,开始加载 C。
3.加载 C,记录 singletonsCurrentlyInCreation = [a, b, c],构造依赖 A,又开始加载 A
4.加载 A,检测到循环依赖,直接抛出异常结束操作
3.单例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); }
1.采用三级缓存
2.singletonObjects(一级缓存),单例缓存,存储已经实例化完成的单例,成品的Bean。
3.earlySingletonObjects(二级缓存),提前暴露的单例缓存,这时候的单例刚刚创建完,后期还会注入依赖,存放半成品的Bean。
4.singletonFactories(三级缓存),生产单例的工厂的缓存,存的是Bean工厂对象,用来生成半成品的Bean并放入到二级缓存中。用以解决循环依赖。如果Bean存在AOP的话,返回的是AOP的代理对象。为了解决二级缓存中 AOP 生成新对象的问题,Spring 中的解决方案就是提前 AOP。
5.从流程图上看,实际上注入 C的 A 实例,还在填充属性阶段,并没有完全地初始化。等递归回溯回去,A 顺利拿到依赖 B,才会真实地完成 A 的加载。
BeanFactory & FactoryBean:
1.BeanFactory: 以Factory结尾,表示它是一个工厂类(接口), 它负责生产和管理bean的一个工厂。在Spring中,BeanFactory是IOC容器的核心接口,它的职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。BeanFactory只是个接口,并不是IOC容器的具体实现,但是Spring容器给出了很多种实现,如DefaultListableBeanFactory、XmlBeanFactory、ApplicationContext等,其中XmlBeanFactory就是常用的一个,该实现将以XML方式描述组成应用的对象及对象间的依赖关系。
2.FactoryBean: 一般情况下,Spring通过反射机制利用的class属性指定实现类实例化Bean,在某些情况下,实例化Bean过程比较复杂,如果按照传统的方式,则需要在中提供大量的配置信息。配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。Spring为此提供了一个org.springframework.bean.factory.FactoryBean的工厂类接口,用户可以通过实现该接口定制实例化Bean的逻辑。FactoryBean接口对于Spring框架来说占用重要的地位,Spring自身就提供了70多个FactoryBean的实现。它们隐藏了实例化一些复杂Bean的细节,给上层应用带来了便利。从Spring3.0开始,FactoryBean开始支持泛型,即接口声明改为FactoryBean的形式
3.以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();