Bean

Posted 桔子汽水蓝莓味

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Bean相关的知识,希望对你有一定的参考价值。

Bean

首先要熟悉两个概念:

配置元数据:元数据配置包含很多配置信息, 包括构造函数参数注入, 对象属性值, 和容器相关的信息比如初始化方法, 工厂方法等等.

bean: 是一个被实例化,组装,并通过 Spring IoC 容器所管理的对象。

这些 bean 是由用容器提供的配置元数据创建的,例如,之前在 XML 的表单中的定义。

这里写图片描述

bean 定义包含称为配置元数据的信息,下述容器也需要知道配置元数据:

  • 如何创建一个 bean
  • bean 的生命周期的详细信息
  • bean 的依赖关系
属性描述
class这个属性是强制性的,并且指定用来创建 bean 的 bean 类。
name这个属性指定唯一的 bean 标识符。在基于 XML 的配置元数据中,你可以使用 ID 和/或 name 属性来指定 bean 标识符。
scope这个属性指定由特定的 bean 定义创建的对象的作用域,它将会在 bean 作用域的章节中进行讨论。
constructor-arg它是用来注入依赖关系的,并会在接下来的章节中进行讨论。
properties它是用来注入依赖关系的,并会在接下来的章节中进行讨论。
autowiring mode它是用来注入依赖关系的,并会在接下来的章节中进行讨论。
lazy-initialization mode延迟初始化的 bean 告诉 IoC 容器在它第一次被请求时,而不是在启动时去创建一个 bean 实例。
initialization 方法在 bean 的所有必需的属性被容器设置之后,调用回调方法。它将会在 bean 的生命周期章节中进行讨论。
destruction 方法当包含该 bean 的容器被销毁时,使用回调方法。它将会在 bean 的生命周期章节中进行讨论。

bean的作用域

Spring 框架支持以下五个作用域。

作用域描述
singleton该作用域将 bean 的定义的限制在每一个 Spring IoC 容器中的一个单一实例(默认)。
prototype该作用域将单一 bean 的定义限制在任意数量的对象实例。
request该作用域将 bean 的定义限制为 HTTP 请求。只在 web-aware Spring ApplicationContext 的上下文中有效。
session该作用域将 bean 的定义限制为 HTTP 会话。 只在web-aware Spring ApplicationContext的上下文中有效。
global-session该作用域将 bean 的定义限制为全局 HTTP 会话。只在 web-aware Spring ApplicationContext 的上下文中有效。

singleton 作用域:

如果作用域设置为 singleton,那么 Spring IoC 容器刚好创建一个由该 bean 定义的对象的实例。该单一实例将存储在这种单例 bean 的高速缓存中,以及针对该 bean 的所有后续的请求和引用都返回缓存对象。

默认作用域是始终是 singleton,但是当仅仅需要 bean 的一个实例时,你可以在 bean 的配置文件中设置作用域的属性为 singleton。
案例:

package com.yangjun;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;

public class MainApp {
    public static void main(String[] args) {        
        ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
        HelloWorld objA = (HelloWorld) context.getBean("helloWorld");
        objA.setMessage("I'm object A");
        objA.getMessage();
        HelloWorld objB = (HelloWorld) context.getBean("helloWorld");
        objB.getMessage();
    }
}

然后修改Beans.xml文件

    <bean id="helloWorld" class="com.yangjun.HelloWorld" scope="singleton">
        <property name="message" value="杨峻!" />
    </bean>

控制台输出信息为:

这里写图片描述

prototype 作用域

如果作用域设置为 prototype,那么每次特定的 bean 发出请求时 Spring IoC 容器就创建对象的新的 Bean 实例。一般说来,满状态的 bean 使用 prototype 作用域和没有状态的 bean 使用 singleton 作用域。

为了定义 prototype 作用域,你可以在 bean 的配置文件中设置作用域的属性为 prototype。
修改Beans.xml文件

    <bean id="helloWorld" class="com.yangjun.HelloWorld" scope="prototype">
        <property name="message" value="杨峻!" />
    </bean>

控制台输出信息为:
这里写图片描述

bean的实例化方式

