Skip to main content

Bean基础

本篇文章主要介绍Bean相关的知识点,让我们进一步去理解Spring 中 Bean的概念。

Spring Bean 基础#

Bean的名称-标识#

每个bean都有一个或多个标识符。这些标识符在bean的容器中必须是唯一的。一个bean通常只有一个标识符。当然,如果需要多个,则可以引用别名。 在xml中可以用idname 来定义bean的标识,但是id只能设置一个名称,而name可以设置多个(用,号或者;号或者空格隔开)

<bean id="observer" name="observer_a,observer_b observer_c" class="com.lykos.ioc.chapter2.Observer"/>

不仅如此,创建bean的别名还有一种方式就是用alias

<alias name="observer_c" alias="observer_d"/><alias name="observer_c" alias="observer_e"/>

Bean的创建方式#

bean支持构造方法工场方法创建

构造方法创建#

当然工场方法创建与构造方法创建使用方法一样

@Datapublic class Observer {    //无参构造方法    public Observer(){}    //有参构造方法    @ConstructorProperties({"name"})    public Observer(String name){        this.name = name;    }    private String name;}
    <!--    使用无参构造方法,创建-->    <bean id="observer_constructor" class="com.lykos.ioc.chapter2.Observer">    </bean>    <!--    使用有参构造方法:属性名创建-->    <bean id="observer_constructor_arg" class="com.lykos.ioc.chapter2.Observer">        <constructor-arg name="name" value="observer_constructor_arg_name"/>    </bean>    <!--    使用有参构造方法:下标创建-->    <bean id="observer_constructor_arg_index" class="com.lykos.ioc.chapter2.Observer">        <constructor-arg index="0" value="observer_constructor_arg_name_index"/>    </bean>

工场方法创建#

支持静态方法和非静态方法

静态工场#
public class ObserverStaticFactory {    public static Observer createObserver(){        return new Observer();    }}
非静态工场#
public class ObserverNonStaticFactory {    public Observer createObserver(){        return new Observer();    }}
    <!--    静态工场方法创建-->    <bean id="observer_from_static_factory" class="com.lykos.ioc.chapter2.ObserverStaticFactory" factory-method="createObserver"></bean>    <!--    非静态工场方法创建-->    <bean id="observerFactory" class="com.lykos.ioc.chapter2.ObserverNonStaticFactory"></bean>    <bean id="observer_from_non_static_factory_b" factory-bean="observerFactory" factory-method="createObserver">    

依赖注入(Dependency Injection)#

依赖注入分为构造函数setter两种方式

构造函数#

构造函数注入属性时有多种方式,分别为

直接注入不需要强调类型,名字或者下标的#

这种注入方式的前题是参数中没有歧义,不是基本类型

public class ConstructA {    private ConstructB constructB;    private ConstructC constructC;    public ConstructA(ConstructB constructB,ConstructC constructC){        this.constructB = constructB;        this.constructC = constructC;    }}public class ConstructB {}public class ConstructC {}
    <bean id="constructA" class="com.lykos.ioc.chapter2.ConstructA">        <!--此处与顺序无关,也不需要指定名称和类型-->        <constructor-arg ref="constructB"/>        <constructor-arg ref="constructC"/>    </bean>    <bean id="constructB" class="com.lykos.ioc.chapter2.ConstructB"/>    <bean id="constructC" class="com.lykos.ioc.chapter2.ConstructC"/>

使用下标#

public class ConstructD {    private int age;    private String name;    public ConstructD(int age,String name){        this.age = age;        this.name = name;    }}
默认情况下就是配置顺序#
        <!--    不用指定下标,默认就是顺序就是我们配置顺序-->    <bean id="constructD" class="com.lykos.ioc.chapter2.ConstructD">        <constructor-arg value="2"/>        <constructor-arg value="this name"/>    </bean>
使用index指定下标#
    <!--    使用index指定下标,此时与我们配置的顺序无关,顺序可以随变-->    <bean id="constructD_index" class="com.lykos.ioc.chapter2.ConstructD">        <constructor-arg index="1" value="this name 1"/>        <constructor-arg index="0" value="2"/>    </bean>
使用type#
    <!--    使用type,此时与我们配置的顺序无关,顺序可以随变-->    <bean id="constructD_type" class="com.lykos.ioc.chapter2.ConstructD">        <constructor-arg type="java.lang.String" value="this name 1 type"/>        <constructor-arg type="int" value="112"/>    </bean>
使用name#

使用name是需要用到@ConstructorProperties注解。因为我们我们编译之后参数名会变成var1等,无法获取真实参数名,所以我们需要注解来标注。

