Spring Ioc和 DI 之 bean 创建与销毁(二)

Spring Ioc和 DI 之 bean 创建与销毁(二)

Bean 的命名
  • Spring bean 名称解析
1、如果<bean>定义了id属性,那么属性的值则会作为bean名称

2、若没有指定id属性,则会查找name属性,如果定义了name属性,则将使用name
属性中定义的第一个名称(之所以为第一个名称,是因为可以再name属性中定义多个
名称)。

3、若既没有指定id属性,也没有指定name属性,则spring使用该bean的类名作为名
称,当然前提是没有其它bean使用相同的类名,如果声明了多个没有id或名称的相同
类型的bean,那么spring在ApplicationContext初始化期间,在注入时抛出异常。

注意:最好自己定义bean的名称,这样spring更改了默认行为,也会继续工作

示例:
beanName-beans.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="string1" class="java.lang.String"></bean>
    <bean id="string2" class="java.lang.String"></bean>
    <bean  class="java.lang.String"></bean>
    <bean  class="java.lang.String"></bean>
</beans>

测试:
package com.zombie.ioc.test;

import com.zombie.ioc.domain.User;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.Arrays;
import java.util.Map;

public class BeanNameTest {

    public static void main(String[] args) {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring/beanName-beans.xml");
   
        Map<String, String> beansOfType = ctx.getBeansOfType(String.class);
        beansOfType.entrySet().stream().forEach(bean -> System.out.println("the beanName : "+bean.getKey()+" the bean is : "+bean.getValue()));
        ctx.close();
    }
}

输出:
the beanName : string1
the beanName : string2
the beanName : java.lang.String#0
the beanName : java.lang.String#1

最后这两个为spring提供给未在配置中明确指定 String类型ID,Spring将确保在整
个ApplicationContext中id为唯一的
  • bean 的别名
Spring允许一个bean拥有多个bean名称,可以通过<bean>标记的name属性指定以
空格、逗号或分号分隔的名称列表实现多个名称 ,也可以使用<alias>标记名称定
义名称。

<bean  id="stringId" name="stringName0 StringName1,stringName2;
    stringName3"  class="java.lang.String">
</bean>
  • 自动装配bean
byName 模式:当使用该模式进行自动装配时,Spring会尝试将每个属性连接到同
名的bean,因此,如果目标bean具有名为 foo 的属性且ApplicationContext中定义
了foo bean,则foo bean 将被分配给目标 bean 的 foo 属性。

byType 模式:当使用该模式自动装配时,Spring通过在ApplicationContext中自
动使用相同类型的bean来尝试连接目标bean模式的每个属性。

构造函数模式:该模式与byType模式在功能上相同,只不过使用的是构造函数而
不是setter来执行注入。Spring试图匹配构造函数中最大数量的参数,所以,如果
bean有两个构造函数,一个接受一个String,另一个接受一个String和一个Integer,
那么Spring将使用带有两个参数的构造函数。

默认模式:Spring将自动在构造函数模式和byType模式之间进行选择,如果bean
有一个默认的(无参数)构造函数,那么Spring使用byType模式,否则就使用构造函
数模式。

无:这是默认设置
Bean 的创建
  • 指定初始化方法
在bean 的 <bean> 标记的init-method 属性中指定初始化的方法

package com.zombie.ioc.domain;

public class Grade {

    private String subject ;

    private float grade;


    public  void initProperties(){
        this.setGrade(89.0f);
        this.setSubject("math");
    }


    public String getSubject() {
        return subject;
    }

    public void setSubject(String subject) {
        this.subject = subject;
    }

    public float getGrade() {
        return grade;
    }

    public void setGrade(float grade) {
        this.grade = grade;
    }
}

xml配置,并指定init-method方法,该方法会在实例化后回调
<?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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"
       default-lazy-init="false">

<bean id="grade" class="com.zombie.ioc.domain.Grade"  
              init-method="initProperties">
    <property name="grade" value="35.0f"/>
    <property name="subject" value="math"/>
</bean>
</beans>

default-lazy-init="false" 该注解用于告诉Spring,在第一次请求该bean时进行
实例化

测试:
public class InitBeanTest {

    public static void main(String[] args) {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring/spring-beans.xml");
        Grade grade = (Grade)ctx.getBean("grade");
        System.out.println(grade.getSubject()+"==="+grade.getGrade());
    }
}

输出:
math===89.0
  • 实现InitializingBean 接口
 只需要bean实现该接口的afterPropertiesSet

 public class Grade implements InitializingBean {

    private String subject ;

    private float grade;

    public String getSubject() {
        return subject;
    }

    public void setSubject(String subject) {
        this.subject = subject;
    }

    public float getGrade() {
        return grade;
    }

    public void setGrade(float grade) {
        this.grade = grade;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        this.setGrade(93);
        this.setSubject("chinese");
    }
}

xml配置如上;

测试:
public class InitBeanTest {

