aware 感知类接口
aware系列的扩展接口,允许spring应用感知/获取特定的上下文环境或对象。
bean生命周期控制类接口
bean生命周期类的接口,可以控制spring容器对bean的处理。
app生命周期控制类接口
app生命周期控制类接口,可以控制spring应用启动/销毁时的逻辑
下面详细介绍各种扩展点的使用
package cn.test.ext.aware;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import java.util.Arrays;
/**
* ApplicationContextAware
* 通过实现该接口,组件可以获取ApplicationContext对象的引用,
* 从而访问应用程序上下文中的bean以及其他特定的spring功能
*/
@Component
@Slf4j
public class TestAppCtxAware implements ApplicationContextAware {
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
String applicationName = applicationContext.getApplicationName();
log.info("context-path:{}",applicationName);
String displayName = applicationContext.getDisplayName();
log.info("displayName:{}",displayName);
String id = applicationContext.getId();
log.info("spring-applicationName:{}",id);
long startupDate = applicationContext.getStartupDate();
log.info("startupDate:{}",startupDate);
// String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
// log.info("beanDefinitionNames:{}", Arrays.asList(beanDefinitionNames).toString());
}
}
package cn.test.ext.aware;
import cn.test.web.TestController;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.stereotype.Component;
/**
* BeanFactoryAware
* 实现该接口,可以使自定义类获取bean工厂对象的引用,
* 允许你在运行时从bean工厂中获取其他bean
*/
@Slf4j
@Component
public class TestBeanFactoryAware implements BeanFactoryAware {
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
boolean b = beanFactory.containsBean("testController");
log.info("spring容器内是否含有 testController这个bean :{}",b);
TestController testController =(TestController) beanFactory.getBean("testController");
String tgwt43t = testController.t1("tgwt43t");
log.info("get bean方式调用 TestController里的t1方法,返回值:{}",tgwt43t);
}
}
package cn.test.ext.aware;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import java.util.Arrays;
/**
* EnvironmentAware
* 实现该接口,你的组件可以获取 Environment 环境对象的引用,
* 从而可以访问spring应用程序的配置属性和配置文件
*/
@Component
@Slf4j
public class TestEnvironmentAware implements EnvironmentAware {
@Override
public void setEnvironment(Environment environment) {
//获取当前默认的profile环境
String[] defaultProfiles = environment.getDefaultProfiles();
log.info("defaultProfiles:{}",Arrays.asList(defaultProfiles));
//获取某个属性配置
String property = environment.getProperty("spring.application.name");
log.info("property->spring.application.name:{}",property);
}
}
package cn.test.ext.aware;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceAware;
import org.springframework.stereotype.Component;
/**
* MessageSourceAware
* 实现该接口,你的组件可以获取 messageSource 消息源对象的引用,
* 从而访问 国际化消息内容
*/
@Component
@Slf4j
public class TestMessageSourceAware implements MessageSourceAware {
@Override
public void setMessageSource(MessageSource messageSource) {
// messageSource.getMessage()
}
}
package cn.test.ext.aware;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Component;
import java.util.Properties;
/**
* ResourceLoaderAware
* 实现该接口,你的组件可以获取 ResourceLoader 资源加载器 对象的引用。
* 从而可以加载外部资源文件
*/
@Slf4j
@Component
public class TestResourceLoaderAware implements ResourceLoaderAware {
@SneakyThrows
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
//ResourceLoader可以加载自定义位置的自定义的properties配置文件
//这里测试加载classpath下的test.properties文件
Resource resource = resourceLoader.getResource("classpath:test.properties");
if (resource.isFile()) {
String filename = resource.getFilename();
log.info("filename:{}",filename);
Properties properties = new Properties();
properties.load(resource.getInputStream());
properties.list(System.out);
String aaa = properties.getProperty("aaa");
log.info("aaa-->:{}",aaa);
}
}
}
package cn.test.ext.aware;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.context.ServletContextAware;
import javax.servlet.ServletContext;
/**
* ServletContextAware
* 实现该接口,你的组件可以获取 ServletContext Servlet上下文对象的引用,
* 从而访问与web应用程序相关的功能
*/
@Component
@Slf4j
public class TestServletContextAware implements ServletContextAware {
@Override
public void setServletContext(ServletContext servletContext) {
//添加java web 过滤器
// servletContext.addFilter()
//添加java web servlet
// servletContext.addServlet()
//添加java web 监听器
// servletContext.addListener();
}
}
package cn.test.ext.beanlife;
import cn.test.ext.ITestStrategy;
import cn.test.ext.MyStrategyFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
/**
* InitializingBean
* 实现该接口,你的bean可以在 spring容器初始化当前bean时 执行自定义逻辑
*
* 必须在afterPropertiesSet方法里写你自定义的执行逻辑
*/
@Slf4j
@Component
public class TestInitializingBean implements ITestStrategy, InitializingBean {
private static String str ;
//实现InitializingBean接口,必须重写afterPropertiesSet方法
//这样当springboot启动加载TestInitializingBean时,会自动执行里边的afterPropertiesSet方法
@Override
public void afterPropertiesSet() throws Exception {
//本方法里可以做一些初始化的操作,如设置类静态变量值 / 将类注册到策略工厂/ 执行一些其他方法或动作/...
//设置类静态变量值
str = "qwe123!";
//将k1策略 (本类) 注册到 策略工厂里
MyStrategyFactory.register("k1",this);
log.info("注册了策略:{}到策略工厂里",this);
}
@Override
public void execTestMethod() {
log.info("start----执行k1策略...");
System.err.println(str);
}
}
package cn.test.ext.beanlife;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.stereotype.Component;
/**
* DisposableBean
* 实现该接口,你的bean可以在销毁阶段执行自定义逻辑。
* 该接口包含一个destory(),你可以在此方法中定义销毁逻辑。
*/
@Slf4j
@Component
public class TestDisposableBean implements DisposableBean {
@Override
public void destroy() throws Exception {
log.info("TestDisposableBean 这个bean销毁了...");
}
}
package cn.test.ext.beanlife;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
/**
* BeanPostProcessor
* 该接口定义了在spring容器实例化bean之后和初始化之前,
* 对bean进行自定义处理的方法。
* 通过实现该接口,你可以插入自定义逻辑来处理bean
*/
@Component
@Slf4j
public class TestBeanPostProcessor implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (beanName.equals("testController")){
log.info("BeanPostProcessor Before 处理到了 beanName:{}",beanName);
}
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (beanName.equals("testController")){
log.info("BeanPostProcessor After 处理到了 beanName:{}",beanName);
}
return bean;
}
}
package cn.test.ext.beanlife;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;
/**
* BeanFactoryPostProcessor接口
* 允许在 所有bean定义加载到容器之后,但在bean实例化之前对他们进行自定义处理。
* 通过实现该接口,你可以修改或添加新的bean定义
*
*
*/
@Component
@Slf4j
public class TestBeanFctoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
log.info("postProcessBeanFactory 触发了....");
}
}
package cn.test.ext.beanlife;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.stereotype.Component;
/**
* BeanDefinitionRegistryPostProcessor接口
*
* 这个接口是BeanFactoryPostProcessor接口的扩展,
* 允许在bean定义注册过程中对 bean定义进行自定义处理,
* 它提供了对bean定义注册表的直接访问,可以添加,修改或删除bean定义
*/
@Slf4j
@Component
public class TestBeanDefineRegistPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
log.info("postProcessBeanDefinitionRegistry 触发了...");
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
log.info("postProcessBeanFactory 触发了...");
}
}
package cn.test.ext.applife;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.SmartLifecycle;
import org.springframework.stereotype.Component;
/**
* SmartLifecycle接口 可以感知springboot应用生命周期变化,是spring的一个扩展点,可以实现随springboot启动和销毁的操作。
*
* 自定义类实现SmartLifecycle接口后,至少要重写3个方法start(),stop(),isRunning() ,其他方法可以按需实现
*
* springboot中的内嵌tomcat启动/关闭,就是利用SmartLifecycle实现的,
* 见 org.springframework.boot.web.servlet.context.WebServerStartStopLifecycle
*/
@Component
@Slf4j
public class TestSmartLifeCycle implements SmartLifecycle {
//模拟一个任务 (实际开发中,可以在这里引入netty,实现netty随springboot启动和销毁)
private static boolean xxxStat;
/**
* bean初始化完毕后,该方法会被执行
*/
@Override
public void start() {
xxxStat = true;
log.info("启动了 xxxStat ");
}
/**
* 容器关闭后,spring容器发现当前对象实现了SmartLifecycle,
* 就调用stop(Runnable),如果只是实现了Lifecycle,就调用stop()
*/
@Override
public void stop() {
log.info("stop---容器关闭了");
xxxStat = false;
}
/**
* 当前状态
* @return
*/
@Override
public boolean isRunning() {
log.info("isRunning 获取当前状态:{}",xxxStat);
return xxxStat;
}
/**
* start方法被执行前,先看此方法返回值,
* 返回false就不执行start方法了
* @return
*/
@Override
public boolean isAutoStartup() {
log.info("isAutoStartup 是否自启动");
return true;
}
/**
* 容器关闭后,spring容器发现当前对象实现了SmartLifecycle,就调用stop(Runnable),
* 如果只是实现了Lifecycle,就调用stop()
* @param callback
*/
@Override
public void stop(Runnable callback) {
//必须调用stop方法 ,确保SmartLifecycle这个bean里的任务停止了
this.stop();
//必须执行callback.run方法,否则app exit时无法销毁这个SmartLifecycle
callback.run();
log.info("stop Runnable---容器关闭了");
}
/**
* 返回值决定
* start方法 在众多Lifecycle实现类中的执行顺序(stop也是)
* @return
*/
@Override
public int getPhase() {
log.info("SmartLifecycle 当前优先级 1");
return 1;
}
}
CommandLineRunner可以让springboot-jar启动时接收一些jvm参数或自定义cmd参数,然后执行一些操作
package cn.test.ext.applife;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class TestCmdLineRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
log.info("CommandLineRunner 执行了...");
}
}
ApplicationRunner可以让springboot-jar启动时接收一些应用配置参数如server.port,然后执行一些操作
package cn.test.ext.applife;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class TestApplictionRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
log.info("ApplicationRunner 执行了...");
}
}
要通过spring内置的ApplicationListener监听ContextRefreshedEvent
上下文刷新事件,springboot启动时会触发一次,
一般常用来做业务缓存预热,如在springboot启动时把系统字典数据/系统配置项数据加到缓存中,方便后续调用。
package cn.test.ext.applife;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class AppCtxRefreshListener implements ApplicationListener<ContextRefreshedEvent> {
/**
* 监听到上下文刷新事件时,
* 执行相应的操作
* @param contextRefreshedEvent 上下文刷新事件
*/
@Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
log.info("监听到了spring应用 上下文刷新事件...");
}
}