    //此处需要ConstructorProperties注释    @ConstructorProperties({"age", "name"})    public ConstructD(int var1, String var2) {        this.age = var1;        this.name = var2;    }
使用c:{}=xx#

在bean标签中使用c:{paramName}=xxx方式。要使用此方式配置文件中必须添加 xmlns:c="http://www.springframework.org/schema/c"

    <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    https://www.springframework.org/schema/beans/spring-beans.xsd">    <!--    使用c:{},此时与我们配置的顺序无关,顺序可以随变-->    <bean id="constructD_c_name" class="com.lykos.ioc.chapter2.ConstructD" c:name="cName" c:age="123"/>
</beans>

setter 注入#

setter注入是在通过构造函数或者工场方法实例化对象后,使用对象setter方法调用的。

property方法#

public class SetterA {    private String age;    private String name;    public void setAge(String age) {        this.age = age;    }    public void setName(String name) {        this.name = name;    }}
    <bean id="setterA" class="com.lykos.ioc.chapter2.SetterA">        <property name="age" value="18"></property>        <property name="name" value="lykos"></property>    </bean>

p:{}方法#

在bean标签中使用p:{propertyName}=xxx方式。要使用此方式配置文件中必须添加 xmlns:p="http://www.springframework.org/schema/p"

<bean id="setterB" class="com.lykos.ioc.chapter2.SetterA" p:age="17" p:name="pName"/>

bean中其它概念#

depends-on#

实例化当前bean时需要依赖的对象,同时也决定了初始化销毁顺序。多个用,隔开

    <bean id="setterC" depends-on="setterB,setterA" class="com.lykos.ioc.chapter2.SetterA" p:age="19" p:name="dependName"/>

懒加载 Lazy-initialized Beans#

正常情况在,IoC容器会在程序启动时全部初始化好singleton的对象,当然我们也可以使用lazy-init=true在指定当前对象不用着急加载,只需要在我们第一次使用时(也就是第一次getBean的时候)在初始化

<bean id="setterD" lazy-init="true" class="com.lykos.ioc.chapter2.SetterA"/>

我们也可以在beans上设置default-lazy-init="true" 使其包含的所有bean默认都懒加载的

Lookup 查找型方法注入#

这个查找型方法,可以理解为就是一个工场方法,他主要是搭配原型-prototype 对象一起使用。主要原理是使用了CGLIB库生成字节码来动态生成覆盖该方法的子类来实现的。因此我们必须要先创建一个抽象类(抽象方法)或者接口(方法签名)

public abstract class LookUp {    public abstract SetterA getSetterA();}

或者

public interface LookUp {    SetterA getSetterA();}
    <!-- 这里必须是scope="prototype" -->    <bean id="setter_to_lookup" scope="prototype" class="com.lykos.ioc.chapter2.SetterA" p:age="17" p:name="pName"/>    <bean id="lookup" class="com.lykos.ioc.chapter2.LookUp">        <!--lookup-method 定义后,每次getSetterA都会是一个新的对象-->        <lookup-method name="getSetterA" bean="setter_to_lookup"></lookup-method>    </bean>

方法替换#

就是写一个通用方法替换原有的方法 定义一个被替换的方法

public class MethodReplace {    public String say(String message){        return message;    }}

定义一个替换方法,必须实现MethodReplacer接口

public class MyMethodReplacer implements MethodReplacer {    @Override    public Object reimplement(Object obj, Method method, Object[] args) throws Throwable {        System.out.println("do your thing");        //不能执行method.invoke(obj,args)        return null;    }}

使用

