本文主要讲解“如何掌握春天”。本文的讲解内容简单明了,易学易懂。请跟随边肖的思路,一起学习学习《如何掌握春天》。
Spring是一个控制反向依赖管理的容器。作为Java Web开发人员,基本上没有不熟悉Spring技术栈的人,虽然Java Web中还有很多其他优秀的框架,比如guice、谷歌的开源依赖管理框架、Jersey web框架。然而,Spring一直是Java Web领域应用最广泛的spring mvc。
本文将重点讨论如何在Spring容器启动时实现我们想要实现的逻辑。我们经常会遇到一些必须在Spring启动时完成的初始化操作,比如创建定时任务和创建连接池。
如果没有Spring容器,不要依赖Spring的实现,回到Java类实现本身,我们可以在静态代码块和类构造器中实现相应的逻辑。Java类的初始化顺序是静态变量静态代码块全局变量初始化代码块构造函数。
例如,Log4j的初始化是在LogManager的静态代码块中实现的:
静态{ hierarchy yh=new hierarchy(new rootlogger)((Level)Level。DEBUG));respitoryselector=new defaultrespitoryselector(h);string goverride=option converter . getsystemproperty(DEFAULT _ INIT _ OVERRIDE _ KEY,null);if(override==null | |“false”。equalsIgnoreCase(override)){ stringconfigurationoptions str=option converter . getsystemproperty(DEFAULT _ CONFIGURATION _ KEY,null);StringconfiguratorClassName=option converter . getsystemproperty(CONFIGURATOR _ CLASS _ KEY,null);URLurl=nullif(configurationoptions str==null){ URL=loader . getresource(DEFAULT _ XML _ CONFIGURATION _ FILE);if(URL==null){ URL=loader . getresource(DEFAULT _ CONFIGuration _ FILE);}}else{nb
sp; try { url = new URL(configurationOptionStr); } catch (MalformedURLException ex) { url = Loader.getResource(configurationOptionStr); } } if(url != null) { LogLog.debug("Using URL ["+url+"] for automatic log4j configuration."); try { OptionConverter.selectAndConfigure(url, configuratorClassName,LogManager.getLoggerRepository()); } catch (NoClassDefFoundError e) { LogLog.warn("Error during default initialization", e); } } else { LogLog.debug("Could not find resource: ["+configurationOptionStr+"]."); } } else { LogLog.debug("Default initialization of overridden by " + DEFAULT_INIT_OVERRIDE_KEY + "property."); } }
比如在构造函数中实现相应的逻辑:
@Component public class CustomBean { @Autowired private Environment env; public CustomBean() { env.getActiveProfiles(); } }
这里考验一下各位,上面的代码是否可以正常运行。—— 不行,构造函数中的env将会发生NullPointException异常。这是因为在 Spring 中将先初始化 Bean,也就是会先调用类的构造函数,然后才注入成员变量依赖的 Bean(@Autowired和@Resource注解修饰的成员变量),注意@Value等注解的配置的注入也是在构造函数之后。
PostConstruct
在 Spring 中,我们可以使用@PostConstruct在 Bean 初始化之后实现相应的初始化逻辑,@PostConstruct修饰的方法将在 Bean 初始化完成之后执行,此时 Bean 的依赖也已经注入完成,因此可以在方法中调用注入的依赖 Bean。
@Component public class CustomBean { @Autowired private Environment env; @PostConstruce public void init() { env.getActiveProfiles(); } }
与@PostConstruct相对应的,如果想在 Bean 注销时完成一些清扫工作,如关闭线程池等,可以使用@PreDestroy注解:
@Component public class CustomBean { @Autowired private ExecutorService executor = Executors.newFixedThreadPool(1) @PreDestroy public void destroy() { env.getActiveProfiles(); } }
InitializingBean
实现 Spring 的InitializingBean接口同样可以实现以上在 Bean 初始化完成之后执行相应逻辑的功能,实现InitializingBean接口,在afterPropertiesSet方法中实现逻辑:
@Component public class CustomBean implements InitializingBean { private static final Logger LOG = Logger.getLogger(InitializingBeanExampleBean.class); @Autowired private Environment environment; @Override public void afterPropertiesSet() throws Exception { LOG.info(environment.getDefaultProfiles()); } }
ApplicationListener
我们可以在 Spring 容器初始化的时候实现我们想要的初始化逻辑。这时我们就可以使用到 Spring 的初始化事件。Spring 有一套完整的事件机制,在 Spring 启动的时候,Spring 容器本身预设了很多事件,在 Spring 初始化的整个过程中在相应的节点触发相应的事件,我们可以通过监听这些事件来实现我们的初始化逻辑。Spring 的事件实现如下:
-
ApplicationEvent,事件对象,由 ApplicationContext 发布,不同的实现类代表不同的事件类型。
-
ApplicationListener,监听对象,任何实现了此接口的 Bean 都会收到相应的事件通知。实现了 ApplicationListener 接口之后,需要实现方法 onApplicationEvent(),在容器将所有的 Bean 都初始化完成之后,就会执行该方法。
与 Spring Context 生命周期相关的几个事件有以下几个:
-
ApplicationStartingEvent: 这个事件在 Spring Boot 应用运行开始时,且进行任何处理之前发送(除了监听器和初始化器注册之外)。
-
ContextRefreshedEvent: ApplicationContext 被初始化或刷新时,该事件被发布。这也可以在 ConfigurableApplicationContext 接口中使用 refresh() 方法来发生。
-
ContextStartedEvent: 当使用 ConfigurableApplicationContext 接口中的 start() 方法启动 ApplicationContext 时,该事件被触发。你可以查询你的数据库,或者你可以在接受到这个事件后重启任何停止的应用程序。
-
ApplicationReadyEvent: 这个事件在任何 application/ command-line runners 调用之后发送。
-
ContextClosedEvent: 当使用 ConfigurableApplicationContext 接口中的 close() 方法关闭 ApplicationContext 时,该事件被触发。一个已关闭的上下文到达生命周期末端;它不能被刷新或重启。
-
ContextStoppedEvent: Spring 最后完成的事件。
因此,如果我们想在 Spring 启动的时候实现一些相应的逻辑,可以找到 Spring 启动过程中符合我们需要的事件,通过监听相应的事件来完成我们的逻辑:
@Component @Slf4j public class StartupApplicationListenerExample implements ApplicationListener<ContextRefreshedEvent> { @Override public void onApplicationEvent(ContextRefreshedEvent event) { log.info("Subject ContextRefreshedEvent"); } }
除了通过实现ApplicationListener接口来监听相应的事件,Spring 的事件机制也实现了通过@EventListener注解来监听相对应事件:
@Component @Slf4j public class StartupApplicationListenerExample { @EventListener public void onApplicationEvent(ContextRefreshedEvent event) { log.info("Subject ContextRefreshedEvent"); } }
Spring Event 是一套完善的进程内事件发布订阅机制,我们除了用来监听 Spring 内置的事件,也可以使用 Spring Event 实现自定义的事件发布订阅功能。
Constructor 注入
在学习 Spring 的注入机制的时候,我们都知道 Spring 可以通过构造函数、Setter 和反射成员变量注入等方式。上面我们在成员变量上通过@Autoware注解注入依赖 Bean,但是在 Bean 的构造函数函数中却无法使用到注入的 Bean(因为 Bean 还未注入),其实我们也是使用 Spring 的构造函数注入方式, 这也是 Spring 推荐的注入机制(在我们使用 IDEA 的时候,如果没有关闭相应的代码 Warning 机制,会发现在成员变量上的@Autoware是黄色的,也就是 idea 不建议的代码)。Spring 更推荐构造函数注入的方式:
@Component @Slf4j public class ConstructorBean { private final Environment environment; @Autowired public LogicInConstructorExampleBean(Environment environment) { this.environment = environment; log.info(Arrays.asList(environment.getDefaultProfiles())); } }
CommandLineRunner
如果我们的项目使用的是 Spring Boot,那么可以使用 Spring Boot 提供的 CommandLineRunner 接口来实现初始化逻辑,Spring Boot 将在启动初始化完成之后调用实现了CommandLineRunner的接口的run方法:
@Component @Slf4j public class CommandLineAppStartupRunner implements CommandLineRunner { @Override public void run(String...args) throws Exception { log.info("Increment counter"); } }
并且,多个CommandLineRunner实现,可以通过@Order来控制它们的执行顺序。
SmartLifecycle
还有一种更高级的方法来实现我们的逻辑。这可以 Spring 高级开发必备技能哦。SmartLifecycle 不仅仅能在初始化后执行一个逻辑,还能再关闭前执行一个逻辑,并且也可以控制多个 SmartLifecycle 的执行顺序,就像这个类名表示的一样,这是一个智能的生命周期管理接口。
-
start():bean 初始化完毕后,该方法会被执行。
-
stop():容器关闭后,spring 容器发现当前对象实现了 SmartLifecycle,就调用 stop(Runnable), 如果只是实现了 Lifecycle,就调用 stop()。
-
isRunning:当前状态,用来判你的断组件是否在运行。
-
getPhase:控制多个 SmartLifecycle 的回调顺序的,返回值越小越靠前执行 start() 方法,越靠后执行 stop() 方法。
-
isAutoStartup():start 方法被执行前先看此方法返回值,返回 false 就不执行 start 方法了。
-
stop(Runnable):容器关闭后,spring 容器发现当前对象实现了 SmartLifecycle,就调用 stop(Runnable), 如果只是实现了 Lifecycle,就调用 stop()。
@Component public class SmartLifecycleExample implements SmartLifecycle { private boolean isRunning = false; @Override public void start() { System.out.println("start"); isRunning = true; } @Override public int getPhase() { // 默认为 0 return 0; } @Override public boolean isAutoStartup() { // 默认为 false return true; } @Override public boolean isRunning() { // 默认返回 false return isRunning; } @Override public void stop(Runnable callback) { System.out.println("stop(Runnable)"); callback.run(); isRunning = false; } @Override public void stop() { System.out.println("stop"); isRunning = false; } }
感谢各位的阅读,以上就是“如何掌握Spring”的内容了,经过本文的学习后,相信大家对如何掌握Spring这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是,小编将为大家推送更多相关知识点的文章,欢迎关注!
内容来源网络,如有侵权,联系删除,本文地址:https://www.230890.com/zhan/38070.html