Spring的实例化Bean有三种方式:

  • 使用类构造器直接实例化
  • 使用静态工厂的方法实例化
  • 使用实例工厂方法实例化

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" 
        xmlns:context="http://www.springframework.org/schema/context" 
        xmlns:tx="http://www.springframework.org/schema/tx" 
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd  
                http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd  
                http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">  
        <!-- 使用类构造器直接实例化 -->    
        <bean id="userBean1" class="com.yangjun.spring.implbean.UserBean" />  
        <!-- 使用静态工厂的方法实例化 -->  
        <bean id="userBean2" class="com.yangjun.spring.factory.BeanFactory" factory-method="UserBeanService" />  
        <!-- 使用实例工厂方法实例化 -->  
        <bean id="factory" class="com.yangjun.spring.factory.BeanFactory" />  
        <bean id="userBean3" factory-bean="factory" factory-method="getUserBeanService" />  
</beans>  

bean的生命周期

Bean 的生命周期从Spring容器着手实例化Bean开始,直到最终销毁Bean,这当中经过了许多关键点,每个关键点都涉及特定方法的调用,可以将这些方法大致划分为4类:

  • Bean自身的方法:如调用Bean构造函数,实例化Bean,调用Setter设置Bean的属性值以及通过xml中bean的init-method和destroy-method所指定的方法;

  • Bean级生命周期接口方法:如BeanNameAware、BeanFactoryAware、InitializationBean和DisposableBean,这些接口方法由Bean类直接实现;

  • 容器级生命周期接口方法:由InstantiationAwareBeanPostProcessor和BeanPostProcessor这两个接口实现,一般称他们的实现类为”后处理器“。

  • 工厂后处理器接口方法:这个包括了AspectJWeavingEnabler, ConfigurationClassPostProcessor, CustomAutowireConfigurer等等非常有用的工厂后处理器接口的方法。工厂后处理器也是容器级的。在应用上下文装配配置文件之后立即调用。

Bean自身的方法:

先说两个简单的生命周期回调方法,为了定义安装和拆卸一个 bean,我们需要声明 init-method和 destroy-method。在实例化 bean 时,立即调用init-method方法方法。同样,只有从容器中移除 bean之后,才能调用destroy-method方法。

初始化回调
org.springframework.beans.factory.InitializingBean 接口指定一个单一的方法:

void afterPropertiesSet() throws Exception;

因此,可以简单地在 afterPropertiesSet() 方法中实现上述接口和初始化工作,如下所示:

package com.yangjun.bean;

import org.springframework.beans.factory.InitializingBean;

public class HelloWorld implements InitializingBean {
    private String message;

    public void setMessage(String message) {
        this.message = message;
    }

    public void getMessage() {
        System.out.println("Your Message : " + message);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        // TODO Auto-generated method stub
        System.out.println("正在准备");
    }

}

在基于 XML 的配置元数据的情况下,你可以使用 init-method 属性来指定带有 void 无参数方法的名称。例如:

    <bean id="helloWorld" class="com.yangjun.bean.HelloWorld"
        init-method="init" >
        <property name="message" value="杨峻!" />
    </bean>

下面是类的定义:

package com.yangjun.bean;

import org.springframework.beans.factory.InitializingBean;

public class HelloWorld implements InitializingBean {
    private String message;

    public void setMessage(String message) {
        this.message = message;
    }

    public void getMessage() {
        System.out.println("Your Message : " + message);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        // TODO Auto-generated method stub
        System.out.println("正在准备");
    }

    public void init() {
        System.out.println("正在生成");
    }

}

销毁回调

org.springframework.beans.factory.DisposableBean 接口指定一个单一的方法:

void destroy() throws Exception;

因此,你可以简单地实现上述接口并且结束工作可以在 destroy() 方法中执行,如下所示:

package com.yangjun.bean;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;

public class HelloWorld implements InitializingBean, DisposableBean {
    private String message;

    public void setMessage(String message) {
        this.message = message;
    }

    public void getMessage() {
        System.out.println("Your Message : " + message);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        // TODO Auto-generated method stub
        System.out.println("正在准备");
    }

    public void init() {
        System.out.println("正在生成");
    }

    public void destroy() {
        // TODO Auto-generated method stub
        System.out.println("正在销毁");
    }

}

在基于 XML 的配置元数据的情况下,你可以使用 destroy-method 属性来指定带有 void 无参数方法的名称。例如:

    <bean id="helloWorld" class="com.yangjun.bean.HelloWorld"
        init-method="init" destroy-method="destroy">
        <property name="message" value="杨峻!" />
    </bean>

需要注意的是当bean的作用域定义为prototype的时候是不会被spring销毁的,只能等待垃圾回收机制销毁

