Spring Bean组件深入理解

一、Spring容器中Bean的常用属性

1-1、作用域

常用作用域有五种
在Spring2.0之前的是两种,分别是 singletonprototype
在Spring2.0之后为支持web应用的ApplicationContext,添加了三种 requestsessionglobal session

  • singletion:单例:这种bean作用域是默认的,这种作用域确保不管接收到多少个请求,每个容器中只有一个bean实例,单例模式由bean factory自身来维护
  • prototype:多例:prototype作用域与singleton作用域相反,为每一个bean请求提供一个实例
  • request:请求:在请求bean作用域内,为每一个来自客户端的网络请求创建一个实例,在请求完成以后,bean会失效并被垃圾回收器回收
  • session:响应会话:确保每个session中有一个bean实例,在session过期后,bean会随之失效
  • global-session:全局session作用域:global-session和portlet应用相关。当应用部署在portlet容器中时,它包含很多portlet。如果想让所有的portlet共用全局存储变量,那么这个全局存储变量需要存储在global-session中。全局作用域与servlet中的session作用域效果相同

XML方式

<bean id="persion" class="com.helloworld.entity.Persion" scope="singleton" />

注解方式

@Scope("singleton")

1-2、延迟加载

默认情况下,当容器启动之后,会将所有作用域为单例的bean创建好,如果配置 lazy-init 值为 true ,表示延迟加载,即容器启动之后,不会立即创建该实例

XML方式

<bean id="persion" class="com.helloworld.entity.Persion" lazy-init="true" />

注解方式

@Lazy

二、@Component和@Bean区别

  • @Component注解作用于类,@Bean注解作用域方法
  • @Bean注解比@Component自定义更强,很多地方我们可以使用@Bean来注解,比如我们引入第三方库中需要用到@Bean

三、Bean的创建方式

创建 Bean 有三种方式,分别是:通过调用构造方法创建 Bean、调用实例工厂方法创建Bean、调用静态工厂方法创建 Bean、通过 setter 方式创建 Bean

3-1、通过调用构造方法创建 Bean

通过XML配置文件或者注解的方式配置 Bean, Spring 会通过调用 Bean 类的构造方法,来创建 Bean 的实例。通过 xml 文件配置,明确指定 Bean 的 class 属性,或者通过注解配置,Spring 容器知道 Bean 的完整类名,然后通过反射调用该类的构造方法即可

3-2、调用实例工厂方法创建Bean

Fruits.java

public interface Fruits {

    void show();

}

Apple.java

public class Apple implements Fruits {

    @Override
    public void show() {
        System.out.println("这是一个生产苹果的工厂");
    }
}

Banana.java

public class Banana implements Fruits {

    @Override
    public void show() {
        System.out.println("这是一个生产香蕉的工厂");
    }
}

FruitsFactory.java

public class FruitsFactory {

    public Fruits getFruits(String type){
        if ("apple".equals(type)) {
            return new Apple();
        } else if ("banana".equals(type)){
            return new Banana();
        } else {
            throw new IllegalArgumentException("fruits type is illegal!");
        }
    }

}

resources 文件夹下配置文件 factorybean.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="fruitsFactory" class="com.helloworld.test.FruitsFactory" />

    <bean id="apple" factory-bean="fruitsFactory" factory-method="getFruits">
        <constructor-arg value="apple" />
    </bean>

    <bean id="banana" factory-bean="fruitsFactory" factory-method="getFruits">
        <constructor-arg value="banana" />
    </bean>

</beans>

测试类,AppTest.java

public class AppTest {
    public static void main(String[] args) {
        Resource resource = new ClassPathResource("ApplicationContext.xml");
        BeanFactory beanFactory = new DefaultListableBeanFactory();
        BeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader((BeanDefinitionRegistry) beanFactory);
        beanDefinitionReader.loadBeanDefinitions(resource);

        Apple apple = (Apple) beanFactory.getBean("apple");
        Banana banana = (Banana)beanFactory.getBean("banana");
        apple.show();
        banana.show();
    }
}

