精华内容
下载资源
问答
  • Spring Bean

    千次阅读 2008-11-11 16:16:00
    Spring Bean和普通的javaBean没有什么区别,都是pojo对象,在spring中,bean的生命周期分为1.定义Bean定义JavaBeanpublic class UpperAction{ String message; public String getMessage() {

    Spring Bean和普通的javaBean没有什么区别,都是pojo对象,在spring中,bean的生命周期分为

    1.定义Bean

    定义JavaBean

    1. public   class  UpperAction{
    2.     
    3.     String message;
    4.     
    5.      public  String getMessage() {
    6.          return  message;
    7.     }
    8.      public   void  setMessage(String message) {
    9.          this .message = message;
    10.     }
    11.      public  String execute(String str) {
    12.          // TODO 自动生成方法存根
    13.          return  (getMessage()+ " " +str).toUpperCase();
    14.     }
    15. }

    通过spring bean的定义文件 applicationContent.xml将javaBean定义成spring bean

    1. <? xml   version = "1.0"   encoding = "UTF-8" ?>
    2. < beans   xmlns = "http://www.springframework.org/schema/beans"
    3.      xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
    4.      xsi:schemaLocation = "http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd" >
    5.      < bean   id = "upper"   class = "com.lnie.spring.bean.UpperAction" >
    6.          < property   name = "message" >
    7.              < value > Hello </ value >
    8.          </ property >
    9.      </ bean >
    10. </ beans >

    spring bean 定义文件中的DTD为,

    xmlns="http://www.springframework.org/schema/beans "
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance "
     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd "

    必不能错,当然spring版本不一样可能xsd版本也会不一样

    2.初始化Bean

    一般是通过定义文件中注入初始化

    1. < property   name = "message" >
    2.              < value > Hello </ value >
    3.          </ property >

    3.使用Bean

    定义并初始化之后,可以通过spring上下文或是beanFactory取得bean

    1. //通过ApplicationContext取得bean 
    2. ApplicationContext atx =  new  FileSystemXmlApplicationContext( "applictionContext.xml" );
    3. UpperAction a= (UpperAction)atx.getBean( "upper" );
    4.         
    5. //通过BeanFactory取得bean 
    6. Resource re =  new  ClassPathResource( "applictionContext.xml" );
    7. BeanFactory beans =  new  XmlBeanFactory(re);
    8. UpperAction a = (UpperAction)beans.getBean( "upper" );
    9. System.out.println(a.execute( "bin" ));

    4销毁bean

    一旦基于spring(web)的应用停止,spring框架将会将bean实例销毁

     

    到此为止,整个spring bean的生命周期已完整都走了一遍。

    展开全文
  • 一文读懂 Spring Bean 的生命周期

    千次阅读 多人点赞 2021-07-05 23:02:56
    今天我们来说一说 Spring Bean 的生命周期,小伙伴们应该在面试中经常遇到,这是正常现象。因为 Spring Bean 的生命周期是除了 IoC、AOP 几个核心概念之外最重要概念,大家务必拿下。可 Spring 源代码又比较复杂,...

    欢迎大家关注我的微信公众号【老周聊架构】,Java后端主流技术栈的原理、源码分析、架构以及各种互联网高并发、高性能、高可用的解决方案。

    一、前言

    今天我们来说一说 Spring Bean 的生命周期,小伙伴们应该在面试中经常遇到,这是正常现象。因为 Spring Bean 的生命周期是除了 IoC、AOP 几个核心概念之外最重要概念,大家务必拿下。可 Spring 源代码又比较复杂,跟着跟着就不知道跟到哪里去了,不太好拿下呀。这倒是真的,而且网上一上来就各种贴流程源码,对初学者来说是真的一脸懵逼,就像字都看的懂,但连在一块就不知道意思了,太绕了。

    本文老周试着讲的通俗易懂些,让更多的小伙伴们轻松的读懂 Spring Bean 的生命周期,并有对它有继续研究学习的想法,那我写此文的目的也就达到了。

    我们讲 Spring Bean 的生命周期之前先来了解两个概念:

    1.1 什么是 Bean

    我们来看下 Spring Framework 的官方文档:

    In Spring, the objects that form the backbone of your application and that are managed by the Spring IoC container are called beans. A bean is an object that is instantiated, assembled, and otherwise managed by a Spring IoC container. Otherwise, a bean is simply one of many objects in your application. Beans, and the dependencies among them, are reflected in the configuration metadata used by a container.

    简而言之,bean 是由 Spring IoC 容器实例化、组装和管理的对象。

    1.2 什么是 Spring Bean 的生命周期

    对于普通的 Java 对象,当 new 的时候创建对象,然后该对象就能够使用了。一旦该对象不再被使用,则由 Java 自动进行垃圾回收。

    而 Spring 中的对象是 bean,bean 和普通的 Java 对象没啥大的区别,只不过 Spring 不再自己去 new 对象了,而是由 IoC 容器去帮助我们实例化对象并且管理它,我们需要哪个对象,去问 IoC 容器要即可。IoC 其实就是解决对象之间的耦合问题,Spring Bean 的生命周期完全由容器控制。

    二、Spring Bean 的生命周期

    这里老周必须要提一下,这里我们说的 Spring Bean 的生命周期主要指的是 singleton bean,对于 prototype 的 bean ,Spring 在创建好交给使用者之后则不会再管理后续的生命周期。

    我们也来复习下 Spring 中的 bean 的作用域有哪些?

    • singleton : 唯一 bean 实例,Spring 中的 bean 默认都是单例的。
    • prototype : 每次请求都会创建一个新的 bean 实例。
    • request : 每一次 HTTP 请求都会产生一个新的 bean,该 bean 仅在当前 HTTP request 内有效。
    • session : 每一次 HTTP 请求都会产生一个新的 bean,该 bean 仅在当前 HTTP session 内有效。
    • global-session: 全局 session 作用域,仅仅在基于 Portlet 的 web 应用中才有意义,Spring5 已经没有了。Portlet 是能够生成语义代码(例如:HTML)片段的小型 Java Web 插件。它们基于 portlet 容器,可以像 servlet 一样处理 HTTP 请求。但是,与 servlet 不同,每个 portlet 都有不同的会话。

    我们知道对于普通的 Java 对象来说,它们的生命周期就是:

    • 实例化
    • 该对象不再被使用时通过垃圾回收机制进行回收

    而对于 Spring Bean 的生命周期来说:

    • 实例化 Instantiation
    • 属性赋值 Populate
    • 初始化 Initialization
    • 销毁 Destruction

    实例化 -> 属性赋值 -> 初始化 -> 销毁

    只有四个步骤,这样拆解的话是不是感觉也不难?不像其他人写的那样直接一上来就各种 BeanPostProcessor、BeanFactoryPostProcessor 全部怼进流程里去,别说读者看着头大,自己写的可能短时间内还记得流程,隔个一段时间,你可能都不知道自己写了个啥。

    本来老周想通过 Bean 创建流程入口
    AbstractApplicationContext#refresh() 方法的 finishBeanFactoryInitialization(beanFactory) 处带大家跟一下源码,想了想还是不带入过多的代码进来,直接给到最终的主要逻辑。

    protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
        BeanWrapper instanceWrapper = null;
        if (mbd.isSingleton()) {
            instanceWrapper = (BeanWrapper)this.factoryBeanInstanceCache.remove(beanName);
        }
    
        if (instanceWrapper == null) {
        	// 实例化阶段
            instanceWrapper = this.createBeanInstance(beanName, mbd, args);
        }
    
        ...
    
        Object exposedObject = bean;
    
        try {
        	// 属性赋值阶段
            this.populateBean(beanName, mbd, instanceWrapper);
            // 初始化阶段
            exposedObject = this.initializeBean(beanName, exposedObject, mbd);
        } catch (Throwable var18) {
            ...
        }
    
        ...
    }
    

    至于销毁,是在容器关闭时调用的,详见 ConfigurableApplicationContext#close()

    是不是很清爽了?至于 BeanPostProcessor、BeanFactoryPostProcessor 以及其他的类,在老周看来,只不过是对主流程四个步骤的一系列扩展点而已。

    三、Spring Bean 的生命周期的扩展点

    Spring Bean 的生命周期的扩展点超级多,老周这里不可能全部列出来,只说核心的扩展点。这也就是为什么 Spring 的扩展性很好的原因,开了很多的口子,尽可能让某个功能高内聚松耦合,用户需要哪个功能就用哪个,而不是直接来一个大而全的东西。

    3.1 Bean 自身的方法

    比如构造函数、getter/setter 以及 init-method 和 destory-method 所指定的方法等,也就对应着上文说的实例化 -> 属性赋值 -> 初始化 -> 销毁四个阶段。

    在这里插入图片描述
    3.2 容器级的方法(BeanPostProcessor 一系列接口)

    主要是后处理器方法,比如下图的 InstantiationAwareBeanPostProcessorBeanPostProcessor 接口方法。这些接口的实现类是独立于 Bean 的,并且会注册到 Spring 容器中。在 Spring 容器创建任何 Bean 的时候,这些后处理器都会发生作用。

    在这里插入图片描述
    3.2.1 InstantiationAwareBeanPostProcessor 源码分析

    我们翻一下源码发现 InstantiationAwareBeanPostProcessor 是继承了 BeanPostProcessor

    在这里插入图片描述
    在这里插入图片描述

    • InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation 调用点

    Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName)
    返回值:如果返回的不为null,那么后续的Bean的创建流程【实例化、初始化afterProperties】都不会执行,而是直接使用返回的快捷Bean,此时的正常执行顺序如下:
    InstantiationAwareBeanPostProcessor接口中的postProcessBeforeInstantiation,在实例化之前调用。
    BeanPostProcessor接口中的postProcessAfterInitialization,在实例化之后调用。

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    总之,postProcessBeforeInstantiation 在 doCreateBean 之前调用,也就是在 bean 实例化之前调用的,英文源码注释解释道该方法的返回值会替换原本的 Bean 作为代理,这也是 AOP 等功能实现的关键点。

    • InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation 调用点

    boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException
    正常情况下在实例化之后在执行populateBean之前调用
    返回值:如果有指定的bean的时候返回false,那么后续的属性填充和属性依赖注入【populateBean】将不会执行,同时后续的postProcessPropertyValues将不会执行,但是初始化和BeanPostProcessor的仍然会执行。

    在这里插入图片描述

    public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName)
    实例化之后调用,在方法applyPropertyValues【属性填充】之前
    返回值:如果返回null,那么将不会进行后续的属性填充,比如依赖注入等,如果返回的pvs额外的添加了属性,那么后续会填充到该类对应的属性中。
    pvs:PropertyValues对象,用于封装指定类的对象,简单来说就是PropertyValue的集合,里面相当于以key-value形式存放类的属性和值。
    pds:PropertyDescriptor对象数组,PropertyDescriptor相当于存储类的属性,不过可以调用set,get方法设置和获取对应属性的值。

    在这里插入图片描述
    3.2.2 BeanPostProcessor 源码分析

    org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

    在这里插入图片描述
    进入初始化接口:

    在这里插入图片描述
    我们先来看

    org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization

    在这里插入图片描述

    • 首先获取到所有的后置处理器 getBeanPostProcessors()
    • 在 for 循环中依次调用后置处理器的方法 processor.postProcessBeforeInitialization(result, beanName);
    • 进入 postProcessBeforeInitialization 方法

    org.springframework.context.support.ApplicationContextAwareProcessor#postProcessBeforeInitialization

    在这里插入图片描述

    进入 invokeAwareInterfaces(bean); 方法,当前 bean 实现了 ApplicationContextAware 接口。

    在这里插入图片描述

    • ApplicationContextAwareProcessor#postProcessBeforeInitialization 首先判断此 bean 是不是各种的Aware,如果是它列举的那几个 Aware 就获取 Bean 工厂的权限,可以向容器中导入相关的上下文环境,目的是为了 Bean 实例能够获取到相关的上下文,如果不是它列举的几个 Aware,那就调用 invokeAwareInterfaces(bean),向容器中添加相关接口的上下文环境。

    3.3 工厂后处理器方法(BeanFactoryProcessor 一系列接口)

    包括 AspectJWeavingEnablerCustomAutowireConfigurerConfigurationClassPostProcessor 等。这些都是 Spring 框架中已经实现好的 BeanFactoryPostProcessor,用来实现某些特定的功能。

    我们知道 Spring IoC 容器初始化的关键环节就在 org.springframework.context.support.AbstractApplicationContext#refresh 方法中 ,容器创建的主体流程都在这个方法里面,这个方法是真的重要!!!

    对于工厂后处理器方法老周这里直接带你看 invokeBeanFactoryPostProcessors(beanFactory); 方法,这个方法处理的是 BeanFactoryPostProcessor 接口的 Bean。调用方法如下:

    在这里插入图片描述
    跟到最重要的方法里去,代码虽长,但逻辑中规中矩。

    BeanFactoryPostProcessor:一切处理 BeanFactory 的父接口
    BeanDefinitionRegistryPostProcessor:实现了 BeanFactoryPostProcessor 接口的接口

    在这里插入图片描述
    流程说明:

    • 调用 BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry(registry) 方法。参数 beanFactoryPostProcessors 传入的优先处理掉。然后获取容器注册的,对于这些 Bean 按照 PriorityOrdered 接口、Ordered、没有排序接口的实例分别进行处理。
    • 调用 BeanFactoryPostProcessor#postProcessBeanFactory(beanFactory) 方法。备注:BeanDefinitionRegistryPostProcessor 属于 BeanFactoryPostProcessor 子接口。先处理属于 BeanDefinitionRegistryPostProcessor 接口实例的 postProcessBeanFactory(beanFactory) 方法,然后获取容器注册的。对于这些 Bean 按照 PriorityOrdered 接口、Ordered、没有排序接口的实例分别进行处理。

    3.4 Bean 级生命周期方法

    可以理解为 Bean 类直接实现接口的方法,比如 BeanNameAwareBeanFactoryAwareApplicationContextAwareInitializingBeanDisposableBean 等方法,这些方法只对当前 Bean 生效。

    3.4.1 Aware 类型的接口

    Aware 类型的接口的作用就是让我们能够拿到 Spring 容器中的一些资源。基本都能够见名知意,Aware 之前的名字就是可以拿到什么资源,例如 BeanNameAware 可以拿到 BeanName,以此类推。调用时机需要注意:所有的 Aware 方法都是在初始化阶段之前调用的

    Aware 接口众多,这里同样通过分类的方式帮助大家记忆。Aware 接口具体可以分为两组,至于为什么这么分,详见下面的源码分析。如下排列顺序同样也是 Aware 接口的执行顺序,能够见名知意的接口不再解释。

    Aware Group1

    • BeanNameAware
    • BeanClassLoaderAware
    • BeanFactoryAware

    Aware Group2

    • EnvironmentAware
    • EmbeddedValueResolverAware
      这个知道的人可能不多,实现该接口能够获取 Spring EL 解析器,用户的自定义注解需要支持 SPEL 表达式的时候可以使用,非常方便。
    • ApplicationContextAware(ResourceLoaderAware/ApplicationEventPublisherAware/MessageSourceAware)
      这几个接口可能让人有点懵,实际上这几个接口可以一起记,其返回值实质上都是当前的 ApplicationContext 对象,因为 ApplicationContext 是一个复合接口,如下:
      在这里插入图片描述

    Aware 调用时机源码分析

    在这里插入图片描述
    可以看到并不是所有的 Aware 接口都使用同样的方式调用。Bean××Aware 都是在代码中直接调用的,而 ApplicationContext 相关的 Aware 都是通过 BeanPostProcessor#postProcessBeforeInitialization() 实现的。感兴趣的可以自己看一下 ApplicationContextAwareProcessor 这个类的源码,就是判断当前创建的 Bean 是否实现了相关的 Aware 方法,如果实现了会调用回调方法将资源传递给 Bean。

    BeanPostProcessor 的调用时机也能在这里体现,包围住 invokeInitMethods 方法,也就说明了在初始化阶段的前后执行。

    关于 Aware 接口的执行顺序,其实只需要记住第一组在第二组执行之前就行了。

    3.4.2 生命周期接口

    至于剩下的两个生命周期接口就很简单了,实例化和属性赋值都是 Spring 帮助我们做的,能够自己实现的有初始化和销毁两个生命周期阶段。

    • InitializingBean 对应生命周期的初始化阶段,在上面源码的 invokeInitMethods(beanName, wrappedBean, mbd);方法中调用。
      有一点需要注意,因为 Aware 方法都是执行在初始化方法之前,所以可以在初始化方法中放心大胆的使用 Aware 接口获取的资源,这也是我们自定义扩展 Spring 的常用方式。
      除了实现 InitializingBean 接口之外还能通过注解或者 xml 配置的方式指定初始化方法,至于这几种定义方式的调用顺序其实没有必要记。因为这几个方法对应的都是同一个生命周期,只是实现方式不同,我们一般只采用其中一种方式。
    • DisposableBean 类似于 InitializingBean,对应生命周期的销毁阶段,以ConfigurableApplicationContext#close()方法作为入口,实现是通过循环取所有实现了 DisposableBean 接口的 Bean 然后调用其 destroy() 方法,感兴趣的可以自行跟一下源码。

    3.5 Spring Bean 生命周期流程图

    在这里插入图片描述

    四、常用接口说明

    4.1 BeanNameAware

    该接口只有一个方法 setBeanName(String name),用来获取 bean 的 id 或者 name

    4.2 BeanFactoryAware

    该接口只有一个方法 setBeanFactory(BeanFactory beanFactory),用来获取当前环境中的 BeanFactory

    4.3 ApplicationContextAware

    该接口只有一个方法 setApplicationContext(ApplicationContext applicationContext),用来获取当前环境中的 ApplicationContext

    4.4 InitializingBean

    该接口只有一个方法 afterPropertiesSet(),在属性注入完成后调用

    4.5 DisposableBean

    该接口只有一个方法 destroy(),在容器销毁的时候调用,在用户指定的 destroy-method 之前调用

    4.6 BeanPostProcessor

    该接口有两个方法:

    • postProcessBeforeInitialization(Object bean, String beanName):在初始化之前调用此方法
    • postProcessAfterInitialization(Object bean, String beanName):在初始化之后调用此方法

    通过方法签名我们可以知道,我们可以通过 beanName 来筛选出我们需要进行个性化定制的 bean。

    4.7 InstantiationAwareBeanPostProcessor

    该类是 BeanPostProcessor 的子接口,常用的有如下三个方法:

    • postProcessBeforeInstantiation(Class beanClass, String beanName):在bean实例化之前调用
    • postProcessProperties(PropertyValues pvs, Object bean, String beanName):在bean实例化之后、设置属性前调用
    • postProcessAfterInstantiation(Class beanClass, String beanName):在bean实例化之后调用

    五、代码演示

    思路:创建一个类 UserBean ,让其实现几个特殊的接口,并分别在接口实现的构造器、接口方法中断点,观察线程调用栈,分析出 Bean 对象创建和管理关键点的触发时机。

    5.1 UserBean 类

    @Component
    public class UserBean implements InitializingBean, BeanNameAware, DisposableBean, ApplicationContextAware {
    	private int id;
    
    	private String name;
    
    	public UserBean(int id, String name) {
    		this.id = id;
    		this.name = name;
    		System.out.println("2. 调用构造函数");
    	}
    
    	public int getId() {
    		return id;
    	}
    
    	public void setId(int id) {
    		this.id = id;
    		System.out.println("5. 属性注入 id");
    	}
    
    	public String getName() {
    		return name;
    	}
    
    	public void setName(String name) {
    		this.name = name;
    		System.out.println("5. 属性注入 name");
    	}
    
    	@Override
    	public void setBeanName(String name) {
    		System.out.println("6. 调用 BeanNameAware.setBeanName() 方法");
    	}
    
    	@Override
    	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    		UserBean userBean = (UserBean) applicationContext.getBean("userBean");
    		System.out.println(userBean);
    		System.out.println("7. 调用 BeanNameAware.setBeanName() 方法");
    	}
    
    	@Override
    	public void afterPropertiesSet() throws Exception {
    		System.out.println("9. 调用 InitializingBean.afterPropertiesSet() 方法");
    	}
    
    	public void myInit() {
    		System.out.println("10. 调用 init-method 方法");
    	}
    
    	@Override
    	public void destroy() throws Exception {
    		System.out.println("12. 调用 DisposableBean.destroy() 方法");
    	}
    
    	public void myDestroy() {
    		System.out.println("13. 调用 destroy-method 方法");
    	}
    
    	@Override
    	public String toString() {
    		return "UserBean{" +
    				"id=" + id +
    				", name='" + name + '\'' +
    				'}';
    	}
    }
    

    5.2 InstantiationAwareBeanPostProcessor 接口实现类

    @Component
    public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
    	@Override
    	public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
    		if ("userBean".equals(beanName)) {
    			System.out.println("1. 调用 InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation() 方法");
    		}
    		return null;
    	}
    
    	@Override
    	public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
    		if ("userBean".equals(beanName)) {
    			UserBean userBean = (UserBean) bean;
    			System.out.println("3. 调用 InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation() 方法");
    			System.out.println(userBean);
    		}
    		return true;
    	}
    
    	@Override
    	public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
    		if ("userBean".equals(beanName)) {
    			System.out.println("4. 调用 InstantiationAwareBeanPostProcessor.postProcessProperties() 方法");
    		}
    		return null;
    	}
    }
    

    5.3 BeanPostProcessor 接口实现类

    @Component
    public class MyBeanPostProcessor implements BeanPostProcessor {
    	@Override
    	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    		if ("userBean".equals(beanName)) {
    			System.out.println("8. 调用 BeanPostProcessor.postProcessBeforeInitialization() 方法");
    		}
    		return bean;
    	}
    
    	@Override
    	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    		if ("userBean".equals(beanName)) {
    			System.out.println("11. 调用 BeanPostProcessor.postProcessAfterInitialization() 方法");
    		}
    		return bean;
    	}
    }
    

    5.4 BeanFactoryPostProcessor 接口实现类

    @Component
    public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    	@Override
    	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    		System.out.println("0. 调用 BeanFactoryPostProcessor.postProcessBeanFactory() 方法");
    	}
    }
    

    5.5 applicationContext.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	   xsi:schemaLocation="
    	    http://www.springframework.org/schema/beans
            https://www.springframework.org/schema/beans/spring-beans.xsd
    ">
    
    	<bean class="com.riemann.test.MyInstantiationAwareBeanPostProcessor" />
    
    	<bean id="userBean" class="com.riemann.test.UserBean" init-method="myInit" destroy-method="myDestroy">
    		<!-- 构造函数注入 -->
    		<constructor-arg index="0" type="int">
    			<value>1</value>
    		</constructor-arg>
    		<constructor-arg index="1" type="java.lang.String">
    			<value>微信公众号【老周聊架构】</value>
    		</constructor-arg>
    
    		<!-- setter方法注入 -->
    		<property name="id" value="2"/>
    		<property name="name" value="riemann"/>
    	</bean>
    
    	<bean class="com.riemann.test.MyBeanPostProcessor" />
    
    	<bean class="com.riemann.test.MyBeanFactoryPostProcessor" />
    	
    </beans>
    

    5.6 测试类

    public class BeanLifeCycleTest {
    	public static void main(String[] args) {
    		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
    		UserBean user = (UserBean) applicationContext.getBean("userBean");
    		((AbstractApplicationContext) applicationContext).close();
    	}
    }
    

    5.7 控制台结果打印

    在这里插入图片描述

    展开全文
  • Spring Bean 是什么?

    千次阅读 2020-03-12 17:09:55
    Spring 框架基础核心之一是 Bean 的概念。Spring beanSpring 框架在运行时管理的对象。Spring bean 是任何 Spring 应用程序的基本构建块。如何去定义 Spring Bean ,这种 Bean 和传统的 Java Bean 是有区别的。

    本文内容如有错误、不足之处,欢迎技术爱好者们一同探讨,在本文下面讨论区留言,感谢。

    简述

    Spring 框架基础核心之一是 Bean 的概念。Spring beanSpring 框架在运行时管理的对象。Spring bean 是任何 Spring 应用程序的基本构建块。如何去定义 Spring Bean ,这种 Bean 和传统的 Java Bean 是有区别的。

    Spring Framework 官方文档对 bean 的定义:

    In Spring, the objects that form the backbone of your application and that are managed by the Spring IoC container are called beans. A bean is an object that is instantiated, assembled, and otherwise managed by a Spring IoC container.
    Spring 中,构成应用程序主干并由 Spring IoC 容器管理的对象称为 beanBean 是由 Spring IoC 容器实例化,组装和以其他方式管理的对象。

    Spring Bean 基础

    Spring bean 只是由 Spring 容器管理的实例对象,它们是由框架创建和关联的,并放入 Spring 容器中,后续可以从那里获取它们。

    定义Spring Bean

    BeanDefinitionSpring Framework 中定义 Bean 的配置元信息接口,包含:

    • Bean 的类名 全称包含包名
    • Bean 行为配置元素,如作用域、自动绑定的模式、生命周期回调等
    • 其他 Bean 引用,又可称作合作者(Collaboratiors) 或者依赖(Dependencies)
    • 配置设置,比如 Bean 属性(Properties)
    BeanDefinition 元信息
    属性说明
    ClassBean 全类名,必须是具体类,不能用抽象类或接口
    NameBean 的名称或者 ID
    ScopeBean 的作用域(如:singleton、prototype等)
    Constructor argumentsBean 构造器参数(用于依赖注入)
    PropertiesBean 属性设置(用于依赖注入)
    Autowiring modeBean 自动绑定模式(如:通过名称 byName)
    Lazy initialization modeBean 延迟初始化模式(延迟和非延迟)
    Initialization methodBean 初始化回调方法名称
    Destruction methodBean 销毁回调方法名称
    • BeanDefinition 构建的两种方式
      1. 通过 BeanDefinitonBuilder
      2. 通过 AbstractBeanDefinition 以及派生类

    User POJO

    public class User {
        private Long id;
        private String name;
    
        public void setId(Long id) {
            this.id = id;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }
    

    BeanDefianitonDemo BeanDefiniton 两种构建方式的代码

    public class BeanDefianitonDemo {
    
        public static void main(String[] args) {
            //1. 通过 BeanDefinitionBuilder
            BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(User.class);
            // 通过属性设置
            beanDefinitionBuilder.addPropertyValue("id", 1);
            beanDefinitionBuilder.addPropertyValue("name", "小明");
            BeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
    
            // 2. 通过 AbstractBeanDefinition 以及派生类
            GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
            // 设置 Bean 类型
            genericBeanDefinition.setBeanClass(User.class);
            // 通过 MutablePropertyValues 批量操作属性
            MutablePropertyValues propertyValues = new MutablePropertyValues();
            propertyValues.add("id", 1).add("name", "小红");
            genericBeanDefinition.setPropertyValues(propertyValues);
            
        }
    }
    
    命名 Spring Bean

    每个 Bean 拥有一个或多个标识符(identifiers),这些标识符在 Bean 所在的容器必须是唯一的。通常,一个 Bean 仅有一个标识符,如果需要额外的,可考虑使用别名(Alias)来扩充。

    通常 Bean 的 标识符由字母组成,允许出现特殊字符。如果要想引入 Bean 的别名的话,可在 name 属性使用半角逗号(“,”)或分号(“;”) 来间隔。

    Beanidname 属性并非必须制定,如果留空的话,容器会为 Bean 自动生成一个唯一的名称。Bean 的命名尽管没有限制,官方建议采用驼峰的方式,更符合 Java 的命名约定。

    Bean 名称生成器(BeanNameGenerator)由 Spring Framework 2.0.3 引入,框架內建两种实现:

    • DefaultBeanNameGenerator:默认通用 BeanNameGenerator 实现。
    • AnnotationBeanNameGenerator:基于注解扫描的 BeanNameGenerator 实现。

    BeanNameGenerator 源码

    public interface BeanNameGenerator {
    
    	/**
    	 * Generate a bean name for the given bean definition.
    	 * @param definition the bean definition to generate a name for
    	 * @param registry the bean definition registry that the given definition
    	 * is supposed to be registered with
    	 * @return the generated bean name
    	 */
    	String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry);
    
    }
    

    DefaultBeanNameGenerator 源码

    public class DefaultBeanNameGenerator implements BeanNameGenerator {
    
    	@Override
    	public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
    		return BeanDefinitionReaderUtils.generateBeanName(definition, registry);
    	}
    
    }
    

    AnnotationBeanNameGenerator 源码

    public class AnnotationBeanNameGenerator implements BeanNameGenerator {
    
    	private static final String COMPONENT_ANNOTATION_CLASSNAME = "org.springframework.stereotype.Component";
    
    
    	@Override
    	public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
    		// 判断是否为 注解 bean 定义
    		if (definition instanceof AnnotatedBeanDefinition) {
    			String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);
    			if (StringUtils.hasText(beanName)) {
    				// Explicit bean name found.
    				return beanName;
    			}
    		}
    		// Fallback: generate a unique default bean name.
    		// 回调,生成默认的bean 名称
    		return buildDefaultBeanName(definition, registry);
    	}
    }
    

    BeanDefinitionReaderUtils#generateBeanName()

    if (isInnerBean) {
    			// Inner bean: generate identity hashcode suffix.
    			// 嵌套bean,bean里面生成bean :生成 定义的 hash编码 作为后缀,使用 # 来连接前缀和后缀
    			id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + ObjectUtils.getIdentityHexString(definition);
    		}
    else {
    	// Top-level bean: use plain class name with unique suffix if necessary.
    	// 顶层 bean 
    	return uniqueBeanName(generatedBeanName, registry);
    }
    

    BeanDefinitionReaderUtils#uniqueBeanName()

    public static String uniqueBeanName(String beanName, BeanDefinitionRegistry registry) {
    	String id = beanName;
    	int counter = -1;
    
    	// Increase counter until the id is unique.
    	// counter 计数器 从0开始
    	while (counter == -1 || registry.containsBeanDefinition(id)) {
    		counter++;
    		// GENERATED_BEAN_NAME_SEPARATOR # 来连接 bean名称和计数器
    		id = beanName + GENERATED_BEAN_NAME_SEPARATOR + counter;
    	}
    	return id;
    }
    
    Spring Bean 的别名

    Bean 别名使开发人员可以覆盖已配置的 Bean,并用不同的对象定义替换它们。当 bean 定义是无法控制的外部资源继承时,这个时候别名非常有用。

    Bean 别名(Alias)的价值

    • 复用现有的 BeanDefinition,不能无中生有
    • 更具有场景化的命名方法,比如:
    <alias name="myApp-dataSource" alias="subsystemA-dataSource"/>
    <alias name="myApp-dataSource" alias="subsystemB-dataSource"/>
    

    呼应上面更具场景化的命名方法,下面将列举一个实际场景中出现的做法。

    src/main/resources/spring/applicationContext-tx.xml

    <bean id="dataSource" class="bitronix.tm.resource.jdbc.PoolingDataSource" init-method="init" destroy-method="close">
        <property name="className" value="${jdbc.driverClassName}"/>
        <property name="uniqueName" value="dataSource"/>
        <property name="minPoolSize" value="0"/>
        <property name="maxPoolSize" value="5"/>
        <property name="allowLocalTransactions" value="false" />
        <property name="driverProperties">
            <props>
                <prop key="user">${jdbc.username}</prop>
                <prop key="password">${jdbc.password}</prop>
                <prop key="url">${jdbc.url}</prop>
            </props>
        </property>
    </bean>
     
    <bean id="jtaTransactionManager" factory-method="getTransactionManager"
        class="bitronix.tm.TransactionManagerServices" depends-on="btmConfig, dataSource"
        destroy-method="shutdown"/>
    

    dataSource bean 定义期望使用 XA 数据源,但是由于 HSQLDB 不提供 XA 数据源,因此必须依靠 LrcXADataSource 来解决此限制。但这意味着 DataSource 将更改为使用不同的 classNamedriverProperties ,因为上下文环境配置来自外部工件,因此实现起来比较复杂。

    src/test/resources/spring/applicationContext-test.xml

    <import resource="classpath:spring/applicationContext-tx.xml" />
     
    <bean id="testDataSource" class="bitronix.tm.resource.jdbc.PoolingDataSource" init-method="init" destroy-method="close">
        <property name="className" value="bitronix.tm.resource.jdbc.lrc.LrcXADataSource"/>
        <property name="uniqueName" value="testDataSource"/>
        <property name="minPoolSize" value="0"/>
        <property name="maxPoolSize" value="5"/>
        <property name="allowLocalTransactions" value="false" />
        <property name="driverProperties">
            <props>
                <prop key="user">${jdbc.username}</prop>
                <prop key="password">${jdbc.password}</prop>
                <prop key="url">${jdbc.url}</prop>
                <prop key="driverClassName">${jdbc.driverClassName}</prop>
            </props>
        </property>
    </bean>
     
    <alias name="testDataSource" alias="dataSource"/>
    

    testDataSource 与继承的 dataSource 具有相同的 Class 类型,但它可以具有不同的对象配置。每次开发人员希望在需要 dataSource 依赖项时都将使用 dataSource 数据源,而不是原始数据源 testDataSource。这可以通过别名关键字来实现,该关键字指示依赖项注入容器将原始数据源定义替换为新版本。

    注册 Spring Bean

    BeanDefinition 注册的三种方式

    1. XML 配置元信息

    <bean name=”…” … />

    1. Java 注解配置元信息
    • @Bean
    • @Component
    • @Import
    1. Java API 配置元信息
    • 命名方式:

    BeanDefinitionRegistry#registerBeanDefinition(String,BeanDefinition)

    • 非命名方式:

    BeanDefinitionReaderUtils#registerWithGeneratedName(AbstractBeanDefinition,BeanDefinitionRegistry)

    • 配置类方式:

    AnnotatedBeanDefinitionReader#register(Class…)

    这里介绍第二种和第三种注册方式。

    @Bean 注册 Bean

    import com.feng.spring.bean.pojo.User;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    import org.springframework.context.annotation.Bean;
    
    public class AnnotationBeanDefinitionDemo {
    
        public static void main(String[] args) {
            // 创建 BeanFactory 容器
            AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
            // 注册 Configuration Class (配置类)
            applicationContext.register(Config.class);
            // 刷新引用上下文
            applicationContext.refresh();
            System.out.println(" User Beans :"+ applicationContext.getBeansOfType(User.class));
            // 显示地关闭 Spring 应用上下文
            applicationContext.close();
        }
    
        public static class Config{
    
            /**
             * 通过 Java 注解的方式,定义一个 Bean
             */
            @Bean(name = "user")
            public User user(){
                User user = new User();
                user.setId(1L);
                user.setName("小明");
                return user;
            }
        }
    }
    

    @Component 注册 Bean

    import com.feng.spring.bean.pojo.User;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    import org.springframework.context.annotation.Bean;
    import org.springframework.stereotype.Component;
    
    public class AnnotationBeanDefinitionDemo {
    
        public static void main(String[] args) {
            // 创建 BeanFactory 容器
            AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
            // 注册 Configuration Class (配置类)
            applicationContext.register(Config.class);
            // 刷新引用上下文
            applicationContext.refresh();
            System.out.println(" User Beans :"+ applicationContext.getBeansOfType(User.class));
            // 显示地关闭 Spring 应用上下文
            applicationContext.close();
        }
    
    	// 使用 @Component 注册 Bean
        @Component
        public static class Config{
    
            /**
             * 通过 Java 注解的方式,定义一个 Bean
             */
            @Bean(name = "user")
            public User user(){
                User user = new User();
                user.setId(1L);
                user.setName("小明");
                return user;
            }
        }
    }
    

    @Import 注册 Bean

    @Import(AnnotationBeanDefinitionDemo.Config.class)
    public class AnnotationBeanDefinitionDemo {
    ...
    // 此处代码同上
    ...
    }
    

    Java API 注册 Bean , 命名方式和非命名方式

    public class AnnotationBeanDefinitionDemo {
    
        public static void main(String[] args) {
            // 创建 BeanFactory 容器
            AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
            registerBeanDefinition(applicationContext, "xiaoming-user");
            registerBeanDefinition(applicationContext);
            // 刷新引用上下文
            applicationContext.refresh();
            System.out.println(" User Beans :"+ applicationContext.getBeansOfType(User.class));
            // 显示地关闭 Spring 应用上下文
            applicationContext.close();
        }
    
        public static void registerBeanDefinition(BeanDefinitionRegistry registry,String beanName){
            BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(User.class);
            beanDefinitionBuilder
                    .addPropertyValue("id", 1L)
                    .addPropertyValue("name", "小明");
            if (StringUtils.hasText(beanName)) {
                // 命名方式 注册 BeanDefinition
                registry.registerBeanDefinition(beanName, beanDefinitionBuilder.getBeanDefinition());
            }else{
                BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinitionBuilder.getBeanDefinition(), registry);
            }
        }
    }
    
    实例化 Spring Bean

    Bean 实例化 (Instantiation)

    • 常规方式
      • 通过构造器(配置元信息:XML、Java 注解和 Java API )
      • 通过静态工厂方法(配置元信息:XML 和 Java API )
      • 通过 Bean 工厂方法(配置元信息: XML和 Java API )
      • 通过 FactoryBean (配置元信息: XML、Java 注解和 Java API )
    • 特殊方式
      • 通过 ServiceLoaderFactoryBean(配置元信息:XML、Java 注解和 Java API )
      • 通过 AutowireCapableBeanFactory#createBean(java.lang.Class, int, boolean)
      • 通过 BeanDefinitionRegistry#registerBeanDefinition(String,BeanDefinition)

    常规方式
    通过静态工厂方法

    创建 bean-instantiation-context.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="user-by-static-method" class="com.feng.spring.bean.pojo.User"
            factory-method="createUser"/>
    </beans>
    

    创建 User#createUser 静态方法

    public class User {
        private Long id;
        private String name;
    
        public void setId(Long id) {
            this.id = id;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public static User createUser() {
            User user = new User();
            user.setName("小明");
            user.setId(1L);
            return user;
        }
    
        @Override
        public String toString(){
            return "User{" +
                    "id = " + id +
                    ", name = " + name +
                    "}";
        }
    }
    

    BeanInstantiationDemo bean 实例化示例

    import com.feng.spring.bean.pojo.User;
    import org.springframework.beans.factory.BeanFactory;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    /**
     * bean 实例化 示例
     */
    public class BeanInstantiationDemo {
        public static void main(String[] args) {
            // 配置 XML 配置文件
            // 启动 Spring 应用上下文
            BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/bean-instantiation-context.xml");
    
            User userByStaticMethod = beanFactory.getBean("user-by-static-method", User.class);
            System.out.println(userByStaticMethod);
        }
    }
    

    通过 Bean 工厂方法
    创建 User 的工厂方法:
    UserFactory.class

    public interface UserFactory {
    	// 采用 Java 1.8 实现方式 
        default User createUser(){
            return new User();
        }
    }
    

    DefaultUserFactory.class

    public class DefaultUserFacoty implements  UserFactory {
    }
    

    xml 配置

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <!-- 工厂方法实例化 Bean-->
        <bean id="user-by-instance-method" factory-bean="userFacoty" factory-method="createUser"/>
        <bean id="userFacoty" class="com.feng.spring.bean.factory.DefaultUserFacoty"/>
    </beans>
    
    /**
     * bean 实例化 示例
     */
    public class BeanInstantiationDemo {
        public static void main(String[] args) {
            // 配置 XML 配置文件
            // 启动 Spring 应用上下文
            BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/bean-instantiation-context.xml");
            User userByInstanceMethod = beanFactory.getBean("user-by-instance-method", User.class);
            System.out.println(userByInstanceMethod);
        }
    }
    

    注意:
    上面使用了 Java 1.8 的新特性 接口默认实现,需要修改编译级别,在 pom.xml 文件中添加如下代码

        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    

    通过 FactoryBean,实现 UserFactoryBean

    public class UserFactoryBean implements FactoryBean<User> {
        @Override
        public User getObject() throws Exception {
            return User.createUser();
        }
    
        @Override
        public Class<?> getObjectType() {
            return User.class;
        }
    }
    

    配置 xml 文件

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <!-- FactoryBean 实例化 Bean-->
        <bean id="user-by-factory-bean" class="com.feng.spring.bean.factory.UserFactoryBean"/>
    </beans>
    

    这种实现方式比较特殊,这里并不是直接去定义一个 User Bean ,而是定义一个 FactoryBean 去链接 User 对应的 实现方法和实现类型。

    public class BeanInstantiationDemo {
        public static void main(String[] args) {
            // 配置 XML 配置文件
            // 启动 Spring 应用上下文
            BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/bean-instantiation-context.xml");
    
            User userByFactoryBean = beanFactory.getBean("user-by-factory-bean", User.class);
    		System.out.println(userByFactoryBean);
        }
    }
    

    特殊方式
    通过 ServiceLoaderFactoryBean
    在介绍这个方法前,先看 Java 1.6 中的 ServiceLoader

    public final class ServiceLoader<S>
        implements Iterable<S>
    {
    
        private static final String PREFIX = "META-INF/services/";
        ...
    }
    

    META-INF/services/ 是资源管理的目录,Spring 中的 ServiceLoaderFactoryBean 整合了 ServiceLoader 这个对应的资源目录,下面看下 ServiceLoader 是如何进行操作的。

    创建一个全类名作为名称的文件没有后缀,内容填写对应的实现类 com.feng.spring.bean.factory.DefaultUserFacoty
    在这里插入图片描述
    在这里插入图片描述
    ServiceLoaderDemo.class

    import java.util.Iterator;
    import java.util.ServiceLoader;
    
    public class ServiceLoaderDemo {
    
        public static void main(String[] args) {
            demoServiceLoader();
            
        }
        public static void demoServiceLoader(){
            // service 加载
            ServiceLoader<UserFactory> serviceLoader = ServiceLoader.load(UserFactory.class, Thread.currentThread()
                    .getContextClassLoader());
            // 使用迭代器 进行迭代
            Iterator<UserFactory> iterator = serviceLoader.iterator();
            while (iterator.hasNext()) {
                UserFactory userFactory = iterator.next();
                System.out.println(userFactory.createUser());
            }
        }
    }
    

    如何使用 ServiceLoaderFactoryBean 进行初始化 Bean
    创建 special-bean-instantiation-context.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="userFactoryServiceLoader" class="org.springframework.beans.factory.serviceloader.ServiceLoaderFactoryBean">
            <property name="serviceType" value="com.feng.spring.bean.factory.UserFactory"/>
        </bean>
    </beans>
    

    注册一个 ServiceLoaderFactoryBean 其中 serviceType 类型为 UserFactory ,可以根据 AbstractServiceLoaderBasedFactoryBean$serviceType 这个类属性进行判断,serviceTypeServiceLoader 所关注的对象,对 serviceType 属性进行赋值后,将影响 createInstance() 进行初始化创建。

    public abstract class AbstractServiceLoaderBasedFactoryBean extends AbstractFactoryBean<Object>
    		implements BeanClassLoaderAware {
    
    	@Nullable
    	private Class<?> serviceType;
    
    	@Nullable
    	private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
    
    	public void setServiceType(@Nullable Class<?> serviceType) {
    		this.serviceType = serviceType;
    	}
    
    	@Nullable
    	public Class<?> getServiceType() {
    		return this.serviceType;
    	}
    
    	@Override
    	public void setBeanClassLoader(@Nullable ClassLoader beanClassLoader) {
    		this.beanClassLoader = beanClassLoader;
    	}
    
    	@Override
    	protected Object createInstance() {
    		Assert.notNull(getServiceType(), "Property 'serviceType' is required");
    		return getObjectToExpose(ServiceLoader.load(getServiceType(), this.beanClassLoader));
    	}
    	...
    }
    

    return getObjectToExpose(ServiceLoader.load(getServiceType(), this.beanClassLoader));

    初始化场景将执行 getObjectToExpose() 方法

    public class ServiceFactoryBean extends AbstractServiceLoaderBasedFactoryBean implements BeanClassLoaderAware {
    
    	/**
    	* 对 ServiceLoader 进行迭代 并返回一个对象
    	*/
    	@Override
    	protected Object getObjectToExpose(ServiceLoader<?> serviceLoader) {
    		Iterator<?> it = serviceLoader.iterator();
    		if (!it.hasNext()) {
    			throw new IllegalStateException(
    					"ServiceLoader could not find service for type [" + getServiceType() + "]");
    		}
    		return it.next();
    	}
    
    	@Override
    	@Nullable
    	public Class<?> getObjectType() {
    		return getServiceType();
    	}
    }	
    

    由此可以写出执行方法:

    public class SpecialBeanInstantiationDemo {
    
        public static void main(String[] args) {
    
            // 配置 xml 配置文件
            // 启动 Spring 应用上下文
            BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/special-bean-instantiation-context.xml");
    
            ServiceLoader<UserFactory> serviceLoader = beanFactory.getBean("userFactoryServiceLoader", ServiceLoader.class);
    
            // 使用迭代器 进行迭代
            Iterator<UserFactory> iterator = serviceLoader.iterator();
            while (iterator.hasNext()) {
                UserFactory userFactory = iterator.next();
                System.out.println(userFactory.createUser());
            }
        }
    }
    

    通过 AutowireCapableBeanFactory

    public class AutowireCapableBeanFactoryDemo {
    
        public static void main(String[] args) {
            // 配置 xml 配置文件,启动 Spring 应用上下文
            ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:/META-INF/special-bean-instantiation-context.xml");
    
            AutowireCapableBeanFactory beanFactory = applicationContext.getAutowireCapableBeanFactory();
    
            UserFactory userFactory = beanFactory.createBean(DefaultUserFacoty.class);
            System.out.println(userFactory.createUser());
        }
    }
    
    初始化 Spring Bean

    Bean 初始化(Initialization) 的三种方式

    1. @PostConstruct 标注方法
    2. 实现 InitializingBean#afterPropertiesSet() 方法
    3. 自定义初始化方法
      • XML 配置:<bean init-method=”init” … />
      • Java 注解:@Bean$initMethod
      • Java API:AbstractBeanDefinition#setInitMethodName(String)

    基于 @PostConstruct 标注方法:
    创建 DefaultUserFacoty#init 方法,并标注 @PostConstruct 注解

    public class DefaultUserFacoty implements  UserFactory {
    
        // 1. 基于 @PostConstruct 注解
        @PostConstruct
        public void init(){
            System.out.println(" @PostConstruct 初始化。。。 ");
        }
    }
    

    创建 BeanInitializationDemo 类,并通过 @BeanDefaultUserFacoty 进行注册,此时将会回调 DefaultUserFacoty 类中被 @PostConstruct 注解的方法。

    /**
     * Bran 初始化 示例
     */
    @Configuration  // 配置 类
    public class BeanInitializationDemo {
    
        public static void main(String[] args) {
            // 创建 BeanFactory 容器
            AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
            // 注册 Configuration 类
            applicationContext.register(BeanInitializationDemo.class);
            // 启动 applicationContext
            applicationContext.refresh();
    
            UserFactory userFactory = applicationContext.getBean(UserFactory.class);
            // 关闭 applicationContext
            applicationContext.close();
        }
    
        @Bean
        public UserFactory initUserFactory(){
            return new DefaultUserFacoty();
        }
    }
    

    实现 InitializingBean#afterPropertiesSet() 方法:
    DefaultUserFacoty 实现 InitializingBean 接口,并实现 afterPropertiesSet 方法。

    public class DefaultUserFacoty implements  UserFactory ,InitializingBean {
    
        @Override
        public void afterPropertiesSet() throws Exception {
            System.out.println(" InitializingBean#afterPropertiesSet 初始化..  ");
        }
    }
    

    自定义初始化方法(这里只介绍一下 @Bean$initMehtod 如何实现)
    Java 注解:@Bean$initMethod
    创建 DefaultUserFacoty#initMethod 方法

    public class DefaultUserFacoty implements  UserFactory {
    
        // 3.2 基于 @Bean initMethod 方法
        public void initMethod(){
            System.out.println(" @Bean$initMethod 初始化...");
        }
    }
    
    public interface UserFactory {
        default User createUser(){
            return new User();
        }
    
        void initMethod();
    }
    

    BeanInitializationDemo 类中对 @Bean$initMethod 属性进行赋值,赋予 DefaultUserFacoty 中初始化的方法名。

    /**
     * Bran 初始化 示例
     */
    @Configuration  // 配置 类
    public class BeanInitializationDemo {
    
        public static void main(String[] args) {
            // 创建 BeanFactory 容器
            AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
            // 注册 Configuration 类
            applicationContext.register(BeanInitializationDemo.class);
            // 启动 applicationContext
            applicationContext.refresh();
    
            UserFactory userFactory = applicationContext.getBean(UserFactory.class);
            // 关闭 applicationContext
            applicationContext.close();
        }
    
        @Bean(initMethod = "initMethod")
        public UserFactory initUserFactory(){
            return new DefaultUserFacoty();
        }
    }
    

    以上三种方法在同一个方法中同时出现,其执行顺序是:

    优先于
    优先于
    PostConstant
    InitializingBean
    自定义
    延迟初始化 Spring Bean

    Spring 官方API:

    By default, ApplicationContext implementations eagerly create and configure all singleton beans as part of the initialization process. Generally, this pre-instantiation is desirable, because errors in the configuration or surrounding environment are discovered immediately, as opposed to hours or even days later. When this behavior is not desirable, you can prevent pre-instantiation of a singleton bean by marking the bean definition as lazy-initialized. A lazy-initialized bean tells the IoC container to create a bean instance when it is first requested, rather than at startup.


    译文:

    默认情况下,Spring 容器在初始化过程中会创建和配置所有单例的bean。这种提前实例化是可取的,因为配置环境错误会被立即发现而不需要过多的时间。如果不采取这种行为,可以将单例的bean标记为延迟初始化。一个延迟初始化的bean告诉Spring IoC容器去创建这个bean实例化对象当它第一次被调用时而不是在容器启动时立即创建。

    设计模式中有一种延迟加载的模式和延迟初始化的效果类似。

    Bean 延迟初始化(Lazy Initialization) 的两种方式:

    • XML 配置:<bean lazy-init=”true” … />
    • Java 注解:@Lazy(true)

    默认情况下是非延迟加载,基于 XML 配置和 Java 注解所产生的效果是类似的,只要在被加载的 Bean 上注解 @Lazy ,那么这个 Bean 就是延迟加载。

    @Bean(initMethod = "initMethod")
    @Lazy
    public UserFactory initUserFactory(){
        return new DefaultUserFacoty();
    }
    

    下面例子通过对容器启动完成后,打印一句话,之后对 UserFactory 进行依赖查找,通过打印的顺序看下,延迟加载的特性。

    非延迟加载例子,打印结果。

    /**
     * Bran 初始化 示例
     */
    @Configuration  // 配置 类
    public class BeanInitializationDemo {
    
        public static void main(String[] args) {
            // 创建 BeanFactory 容器
            AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
            // 注册 Configuration 类
            applicationContext.register(BeanInitializationDemo.class);
            // 启动 applicationContext
            applicationContext.refresh();
            // 非延迟初始化在 Spring 应用上下文启动完成后,被初始化
            System.out.println("Spring 应用上下文已启动...");
            UserFactory userFactory = applicationContext.getBean(UserFactory.class);
            // 关闭 applicationContext
            applicationContext.close();
        }
    
        @Bean(initMethod = "initMethod")
        public UserFactory initUserFactory(){
            return new DefaultUserFacoty();
        }
    }
    

    控制台输出:

    @PostConstruct 初始化。。。 
    InitializingBean#afterPropertiesSet 初始化..  
    @Bean$initMethod 初始化...
    Spring 应用上下文已启动..
    

    延迟加载例子,打印结果。

    /**
     * Bran 初始化 示例
     */
    @Configuration  // 配置 类
    public class BeanInitializationDemo {
    
        public static void main(String[] args) {
            // 创建 BeanFactory 容器
            AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
            // 注册 Configuration 类
            applicationContext.register(BeanInitializationDemo.class);
            // 启动 applicationContext
            applicationContext.refresh();
            // 非延迟初始化在 Spring 应用上下文启动完成后,被初始化
            System.out.println("Spring 应用上下文已启动...");
            UserFactory userFactory = applicationContext.getBean(UserFactory.class);
            // 关闭 applicationContext
            applicationContext.close();
        }
    
        @Bean(initMethod = "initMethod")
        @Lazy
        public UserFactory initUserFactory(){
            return new DefaultUserFacoty();
        }
    }
    

    打印结果:

    Spring 应用上下文已启动...
    @PostConstruct 初始化。。。 
    InitializingBean#afterPropertiesSet 初始化..  
    @Bean$initMethod 初始化...
    

    从结果对比可以看出,延迟初始化是在容器启动完成后,依赖查找对应的 Bean 时,才会触发 Bean 的初始化。
    延迟初始化是一种按需初始化。

    Spring 容器返回的对象与非延迟的对象存在怎样的差异?
    差异在于依赖查找和依赖注入时,是否在容器启动前后进行初始化。
    相同在于注册 Bean 时,容器都是无差别进行注册。

    销毁 Spring Bean

    Bean 销毁(Destroy) 的三种方式:

    1. @PreDestroy 标注方法
    2. 实现 DisposableBean 接口的 destroy() 方法
    3. 自定义销毁方法
      • XML 配置:<bean destroyMethod=”destroyMethod” … />
      • Java 注解:@Bean(destroyMethod=”destoryMethod”)
      • Java API:AbstractBeanDefinition#setDestroyMethodName(String)

    销毁 Bean 和初始化 Bean 相似,下面通过代码来讲述一下:

    DefaultUserFacoty

    /**
     * 默认 {@link UserFactory} 实现
     */
    public class DefaultUserFacoty implements  UserFactory,DisposableBean {
    
        // 1. 基于 @PreDestroy 注解
        @PreDestroy
        public void perDestroy(){
            System.out.println(" @PreDestroy 销毁... ");
        }
    
        // 4.2 基于 @Bean initMethod 方法
        public void destoryMethod(){
            System.out.println(" @Bean$destoryMethod 初始化...");
        }
    
        @Override
        public void destroy() throws Exception {
            System.out.println(" DisposableBean#destroy 销毁... ");
        }
    }
    
    @Configuration  // 配置 类
    public class BeanInitializationDemo {
    
        public static void main(String[] args) {
            // 创建 BeanFactory 容器
            AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
            // 注册 Configuration 类
            applicationContext.register(BeanInitializationDemo.class);
            // 启动 applicationContext
            applicationContext.refresh();
            // 非延迟初始化在 Spring 应用上下文启动完成后,被初始化
            System.out.println("Spring 应用上下文已启动...");
            UserFactory userFactory = applicationContext.getBean(UserFactory.class);
            // 关闭 applicationContext
            applicationContext.close();
        }
    
        @Bean(destroyMethod = "destoryMethod")
        public UserFactory initUserFactory(){
            return new DefaultUserFacoty();
        }
    }
    

    控制台输出:

    Spring 应用上下文已启动...
    @PreDestroy 销毁... 
    DisposableBean#destroy 销毁... 
    @Bean$destoryMethod 初始化...
    

    由控制台输出顺序得出,以上三种方法在同一个方法中同时出现,其执行顺序是:

    优先于
    优先于
    PreDestroy
    DisposableBean
    自定义

    销毁触发的条件是什么?
    通过在 关闭 applicationContext applicationContext.close(); 语句上下打印两条语句可以进行判断:

    /**
     * Bran 初始化 示例
     */
    @Configuration  // 配置 类
    public class BeanInitializationDemo {
    
        public static void main(String[] args) {
            // 创建 BeanFactory 容器
            AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
            // 注册 Configuration 类
            applicationContext.register(BeanInitializationDemo.class);
            // 启动 applicationContext
            applicationContext.refresh();
            // 非延迟初始化在 Spring 应用上下文启动完成后,被初始化
            System.out.println("Spring 应用上下文已启动...");
            UserFactory userFactory = applicationContext.getBean(UserFactory.class);
            System.out.println("Spring 应用准备关闭...");
            // 关闭 applicationContext
            applicationContext.close();
            System.out.println("Spring 应用已关闭...");
        }
    
        @Bean(initMethod = "initMethod",destroyMethod = "destoryMethod")
        public UserFactory initUserFactory(){
            return new DefaultUserFacoty();
        }
    }
    

    控制台输出:

    Spring 应用上下文已启动...
    Spring 应用准备关闭...
    @PreDestroy 销毁... 
    DisposableBean#destroy 销毁... 
    @Bean$destoryMethod 初始化...
    Spring 应用已关闭...
    

    通过对比控制台输出语句可以得出,Spring Bean 销毁是由 applicationContext.close(); 触发的。

    总结

    本文主要介绍 Spring Bean 的生命周期过程中的阶段,讨论 Spring 容器如何针对各个阶段编写代码。

    参考资料

    Spring Bean

    how is Spring bean

    what in the world are spring beans

    Spring 核心

    Spring中Bean命名源码分析

    1.3.1.Bean的命名

    How to create an alias for a bean and inner bean in Spring Framework

    为什么我喜欢Spring bean别名(Why I like Spring bean aliasing

    SpringBoot基础篇Bean之动态注册

    spring之Bean实例化过程

    展开全文
  • Spring Bean详细讲解 什么是Bean?

    万次阅读 多人点赞 2018-11-19 15:45:37
    Spring Bean是被实例的,组装的及被Spring 容器管理的Java对象。 Spring 容器会自动完成@bean对象的实例化。 创建应用对象之间的协作关系的行为称为:装配(wiring),这就是依赖注入的本质。 Spring 三种配置方案 ...

    什么是Bean?

    Spring Bean是被实例的,组装的及被Spring 容器管理的Java对象。

    Spring 容器会自动完成@bean对象的实例化。

    创建应用对象之间的协作关系的行为称为:装配(wiring),这就是依赖注入的本质。

    Spring 三种配置方案

    1.在XML中进行显示配置
    2.使用Java代码进行显示配置
    3.隐式的bean发现机制和自动装配
    推荐方式: 3>2>1

    一、自动化装配bean

    1.组件扫描(component scanning):Spring 会自动发现应用上下文中所创建的bean。
    2.自动装配(autowiring):Spring自动满足bean之间的依赖。

    package com.stalkers;
    
    /**
     * CD唱片接口
     * Created by stalkers on 2016/11/17.
     */
    public interface ICompactDisc {
        void play();
    }
    
    package com.stalkers.impl;
    
    import com.stalkers.ICompactDisc;
    import org.springframework.stereotype.Component;
    
    /**
     * Jay同名专辑
     * Created by stalkers on 2016/11/17.
     */
    @Component
    public class JayDisc implements ICompactDisc {
    
        private String title = "星晴";
    
        public void play() {
            System.out.println(title + ":一步两步三步四步,望着天上星星...");
        }
    }
    

    Component注解作用:
    表明该类会作为组件类。

    不过,组件扫描默认是不开启用的,我们还需要显示配置下Spring,从而命令它去寻找带有@Component注解的类,并为其创建bean。

    1.java code开启组件扫描:
    其中,如果CompoentScan后面没有参数的话,默认会扫描与配置类相同的包

    @Configuration
    @ComponentScan
    public class CDPlayerConfig {
        @Bean
        public ICompactDisc disc() {
            return new JayDisc();
        }
    }
    

    2.xml启动组件扫描

    <?xml version="1.0" encoding="utf-8" ?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/context
              http://www.springframework.org/schema/context/spring-context.xsd">
        <context:component-scan base-package="com.stalkers.impl"/>
    </beans>

    测试代码

    package com.stalkers;
    
    import com.stalkers.config.CDPlayerConfig;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    /**
     * Created by stalkers on 2016/11/18.
     */
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = CDPlayerConfig.class)
    public class TestPlay {
        @Autowired
        private ICompactDisc jayDisc;
    
        @Test
        public void play() {
            jayDisc.play();
        }
    }
    

    在ComponentScan扫描的包中,所有带有@Component注解的类都会创建为bean

    为组件扫描的bean命名

    Spring应用上下文种所有的bean都会给定一个ID。在前面的例子中,尽管我们没有明确地为JayDisc bean设置ID,但是Spring会默认为JayDisc设置ID为jayDisc,也就是将类名的第一个字母变成小写

    如果想为这个bean设置不同的ID,那就将期望的值传递给@Component注解

    @Component("zhoujielun")
    public class JayDisc implements ICompactDisc {
      ...
    }

    如果不使用@Component注解的话,则使用Java依赖注入规范(Java Dependency Injection)中所提供的@Named注解bean的ID。

    需要引入:

       <dependency>
                <groupId>javax.inject</groupId>
                <artifactId>javax.inject</artifactId>
                <version>1</version>
            </dependency>
    
    @Named("zhoujielun")
    public class JayDisc implements ICompactDisc {
      ....
    }

    设置组件扫描的基础包

    前面再给CDPlayerConfig类设置@ComponentScan,我们并没有设置任何属性,这个时候默认扫描默认包是:CDPlayerConfig类所在包及其包的子包。

    如果是下图这种情况,DisConfig与其这时候就需要设置@ComponentScan的扫描的包。

    image

    @Configuration
    @ComponentScan(basePackages = {"com.stalkers.soundsystem"})
    public class DiscConfig {
    }
    

    basePackages使用的是复数,则意味着可以设置多个基础包。

    但是basePackages后面跟的是String类型,这种类型并不安全。可以使用basePackageClasses有下面这种写法:

    @Configuration
    @ComponentScan(basePackageClasses = {com.stalkers.soundsystem.JayCompactDisc.class})
    public class DiscConfig {
    }

    通过为bean添加注解实现自动装配

    如果所有的对象都是独立的,彼此之间没有任何依赖,那么使用组件扫描就能自动化装配bean。

    但是实际工作中,很多对象会依赖其他对象完成任务。这时候就需要能够将组件扫描得到的bean和他们依赖装配在一起。这就是自动装配(autowiring)

    使用Spring的Autowired

    public interface IMediaPlayer {
        void play();
    }
    @Component
    public class CDPlayer implements IMediaPlayer {
    
        private ICompactDisc cd;
        
        @Autowired
        public CDPlayer(ICompactDisc cd) {
            this.cd = cd;
        }
    
        public void play() {
            System.out.println("cd Play:");
            cd.play();
        }
    }

    CDPlayer类的构造器上添加了@Autowired注解,表明当Spring创建CDPlayerbean的时候,会通过这个构造器来进行实例化

    Autowired的多种方式
    1.构造器注解(constructor)

    2.属性setter注解

    3.field注解

    不管使用上面3中的哪个方法,Spring都会满足声明的依赖。假如有且只有一个bean匹配依赖的话,那么这个bean将会被装配进来。

    如果使用2,3方式注解,有多个bean的话,则用Qualifier指定。

    如果没有匹配的bean,那么在应用上下文创建的时候,Spring会抛出一个异常。为了避免异常的出现,可以使用

    @Autowired(required = false)
    private IMediaPlayer CDPlayer;

    required=false表示如果没有匹配的话,Spring会让bean处于未装配的样子。使用未装配的属性,会出现NullPointerException

    总结:
    所以在使用开发的时候一般建议使用Resource(package javax.annotation)进行注解。但是Resource不支持构造器注解

    二、通过Java代码装配Bean

    尽管在很多场景下通过组件扫描和自动装配实现Spring的自动化更为推荐,但是有时候行不通。比如引用第三方组件,没办法在它的类上添加@Component及@Autowired。所以就需要JavaConfig或者XML配置

    在进行显示配置的时候,JavaConfig是更好的解决方案。

    JavaConfig与其他的Java代码又有所区别,在概念上它与应用程序中的业务逻辑和领域代码又有所不同。JavaConfig是配置相关代码,不含任何逻辑代码。通常会将JavaConfig放到单独的包中。

    创建JavaConfig类

    @Configuration
    public class CDPlayerConfig {
    }

    使用@Configuration表明CDPlayerConfig是一个配置类

    声明简单的bean

    @Bean
    public IMediaPlayer cdplayer() {
        return new VCDPlayer(new JayCompactDisc());
    }

    @Bean注解会告诉Spring将返回一个对象。

    默认情况下,@Bean的Id与带有@Bean的方法名一样。当然也可以通过@Bean的name属性指定额外的方法名。

    借助JavaConfig注入

    在上面的例子中,初始化个VCDPlayer都需要new一个JayCompactDisc对象。如果其他的对象的也需要JayCompactDisc,所以优化如下:

    @Bean
    public IMediaPlayer cdplayer() {
        return new VCDPlayer(disc());
    }
    
    @Bean
    public ICompactDisc disc() {
        return new JayCompactDisc();
    }

    单独抽出disc()方法,在其方法上加上Bean注解,Spring上加@Bean注解的都是默认单例模式,不管disc()被多个方法调用,其disc()都是同一个实例。

    当然上面的初始化可以优化如下:

    @Bean
    public IMediaPlayer cdplayer(ICompactDisc disc) {
        return new VCDPlayer(disc);
    }

    三、通过XML装配Bean

    在xml配置中,创建一个xml文件,并且要以元素为根。

    <?xml version="1.0" encoding="utf-8" ?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd">
       
    </beans>

    在使用xml的时候,需要在配置文件顶部声明多个xml模式(XML Schema Definition xsd)文件

    对于我们需要配置bean的则在spring-beans模式中。

    <?xml version="1.0" encoding="utf-8" ?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd">
        <bean id="jayCompactDisc" class="com.stalkers.soundsystem.JayCompactDisc"></bean>
    </beans>

    1.借助构造器注入初始化bean

    构造器注入的方案:
    1.元素

    <?xml version="1.0" encoding="utf-8" ?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd">
        <bean id="jayCompactDisc" class="com.stalkers.soundsystem.JayCompactDisc"></bean>
        <bean id="cdPlayer" class="com.stalkers.soundsystem.VCDPlayer">
            <constructor-arg ref="jayCompactDisc"/>
        </bean>
    </beans>

    2.使用Spring3.0所引入的c-命名空间

    使用c-命名空间,需要引入:

    xmlns:c="http://www.springframework.org/schema/c"
    <?xml version="1.0" encoding="utf-8" ?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:c="http://www.springframework.org/schema/c"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd">
        <bean id="jayCompactDisc" class="com.stalkers.soundsystem.JayCompactDisc"></bean>
        <bean id="cdPlayer" class="com.stalkers.soundsystem.VCDPlayer" c:cd-ref="jayCompactDisc">
        </bean>
    </beans>

    解析:c-命名空间的语法:

    c:cd-ref="jayCompactDisc"

    1.c 代表命名空间前缀

    2.cd 代表VCDPlayer类的构造器参数名。当然我们也可以使用参数在整个参数列表的位置 c:_0-ref

     <bean id="cdPlayer" class="com.stalkers.soundsystem.VCDPlayer" c:_0-ref="jayCompactDisc">

    使用下划线因为参数不能以数字开头,所以加下划线。

    3.-ref 代表注入bean引用

    4.jayCompactDisc 要注入的bean的id

    注意:

    c-命名需要写在标签内,与constructor-arg写法差别很大

    将字面量注入到构造器中

    上面我们所做的DI通常指的是类型的装配,也就是将对象的引用装配到依赖他们的其他对象中,但是有时候我们传的只是一个字面量值

    public class VaeCompactDisc implements ICompactDisc {
        private String title;
    
        public VaeCompactDisc(String title) {
            this.title = title;
        }
    
        public void play() {
            System.out.println("大家好,我是Vae,下面这首:" + title + "献给大家的");
        }
    }
    <bean id="cdPlayer" class="com.stalkers.soundsystem.VCDPlayer" c:_0-ref="vaeCompactDisc">
    </bean>
    <bean id="vaeCompactDisc" class="com.stalkers.soundsystem.VaeCompactDisc">
        <constructor-arg value="浅唱"></constructor-arg>
    </bean>

    c-命名空间的写法

    <bean id="cdPlayer" class="com.stalkers.soundsystem.VCDPlayer" c:_0-ref="vaeCompactDisc">
    </bean>
    <bean id="vaeCompactDisc" class="com.stalkers.soundsystem.VaeCompactDisc" c:title="城府">
        <!--<constructor-arg value="浅唱"></constructor-arg>-->
    </bean>

    装配集合

    public class VaeCompactDisc implements ICompactDisc {
        private String title;
    
        private List<String> tracks;
    
        public VaeCompactDisc(String title, List<String> tracks) {
            this.title = title;
            this.tracks = tracks;
        }
    
        public void play() {
            System.out.println("大家好,我是Vae,下面这专辑:" + title + "献给大家的");
            for (String s : tracks) {
                System.out.println(s);
            }
        }
    }

    Spring配置使用constructor-arg。而c-命名的是无法使用装配集合的功能

    <bean id="cdPlayer" class="com.stalkers.soundsystem.VCDPlayer" c:_0-ref="vaeCompactDisc">
    </bean>
    <bean id="vaeCompactDisc" class="com.stalkers.soundsystem.VaeCompactDisc">
        <constructor-arg name="title" value="自定义"></constructor-arg>
        <constructor-arg name="tracks">
            <list>
                <value>有何不可</value>
                <value>多余的解释</value>
            </list>
        </constructor-arg>
    </bean>

    2.使用属性Setter方法注入

    public class CDPlayer implements IMediaPlayer {
    
        private ICompactDisc cd;
    
        @Autowired
        public void setCd(ICompactDisc cd) {
            this.cd = cd;
        }
        
        public CDPlayer(ICompactDisc cd) {
            this.cd = cd;
        }
    
        public void play() {
            System.out.println("cd Play:");
            cd.play();
        }
    }
    

    Spring.xml配置里面

    <bean id="cdPlayer" class="com.stalkers.soundsystem.VCDPlayer">
        <property name="cd" ref="jayCompactDisc"></property>
    </bean>

    元素为属性的Setter方法所提供的功能与元素为构造器所提供的功能是一样的。

    与c-命名空间的类似的作为property的替代方案:p-命名空间。使用p-命名空间需要引入:

    xmlns:p="http://www.springframework.org/schema/p"

    Spring.xml配置如下

    <bean id="cdPlayer" class="com.stalkers.soundsystem.VCDPlayer" p:cd-ref="vaeCompactDisc">

    语法解析:

    p:cd-ref="vaeCompactDisc"

    1.p-:命名空间的前缀

    2.cd:属性名称

    3.-ref:注入bean引用

    4.vaeCompactDisc:所注入的bean的id

    将字面量注入到属性中

    字面量注入到属性与上面将字面量注入到构造方法中方式一样。只不过标签名改成了property。

    装配list也是与上面的构造器的装配list一样。

    虽然我们无法使用c-及p-命名空间装配list,但是我们可以使用

    <bean id="vaeCompactDisc" class="com.stalkers.soundsystem.VaeCompactDisc" c:title="自定义" c:tracks-ref="songs">
    </bean>
    <util:list id="songs">
        <value>有何不可</value>
        <value>多余的解释</value>
    </util:list>

    Spring util命名空间的中的元素:

    元素描述
    util:constant引用某个类型的public static 域
    util:list创建一个java.util.List类型的bean,其中包含值或引用
    util:map创建一个java.util.Map类型的bean,其中包含值或引用
    util:properties创建一个java.util.Properties类型的bean
    util:property-path引用一个bean的属性
    util: set创建一个java.util.Set类型的bean

    在此我向大家推荐一个架构学习交流群。交流学习群号:895244712 里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化、分布式架构等这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

    四、导入和混合配置

    在Spring应用中,我们可以同时使用自动化和显示配置。

    如果一个JavaConfig配置太臃肿,我们可以把其进行拆分,然后使用@Import将拆分的类进行组合。

    如果希望在JavaConfig里引用xml配置。则可以使用@ImportResource

     

    展开全文
  • Spring Bean生命周期详解

    万次阅读 多人点赞 2016-08-12 17:40:08
    Spring Bean生命周期详解
  • 关于Spring Bean的生命周期

    万次阅读 多人点赞 2018-10-08 10:47:04
     Spring Bean 的生命周期在整个 Spring 中占有很重要的位置,从BeanFactory或ApplicationContext取得的实例为Singleton,也就是预设为每一个Bean的别名只能维持一个实例,而不是每次都产生一个新的对象使用...
  • springBean生命周期

    千次阅读 多人点赞 2020-12-18 14:46:36
    一、springBean的生命周期 1、 启动spring容器,也就是创建beanFactory(bean工厂), 一般用的是beanFactory的子类applicationcontext, applicationcontext比一般的beanFactory要多很多功能,比如aop、事件等。 通过...
  • spring bean加载原理

    千次阅读 2018-01-05 22:15:21
    简单的分析了一下spring bean的加载原理,属于个人的理解,源码比这个要复杂的多: spring的配置文件applicationContext.xml的内容如下: beans> bean id="userDao" class=...
  • Spring Bean加载过程

    千次阅读 2020-04-17 22:05:39
    Spring bean的加载过程的文章有很多。但是一些文章贴的代码太多了,一些文章图片太简单又反映不出代码。索性自己整理了一个Spring 单例bean的加载流程图,方便个人记忆。使用的版本:spring-beans-5.0.5.RELEASE. ...
  • SpringBean生命周期详解

    千次阅读 2020-10-28 17:51:28
    SpringBean生命周期详解 一、简述: Spring是我们每天都在使用的框架,Bean是被Spring管理的Java对象,是Spring框架最重要的部分之一,那么让我们一起了解一下Spring中Bean的生命周期是怎样的吧 二、流程图 我们先从...
  • Spring Bean注册表

    千次阅读 2010-09-13 17:29:00
    Spring Bean注册表。管理spring bean配置
  • spring bean懒加载

    万次阅读 2017-12-10 11:51:31
    spring bean懒加载 使用spring,则所有bean统一交给spring管理,spring负责初始化、销毁等整个声明周期。所有bean的定义通过配置文件或javaConfig方式。启动时统一初始化并加载至spring的applicationContext中,...
  • Spring bean和javabean的区别

    千次阅读 2019-06-17 21:08:11
    一.spring Bean是什么 在传统的java应用中,bean的生命周期很简单。使用java关键字new进行bean实例化,然后该bean就可以使用...简单地说:SpringBean是受Spring管理的对象。 二.Spring中如何创建Bean 在Spring中,Be...
  • Spring Bean的作用域

    万次阅读 多人点赞 2018-08-25 14:05:02
    Spring中,bean作用域用于确定哪种类型的bean实例应该从Spring容器中返回给调用者。 目前Spring Bean的作用域或者说范围主要有五种。 作用域 描述 singleton 在spring IoC容器仅存在一个Bean实例,Bean...
  • 查看运行环境中所有的spring bean

    千次阅读 2020-05-21 15:52:11
    查看运行环境中所有的spring bean
  • Spring Bean的延迟初始化

    千次阅读 2016-03-23 15:14:51
    Spring Bean默认的作用范围(scope)是singleton,而且默认设置了pre-instantiation="true"属性,所以Spring Bean默认将在初始化ApplicationContext的时候被立即创建,同时其所依赖的其他Spring Bean也将被实例化并...
  • Spring Bean详细讲解

    千次阅读 2019-10-03 13:42:07
    Spring Bean是被实例的,组装的及被Spring 容器管理的Java对象。 Spring 容器会自动完成@bean对象的实例化。 创建应用对象之间的协作关系的行为称为:装配(wiring),这就是依赖注入的本质。 Spring 三种配置方案 1....
  • eclipse没有spring Bean configuration file

    千次阅读 2020-07-18 12:48:45
    eclipse4.15.0没有spring Bean configuration file 但是视频教程里用的IDE不是eclipse,而是SpringToolSuite这个软件,实际上eclipse4.15.0也是可以用spring Bean configuration file这个文件功能的。我在网上找了...
  • Spring Bean的生命周期(非常详细)

    万次阅读 多人点赞 2019-06-12 16:11:07
    生命周期图 ...准确的了解Spring Bean的生命周期是非常必要的。我们通常使用ApplicationContext作为Spring容器。这里,我们讲的也是 ApplicationContext中Bean的生命周期。而实际BeanFactory也是差...
  • spring bean什么时候加载

    千次阅读 2016-07-26 23:18:26
    Spring什么时候实例化bean,首先要分2种情况 第一:如果你使用BeanFactory作为Spring Bean的工厂类,则所有的bean都是在第一次使用该Bean的时候实例化 第二:如果你使用ApplicationContext作为Spring Bean的...
  • spring bean循环引用问题

    千次阅读 2017-07-04 17:19:56
    spring bean在创建过程中的循环依赖
  • 问下这是什么情况,new>Spring Bean Definition file这个选项没有,百度查了2天都没解决这个问题,也没找到原因,有些是说在eclipse 里面的Spring选项设置,然而我的根本就没有Spring这个选项。然后看到别人安装好了...
  • spring bean setter属性注入

    千次阅读 2015-11-13 17:09:56
    quote: ...我们可以方便的通过构造函数来注入spring bean,也可以通过setter属性来做spring bean的注入。 注入简单类型的属性 一个简单的示例,我们给Person类定义age和name两个属性,然后在spring
  • Spring Bean在XML配置中的父子继承

    千次阅读 2016-04-05 11:54:17
    与Java类的继承相似,Spring框架也提供了Spring Bean的属性的继承。也就是说,有的Spring Bean是抽象的,可以被继承;有的Spring Bean可以指定继承的父Spring Bean。这时候,抽象的Spring Bean也可以被称为模板,供...
  • 记录 Duplicate spring bean id dubbo

    千次阅读 2017-12-19 12:05:09
    启动工程 报错如题:Duplicate spring bean id dubbo ,意思是id 重复。 原因是我在加载配置文件时加载了两个一样的配置文件: 在spring文件夹下 红框中的两个配置文件是一样的: 改为只加载一个就可以...
  • Spring bean注入为null

    千次阅读 2017-07-27 20:09:20
    最近在学习SSM框架的过程中, 总是... 使用了new实例化对象, 而new实例化出来的对象不是由spring bean容器进行管理, 从而导致spring 在自动扫描@Autowired注解时无法注入. 从另一个方面说, spring实现@Autowired注解自动
  • Spring Bean的生命周期

    千次阅读 2017-03-07 13:36:29
    准确的了解Spring Bean的生命周期是非常必要的。我们通常使用ApplicationContext作为Spring容器。这里,我们讲的也是 ApplicationContext中Bean的生命周期。而实际上BeanFactory也是差不多的,只不过处理器需要手动...
  • 覆盖重写 原有Spring Bean的几种方式

    千次阅读 2020-10-28 16:50:40
    什么情况下要覆写原有的Spring Bean ? 例如引入的第三方jar包中的某个类有些问题,然有没有源码提供或者嫌编译源码太费事,这个时间可以考虑覆写原有的类。 方法1 直接在自己工程中建同包同类名的类进行替换 方式...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 889,465
精华内容 355,786
关键字:

springbean

spring 订阅