    <bean id="methodReplace" class="com.lykos.ioc.chapter2.MethodReplace">        <!-- 使用replaced-method替换原有的方法-->        <replaced-method name="say" replacer="myMethodReplacer"></replaced-method>    </bean>    <bean id="myMethodReplacer" class="com.lykos.ioc.chapter2.MyMethodReplacer"></bean>

的Scopes#

spring ioc中常用的是singletonprototype。而singleton是默认的。

singleton 在ioc容器中有且仅有一个实例对象 prototype 在ioc容器中是多实例,也就是每当getBean时都会创建一个新的对象

当然在ioc中还有request,session,application,websocket不过这些scope只能在web应用容器中使用。 通常我们用scope="prototype"来设置bean的scope

    <bean id="xxx" class="xxx" scope="prototype" />

父级 Bean#

xml 中也可以用 <bean parent="parentBean">来继成父类bean的相关配置信息,例如:属性构造参数初始化方法等 parent既可以是具体的bean也可以是用于 <bean abstract="true"> 定义的抽象bean。 abstract=true 意味着在定义此类bean时我们可以不需要用class指定具体类限定名,当然容器在初始化过程中也会跳过带有abstract=true属性相关bean的实例化。

    <bean id="parentBean2" abstract="true">        <property name="name" value="parentBean2"></property>        <property name="age" value="18"></property>    </bean>

子类使用

    <bean id="childBean" parent="parentBean2" class="com.lykos.ioc.chapter2.ChildBean">        <property name="desc" value="this is child"></property>    </bean>

此处com.lykos.ioc.chapter2.ChildBean需要有name和age属性

public class ChildBean {    private String name;    private int age;    private String desc;}

注意:子类也可以覆盖父类配置

    <bean id="childBean2" parent="parentBean2" class="com.lykos.ioc.chapter2.ChildBean">        <property name="name" value="coverParentName"></property>        <property name="desc" value="this is child"></property>    </bean>

Bean的自定义特性#

spring提供了许多接口,用于自定义Bean的特性

Lifecycle Callbacks#

从spring 2.5开始我们有三个选项来控制bean的生命周期行为

InitializingBean#

实现此接口后。当容器中所有必要属性设置完成后,spring容器会回调afterPropertiesSet方法 定义initializingBean

public class MyInitializingBean implements InitializingBean {    @Override    public void afterPropertiesSet() throws Exception {        System.out.println("所有属性设置完成!");    }}

注册initializingBean

<bean id="myInitializingBean" class="com.lykos.ioc.chapter2.MyInitializingBean"></bean>

DisposableBean#

实现此接口后,当容器被销毁后,可以得到回调。当然如果需要获取回调,我们还必须向容器中注册回调钩子。web容器中默认已加上 注册回调钩子

        ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");        // add a shutdown hook for the above context...        ctx.registerShutdownHook();

创建disposableBean

public class MyDisposableBean implements DisposableBean {    @Override    public void destroy() throws Exception {        System.out.println("destroy");    }}

注册disposableBean