结果

这是一个生产苹果的工厂
这是一个生产香蕉的工厂

3-3、调用静态工厂方法创建 Bean

在实例工厂方法的基础上作修改
FruitsFactory

public class FruitsFactory {

    public static Fruits getFruits(String type){
        if ("apple".equals(type)) {
            return new Apple();
        } else if ("banana".equals(type)){
            return new Banana();
        } else {
            throw new IllegalArgumentException("fruits type is illegal!");
        }
    }

}

resources 文件夹下配置文件 factorybean.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="apple" class="com.helloworld.test.FruitsFactory" factory-method="getFruits">
        <constructor-arg value="apple" />
    </bean>

    <bean id="banana" class="com.helloworld.test.FruitsFactory" factory-method="getFruits">
        <constructor-arg value="banana" />
    </bean>

</beans>

3-4、通过 setter 方式创建 Bean

UserInfo.java

/**
 * @author q-linyu
 * @className UserInfo
 * @description 用户详情
 * @date 2021/04/01 0:33
 */
public class UserInfo {

    private long id;
    private String nickname;

    public UserInfo(){}

    public UserInfo(long id, String nickname) {
        this.id = id;
        this.nickname = nickname;
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getNickname() {
        return nickname;
    }

    public void setNickname(String nickname) {
        this.nickname = nickname;
    }

    @Override
    public String toString() {
        return "UserInfo{" +
                "id=" + id +
                ", nickname='" + nickname + '\'' +
                '}';
    }
}

resources 文件夹下配置文件 factorybean.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="userInfo" class="com.helloworld.entity.UserInfo">
        <constructor-arg name="id" value="1" />
        <constructor-arg name="nickname" value="zhangsan" />
    </bean>

</beans>

3-5、调用实例工厂方法和调用静态工厂方法创建 Bean 的区别

异同之处

  • 配置实例工厂方法创建 Bean,必须将实例工厂配置成 Bean 实例;而配置静态工厂方法创建 Bean,则无需配置工厂 Bean;
  • 配置实例工厂方法创建 Bean,必须使用 factory-bvean 属性确定工厂 Bean; 而配置静态工厂方法创建 Bean,则使用 class 属性确定静态工厂类。

相同之处

  • 都需要使用 factory-method 指定生产 Bean 实例的工厂方法;
  • 工厂方法如果需要参数,都使用 <constructor-arg.../> 元素指定参数值;
  • 普通的设值注入,都使用 <property.../>元素确定参数值。

四、FactoryBean 和 BeanFactory

FactoryBean 和 BeanFactory 它们是两个极易混淆的概念,逐一分析和理解

4-1、FactoryBean

FactoryBean 见名知意就是 工厂Bean 的意思,它是 Spirng 提供的 工厂bean 的一个接口
其中从源码中我们就会发现它有三个方法

@Nullable
T getObject() throws Exception;

 @Nullable
Class<?> getObjectType();

default boolean isSingleton() {
    return true;
}

当自定义一个类实现了 FactoryBean 接口后,将该类部署在 Spring 容器里,再通过 Spring 容器调用 getBean 方法获取到的就不是该类的实例了,而是该类实现的 getObject 方法的返回值。这三个方法意义如下:

  • getObject() 方法返回了该工厂Bean 生成的 java 实例
  • getObjectType() 该方法返回该工厂Bean 生成的 java 实例的类型
  • isSingleton() 该方法返回该工厂Bean 生成的 java 实例是否为单例

直接上代码:

/**
 * @author q-linyu
 * @className UserInfoFactoryBean
 * @description 使用FactoryBean 管理 UserInfo 对象
 * @date 2021/04/01 12:20
 */
public class UserInfoFactoryBean implements FactoryBean<UserInfo> {

    /**
     * 创建具体对象是由此getObject()方法来返回的
     * @return
     * @throws Exception
     */
    @Override
    public UserInfo getObject() throws Exception {
        return new UserInfo();
    }

