zoukankan      html  css  js  c++  java
  • Spring源码解析之基础应用(二)

    方法注入

    在spring容器中,大部分bean的作用域(scope)是单例(singleton)的,少部分bean的作用域是原型(prototype),如果一个bean的作用域是原型,我们A bean的作用域是原型,B bean中以@Autowired的方式注入A,那么B在A中依旧是单例。我们可以让B类实现ApplicationContextAware接口,这个接口会注入一个ApplicationContext对象,如果我们有需要A bean,则从ApplicationContextAware对象中获取。

    package org.example.service;
    
    
    import org.springframework.context.annotation.Scope;
    import org.springframework.stereotype.Component;
    
    @Component
    @Scope("prototype")
    public class CommandService {
    }
    
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.stereotype.Component;
    
    @Component
    public class CommandManager implements ApplicationContextAware {
        private ApplicationContext applicationContext;
        @Autowired
        private CommandService commandService;
    
        public CommandService getCommandService() {
            return commandService;
        }
    
        //方法注入
        public CommandService createCommandService() {
            return applicationContext.getBean(CommandService.class);
        }
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            this.applicationContext = applicationContext;
        }
    }
    
    package org.example.config;
    
    import org.springframework.context.annotation.ComponentScan;
    
    @ComponentScan("org.example.service")
    public class MyConfig {
    }
    

      

    为了验证笔者之前的说法,笔者特意在CommandManager类中注入一个原型的commandService的bean,也提供了方法注入,从applicationContext中获取CommandService对象。现在,我们编写测试用例,CommandManager有两种获取CommandService的方式,一种是getCommandService(),一种是createCommandService(),我们的目标是希望每次获取CommandService对象都是新创建的对象。

        @Test
        public void test05() {
            ApplicationContext ac = new AnnotationConfigApplicationContext(MyConfig.class);
            CommandManager commandManager = ac.getBean(CommandManager.class);
            //获取@Autowired注入的CommandService
            System.out.println(commandManager.getCommandService());
            System.out.println(commandManager.getCommandService());
            //获取方法注入的CommandService
            System.out.println(commandManager.createCommandService());
            System.out.println(commandManager.createCommandService());
        }
    

      

    运行结果:

    org.example.service.CommandService@351d0846
    org.example.service.CommandService@351d0846
    org.example.service.CommandService@77e4c80f
    org.example.service.CommandService@35fc6dc4

    除了像上面那样从环境上下文获取CommandService对象之外,我们还可以借助@Lookup注解:

    package org.example.service;
    
    import org.springframework.beans.factory.annotation.Lookup;
    import org.springframework.stereotype.Component;
    
    @Component
    public abstract class CommandManager1 {
        @Lookup
        public abstract CommandService createCommandService();
    }
    
    
    import org.springframework.beans.factory.annotation.Lookup;
    import org.springframework.stereotype.Component;
    
    @Component
    public class CommandManager2 {
        @Lookup
        public CommandService createCommandService() {
            return null;
        }
    }
    

      

    测试用例:

        @Test
        public void test06() {
            ApplicationContext ac = new AnnotationConfigApplicationContext(MyConfig.class);
            CommandManager1 commandManager1 = ac.getBean(CommandManager1.class);
            System.out.println(commandManager1.createCommandService());
            System.out.println(commandManager1.createCommandService());
            CommandManager2 commandManager2 = ac.getBean(CommandManager2.class);
            System.out.println(commandManager2.createCommandService());
            System.out.println(commandManager2.createCommandService());
        }
    

      

    运行结果:

    org.example.service.CommandService@6193932a
    org.example.service.CommandService@647fd8ce
    org.example.service.CommandService@159f197
    org.example.service.CommandService@78aab498
    

      

    不管是CommandManager1还是CommandManager2,spring在对这两个类生成代理对象,当调用被@Lookup注解标记的方法时,产生新的原型对象返回。

    生命周期

    从spring2.5开始,我们有三种选项来控制bean生命周期行为:

    1. InitializingBean和DisposableBean回调接口。
    2. 自定义init()和destroy()方法。
    3. @PostConstruct和@PreDestroy注解。 

    我们可以使用上面三种选项来控制bean在不同生命周期时机进行函数回调,比如当一些dao在spring创建好之后,在回调方法里执行热点数据加载。下面的A类,我们按照第一和第三个选项,实现InitializingBean、DisposableBean接口,并标注@PostConstruct、@PreDestroy方法,分别是aaa()、bbb()。然后,我们在配置类MyConfig2通过@Bean注解获取一个类型为A的bean,并且按照第二个选项,在@Bean标注了init()和destroy()方法,分别是ccc()、ddd()。

    package org.example.beans;
    
    import org.springframework.beans.factory.DisposableBean;
    import org.springframework.beans.factory.InitializingBean;
    
    import javax.annotation.PostConstruct;
    import javax.annotation.PreDestroy;
    
    public class A implements InitializingBean, DisposableBean {
        @Override
        public void destroy() throws Exception {
            System.out.println("A destroy...");
        }
    
        @Override
        public void afterPropertiesSet() throws Exception {
            System.out.println("A afterPropertiesSet...");
        }
    
        @PostConstruct
        public void aaa() {
            System.out.println("A aaa...");
        }
    
        @PreDestroy
        public void bbb() {
            System.out.println("A bbb...");
        }
    
        public void ccc() {
            System.out.println("A ccc...");
        }
    
        public void ddd() {
            System.out.println("A ddd...");
        }
    }
    
    package org.example.config;
    
    import org.example.beans.A;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    @ComponentScan("org.example.service")
    public class MyConfig2 {
        @Bean(initMethod = "ccc", destroyMethod = "ddd")
        public A getA() {
            return new A();
        }
    }
    

      

    测试用例:

        @Test
        public void test07() {
            AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(MyConfig2.class);
            ac.close();
        }
    

      

    运行结果:

    A aaa...
    A afterPropertiesSet...
    A ccc...
    A bbb...
    A destroy...
    A ddd...
    

      

    从上面的执行结果,我们可以得出:如果存在不同的初始化方法,bean的回调顺序是:

    1. 先执行@PostConstruct注解所标注的方法。
    2. 再执行InitializingBean接口所实现的afterPropertiesSet()方法
    3. 最后执行自定义的init()方法。

    销毁方法也是一样的顺序:

    1. 先执行@PreDestroy注解所标注的方法。
    2. 在执行DisposableBean接口所实现的destroy()方法。
    3. 最后执行自定义的destroy()方法。

    服务启动关闭回调

    之前我们学习了bean的生命周期回调,我们可以在bean的不同生命周期里执行不同的方法,如果我们想针对整个服务进行生命周期回调呢?比如要求在spring容器初始化好所有的bean之后、或者在关闭spring容器时进行方法回调。spring提供了LifeCycle接口来帮助我们实现针对服务的生命周期回调。LifeCycle提供了三个接口:

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

      

    当isRunning()返回为false时,start()会在初始化完所有的bean之后,进行回调。比如我们可以在初始化好所有的bean之后,监听一个消息队列,并处理队列里的消息。当isRunning()返回为true时,stop()会在容器关闭时回调stop()方法。

    来看下面的例子,TestLiftCycle的isRunning()默认返回false,所以在bean初始化后,允许spring容器回调start(),在start()方法中,将isRun置为true,允许在spring容器关闭时,回调stop()方法。

    package org.example.service;
    
    import org.springframework.context.Lifecycle;
    import org.springframework.stereotype.Component;
    
    @Component
    public class TestLiftCycle implements Lifecycle {
        private boolean isRun = false;
    
        @Override
        public void start() {
            isRun = true;
            System.out.println("TestLiftCycle start...");
        }
    
        @Override
        public void stop() {
            System.out.println("TestLiftCycle stop...");
        }
    
        @Override
        public boolean isRunning() {
            return isRun;
        }
    }
    

      

    要回调TestLiftCycle的start()和stop(),在测试用例里需要显式调用容器的start()和stop()方法。如果项目是部署在类似Tomcat、JBoss的微博服务器上,我们可以省去容器显式调用stop()方法,因为Tweb服务器在结束进程的时候,会回调spring容器的stop(),进而回调到我们编写的stop(),但是start()必须显式调用,不管是否部署在web服务器上。

    测试用例:

        @Test
        public void test08() {
            AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(MyConfig.class);
            ac.start();
            ac.stop();
        }
    

      

    运行结果:

    TestLiftCycle start...
    TestLiftCycle stop...
    

      

    为了方便程序员偷懒,spring还提供了SmartLifecycle接口,这个接口不需要我们显式调用容器的start()方法,也可以在初始化所有的bean之后回调start()。下面,我们来看下SmartLifecycle接口:

    public interface SmartLifecycle extends Lifecycle, Phased {
    
        boolean isAutoStartup();
    
        void stop(Runnable callback);
    }
    

     

    SmartLifecycle扩展了Lifecycle和Phased两个接口,Phased要求实现int getPhase()接口,返回的数值越低越优先执行。boolean isAutoStartup()代表是否自动执行SmartLifecycle的start()方法,如果为true会自动调用start(),为false的话,需要显式调用容器的start()才会回调SmartLifecycle的start()。SmartLifecycle扩展了原先Lifecycle的stop()方法,当我们执行完stop(Runnable callback)中的业务,需要执行callback.run(),这将启用异步关闭,否则要等待30s才会关闭容器。当然,这30s是可以修改的:

    <bean id="lifecycleProcessor" class="org.springframework.context.support.DefaultLifecycleProcessor">
        <!-- timeout value in milliseconds -->
        <property name="timeoutPerShutdownPhase" value="10000"/>
    </bean>

    下面,我们来测试TestSmartLifecycle,这里的isRunning()依旧和Lifecycle一样,只是关闭容器时不会再回调TestSmartLifecycle的stop(),转而回调stop(Runnable callback)。

    package org.example.service;
    
    import org.springframework.context.SmartLifecycle;
    import org.springframework.stereotype.Component;
    
    @Component
    public class TestSmartLifecycle implements SmartLifecycle {
        private boolean isRun = false;
    
        @Override
        public void start() {
            isRun = true;
            System.out.println("TestSmartLifecycle start...");
        }
    
        @Override
        public boolean isAutoStartup() {
            return true;
        }
    
        @Override
        public void stop(Runnable callback) {
            System.out.println("TestSmartLifecycle stop callback...");
            //执行callback.run()可以让容器立即关闭,否则容器会等待30s再关闭
            callback.run();
        }
    
        @Override
        public int getPhase() {
            return 0;
        }
    
    
        @Override
        public void stop() {
        }
    
        @Override
        public boolean isRunning() {
            return isRun;
        }
    
    }
    

      

    测试用例:

        @Test
        public void test09() {
            AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(MyConfig.class);
            ac.stop();
        }
    

      

    运行结果:

    TestSmartLifecycle start...
    TestSmartLifecycle stop callback...
    

      

  • 相关阅读:
    LeetCode 452. 用最少数量的箭引爆气球
    LeetCode 451. 根据字符出现频率排序
    LeetCode 464. 我能赢吗
    LeetCode 2. 两数相加
    @RendSection{"scripts",require:false}的作用
    js中变量含(参数、数组)作用域传递问题
    Web Api通过Route、RoutePrefix等特性设置路由
    SQLserver中存储图片
    RDLC的部署(无法找到Microsoft.ReportViewer.ProcessingObjectModel.dll文件)
    C# 发送Http协议 模拟 Post Get请求
  • 原文地址:https://www.cnblogs.com/beiluowuzheng/p/13803590.html
Copyright © 2011-2022 走看看