Spring IoC容器(二)Bean的自定义及容器的扩展

 Spring提供了一些接口来让我们定制bean 及扩展容器。

1 定制Bean

我们可以通过bean及容器的生命周期回调及一些Aware接口来定制bean。

1.1 生命周期回调

1.1.1 InitializingBean 与 DisposableBean

我们可以通过让Bean 实现InitializingBean 及DisposableBean 接口,当容器初始化完bean后会调用InitializingBean的afterPropertiesSet()方法,当容器销毁bean前会调用DisposableBean接口的destroy()方法。

上面这两个接口方法在JSR-250中,可以使用@PostConstruct 及@PreDestory注释来代替。

也可以在xml的bean标签的init-method 的属性指定bean中的一个实例方法来作为初始化方法(与afterPropertiesSet()作用一致)。

在xml的bean标签的destory-method属性指定bean中的一个实例方法作为销毁方法(与destroy()作用一致)。

在beans 标签中通过default-init-method及default-destroy-method属性用来指定bean默认的初始化及销毁的方法名。

可以将bean中指定初始化方法(销毁方法)、继承InitializingBean或DisposableBean及使用@PostConstruct或@PreDestory注释这三种方案组合一起使用。

初始化方法的执行顺序为:@PostConstruct -> afterPropertiesSet() -> init-method 属性。

销毁方法的执行顺序为:@PreDestory -> destroy() -> destroy-method属性。

public class InitService implements InitializingBean {

    public InitService() {
        System.out.println("initService 实例化");
    }

    private String name;

    public void setName(String name) {
        this.name = name;
        System.out.println("赋值完成:" + name);
    }

    public void customInit() {
        System.out.println("init-method指定的customInit()");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("afterPropertiesSet");
    }

    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/article/article.xml");
        applicationContext.getBean(InitService.class);
    }
}

1.1.2 启动和关闭的回调

Lifecycle 接口定义了三个跟生命周期相关的方法:start() 启动,stop() 停止,isRuning() 是否运行。某些Io C容器实现了这个接口的方法,LifecycleProcessor接口扩展了Lifecycle接口并新增两个方法:onRefresh() 及 onClose()。

public interface Lifecycle {

    void start();

    void stop();

    boolean isRunning();
}

public interface LifecycleProcessor extends Lifecycle {

    void onRefresh();

    void onClose();
}

当spring context被启动时会调用容器的start方法,当关闭时候会调用容器的stop()方法。同时bean也可以实现这个接口,当容器被调用start方法时,bean的start方法也会被调用。

在bean中,所依赖的bean会被先创建,也可以指定depend-on关系来控制bean的创建顺序。对于那些没有直接依赖关系的,但是我们想指定start()被调用顺序,这时bean可以通过实现Phased接口来控制被调用的顺序。该接口有个getPhase()方法返回一个int类型,值越小越先启动,越小越后关闭。而没有实现Phased接口的Bean,值为0.

注意,Lifecycle 是针对的是常规启动关闭情况,如果想在热启动或其他自动启动模式下触发,那么应该使用SmartLifecycle接口。

public class StartService1 implements Lifecycle, Phased {

    public StartService1() {
        System.out.println("StartService1 实例化");
    }

    private boolean running = false;

    @Override
    public void start() {
        System.out.println("StartService1 启动");
        running = true;
    }

    @Override
    public void stop() {
        System.out.println("StartService1 关闭");
        running = false;
    }

    @Override
    public boolean isRunning() {
        return running;
    }

    @Override
    public int getPhase() {
        return -1; // 比没有实现Phased接口的Bean更先调用start()
    }

    public static void main(String[] args) {
        ConfigurableApplicationContext applicationContext = new ClassPathXmlApplicationContext("/article/article.xml");
        applicationContext.start(); // 手动发出启动的信号
    }
}

public class StartService2 implements Lifecycle {
    public StartService2() {
        System.out.println("StartService2实例化");
    }

    private boolean running = false;

    @Override
    public void start() {
        System.out.println("StartService2 启动");
        running = true;
    }

    @Override
    public void stop() {
        System.out.println("StartService2 关闭");
        running = false;
    }

    @Override
    public boolean isRunning() {
        return running;
    }
}


在上面的代码中,ConfigurableApplicationContext接口继承Lifecycle接口。而ClassPathXmlApplicationContext的一个祖先类AbstractApplicationContext 实现了ConfigurableApplicationContext的方法。

Spring IoC容器(二)Bean的自定义及容器的扩展_第1张图片

图 AbstractApplicationContext类中的start 方法

Spring IoC容器(二)Bean的自定义及容器的扩展_第2张图片

图 AbstractApplicationContext类中初始化生命周期处理器的方法

而在DefaultLifecycleProcessor类中的start方法内部,调用了该类的实例方法startBeans。