    /**
     * 创建的具体bean对象的类型
     * @return
     */
    @Override
    public Class<?> getObjectType() {
        return UserInfo.class;
    }

    /**
     * 是否是单例
     * @return
     */
    @Override
    public boolean isSingleton() {
        return true;
    }
}

resources 文件夹下配置文件 factorybean.xml

<bean id="userInfoFactoryBean" class="com.helloworld.beans.UserInfoFactoryBean" />

测试类:AppTest.xml

public class AppTest {
    public static void main(String[] args) throws Exception {
        ApplicationContext context = new ClassPathXmlApplicationContext("factorybean.xml");
        Object bean = context.getBean("userInfoFactoryBean");
        System.out.println(bean.getClass().toString());
    }
}

结果

class com.helloworld.entity.UserInfo

得出的结论是:FactoryBean是个bean,在IOC容器的基础上给Bean的实现加上了一个简单工厂模式和装饰模式,是一个可以生产对象和装饰对象的工厂bean,由Spring管理后,生产的对象是由getObject()方法决定的

4-2、BeanFactory

用一个最直观的方式去理解。打开IDEA,Ctrl + N 搜索 BeanFactory,再按 Ctrl + H 此时右侧显示 接口/类 的关系导航栏
image
在图中我们就可以看得出,BeanFactory 作为一个根接口,定义了IOC容器的最基本形式,并提供了IOC容器应遵守的的最基本的接口,也就是Spring IOC所遵守的最底层和最基本的编程规范。在Spring代码中,BeanFactory只是个接口,并不是IOC容器的具体实现,同时也是某种功能的扩展实现。比如我们最常用:ApplicationContext、AnnotationConfigApplicationContext、FileSystemXmlApplicationContext、ClassPathXmlApplicationContext

BeanFactory 包含如下几个基本方法:

public interface BeanFactory {
    String FACTORY_BEAN_PREFIX = "&";
    // 返回容器中 id 为 name 的 Bean 实例
    Object getBean(String var1) throws BeansException;
    // 返回容器中 id 为name,并且类型为 var1 的Bean
    <T> T getBean(String var1, Class<T> var2) throws BeansException;

    Object getBean(String var1, Object... var2) throws BeansException;
    // 获取Spring容器中属于 var1 类型的、唯一的 Bean 实例
    <T> T getBean(Class<T> var1) throws BeansException;

    <T> T getBean(Class<T> var1, Object... var2) throws BeansException;

    <T> ObjectProvider<T> getBeanProvider(Class<T> var1);

    <T> ObjectProvider<T> getBeanProvider(ResolvableType var1);
    // 判断Spring容器中是否包含 id 为 name 的 Bean 实例
    boolean containsBean(String var1);

    boolean isSingleton(String var1) throws NoSuchBeanDefinitionException;

    boolean isPrototype(String var1) throws NoSuchBeanDefinitionException;

    boolean isTypeMatch(String var1, ResolvableType var2) throws NoSuchBeanDefinitionException;

    boolean isTypeMatch(String var1, Class<?> var2) throws NoSuchBeanDefinitionException;
    // 返回 id 为 name 的 Bean 实例的类型
    @Nullable
    Class<?> getType(String var1) throws NoSuchBeanDefinitionException;

    String[] getAliases(String var1);
}

五、生命周期

5-1、执行流程

image

5-2、接口方法分类

  1. Bean标签级方法:这个包括了Bean本身调用的方法和通过配置文件中的init-method和destroy-method指定的方法
  2. Bean级生命周期接口方法:BeanNameAware、BeanFactoryAware、InitializingBean和DiposableBean
  3. 容器级生命周期接口方法:InstantiationAwareBeanPostProcessor 和 BeanPostProcessor接口实现,通常叫为 "后置处理器"
  4. 后处理器工厂接口方法:AspectJWeavingEnabler, ConfigurationClassPostProcessor, CustomAutowireConfigurer等等工厂接口的方法,工厂后处理器也是容器级的,可在应用上下文装配配置文件之后立即调用

5-3、示例

UserInfo.java

package com.helloworld.entity;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.*;

/**
 * @author q-linyu
 * @className UserInfo
 * @description 用户信息
 * @date 2021/04/22 13:00
 */
public class UserInfo implements BeanNameAware,BeanFactoryAware, InitializingBean, DisposableBean {

