• 后端——框架——容器框架——spring_core——bean以及xml配置


      bean的知识点有以下五个部分

    第一部分,介绍bean的基础,bean的概念,bean的基础配置,名称,注入方式等。

    第二部分,介绍bean自动注入。

    第三部分,介绍bean的依赖关系,循环依赖,前置。

    第四部分,介绍bean的作用域。

    第五部分,介绍bean的生命周期。

    1、基础

    1.1  概念

    bean的概念,引用原著:

      A bean is an object that is instantiated, assembled, and otherwise managed by a Spring IoC container

    这段话包含两个要素

    A bean is an object,bean是Java语言中的任何一个对象。Spring配置文件中的bean标签本质就是建立它与类之间的关系,标签内部属性与Java对象属性之间的关系。所以在配置bean时,一定熟悉与之关联的对象。

    IOC container的作用:它用来管理,创建,配置bean。

    bean本质上是Java类的抽象,配置bean的过程,本质就是将Java类抽象为bean的过程。

    bean的class属性为Java类的全名称。

    bean的属性为Java类的属性。属性的数据类型若为一般数据类型,有相应的转换器,若为其他对象类型,将其他类型注入到容器中,使用ref引用。

    bean的配置对应某些方法的参数,例如构造器,实现方式有三种,根据参数的顺序,类型,名称。

    1.2  命名

    Bean的命名有三种方式,id,name,alias。

    id:bean的唯一标识,不能重复,一般都是使用此方式为bean命名。

    name:bean的名称,IOC容器允许bean的名称重复,一个bean可以拥有多个名称。不指定时,类名称首字母小写作为name属性的默认值。

    alias:bean的别名,一个类可以有多个别名。别名最好有意义。

    <!-- naming bean, bean的命名 -->
     <!-- id方式 -->
     <bean id="user" class="learn.springcore.bean.User"/>
     <!-- name方式 -->
     <bean name="manager" class="learn.springcore.bean.User"/>
     <bean name="vip" class="lean.springcore.bean.User"/>
     <!-- alias方式 -->
     <alias name="manager" alias="boss"/>
     <alias name="manager" alias="projectManager"/>
    

    1.3     注入

    注入方式有三种,构造器,set方法,工厂方法。

    1.3.1  构造器

    当使用类的默认构造器时,设置bean的class属性为类全名即可。

    当使用类的其他构造器时,需要在bean内部配置constructor-arg标签,它代表构造器的参数。参数的映射方式有三种方式,参数的顺序,参数的名称,参数的类型。

    // 假设用户存在三个构造器,默认构造器,User(String name),User(String name, int age)
    <!-- naming bean, bean的命名 -->
    <!-- 默认构造器方式,class属性为类全名即可 -->
    <bean id="user" class="learn.springcore.bean.User" />
    <!--只有单个名称的构造器,使用参数名称映射,name属性的值为参数的名称 -->
    <bean id="user" class="learn.springcore.bean.User">
    	<constructor-arg name="name" value="jack" />
    </bean>
    <!-- 第三个构造器,使用参数顺序映射,0映射为名称参数,1映射为年龄参数 -->
    <bean id="user" class="learn.springcore.bean.User">
    	<constructor-arg index="0" value="jack" />
    	<constructor-arg index="1" value="18" />
    </bean>
    <!-- 第三个构造器,使用参数类型映射,参数的数据类型不予许重复,string映射为名称参数,int映射为年龄参数 -->
    <bean id="user" class="learn.springcore.bean.User">
    	<constructor-arg type="java.lang.String" value="jack" />
    	<constructor-arg type="java.lang.Integer" value="18" />
    </bean>
    

      1.3.2   set方法

    set方法本质就是注入属性值。

    当为基本数据类型时,使用property子标签,其中name为属性名,value为属性值

    <property name=”fieldName” value=”fieldVal”>
    

      当为对象时,使用ref子标签,其中bean为对象的名称(ID,name,alias中的任意一个)。此时bean引用的对象必须已注入到容器中。

    <ref bean="ID "></ref>
    

      1.3.2.1     集合

    注入集合类型的属性。

    1.3.2.1.1    List
    <property name="someList">
    	<list>
    		<value>element</value>
    		<ref bean="date"></ref>
    	</list>
    </property>
    
      1.3.2.1.2    Set
    <property name="someSet">
    	<set>
    		<value>element</value>
    		<ref bean="date"></ref>
    	</set>
    </property>
    
      1.3.2.1.3    Map
    <property name="someMap">
    	<map>
    		<entry key="entry" value="just something"/>
    		<entry key="birthDate" value-ref="date"/>
    	</map>
    </property>
    
      1.3.2.1.4    Properties
    <property name="someProperties">
    	<props>
    		<prop key="name">jack</prop>
    		<prop key="age">21</prop>
    	</props>
    </property>
    

      1.3.2.2    null

    注入null值,示例如下:

    <property name="nullProp">
    	<null/>
    </property>
    <property name="name" value=""/>
    

      1.3.2.3     P 前缀 & C前缀

    P字母代表property,它是属性的意思,为了更方便配置bean的属性

    C字母代表constructor,它是构造器的意思,为了更方便配置bean构造器的参数。

    使用它们,首先需要引入schema。

    <!-- p的schema-->
    xmlns:p="http://www.springframework.org/schema/p"
    <!-- c的schema-->
    xmlns:c="http://www.springframework.org/schema/c"
    

      其次需要建立映射关系,

      当属性为基本数据类型时,p:propName它会映射为对象的propName属性,它的值为属性的值。

      当属性为引用数据类型时,p:propName-ref它会映射为对象的propName属性,它的值为容器中引用对象bean的ID或name。

    1.3.3    工厂方法

    静态工厂方法方式下,class属性为工厂方法的类全名,factory-method为静态方法名称,它的返回值为注入bean的类型

    <bean id="user" class="learn.springcore.factory.UserFactory" factory-method="getInstance"/>
    

      此时UserFactory类存在getInstance方法,它必须是static修饰,返回值为User对象。

      非静态方法方式,class属性不指定,factory-bean属性为工厂类bean标签的ID或者name属性,factory-method为工厂类的方法。此时工厂类必须已注入到容器中。

    <bean id="user" factory-bean="userFactory" factory-method="getInstance"/>
    

    1.4     延迟加载

    默认情况下,伴随着容器的创建,会触发容器中所有bean的创建。若想实现按需创建,即在获取该bean的时,才触发bean的创建过程,此时可以设置lazy-init的值为true。

      当bean被其他bean依赖时,该属性会被忽略。例如beanA依赖bean B,此时设置beanB的lazy-init属性为true。容器的创建过程会触发beanA的创建过程,而beanA的创建过程会触发所有它依赖类的创建过程,此时即使beanB的lazy-init为true,它也会被创建。

    2、 自动注入

    spring提供一种自动注入方式。它有四种方式。

    No:相当于禁用自动配置功能,所有的依赖都需要手动去配置。

    byName:根据属性名称,例如当User对象存在date属性时,此时若容器中存在id为date的bean标签,会自动获取date,并将返回的对象作为User对象date属性的值

    <bean id="user" class="learn.springcore.bean.User" p:birthDate-ref="date"/>
    <bean id="date" class="java.util.Date"/>
    

      这种方式使用ref引用到id为date的bean标签。手动配置。

    <bean id="user" class="learn.springcore.bean.User" />
    <bean id="birthDate" class="java.util.Date"/>
    

      此时在创建User对象,设置birthDate属性时,会获取标识为birthDate的bean。并将返回的对象作为birthDate的属性值。标识可以为id,name,alias。

    byType:根据属性的数据类型,例如User对象存在birthDate属性,它的数据类型为java.util.Date时,当容器中存在java.util.Date类型的对象时,会将该对象作为属性值。

    constructor:与byType相似,只不过它只适用于构造器的参数。

    无论是byType,还是byName,当容器中存在多个匹配项时,会报错。

    当启用自动注入功能时(autowire的属性值不为no),需要查找匹配的bean时,可以设置当前bean标签autowire-candidate属性为false,忽略当前bean。

    例如指定自动注入方式为byName时,创建User对象,设置birthDate属性的值,此时会在容器中查询标识为birthDate的bean,当设置autowire-candidate属性为false时,<bean ** autowire-candidate="false"/>,查找过程会忽略该bean。

    3、依赖关系

    3.1  循环依赖

      假设存在A依赖B,B又依赖A的情形,

      当它们以构造器方式去配置依赖关系时,会存在错误,这是因为相互在等待对方构造器的调用。

      当以Set方式去配置时,此时需要首先去创建对象A,或者是对象B,然后在调用它的set方法,此时不会出错,是因为IOC容器首先会去调用A的构造器,或者是B的构造器,然后调用a.setB或者是b.setA,这两个方法之间不存在任何关系,完全可以把构造器的返回的对象A或者B作为这两个方法的参数。

      当以工厂方法参数去配置时,是同理的,因为彼此可以调用默认的构造器,在调用完构造器之后,然后才去调用工厂方法。

    3.2  前置依赖

      当bean B的创建过程作为bean A创建过程的前置条件时,并且此时A与B不存在依赖关系。即要保证bean B在bean A 之前被创建。此时可以通过设置beanA的depends-on属性,它的值为beanB的ID,此时在创建beanA时,总会先创建beanB,保证创建bean的顺序。

      这种情况通常发生在bean之间存在间接依赖关系。

    3.3   单例依赖原型

      当singleton的beanA依赖prototype的beanB时,要达到每次获取beanB不同,只能通过方法注入。本示例中User类为beanA,Address类为beanB。

      第一种set方式,beanA实现ApplicationContextAware接口,在获取beanB时通过applicationContext对象获取

    public class User implements ApplicationContextAware {
    	// applicationContext对象,
    	private ApplicationContext applicationContext;
    	// 用户的家庭地址
    	private Address homeAddress;
        // 通过applicationContext获取,不再是this.homeAddress
    	public Address getHomeAddress() {
    		return 
    this.applicationContext.getBean("address", Address.class);
    	}
    	public void setApplicationContext(ApplicationContext arg0) throws BeansException {
    		this.applicationContext = arg0;
    	}
    }
    

      第二种方式使用lookup-method子标签,此时该方法只能是抽象方法

    public abstract Address getHomeAddress();
    <bean id="address" class="com.bean.Address" scope="prototype"/>
    <!-- 定义User类,它的作用域为singleton -->
    <bean id="singleton_user" class="com.bean.User">
    	<lookup-method name="getHomeAddress" bean="address"/>
    </bean>
    

      其中name属性为方法的名称,方法的返回值为Address类型。Bean属性值为Address类的bean ID。

      个人建议采用第一种方式,因为第二种必须包含抽象方法,类必须为抽象类。

    4、作用域

    作用域有两种,内置,自定义。

    4.1  内置

    4.1.1   Singleton

    对于同一个IOC容器,每次获取对象,都返回相同的实例。它是默认的作用域。

    设计模式中的单例是指在每个JVM中只存在一个实例,而这里是指每个IOC容器,即每一个applicationContext对象。

    4.1.2  Prototype

    对于同一个IOC容器,每次获取对象,都会返回不同的实例。

    // 获取prototype的User实例
    User user3 = ac.getBean("prototype_user",User.class);
    User user4 = ac.getBean("prototype_user",User.class);
    System.out.println(user3 == user4); // 返回false
    

      当singleton的beanA依赖singleton的beanB时,beanA和beanB都只会创建一次,并且beanB在beanA之前创建

      当singleton的beanA依赖prototype的beanB时,理论上每次获取beanA都应该返回不同的beanB,但实际上每次获取得到的beanB是相同的,是因为对象beanA只会创建一次,依赖关系在创建过程中已经决定。

    // 获取User实例
    User user1 = ac.getBean("singleton_user",User.class);
    User user2 = ac.getBean("singleton_user",User.class);
    System.out.println(user1.getHomeAddress() == user2.getHomeAddress());
    

      当prototype的beanA依赖prototype的beanB时,每次获取都会创建beanA和beanB,并且beanB在beanA之前创建。

      当prototype的beanA依赖singleton的beanB时,每次获取都会创建beanA,如果beanB不存在,则创建beanB,在beanA之前创建。之后都会使用已创建的beanB。

    4.1.3  web相关

    request的作用域只有在Web application下才会有效果。

    Session也只有在web application下才有用,对应Session的作用域。

    Application对应web application的Application作用域。

    HttpRequest,Session,Application这些都是Servlet的九大内置对象。

    webSocket对应webSocket的作用域。

    4.2  自定义

    编写自定义作用域的步骤只有两步:

    第一步,编写自定义XXScope,实现Scope接口

    第二步,注册XXScope,通过ConfigurableBeanFactory的registerScope方法,它的第一个参数为作用域的名称,第二个参数为作用域对象。

    Spring框架中有simpleThreadScope,但是没有注册,可以尝试注册线程作用域作为练习。

    5、生命周期

    5.1    概念

    Bean的生命周期如下图

       配置bean的生命周期,有三种方式:LifeCycle callback, BeanNameAware & ApplicationContextAware, 其他Aware接口。

      Bean的生命周期分为三个阶段,创建,使用,销毁。

    每个阶段的步骤如下:

    创建:创建对象包含两个层面,第一层面是指程序层面,调用它的构造器,以及设置它的依赖等。第二层面是指用户层面,执行用户自定义的init方法。最典型的示例就是Servlet。

    1. 加载类,准备环境。
    2. 环境已就绪,准备创建对象。触发BeanPostProcessor的xxbefore方法
    3. 调用对象的构造器,若存在@PostContruct注解,在调用构造器之前执行此注解下的方法。之后执行构造器
    4. 程序层面的创建完成,执行用户自定义的init方法,若存在InitializingBean接口,在init方法调用之前执行此接口中的方法。
    5. 对象创建完毕。触发BeanPostProcessor的xxAfter方法。

      使用

    销毁:

    1. 销毁之前,若存在destroy方法,执行。若存在@Predestroy注解,在destroy方法之前执行此注解下的方法。@Predestroy,@PostConstruct都是Java的注解,非spring框架注解,它们的含义与destroy,init方法完全相同,存在一个即可。
    2. 调用完之后,触发DisposableBean的destroy方法。

       注:

    • 要使用注解,必须配置<context:annotation-config />
    • BeanPostProcessor,InitializingBean,DisposableBean必须在容器中,且在对象创建之前存在才生效。

     5.2  示例

      下面演示User bean的生命周期。

      配置

    <!-- 开启注解 -->
    <context:annotation-config/>
    <!-- BeanInitialization -->
    <bean id="initalization" class="learn.springcore.bean.lifeCycle.TestBeanInitialization"/>
    <!-- DisposableBean -->
    <bean id="disposable" class="learn.springcore.bean.lifeCycle.TestDisposableBean"/>
    <!-- BeanPostProcessor -->
    <bean id="postProcessor" class="learn.springcore.bean.lifeCycle.TestBeanPostProcessor"/>
    <!-- 实体User类 -->
    <bean id="user" class="learn.springcore.bean.User"  init-method="init" destroy-method="destroy"/>
    

      代码

    // 获取applicationContext对象
    AbstractApplicationContext  ac =  new ClassPathXmlApplicationContext("spring/application.xml");;
    // 获取User实例
    User user = ac.getBean("singleton_user", User.class);
    // 关闭容器
    ac.close();
    

      创建阶段效果

    // 创建阶段,运行User实体类的构造器
    2020-04-13 15:31:59.615 learn.springcore.bean.User 
    [main]    INFO: **************User Constructor Start*************** 
    2020-04-13 15:31:59.615 learn.springcore.bean.User 
    [main]    INFO: user constructor execution 
    2020-04-13 15:31:59.615 learn.springcore.bean.User 
    [main]    INFO: *************User Constructor End****************
    
    // 创建阶段,注入User实体类的post process before
    2020-04-13 15:31:59.617 learn.springcore.bean.lifeCycle.TestBeanPostProcessor 
    [main]    INFO: **************post process before Initialization Start*************** 
    2020-04-13 15:31:59.617 learn.springcore.bean.lifeCycle.TestBeanPostProcessor 
    [main]    INFO: postProcessBeforeInitialization execution 
    2020-04-13 15:31:59.617 learn.springcore.bean.lifeCycle.TestBeanPostProcessor 
    [main]    INFO: *************post process before Initialization  End****************
    
    // 创建阶段,User实体类中有@PostConstructor 
    2020-04-13 15:31:59.617 learn.springcore.bean.User 
    [main]    INFO: **************post construct Start*************** 
    2020-04-13 15:31:59.617 learn.springcore.bean.User 
    [main]    INFO: post construct execution 
    2020-04-13 15:31:59.617 learn.springcore.bean.User 
    [main]    INFO: *************post construct End**************** 
    
    // 创建阶段,User实体类中的init方法
    2020-04-13 15:31:59.618 learn.springcore.bean.User 
    [main]    INFO: **************init Start*************** 
    2020-04-13 15:31:59.618 learn.springcore.bean.User 
    [main]    INFO: init  execution 
    2020-04-13 15:31:59.618 learn.springcore.bean.User 
    [main]    INFO: *************init End**************** 
    
    // 创建阶段,注入User实体类的post process after
    2020-04-13 15:31:59.618 learn.springcore.bean.lifeCycle.TestBeanPostProcessor 
    [main]    INFO: **************post process after Initialization Start*************** 
    2020-04-13 15:31:59.618 learn.springcore.bean.lifeCycle.TestBeanPostProcessor 
    [main]    INFO: postProcessAfterInitialization execution 
    2020-04-13 15:31:59.618 learn.springcore.bean.lifeCycle.TestBeanPostProcessor 
    [main]    INFO: *************post process after Initialization End****************
    

      销毁阶段效果

    // 销毁阶段,运行User中@Predestroy注解的方法
    2020-04-13 15:31:59.634 learn.springcore.bean.User 
    [main]    INFO: **************pre destroy Start*************** 
    2020-04-13 15:31:59.634 learn.springcore.bean.User 
    [main]    INFO: pre destroy execution 
    2020-04-13 15:31:59.634 learn.springcore.bean.User 
    [main]    INFO: *************pre destroy End**************** 
    
    // 销毁阶段,运行User配置的destroy方法
    2020-04-13 15:31:59.634 learn.springcore.bean.User 
    [main]    INFO: **************destroy Start*************** 
    2020-04-13 15:31:59.634 learn.springcore.bean.User 
    [main]    INFO: destroy  execution 
    2020-04-13 15:31:59.634 learn.springcore.bean.User 
    [main]    INFO: *************destroy End****************
    
    // 销毁阶段,运行DisposableBean的destroy方法
    2020-04-13 15:31:59.634 learn.springcore.bean.lifeCycle.TestDisposableBean 
    [main]    INFO: **************Disposable Bean Start*************** 
    2020-04-13 15:31:59.634 learn.springcore.bean.lifeCycle.TestDisposableBean 
    [main]    INFO: Disposable Bean execution 
    2020-04-13 15:31:59.634 learn.springcore.bean.lifeCycle.TestDisposableBean 
    [main]    INFO: *************Disposable Bean  End****************
    

      注入InitializingBean,disposableBean也会触发BeanPostProcessor中的方法。

  • 相关阅读:
    Flask、Vue部署总结
    Recat教程(二)
    C第一个程序
    React教程(一)
    Flask+uwsgi+nginx 部署
    使用Dockerfile部署简单的应用
    Typescript搭建开发环境
    Typescript模块化/命名空间/装饰器
    Typescript的泛型/泛型接口
    修改git密码后,终端fatal: Authentication failed for'http://xxx/xxx.git'
  • 原文地址:https://www.cnblogs.com/rain144576/p/14758740.html
Copyright © 2020-2023  润新知