Spring IoC容器(二)Bean的自定义及容器的扩展_第3张图片

图 DefaultLifecycleProcessor 的startBeans方法

在非Web 环境的容器中,要安全的来关闭容器,需要调用ConfigurableApplicationContext的registerShutdownHook实例方法,来让JVM关闭前能调用容器的stop方法。

而Web环境的容器实现了安全关闭容器的方法。

1.2 Aware 接口

Spring 提供了广泛的Aware回调接口,让bean向容器指示它们需要特定的基础结构依赖关系。

ApplicationContextAware

向bean提供ApplicationContext变量。

ApplicationEventPublisherAware

发布ApplicationContext的事件。

BeanClassLoaderAware

用于加载bean类。

BeanFactoryAware

向bean提供BeanFactory变量。

BeanNameAware

向bean提供bean的命名。

BootstrapContextAware

当前容器里的资源适配器BootstrapContext

LoadTimeWeaverAware

定义的weaver用于在加载时处理类定义。

MessageSourceAware

配置解析消息的策略(支持参数化与国际化)。

NotificationPublisherAware

Spring JMX 通知的发布者。

ResourceLoaderAware

配置用于低级访问资源的加载器。

ServletConfigAware

当前在容器运行的ServletConfig。

ServletContextAware

当前在容器运行的ServletContext。

表 Spring提供的Aware接口

2 定义Bean的继承关系

子bean 可以从父bean继承配置数据,子bean可以根据需要覆盖某些值或添加其他值。使用这种继承关系可以节省大量的键入工作,这是一种模版形式。

如果bean 标签的abstract属性标志为true,那么这个bean将不会被实例化(class属性的值为空),但是可以被子bean继承。

public class ChildrenService {

    private String name;
    private Integer age;
    private String address;

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/article/article.xml");
        ChildrenService childrenService = applicationContext.getBean(ChildrenService.class);
        System.out.println(childrenService);
    }

    @Override
    public String toString() {
        return "ChildrenService{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", address='" + address + '\'' +
                '}';
    }
}


        
        
    

    
        
        
    

3 容器扩展

可以通过向容器中插入实现了特定接口的实例来对容器进行扩展,而不必通过继承ApplicationContext的方式。

3.1 BeanPostProcessor

BeanPostProcessor 接口提供了可以覆盖容器实例化逻辑或依赖逻辑的方法。bean 先由容器实例化,然后由BeanPostProcessor进行操作。并且可以向容器中插入多个这里的实例,控制执行顺序,则还需要实现Ordered接口。

public class CustomBeanPostProcessor1 implements BeanPostProcessor, Ordered {
    @Override
    public int getOrder() {
        return 1; // 越小越先执行
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("CustomBeanPostProcessor1 postProcessBeforeInitialization() 实例化之前");
        return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("CustomBeanPostProcessor1 postProcessAfterInitialization() 实例化之后");
        return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
    }
}

public class CustomBeanPostProcessor2 implements BeanPostProcessor, Ordered {
    @Override
    public int getOrder() {
        return 2;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("CustomBeanPostProcessor2 postProcessBeforeInitialization() 实例化之前");
        return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("CustomBeanPostProcessor2 postProcessAfterInitialization() 实例化之后");
        return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
    }
}


3.2 BeanFactoryPostProcessor 定制配置元数据

IoC容器允许BeanFactoryPostProcessor 读取配置元数据,并可能在实例化之前更改元数据。同样允许配置多个BeanFactoryPostProcessor。当被注入到容器时会自动运行。

Spring 定义了多个实现了BeanFactoryPostProcessor的类。

3.2.1 PropertySourcesPlaceholderConfigurer

通过配置Properties的方式来取代bean中定义的占位符。

使用标准的Java Properties 格式将bean定义中的属性值外部化到一个单独的文件中。这样可以使部署时能自定义特定于环境的属性,如数据库host和帐号密码,而无需修改容器的一个或多个xml中bean定义的属性。

public class DataService {

    private String host;
    private String username;
    private String password;

    public void setHost(String host) {
        this.host = host;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "DataService{" +
                "host='" + host + '\'' +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }

    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/article/article.xml");
        DataService dataService = applicationContext.getBean(DataService.class);
        System.out.println(dataService);
    }
}

// data.properties
jdbc.host=localhost
jdbc.username=root
jdbc.password=123


        


   
   
   

3.2.1 PropertyOverrideConfigurer

可以覆盖任何bean 中的任何属性。

public class StudentService {

    private String name;

    private Integer age;

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "StudentService{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/article/article.xml");
        StudentService studentService = applicationContext.getBean(StudentService.class);
        System.out.println(studentService); // 输出值 StudentService{name='newName', age=1}
    }
}


    
        
        
    

    
        
            
                newName
                1
            
        
    

你可能感兴趣的:(Spring,java,spring,依赖倒置原则,后端)