    private long id;
    private String nickname;
    private int age;
    private String email;
    private String address;

    public UserInfo() {
        System.out.println("[构造器] 调用[UserInfo]的构造器实例化");
    }

    public UserInfo(long id, String nickname, int age, String email, String address) {
        this.id = id;
        this.nickname = nickname;
        this.age = age;
        this.email = email;
        this.address = address;
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        System.out.println("[注入属性] 注入属性[id]");
        this.id = id;
    }

    public String getNickname() {
        return nickname;
    }

    public void setNickname(String nickname) {
        System.out.println("[注入属性] 注入属性[nickname]");
        this.nickname = nickname;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        System.out.println("[注入属性] 注入属性[age]");
        this.age = age;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        System.out.println("[注入属性] 注入属性[email]");
        this.email = email;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        System.out.println("[注入属性] 注入属性[address]");
        this.address = address;
    }

    @Override
    public String toString() {
        return "UserInfo{" +
                "id=" + id +
                ", nickname='" + nickname + '\'' +
                ", age=" + age +
                ", email='" + email + '\'' +
                ", address='" + address + '\'' +
                '}';
    }

    private BeanFactory beanFactory;
    private String beanName;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("[BeanFactoryAware接口] 调用BeanFactoryAware.setBeanFactory()");
        this.beanFactory = beanFactory;
    }

    @Override
    public void setBeanName(String beanName) {
        System.out.println("[BeanNameAware接口] BeanNameAware接口.setBeanName()");
        this.beanName = beanName;

    }

    @Override
    public void destroy() throws Exception {
        System.out.println("[DiposibleBean接口] 调用DiposibleBean.destory()");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("[InitializingBean接口] 调用InitializingBean.afterPropertiesSet()");
    }

    public void myinit(){
        System.out.println("[init-method] 调用<bean>的init-method属性指定的初始化方法");
    }

    public void myDestory(){
        System.out.println("[destroy-method] 调用<bean>的destroy-method属性指定的初始化方法");
    }
}


MyBeanPostProcessor.java

package com.helloworld.processors;

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

/**
 * @author q-linyu
 * @className MyBeanPostProcessor
 * @description 自定义后置处理器
 * @date 2021/04/22 13:15
 */
public class MyBeanPostProcessor implements BeanPostProcessor {

    public MyBeanPostProcessor() {
        super();
        System.out.println("这是BeanPostProcessor实现类构造器");
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("BeanPostProcessor接口方法postProcessBeforeInitialization对属性进行更改");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("BeanPostProcessor接口方法postProcessAfterInitialization对属性进行更改");
        return bean;
    }
}


MyInstantiationAwareBeanPostProcessor.java

package com.helloworld.processors;

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

/**
 * @author q-linyu
 * @className MyInstantiationAwareBeanPostProcessor
 * @description
 * @date 2021/04/22 13:24
 */
public class MyInstantiationAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter {

    public MyInstantiationAwareBeanPostProcessor(){
        super();
        System.out.println("这是InstantiationAwareBeanPostProcessorAdapter实现类构造器");
    }

    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        System.out.println("InstantiationAwareBeanPostProcessor调用postProcessBeforeInstantiation方法");
        return null;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("InstantiationAwareBeanPostProcessor调用postProcessAfterInitialization方法");
        return bean;
    }



}


MyBeanFactoryPostProcessor.java

package com.helloworld.processors;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;

