zoukankan      html  css  js  c++  java
  • Spring Boot 学习系列(09)—自定义Bean的顺序加载

    此文已由作者易国强授权网易云社区发布。

    欢迎访问网易云社区,了解更多网易技术产品运营经验。

    Bean 的顺序加载

    • 有些场景中,我们希望编写的Bean能够按照指定的顺序进行加载。比如,有UserServiceBean和OrderServiceBean,我们需要在OrderServiceBean中调用UserServiceBean,获取其提供的一些数据信息。针对这一场景,通常来说,有这么几种方式:

    • 1、将UserServiceBean封装成一个服务类(如采用@Service注解),然后在OrderServiceBean中引入这个服务类,直接调用即可,简单快捷。示例如下所示:

        @Service("userServiceBean")  public class UserServiceBean {      public String print() {
                System.out.println("this is UserServiceBean print");          return "print ok";
            }
        }  @Service("orderServiceBean")  public class OrderServiceBean {      @Resource
            UserServiceBean userServiceBean;      public void invoke(){
                String ret = userServiceBean.print();
                System.out.println("this is OrderServiceBean invoke " + ret );
            }
        }
    • 2、然而有些时候,我们的xxxServiceBean是没有封装成服务的,只是作为一个单纯的Bean注入到Spring容器中。这个时候如果我们需要使用这个Bean实例,通常会考虑直接从ApplicationContext中以getBean("xxxServiceBean")的方式获取。

      • 在传统的项目中,我们一般都会在xml配置文件中注入xxxServiceBean,这个时候Spring容器会依据xml中代码编写的顺序依次加载各个Bean,示例如下所示:

        <!-- 按代码编写顺序依次加载 --><!-- 订单服务Bean --><bean id="orderServiceBean" class="com.example.a.OrderServiceBean"></bean><!-- 演示服务--><bean id="depService" class="com.example.a.DepService"></bean><!-- 演示服务--><bean id="demoService" class="com.example.a.OtherDemoServiceImpl"></bean><!-- 用户服务Bean--><bean id="userServiceBean" class="com.example.a.UserServiceBean"></bean>

        在各构造函数中加入日志输出可发现,会按照顺序依次加载。如下图所示:

    ![image](https://github.com/siyuyifang/image/blob/master/spring-boot/9/9-1.png?raw=true)- 如果我们在OrderServiceBean中有调用UserServiceBean,那么UserServiceBean则会优先于DepService和OtherDemoServiceImpl加载,调用代码如下:
    
    ```public class OrderServiceBean {public OrderServiceBean() {
        System.out.println("OrderServiceBean constructor init.");
        UserServiceBean  userServiceBean =  SpringContextHolder.getBean("userServiceBean");
        String ret = userServiceBean.print();
        System.out.println("this is OrderServiceBean invoke " + ret );
    }
    }
    ```
    这个时候观察加载的顺序如下图所示:
    
    
    
    
    ![image](https://github.com/siyuyifang/image/blob/master/spring-boot/9/9-2.png?raw=true)- 在Spring Boot项目中,我们一般用@Configuration + @Bean注解的方式来替代xml中Bean的注入,这个时候定义Bean的加载顺序也很简单,在同一个配置类中,也是按照代码的编写顺序加载实例化的。示例如下所示:
    
    ```@Configurationpublic class MyConfigs {@Bean("userServiceBean")public UserServiceBean userServiceBean(){    return new UserServiceBean();
    }@Bean("orderServiceBean")public OrderServiceBean orderServiceBean(){    return new OrderServiceBean();
    }
    ```
    • 有这么一个使用场景,如果UserServiceBean 采用@Bean + @Configuration的方式注入,而OrderServiceBean采用@Service注解的形式提供服务,同时在OrderServiceBean中仍然通过ApplicationContext的getBean()方式获取UserServiceBean的示例,那么在编译时候会报如下错误:

    image

    其中SpringContextHolder.java的代码如下所示:

    @Component("springContextHolder")public class SpringContextHolder implements ApplicationContextAware {    private static ApplicationContext applicationContext;    /**
         * 实现ApplicationContextAware接口的context注入函数, 将其存入静态变量.
         */
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) {
            SpringContextHolder.applicationContext = applicationContext;
        }    /**
         * 取得存储在静态变量中的ApplicationContext.
         */
        public static ApplicationContext getApplicationContext() {
            checkApplicationContext();        return applicationContext;
        }    /**
         * 从静态变量ApplicationContext中取得Bean, 自动转型为所赋值对象的类型.
         */
        @SuppressWarnings("unchecked")    public static <T> T getBean(String name) {
            checkApplicationContext();        return (T) applicationContext.getBean(name);
        }    private static void checkApplicationContext() {        if (applicationContext == null) {            throw new IllegalStateException("applicaitonContext未注入,请在applicationContext.xml中定义SpringContextUtil");
            }
        }
    }
    • 这个时候,我们需要在OrderServiceBean类前加入如下注解,表示此Bean依赖于springContextHolder实例的加载,代码示例如下所示,再次编译通过。

    @Service
    @DependsOn("springContextHolder")public class OrderServiceBean {    public OrderServiceBean() {
            System.out.println("OrderServiceBean constructor init.");
            UserServiceBean  userServiceBean =  SpringContextHolder.getBean("userServiceBean");
            String ret = userServiceBean.print();
            System.out.println("this is OrderServiceBean invoke " + ret );
        }
    
    }
    • 此外,如果需要指定一个Bean A 先于 Bean B加载,那么可以在Bean B类前加入@DependsOn("beanA"),指定依赖加载顺序。

    • 不足之处,欢迎指正,谢谢~


    免费体验云安全(易盾)内容安全、验证码等服务

    更多网易技术、产品、运营经验分享请点击



    相关文章:
    【推荐】 基于开源,强于开源,轻舟微服务解决方案深度解读
    【推荐】 当Shell遇上了NodeJS

  • 相关阅读:
    自动配置原理5
    JSR303数据校验4
    mysql 安装(压缩包安装和exe安装)
    jQuery关于复制(复制隐藏文字+显示文字)跳转,Clipboard
    html页面设置一个跟随鼠标移动的DIV(jQuery实现)
    js中将时间(如:2017-10-8 22:44:55)转化为时间搓,时间戳转为标准格式时间
    异常:java.lang.IllegalArgumentException: Control character in cookie value or attribute.
    从数据库导出数据到excel之List<List<Object>>导出
    从数据库导出数据到excel之List<map>导出
    从数据库导出数据到excel之POI操作
  • 原文地址:https://www.cnblogs.com/zyfd/p/9887318.html
Copyright © 2011-2022 走看看