Spring 复习

1.Spring IoC

1.1 基本概念

1.1.1 DIP(Dependency Inversion Principle)

字面意思依赖反转原则,即调用某个类的构造器创建对象时,其构造器的参数不应包含相关基层类的属性,只应传入相关基层类实例,也即将一个封装好的实例传给本类,也就是让相关基层类依赖更高层的类(不是指继承关系)

1.1.2 IoC(Inversion of Control)

字面意思为控制反转,其是DIP的一种具体策略,其目的是为了将对象的初始化和维护交由第三方(如Spring IoC容器)管理,当用户想要获取某个实例时,只需要提供实例的名称即可获取

1.1.3 DI(Dependency Injection)

字面意思为依赖注入,根据DIP,由于构造器传参时都是传入封装好的基层类实例,那么如何将这些实例赋值给(注入)当前类的属性中呢?那么就需要依赖注入,其执行策略如下

  • 检查当前类的属性那些需要注入对象,如有a,b,c
  • 对a,b,c对象从属的类分别执行上一步,也即分析其中是否有需要注入的其他对象
  • 重复类似上一步的过程,直到没有需要注入的对象
  • 开始反向遍历(类似于DFS的回流),按照先new对象,后注入到上一步的原则执行,直到创建出最初要创建的实例

由此可见,DI是一种IoC的实现方法

1.2 Spring IoC使用

两种方式均为先获取IoC容器,之后调用getBean方法传入bean的名称来获取对应的bean

1.2.1 基于XML

创建FileSystemXmlApplicationContext/ClassPathXmlApplicationContext对象,并调用getBean

1.2.2 基于注解

创建ClassPathXmlApplicationContext对象,并调用getBean

1.3 Spring IoC原理

bean的生命周期概览

  • BeanFactory的创建

    • 创建IoC容器(obtainFreshBeanFactory()
  • Bean的创建
    • 加载配置文件中 Spring Bean 的定义,注册为一个个BeanDefinitionsobtainFreshBeanFactory()
    • 设置BeanFactory的类加载器,添加一些BeanPostProcessor(prepareBeanFactory(beanFactory)
    • 如果有任何BeanFactoryPostProcessor的实现类,则执行postProcessBeanFactoryinvokeBeanFactoryPostProcessors(beanFactory)
    • 注册一些BeanPostProcessor的实现类(registerBeanPostProcessors(beanFactory)
    • finishBeanFactoryInitialization(beanFactory)中的beanFactory.preInstantiateSingletons()负责实例化(创建+初始化)所有非懒加载的singletons
      • Bean 容器利用 Java Reflection API 创建一个Bean的实例(仅仅是实例化
      • 如果涉及到一些属性值 利用 set()方法设置一些属性值
      • 如果 Bean 实现了 BeanNameAware 接口,调用 setBeanName()方法,传入Bean的名字
      • 如果 Bean 实现了 BeanClassLoaderAware 接口,调用 setBeanClassLoader()方法,传入 ClassLoader对象的实例
      • 与上面的类似,如果实现了其他 *.Aware接口,就调用相应的方法
      • 如果有和加载这个 Bean 的 Spring 容器相关的 BeanPostProcessor 对象,执行postProcessBeforeInitialization() 方法
      • 如果Bean实现了InitializingBean接口,执行afterPropertiesSet()方法
      • 如果 Bean 在配置文件中的定义包含 init-method 属性(@PostConstruct),执行指定的方法
      • 如果有和加载这个 Bean的 Spring 容器相关的 BeanPostProcessor 对象,执行postProcessAfterInitialization() 方法
      • 将创建好的Bean以(beanName,singletonObject)放入IoC容器中,也即DefaultSingletonBeanRegistry的singletonObjects对象(ConcurrentHashMap)中
  • Bean的销毁
    • 如果Bean实现了DestructionAwareBeanPostProcessors接口,执行postProcessBeforeDestruction
    • 如果 Bean 实现了 DisposableBean 接口,执行 destroy() 方法
    • 如果 Bean 在配置文件中的定义包含 destroy-method (@PreDestroy)属性,执行指定的方法

上面步骤借鉴自JavaGuide (gitee.io)

1.3.1 IoC容器分类

其中可见BeanFactory为所有IoC容器的顶级接口,其中给出了许多getBean的重载方法,其中要注意到一个接口

AutowireCapableBeanFactory,这个接口意为实现此类的工厂有自动注入的功能,其常用实现类DefaultListableBeanFactory就实现了此接口,还额外实现了ListableBeanFactory和HierarchicalBeanFactory接口,是功能最全面的BeanFactory

为了让ClassPathXmlApplicationContext等容器也能具有自动依赖注入的功能其父类提供了一个DefaultListableBeanFactory的实例,实际执行时调用的是此对象

1.3.2 IoC容器的创建

1)构造器
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException { super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
2)refresh()
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//创建容器前的一些准备工作,如校验xml文件格式等
prepareRefresh(); // 获取BeanFactory实例(DefaultListableBeanFactory对象),并读取xml文件注册bean的配置信息,以(beanName,BeanDefinition)的形式注册到beanDefinitionMap中
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // 设置 BeanFactory 的类加载器,添加几个 BeanPostProcessor,手动注册几个特殊的 bean
prepareBeanFactory(beanFactory); try {
// 留给子类实现
postProcessBeanFactory(beanFactory); // 如果有BeanFactoryPostProcessor的实现类,则触发其postProcessBeanFactory方法
invokeBeanFactoryPostProcessors(beanFactory); // 如果有BeanPostProcessor的实现类,则注册其进入BeanFactory
registerBeanPostProcessors(beanFactory); // 初始化应用的Message Source
initMessageSource(); // Initialize event multicaster for this context.
initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses.
onRefresh(); // 检查Listeners并注册
registerListeners(); // 新建并初始化所有的非懒加载的Singletons
finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event.
finishRefresh();
} catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
} // Destroy already created singletons to avoid dangling resources.
destroyBeans(); // Reset 'active' flag.
cancelRefresh(ex); // Propagate exception to caller.
throw ex;
} finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}