    <bean id="myDisposableBean" class="com.lykos.ioc.chapter2.MyDisposableBean"></bean>

Custom init() 和 destory()#

自定义初始化方法和销毁方法 在xml中可以在<beans default-init-method="init" default-destroy-method="destory"> 全局设置多个bean的初始化和销毁方法。当然也可以在<bean init-method="" destroy-method=""/>覆盖全局的配置 当然在annotation中可以使用@Bean(initMethod = "",destroyMethod = "")设置

@PostContruct @PreDestroy#

这两个都是在使用annotation时使用

SmartLifecycle#

在讲解SmartLifecycle之前我们先认识一下

Lifecycle 接口可以为任何对象提供基本的生命周期方法 Phased 接口在多个SmartLifecycle中决定执行顺序

Lifecycle#
public interface Lifecycle {    void start();    void stop();    boolean isRunning();}
Phased#
public interface Phased {    int getPhase();}
SmartLifecycle#
public interface SmartLifecycle extends Lifecycle, Phased {    boolean isAutoStartup();    void stop(Runnable callback);}

可以看到SmartLifecycle继承了Lifecycle,Phased接口 下面根据一个自己实现了SmartLifecycle接口的类做详细说明

public class LifecycleBean implements SmartLifecycle {    /**     * 当所有对象已被实例化和初始化之后,将调用该方法     * 默认生命周期处理器将检查每个SmartLifecycle对象的     * isAutoStartup()方法和isRunning()方法返回值,如果都为true则被调用。     */    @Override    public void start() {        System.out.println("LifecycleBean start");    }
    /**     * isRunning()方法返回值为true则被调用。     */    @Override    public void stop() {        System.out.println("LifecycleBean stop");    }
    /**     * 1. 只有该方法返回false时,start方法才会被执行     * 2. 只有该方法返回true时,stop(Runnable callback)或stop()方法才会被执行。     */    @Override    public boolean isRunning() {        return true;    }
    /**     * 如果工程中有多个实现接口SmartLifecycle的类,则这些类的start的执行顺序按getPhase方法返回值从小到大执行     * stop方法的执行顺序则相反,getPhase返回值较大类的stop方法先被调用,小的后被调用。     */    @Override    public int getPhase(){        return 1;    }
    /**     * 只有返回true star才会执行     * @return     */    @Override    public boolean isAutoStartup() {        return true;    }}

ApplicationContextAware and BeanNameAware#

ApplicationContextAware#

此接口提供了为对象注入ApplicationContext的能力

public interface ApplicationContextAware {    void setApplicationContext(ApplicationContext applicationContext) throws BeansException;}

BeanNameAware#

在填充好bean属性之后且初始化回调(InitializingBean,Custom init)之前调用

public interface BeanNameAware {    void setBeanName(String name) throws BeansException;}

其它Aware接口#

spring还提供了多种Aware回调接口,这些接口使Bean向IoC容器表示它们需要某种基础结构依赖性

ApplicationEventPublisherAware#

事件发布

public interface ApplicationEventPublisherAware extends Aware {    void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher);
}

BeanClassLoaderAware#

public interface BeanClassLoaderAware extends Aware {    void setBeanClassLoader(ClassLoader classLoader);}

BeanFactoryAware#

public interface BeanFactoryAware extends Aware {    void setBeanFactory(BeanFactory beanFactory) throws BeansException;}

BootstrapContextAware#

public interface BootstrapContextAware extends Aware {    void setBootstrapContext(BootstrapContext bootstrapContext);}

LoadTimeWeaverAware#

public interface LoadTimeWeaverAware extends Aware {    void setLoadTimeWeaver(LoadTimeWeaver loadTimeWeaver);}

MessageSourceAware#

public interface MessageSourceAware extends Aware {    void setMessageSource(MessageSource messageSource);}

NotificationPublisherAware#

public interface NotificationPublisherAware extends Aware {    void setNotificationPublisher(NotificationPublisher notificationPublisher);}

ResourceLoaderAware#

public interface ResourceLoaderAware extends Aware {    void setResourceLoader(ResourceLoader resourceLoader);}

ServletConfigAware#

public interface ServletConfigAware extends Aware {    void setServletConfig(ServletConfig servletConfig);}

ServletContextAware#

public interface ServletContextAware extends Aware {    void setServletContext(ServletContext servletContext);}