/**
 * @author q-linyu
 * @className MyBeanFactoryPostProcessor
 * @description
 * @date 2021/04/22 13:42
 */
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    public MyBeanFactoryPostProcessor() {
        super();
        System.out.println("这是BeanFactoryPostProcessor实现类构造器");
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        System.out.println("BeanFactoryPostProcessor调用postProcessBeanFactory方法");
        BeanDefinition beanDefinition = configurableListableBeanFactory.getBeanDefinition("userInfo");
        beanDefinition.getPropertyValues().addPropertyValue("nickname","zhangsan");
    }
}


MyBeanFactoryPostProcessor.java

package com.helloworld.processors;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;

/**
 * @author q-linyu
 * @className MyBeanFactoryPostProcessor
 * @description
 * @date 2021/04/22 13:42
 */
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    public MyBeanFactoryPostProcessor() {
        super();
        System.out.println("这是BeanFactoryPostProcessor实现类构造器");
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        System.out.println("BeanFactoryPostProcessor调用postProcessBeanFactory方法");
        BeanDefinition beanDefinition = configurableListableBeanFactory.getBeanDefinition("userInfo");
        beanDefinition.getPropertyValues().addPropertyValue("id",1L);
        beanDefinition.getPropertyValues().addPropertyValue("nickname","刘备");
    }
}


编写 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
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="beanPostProcessor" class="com.helloworld.processors.MyBeanPostProcessor" />
    <bean id="instantiationAwareBeanPostProcessor" class="com.helloworld.processors.MyInstantiationAwareBeanPostProcessor" />
    <bean id="beanFactoryPostProcessor" class="com.helloworld.processors.MyBeanFactoryPostProcessor" />

    <bean id="userInfo" class="com.helloworld.entity.UserInfo" init-method="myinit" destroy-method="destroy">
        <property name="id" value="1" />
        <property name="nickname" value="刘备" />
        <property name="age" value="25" />
        <property name="email" value="123456789@qq.com" />
        <property name="address" value="成都市蜀汉王朝辅助大臣李严代收" />
    </bean>

</beans>

测试用例

package com.helloworld.test;

import com.helloworld.entity.UserInfo;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author q-linyu
 * @className AppTest
 * @description 测试用例
 * @date 2021/04/01 12:25
 */
public class AppTest {
    public static void main(String[] args) throws Exception {

        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
        System.out.println("容器初始化成功");
        UserInfo userInfo = context.getBean("userInfo",UserInfo.class);

        System.out.println("现在开始关闭容器!");
        System.out.println(userInfo.toString());
        context.registerShutdownHook();
    }
}


结果

这是BeanFactoryPostProcessor实现类构造器
BeanFactoryPostProcessor调用postProcessBeanFactory方法
这是BeanPostProcessor实现类构造器
这是InstantiationAwareBeanPostProcessorAdapter实现类构造器
InstantiationAwareBeanPostProcessor调用postProcessBeforeInstantiation方法
[构造器] 调用[UserInfo]的构造器实例化
[注入属性] 注入属性[id]
[注入属性] 注入属性[nickname]
[注入属性] 注入属性[age]
[注入属性] 注入属性[email]
[注入属性] 注入属性[address]
[BeanNameAware接口] BeanNameAware接口.setBeanName()
[BeanFactoryAware接口] 调用BeanFactoryAware.setBeanFactory()
BeanPostProcessor接口方法postProcessBeforeInitialization对属性进行更改
[InitializingBean接口] 调用InitializingBean.afterPropertiesSet()
[init-method] 调用<bean>的init-method属性指定的初始化方法
BeanPostProcessor接口方法postProcessAfterInitialization对属性进行更改
InstantiationAwareBeanPostProcessor调用postProcessAfterInitialization方法
容器初始化成功
现在开始关闭容器!
UserInfo{id=1, nickname='zhangsan', age=25, email='123456789@qq.com', address='成都市蜀汉王朝刘备代收'}
[DiposibleBean接口] 调用DiposibleBean.destory()
posted @ 2021-04-22 14:05  q-linyu  阅读(178)  评论(0)    收藏  举报