上面过程中,最为重要的几步如下

  • prepareRefresh();
  • obtainFreshBeanFactory();
  • prepareBeanFactory(beanFactory);
  • postProcessBeanFactory(beanFactory);
  • invokeBeanFactoryPostProcessors(beanFactory);
  • registerBeanPostProcessors(beanFactory);
  • finishBeanFactoryInitialization(beanFactory);

此方法创建了IoC容器,并在这一步完成singleton的实例化

#附

#1. xxxAware原理

有一系列的xxxAware接口,如果一个bean先要注入xxx,那么只需要重写该接口中的setxxx方法即可

为什么重写了就可以呢?

因为在调用refresh()方法时(调用ClassPathXmlApplicationContext构造器时会调用到),其中有如下步骤

  • prepareRefresh()

    创建容器前的一些准备工作,如校验xml文件格式等

  • obtainFreshBeanFactory

    其中会调用AbstractRefreshableApplicationContext#refreshBeanFactory方法创建DefaultListableBeanFactory(其是全能beanFactory)实例,之后调用AbstractRefreshableApplicationContext#getBeanFactory方法获取此实例

  • prepareBeanFactory(beanFactory) --- 关键

    该方法传入刚刚获取的DefaultListableBeanFactory的实例,之后调用了下面这一句

    beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));

    为beanFactory添加了一个BeanPostProcessor,其会在所有bean注册好后,在bean的属性被注入后,在bean初始化前(这里指执行InitializingBeanafterPropertiesSet()方法之前,如果bean实现了该接口的话),会调用ApplicationContextAwareProcessor的postProcessBeforeInitialization方法,其中又调用invokeAwareInterfaces方法,如果bean实现了ApplicationContextAware接口,就会调用其重写的方法将applicationContext注入此bean中

ApplicationContextAware一个常用的场景

当singleton的创建依赖一个prototype时,由于singleton的创建只发生一次,如果采用自动注入的方式,其注入的prototype也只会被创建一次,且每次getBean时,也只会获取创建好的singleton。为了让每次获取singleton时,对应的prototype域可以每次都新建,因而可以让singleton的类实现ApplicationContextAware接口,并重写其setApplicationContext方法,由于prototype在每次getBean时都会新建,因而只需要在singleton类的内部使用注入的ApplicationContext直接getBean即可达到目的

示例如下

@Component
@Scope("singleton")
public class mySingleton implements ApplicationContextAware {
private ApplicationContext ac;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
ac = applicationContext;
}
public void doTheThing(){
getAs().print();
}
public myPrototype getAs(){
return (myPrototype) ac.getBean("myPrototype");
} }
@Component
@Scope("prototype")
public class myPrototype {
public void print(){
System.out.println(this);
}
}
public class client_bean_prototype {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ConfigClass.class);
mySingleton bean = (mySingleton)applicationContext.getBean("mySingleton");
bean.doTheThing(); bean = (mySingleton)applicationContext.getBean("mySingleton");
bean.doTheThing();
}
}