    public static void main(String[] args) {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring/spring-beans.xml");
        Grade grade = (Grade)ctx.getBean("grade");
        System.out.println(grade.getSubject()+"==="+grade.getGrade());
    }
}

输出:
chinese===93.0

若同时实现该接口,也指定了init-method则最后调用afterProperties接口方法
  • 使用JSR-250 @PostConstruct注解
public class Grade  {

    private String subject ;

    private float grade;

    @PostConstruct
    private  void initPostProperties(){
        System.out.println("调用指定的PostConstruct方法开始");
        this.setGrade(100.0f);
        this.setSubject("english");
    }
    ..
}

xml配置需要增加注解扫描
<context:annotation-config/>
<bean id="grade" class="com.zombie.ioc.domain.Grade" >
</bean>

测试:
    public static void main(String[] args) {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring/spring-beans.xml");
        Grade grade = (Grade)ctx.getBean("grade");
        System.out.println(grade.getSubject()+"==="+grade.getGrade());
    }
输出:
调用指定的PostConstruct方法开始
english===100.0

  • 三种方式优缺点
1、使用初始化方法,可以使应用程序与Spring分离,但必须要记住初始化方法
的bean配置相应的初始化方法

2、如果要使用InitializingBean接口,则可以一次性为bean类的所有指定初始化
回调,但该种方式会与Spring耦合

3、如果使用注解,需要将注解应用于方法,并确保IOC容器支持JSR-250

若不考虑移值,推荐使用InitializingBean接口,当使用init-method或
@PostConstruct配置初始化时,声明带有不同访问权限的初始化方法,该方法应
该在bean创建时由Spring IOC调用一次,而随后的调用将导致意外的结果或失败,
可以设置方法为私有化来禁止外部调用,spring ioc能够通过反射来调用它,但代
码中的其它任何调用都不被允许
  • 上述三种方式的调用解析顺序
调用指定的PostConstruct方法开始
调用指定的afterPropertiesSet方法开始
调用指定的init-method方法开始

bean的创建主要步骤为:
1、首先调用构造函数创建bean

2、注入依赖项(调用setter)

3、bean已经存在且提供了依赖项预初始化的BeanPostProcessor基础结构bean
将被查询,以查看是否想从创建的 bean 中调用东西,它们在创建后执行bean
修改操作。@PostConstruct 注解由CmmonAnnotationPostProcessor注册,
所以该bean将调用使用了@PostConstruct注解的方法,该方法在bean创建后,
在类被投入使用之前且在bean的实际初始化之前(即在afterPropertiesSet()
和init-method之前)执行。

4、InitializingBean的afterPropertiesSet()方法在注入依赖项后立即执行

5、最后执行init-method属性,这是因为它是bean的实际初始化方法
Bean 的销毁
  • 在bean 被销毁时执行一个方法
只需要在bean的<bean> 标记的destroy-method 属性指定该方法的名称即可,
Spring 在销毁bean的单例实例之前会调用该方法,对于那些有原型作用域的
bean,Spring不会调用此方法。
  • 实现 DisposableBean 接口

  • 使用JSR-250 @PreDestroy 注解

  • bean销毁的解析顺序

若在同一个bean实例上使用所有的机制进行bean销毁,则Spring会先调用
@PreDestroy注解的方法,然后调用DisposableBean.destroy(),最后调用
Xml定义的destory()方法。

在Spring中销毁函数的缺点:不会自动触发,需要记住在应用程序关闭之前
调用AbstractApplicationContext.destroy()。java允许创建一个关闭钩子
(shutdown hook) ,它是在应用程序关闭之前执行的一个线程,这是调用
AbstractApplicationContext#registerShutdownHook(),该种方式也可以实
现同样的销毁bean
让 Spring 感知 bean
  • 使用 BeanNameAware 接口
package com.zombie.ioc.domain;

import org.springframework.beans.factory.BeanNameAware;

public class SingerName implements BeanNameAware {

    private String name;

    @Override
    public void setBeanName(String name) {
        this.name = name;
    }

    public void sing() {
        System.out.println("Singer "+ name +" -sing()");
    }
}

可以通过该种方式,注入bean的名称,从而根据名称做一些处理
  • 使用 ApplicationContextAware 接口
package com.zombie.ioc.domain;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.support.GenericApplicationContext;

public class AppWare implements ApplicationContextAware {

    private ApplicationContext ctx;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.ctx = applicationContext;
        if (ctx instanceof GenericApplicationContext) {
            ((GenericApplicationContext) ctx).registerShutdownHook();
        }
    }
}

通过该接口,bean可以获取对配置它们的ApplicationContext实例的引用,创建
该接口的主要原因:是为了bean在应用程序中访问Spring的ApplicationContex,
使用getBean()可以获取其它bean,但应避免使用该做法,并使用依赖注入为bean
提供协作者,否则会导致与Spring耦合;也可以注册销毁bean的钩子
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值