修改主程序MainApp.java

        AbstractApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
        HelloWorld objA = (HelloWorld) context.getBean("helloWorld");
        objA.getMessage();
        context.registerShutdownHook();

运行MainApp.java

控制台输出:

这里写图片描述

如果有多个bean的初始化和销毁的方法名都是一样的,可以在beans里面声明default-init-method 和 default-destroy-method来配置

<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-3.0.xsd"
    default-init-method="init" 
    default-destroy-method="destroy">

   <bean id="..." class="...">
       <!-- collaborators and configuration for this bean go here -->
   </bean>

</beans>

Bean的后处理器:

容器级生命周期接口方法是由InstantiationAwareBeanPostProcessor和BeanPostProcessor这两个接口实现,一般称他们的实现类为“后处理器”

后处理器可以用于提供自己的实例化逻辑,依赖解析逻辑等。
也可以在 Spring 容器通过插入一个或多个 BeanPostProcessor 的实现,来完成经过实例化,配置和初始化一个bean之后的一些自定义逻辑回调方法。

可以配置多个 BeanPostProcessor接口,然后通过设置 BeanPostProcessor 实现的 Ordered 接口提供的 order 属性来控制这些 BeanPostProcessor 接口的执行顺序。

BeanPostProcessor 可以对 bean(或对象)实例进行操作,这意味着 Spring IoC 容器实例化一个 bean 实例,然后 BeanPostProcessor 接口进行它们的工作。

ApplicationContext 会自动检测由 BeanPostProcessor 接口的实现定义的 bean,注册这些 bean 为后置处理器,然后通过在容器中创建 bean,在适当的时候调用它。

在上面的例子的基础之上,创建一个BeanPostProcessor后处理器:

package com.yangjun.postprocessor;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class BPP01 implements BeanPostProcessor {

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        // TODO Auto-generated method stub
        System.out.println("BeforeInitialization : " + beanName);
        return bean;
        // return null;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        // TODO Auto-generated method stub
        System.out.println("AfterInitialization : " + beanName);
        return bean;
        // return null;
    }

}

配置Bean.xml

    <bean class="com.yangjun.postprocessor.BPP01" />

运行主程序:
这里写图片描述

剩下的我还在研究

其他例子和我的理解:

实现各种生命周期控制访问的Car

package com.yangjun.bean;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;

public class Car implements BeanFactoryAware, BeanNameAware, InitializingBean, DisposableBean {
    private String brand;
    private String color;
    private int maxSpeed;
    private BeanFactory beanFactory;
    private String beanName;

    public String getColor() {
        return color;
    }
    public void setColor(String color) {
        this.color = color;
    }
    public int getMaxSpeed() {
        return maxSpeed;
    }
    public void setMaxSpeed(int maxSpeed) {
        this.maxSpeed = maxSpeed;
    }
    public String getBrand() {
        return brand;
    }
    public BeanFactory getBeanFactory() {
        return beanFactory;
    }
    public String getBeanName() {
        return beanName;
    }
    // 管理Bean生命周期的接口
    public Car() {
        System.out.println("调用Car构造函数");
    }
    public void setBrand(String brand) {
        System.out.println("调用setBrand()设置属性");
        this.brand = brand;
    }
    // BeanNameAware接口方法
    public void setBeanName(String beanName) {
        System.out.println("调用BeanNameAware.setBeanName()");
        this.beanName = beanName;
    }
    // BeanFactoryAware接口方法
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("调用BeanFactoryAware.setBeanFactory()");
        this.beanFactory = beanFactory;
    }

    // initializingBean接口方法
    public void afterPropertiesSet() throws Exception {
        System.out.println("调用InitializingBean.afterPropertiesSet()");
    }
    // 通过<bean>的init-method属性指定的初始化方法
    public void myInit() {
        System.out.println("调用inti-method所指定的myInit(),将maxSpeed设置为240.");
        this.maxSpeed = 240;
    }
    public void introduce() {
        System.out.println("bradn:" + brand + ";color" + color + "; maxSpeed:" + maxSpeed);
    }
    // DisposableBean接口方法
    public void destroy() throws Exception {
        System.out.println("调用DisposableBean.destroy()");
    }
    // 通过<bean>的destory-method属性指定的销毁方法
    public void myDestory() {
        System.out.println("调用destory-method所指定的myDestory()方法。");
    }
}

InstantiationAwareBeanPostProcessor实现类

