简介
可以用于从java主方法中引导和启动Spring应用程序的类,在默认情况下,通过以下步骤来启动应用:
- 创建一个ApplicationContext实例
- 注册CommandLinePropertySource用来暴露命令行参数作为spring的属性
- refresh applicationContext,加载所有的singleton bean
- 触发任何CommandLineRunner bean
自定义SpringApplication
默认的启动方式,代码如下,不做深入介绍
1 |
SpringApplication.run(AppBoot1.class, args); |
当然也可以做一些定制,如在Banner里面介绍的自定义banner,还有添加Listener等诸多设置,格式如下:
123 |
SpringApplication app = new SpringApplication(MySpringConfiguration.class); app.setBannerMode(Banner.Mode.OFF); app.run(args); |
使用Fluent Builder API进行构建
先上代码,如下:
12345678910111213141516171819202122232425262728293031 |
new SpringApplicationBuilder() .sources(ParentConfig.class) .child(Child1Config.class) .sibling(Child2Config.class) .banner(new AppBanner()) .logStartupInfo(false) .run(args); //@Configuration @PropertySource("classpath:/parent.properties")public class {} // Child1Config.class @PropertySource("classpath:/child1.properties")public class Child1Config { } // Child2Config.class @PropertySource("classpath:/child2.properties")public class Child2Config { } |
使用SpringApplicationBuilder在构建分层的ApplicationContext应用的时候非常便利,但是官网给的代码示例很简单,对于一些初次介入的人可能理解上不是那么透彻。就上面的代码做一些简单的介绍
- .sources(ParentConfig.class)该方法是用来配置父配置,或主配置的。但是有坑!!!!请看上面的ParentConfig的代码,注解我使用的是@SpringBootApplication,如果你使用的是springboot的1.x的版本,那么你会很顺利,如果你正在研究springboot 2.x的版本,你会发现无论如何也无法启动成功(我被坑的好惨)…。聪明的人也许看到我的注释,没错换成@Configuration之后就可以正常工作。但是很抱歉,这是为什么暂时还没找到原因,在github上请教暂时也没得到正确的结果,后面继续研究,如果有人发现了其中的原因,请通知我一下
- .child(Child1Config.class)那么通过名字就可以看到是子环境了,Child1Config就是child1的配置文件。也许你的应用里有多个child,那么你可能会想用多个child().child(),那么这样你的第二个child不是parent的child,而是第一个child的child,parent的孙子。想要实现多个同级的孩子,可以使用代码中的.sibling(Child2Config.class)方法。这里同样存在一个springboot的版本改动问题,那就是如果你要在配置文件里面为child1配置一个context path,那么在版本1里面的方法是
server.contextPath=child1
,但是如果使用版本2的朋友就需要做一点小改动了,改为server.servlet.contextPath=child1
可以参照github上的代码进行详细的理解github
事件和监听器
spring里面的监听器有三种实现方式:
- @EventListener注解方式
- 实现ApplicationListener接口
- 实现SmartApplicationListener接口
上面三种方式代码分别为
12345678910 |
@Componentpublic class AnnotationRegisterListener { @EventListener public void register(UserRegisterEvent event) { User user = event.getUser(); System.out.println("AnnotationRegisterListener " + user.getName() + ", " + user.getPassword()); }} |
1234567891011 |
@Componentpublic class RegisterListener implements ApplicationListener<UserRegisterEvent>{ @Override public void onApplicationEvent(UserRegisterEvent event) { User user = event.getUser(); System.out.println("RegisterListener " + user.getName() + ", " + user.getPassword()); }} |
123456789101112131415161718192021222324252627282930313233343536373839 |
@Componentpublic class UserRegisterListener implements SmartApplicationListener { /** * 该方法返回true&supportsSourceType同样返回true时,才会调用该监听内的onApplicationEvent方法 * @param eventType * @return */ @Override public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) { return UserRegisterEvent.class.isAssignableFrom(eventType); } /** * 该方法返回true&supportsEventType同样返回true时,才会调用该监听内的onApplicationEvent方法 * @param sourceType * @return */ @Override public boolean supportsSourceType(@Nullable Class<?> sourceType) { return UserService.class.isAssignableFrom(sourceType); } @Override public void onApplicationEvent(ApplicationEvent event) { UserRegisterEvent userRegisterEvent = (UserRegisterEvent) event; User user = userRegisterEvent.getUser(); System.out.println("UserRegisterListener " + user.getName() + ", " + user.getPassword()); } /** * 同步情况下监听执行的顺序 * @return */ @Override public int getOrder() { return 3; }} |
前两种实现方式比较简单,稍微介绍一下第三种,这种实现方式必须在supportsEventType和supportsSourceType同时返回true的情况下才会执行事件,具体如何返回true和false就看你自己的业务实现。在这里我使用了jdk的isAssignableFrom方法来判断父子关系的。
上面两种方式事件的执行顺序是无序的,第三种提供了一种指定属性的方法getOrder()
具体的isAssignableFrom是如何使用的,请看如下代码:
123456789101112131415161718192021222324252627282930313233343536373839404142 |
public class Animal { |