输出结果如下

ioctest1.singletonDependsPrototype.myPrototype@1a052a00
ioctest1.singletonDependsPrototype.myPrototype@4d826d77

可见,两次拿到的bean不同

#2. BeanFactory与FactoryBean

BeanFactory是IoC容器的顶层接口,负责实例化Bean并保存着所有的Bean对象

FactoryBean也是一个接口,其定义如下

public interface FactoryBean<T> {
String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
//返回包装的对象
@Nullable
T getObject() throws Exception; //返回包装的对象的类型
@Nullable
Class<?> getObjectType(); //判断是否为单例
default boolean isSingleton() {
return true;
}
}

对于某些创建过程比较复杂的对象,可以将其复杂重复的创建过程放入getObject()方法中,并将最后创建好的对象由此方法返回

假设有下面例子

  • 自定义FactoryBean

    public class myFactoryBean implements FactoryBean<accountService> {
    @Override
    public accountService getObject() {
    //实现一些复杂的实例化逻辑
    //可以为accountService对象set一些通用的属性
    return new accountService();
    } @Override
    public Class<?> getObjectType() {
    return accountService.class;
    }
    }
  • 配置Bean

    public class ConfigureClass {
    
        @Bean
    public myFactoryBean myFactoryBean() {
    return new myFactoryBean();
    } @Bean
    public accountService accountService() {
    //同样可以为返回的accountService实例修改一些属性
    return myFactoryBean().getObject();
    } }

那么首先会创建名称为"myFactoryBean"的myFactoryBean实例,注意!仅仅是创建这个类的实例而已

之后再创建accountService的实例时,由于其由myFactoryBean负责,在preInstantiateSingletons方法中调用getBean方法时,其在调用createBean方法创建时,会通过反射,再次进入下面这段代码

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException { final String beanName = transformedBeanName(name);
Object bean; // Eagerly check singleton cache for manually registered singletons.
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
if (logger.isTraceEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
}
}
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}

这里传入的name是之前创建的myFactoryBean,那么在getObjectForBeanInstance(sharedInstance, name, beanName, null);方法中,就会调用到myFactoryBeangetObject()方法来创建accountService的实例,从而完成实例化

这里要注意,singletonObjects这个map中,会同时存放myFactoryBean和accountService的beans,也即在使用FactoryBean创建好需要的bean之后,下次使用accountService实例就只需要按beanName直接获取即可。

由此可见,FactoryBean也是一种Bean,但其不同于工厂模式,其负责某一类的全部对象实例化过程中的复杂部分,也即创建每个FactoryBean接口泛型传入的类的对象时,均需要这个FactoryBean来插一手,这样就极大的简化了此类Bean的实例化流程

最新文章

  1. Centos7中安装Mysql及配置
  2. Charles 从入门到精通
  3. C++中string查找和取子串和整形转化
  4. [OpenCV] Samples 05: convexhull
  5. 用python+selenium将腾讯首页今日话题的内容自动发表到自己cnblog里
  6. AC日记——搞笑世界杯 codevs 1060(dp)
  7. 整理:sql语句优化之SQL Server
  8. jQuery autoResize
  9. Vim 命令 【转】
  10. HDU 1232 畅通工程(最小生成树+并查集)
  11. Integer和int
  12. Javaweb-request与response
  13. vue使用$http服务端收不到参数
  14. IE低版本 JSON.parse 和stringify 的兼容 (IE8以下)
  15. mybatis做if 判断 传入值0 建议最好不要使用值0
  16. 翻译:使用红外传感器与Arduino进行简单动作与手势检测
  17. linux shell 脚本攻略学习9--rename命令详解
  18. ORACLE RAC clusterware/GI 启动诊断流程图11.2+
  19. SSO流程
  20. Linux查看GPU信息和使用情况

热门文章

  1. linux 一分钟安装maven linux
  2. PAT甲级—暴力搜索
  3. Codeforces Round #657 (Div. 2) A. Acacius and String(字符串)
  4. 2020牛客暑期多校训练营(第二场)Fake Maxpooling
  5. GCD HDU - 1695 容斥原理(复杂度低的版本)
  6. Codeforces Round #654 (Div. 2) B. Magical Calendar (结论)
  7. Linux 设置简单密码
  8. Selenium和ChromeDriver下载地址
  9. Http和Https之为什么Https更安全
  10. C++ part2