Spring 系列篇之Bean基础

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


何来胜利可言,挺住意味着一切

目录

1 Bean 基础
    1.1 Bean的名称-标识
    1.2 Bean的创建方式
        1.2.1 构造方法创建
        1.2.2 工场方法创建
            1.2.2.1 静态工场
            1.2.2.2 非静态工场
2 依赖注入(Dependency Injection)
    2.1 构造函数
        2.1.1 直接注入不需要强调类型,名字或者下标
        2.1.2 使用下标
        2.1.3 使用type
        2.1.4 使用name
        2.1.5 使用c:{}=xx
    2.2 setter 注入
        2.2.1 property方法
        2.2.2 p:{}方法
3 bean中其它概念
    3.1 depends-on
    3.2 懒加载 Lazy-initialized Beans
    3.3 Lookup 查找型方法注入
    3.4 方法替换
    3.5 Bean 的Scopes
    3.6 父级 Bean
4 Bean的自定义特性
    4.1 Lifecycle Callbacks
        4.1.1 InitializingBean
        4.1.2 DisposableBean
        4.1.3 Custom init() 和 destory()
        4.1.4 @PostContruct @PreDestroy
        4.1.5 SmartLifecycle
    4.2 ApplicationContextAware and BeanNameAware
        4.2.1 ApplicationContextAware
        4.2.2 BeanNameAware
    4.3 其它Aware接口
        4.3.1 ApplicationEventPublisherAware
        4.3.2 BeanClassLoaderAware
        4.3.3 BeanFactoryAware
        4.3.4 BootstrapContextAware
        4.3.5 LoadTimeWeaverAware
        4.3.6 MessageSourceAware
        4.3.7 NotificationPublisherAware
        4.3.8 ResourceLoaderAware
        4.3.9 ServletConfigAware
        4.3.10 ServletContextAware

1 Bean 基础

1.1 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"/>

1.2 Bean的创建方式

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

1.2.1 构造方法创建

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

@Data
public 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>

1.2.2 工场方法创建

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

1.2.2.1 静态工场

public class ObserverStaticFactory {
    public static Observer createObserver(){
        return new Observer();
    }
}

1.2.2.2 非静态工场

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">
    

2 依赖注入(Dependency Injection)

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

2.1 构造函数

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

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

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

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"/>

2.1.2 使用下标

public class ConstructD {
    private int age;
    private String name;
    public ConstructD(int age,String name){
        this.age = age;
        this.name = name;
    }
}

2.1.2.1 默认情况下就是配置顺序

        <!--    不用指定下标,默认就是顺序就是我们配置顺序-->
    <bean id="constructD" class="com.lykos.ioc.chapter2.ConstructD">
        <constructor-arg value="2"/>
        <constructor-arg value="this name"/>
    </bean>

2.1.2.2 使用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>

2.1.3 使用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>

2.1.4 使用name

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

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

2.1.5 使用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>

2.2 setter 注入

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

2.2.1 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>

2.2.2 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"/>

3 bean中其它概念

3.1 depends-on

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

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

3.2 懒加载 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默认都懒加载的

3.3 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>

3.4 方法替换

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

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>

3.5 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" />

3.6 父级 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>

4 Bean的自定义特性

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

4.1 Lifecycle Callbacks

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

4.1.1 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>

4.1.2 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>

4.1.3 Custom init() 和 destory()

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

4.1.4 @PostContruct @PreDestroy

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

4.1.5 SmartLifecycle

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

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

4.1.5.1 Lifecycle

public interface Lifecycle {
    void start();
    void stop();
    boolean isRunning();
}

4.1.5.2 Phased

public interface Phased {
    int getPhase();
}

4.1.5.3 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;
    }
}

4.2 ApplicationContextAware and BeanNameAware

4.2.1 ApplicationContextAware

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

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

4.2.2 BeanNameAware

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

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

4.3 其它Aware接口

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

4.3.1 ApplicationEventPublisherAware

事件发布

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

}

4.3.2 BeanClassLoaderAware

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

4.3.3 BeanFactoryAware

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

4.3.4 BootstrapContextAware

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

4.3.5 LoadTimeWeaverAware

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

4.3.6 MessageSourceAware

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

4.3.7 NotificationPublisherAware

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

4.3.8 ResourceLoaderAware

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

4.3.9 ServletConfigAware

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

4.3.10 ServletContextAware

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

4. 感谢

感谢各位老铁花时间观看!
欢迎留言指正!
内容持续更新!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,826评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,968评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,234评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,562评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,611评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,482评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,271评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,166评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,608评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,814评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,926评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,644评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,249评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,866评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,991评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,063评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,871评论 2 354

推荐阅读更多精彩内容