方案二(推荐):使用@DependsOn
Spring提供了一个@DependsOn注解,能够解决这类问题。这个场景的核心思想是:猫(监听者)必须确保在主人(事件发送者)放鱼(发送事件动作)之前完成实例化且注册监听,这样才不会错过每一条鱼。所以我们可以这么做(依旧基于原Config.java文件做出修改):
@Configuration(proxyBeanMethods = false) public class Config { // @DependsOn // 若里面不写值,该注解无效。但若写了值,请确保里面的Bean都有,否则报错 @DependsOn({"cc", "tom"}) @Bean public MasterBean master() { return new MasterBean(); } @Bean public Cat tom() { Cat tom = new Cat("Tom"); Master.getMaster().addObserver(tom); return tom; } @Bean public Cat cc() { Cat cc = new Cat("Cc"); Master.getMaster().addObserver(cc); return cc; } }
其它不变,再次运行程序,控制台输出
... 10:04:10.729 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'config' 10:04:10.736 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'cc' 10:04:10.741 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'tom' 10:04:10.741 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'master' YoutBatman主人放了一条鱼,通知猫过来吃~~~~~~ Tom吃了主人YoutBatman放的鱼 Cc吃了主人YoutBatman放的鱼 YoutBatman主人放了一条鱼,通知猫过来吃~~~~~~ Tom吃了主人YoutBatman放的鱼 Cc吃了主人YoutBatman放的鱼 YoutBatman主人放了一条鱼,通知猫过来吃~~~~~~ Tom吃了主人YoutBatman放的鱼 Cc吃了主人YoutBatman放的鱼
完美。这种处理方式是被推荐的方式,它能显示的控制Bean的依赖关系,而不受到其它影响,是值得信赖的使用方式。
说明:在“编程界”有个设计原则:显示的指出往往比隐式的更好,更稳定和更具表达力
方案三(不推荐):使用@Lazy
使用@Lazy只是一种曲线的解决方案,有些case它并不适合,因此并不推荐。
场景二示例:
这种场景在纯Spring环境下我们几乎遇不见,缘由是在Spring下所有的配置文件都是我们手动确定和编写,所以“哪些能写、哪些不能写,哪些在前,哪些在后”均是确定的,由我们程序员自行控制。该场景在Spring Boot场景下被大量用到,下面会举例说明。
当然,即使到了Spring Boot下,此部分初始化原理依旧是Spring Framwork的,因此这里也不闲着,通过代码示例来展示其加载顺序,核心便是介绍static关键字的使用。
使用static提升Bean的优先级
static代表静态,标注在类上表示该类是静态(内部)类,标注在方法上表示该方法是属于类的静态方法(不需要实例化即可调用),“看起来”可以是可以提升优先级的,那么实际如何呢?不能臆断,且看下面示例
static使用在@Bean方法上
准备两个配置类:
@Configuration(proxyBeanMethods = false) public class PersonConfig { public PersonConfig() { System.out.println("配置类PersonConfig构造器执行..."); } @Bean public Person son() { System.out.println("@Bean -> son执行..."); return new Person("YourBatman-son", 18); } @Bean public Person father() { System.out.println("@Bean -> father执行..."); return new Person("YourBatman", 48); } } @Configuration(proxyBeanMethods = false) public class Config { public Config() { System.out.println("配置类Config构造器被执行..."); } @Bean public Family family() { System.out.println("@Bean -> family执行..."); return new Family(); } @Bean public static Family staticFamily() { System.out.println("@Bean -> staticFamily执行..."); return new Family(); } }
书写测试程序:
public static void main(String[] args) { new AnnotationConfigApplicationContext(Config.class, PersonConfig.class); }
运行程序,控制台打印:
/
配置类Config构造器被执行... 配置类PersonConfig构造器执行... @Bean -> family执行... @Bean -> staticFamily执行... @Bean -> son执行... @Bean -> father执行...
结论:
- @Configuration配置类最优先被初始化,才会继续初始化其里面的@Bean
- 若有多个 @Configuration配置类,顺序由你构造AnnotationConfigApplicationContext时传入的顺序为准(若是被scan扫描进去的,则无序)
- @Bean方法上加static成为静态方法,并不能提升此Bean的优先级
- 主要是因为@Bean的解析,必须是发生在@Configuration配置类被实例化后,因此它并不能提升优先级