package com.yangjun.bean;

import java.beans.PropertyDescriptor;

import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;

@SuppressWarnings("unchecked")
public class MyInstantiationAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter {

    // 接口方法:实例化bean前进行调用
    public Object postProcessBeforeInstantiation(Class beanClass, String beanName) throws BeansException {
        // 1-1仅对容器中的car-bean进行处理
        if ("car".equals(beanName)) {
            System.out.println("InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation()");
        }
        return null;
    }

    // 接口方法:在实例化bean后进行调用
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        if ("car".equals(beanName)) {
            System.out.println("InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation()");
        }
        return true;
    }

    // 接口方法:在设置某个属性时调用
    public PropertyValues postProcessPropertyValues(PropertyValues propertyvalues,
            PropertyDescriptor apropertydescriptor[], Object bean, String beanName) throws BeansException {
        // 3-1仅对容器中car-bean进行处理,还可以通过post入参进行过滤,
        // 仅对car的某个特定属性进行处理
        if ("car".equals(beanName)) {
            System.out.println("InstantiationAwareBeanPostProcessor.postProcessPropertyValues");
        }
        return propertyvalues;
    }
}

BeanPostProcessor实现类

package com.yangjun.bean;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class MyBeanPostProcessor implements BeanPostProcessor {

    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (beanName.equals("car")) {
            Car car = (Car) bean;
            if (car.getColor() == null) {
                System.out.println("调用BeanPostProcessor.postProcessBeforeInitialization(),color为空,设置为默认黑色.");
                car.setColor("黑色");
            }
        }
        return bean;
    }

    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

        if (beanName.equals("car")) {
            Car car = (Car) bean;
            if (car.getMaxSpeed() >= 200) {
                System.out.println("调用BeanPostProcessor.postProcessAfterInitialization(),将maxSpedd调整为200.");
                car.setMaxSpeed(200);
            }
        }
        return bean;
    }

}

在Spring配置文件中定义Car的配置信息

<?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:p="http://www.springframework.org/schema/p" 
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

    <bean id="helloWorld" class="com.yangjun.bean.HelloWorld" scope="prototype">
        <property name="message" value="杨峻!" />
    </bean>

    <bean id="car" class="com.yangjun.bean.Car" init-method="myInit" destroy-method="myDestory" 
        p:brand="红旗CA72" 
        p:maxSpeed="300" 
        scope="singleton"
    />

</beans>

让容器装载配置文件,然后再分别注册上面所提供的两个后处理器:

package com.yangjun.beanfactory;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;

import com.yangjun.bean.Car;
import com.yangjun.bean.MyBeanPostProcessor;
import com.yangjun.bean.MyInstantiationAwareBeanPostProcessor;

public class BeanLifeCycle {

    private static void LifiCycleInBeanFactory() {
        // 第1步:装载配置文件,并启动容器
        Resource res = new ClassPathResource("beans.xml");
        BeanFactory bf = new XmlBeanFactory(res);

        // 第2步:向容器中注册MyBeanPostProcesser处理器
        ((ConfigurableBeanFactory) bf).addBeanPostProcessor(new MyBeanPostProcessor());
        // 第3步:向容器中注册MyInstantiationAwareBeanPostProcessor后处理器
        ((ConfigurableBeanFactory) bf).addBeanPostProcessor(new MyInstantiationAwareBeanPostProcessor());

        // 第4步:第一次从容器中获取car,将处罚容器实例化该Bean,这将引发Bean生命周期方法的调用
        Car car1 = (Car) bf.getBean("car");
        car1.introduce();
        car1.setColor("红色");

        // 第5步:第二次从容器中获取car,直接从缓存池中取(因为 scope="singleton")
        Car car2 = (Car) bf.getBean("car");

        // 第6步:查看car1和car2是否指向同一引用
        System.out.println("car1==car2" + (car1 == car2));

        // 第7步:关闭容器
        ((XmlBeanFactory) bf).destroySingletons();

    }

    public static void main(String[] args) {
        LifiCycleInBeanFactory();
    }

}

流程说明:

这里写图片描述

以上是关于Bean的主要内容,如果未能解决你的问题,请参考以下文章

Spring IoCSpring Bean

spring之注解概述

Spring之Bean定义继承和依赖注入

Spring学习笔记5 - Bean定义继承

Spring组件BeanDefinition 源码解析

springboot启动流程ioc依赖注入