1、调用getBean方法
@SpringBootApplication
public class SpringBootDemoApplication {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(SpringBootDemoApplication.class, args);
applicationContext.getBean("1");
}
}
先创建ConfigurableApplicationContext对象,然后调用getBean方法
ctrl+alt+u:显示类图
BeanFactory是ApplicationContext的父接口,BeanFactory是容器的核心接口,ApplicationContext是提供组合
getBean方法会先获取BeanFactory对象,然后调用BeanFactory对象的getBean方法,不是ApplicationContext直接调用的getBean方法
创建的BeanFactory对象是DefaultListableBeanFactory类的对象,BeanFactory对象是保存在GenericApplicationContext类中的
实际创建DefaultListableBeanFactory容器对象,DefaultListableBeanFactory类实现了ConfigurableListableBeanFactory接口,ConfigurableListableBeanFactory接口继承了ListableBeanFactory接口,ListableBeanFactory接口继承了BeanFactory接口
调用DefaultListableBeanFactory对象的getBean方法,实际调用的是AbstractBeanFactory抽象类中的getBean方法
结论:DefaultListableBeanFactory类非常重要!!!它提供了IOC控制反转、DI依赖注入、Bean的生命周期
2、获取BeanFactory对象中所有的单例对象
需求:输出singletonObjects容器中所有的单例对象
DefaultSingletonBeanRegistry类是DefaultListableBeanFactory类的父类,DefaultSingletonBeanRegistry类中包含singletonObjects是单例对象容器,它是一个ConcurrentHashMap,它是一级缓存
@SpringBootApplication
@Log4j2
public class SpringBootDemoApplication {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
ConfigurableApplicationContext applicationContext = SpringApplication.run(SpringBootDemoApplication.class, args);
// 获取单例对象singletonObjects
Class<DefaultSingletonBeanRegistry> cls = DefaultSingletonBeanRegistry.class;
Field field = cls.getDeclaredField("singletonObjects");
field.setAccessible(true);
// field.get(对象实例),对象是BeanFactory对象,
// application.getBeanFactory()方法返回的是ConfigurableListableBeanFactory接口,实际返回的是DefaultListableBeanFactory类
// DefaultListableBeanFactory类是DefaultSingletonBeanRegistry的子类,因此DefaultListableBeanFactory对象可以获取到DefaultSingletonBeanRegistry的属性
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext.getBeanFactory();
Map<String, Object> map = (Map<String, Object>) field.get(beanFactory);
map.forEach((k, v) -> {
log.info("k = {}, v = {}", k, v);
});
}
}
思路:利用反射获取容器中所有的单例对象,即属性.get(对象实例),singletonObjects容器在DefaultSingletonBeanRegistry类中,DefaultSingletonBeanRegistry对象如何获取呢?DefaultListableBeanFactory类是DefaultSingletonBeanRegistry类的子类,可以通过applicationContext.getBeanFactory()方法获取DefaultListableBeanFactory对象
3、ApplicationContext相比BeanFactory的扩展功能
ApplicationContext接口继承了BeanFactory接口,ApplicationContext对象封装了BeanFactory对象,并实现4种扩展功能
先点击类,然后点击F4可以跳转到指定类中
(1)MessageSource接口
MessageSource接口是用于国际化的,它包含getMessage方法可以选择指定code指定locale的语言值
其中,messages.properties是国际化的通用配置文件,它必须存在(可以不写内容)
message_zh.properties配置文件中填写menu.menuName.code=按钮
message_en.properties配置文件中填写menu.menuName.code=button
@SpringBootApplication
@Log4j2
public class SpringBootDemoApplication {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
ConfigurableApplicationContext applicationContext = SpringApplication.run(SpringBootDemoApplication.class, args);
String message = applicationContext.getMessage("menu.menuName.code", null, Locale.CHINA);
log.info("中文:{}", message);
message = applicationContext.getMessage("menu.menuName.code", null, Locale.ENGLISH);
log.info("英文:{}", message);
}
}
由于ApplicationContext接口继承了MessageSource接口,因此可以调用MessageSource接口的getMessage方法
输出结果是国际化语言值中文和英文
解析浏览器请求头的语言信息是LocaleResolver
(2)ResourcePatternResolver接口
ResourcePatternResolver类用来查找资源文件
@SpringBootApplication
@Log4j2
public class SpringBootDemoApplication {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
ConfigurableApplicationContext applicationContext = SpringApplication.run(SpringBootDemoApplication.class, args);
Resource[] resources = applicationContext.getResources("classpath:application.yml");
Arrays.stream(resources).forEach(resource -> {
log.info("resource: {}", resource);
String filename = resource.getFilename();
log.info("filename: {}", filename);
});
log.info("===========================================");
resources = applicationContext.getResources("classpath*:META-INF/spring.factories");
Arrays.stream(resources).forEach(resource -> {
log.info("resource: {}", resource);
String filename = resource.getFilename();
log.info("filename: {}", filename);
});
}
}
寻找classpath类路径下的指定资源文件,比如找application.yml、META-INF/spring.factories
其中,classpath*:a.txt的*是查找jar包下的a.txt文件
共有3个地方有META_INF/spring.factories文件
(3)EnvironmentCapable接口
public interface EnvironmentCapable {
/**
* Return the {@link Environment} associated with this component.
*/
Environment getEnvironment();
}
这个接口是获取环境变量的
@SpringBootApplication
@Log4j2
public class SpringBootDemoApplication {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
ConfigurableApplicationContext applicationContext = SpringApplication.run(SpringBootDemoApplication.class, args);
ConfigurableEnvironment environment = applicationContext.getEnvironment();
String javaHome = environment.getProperty("java_home");
log.info("Java home: " + javaHome);
String serverPort = environment.getProperty("server.port");
log.info("Server port: " + serverPort);
}
}
调用getEnvironment方法和getProperty方法获取环境变量
结果是获取得到环境变量包括application.yml
(4)ApplicationEventPublisher接口
事件用来解耦的,ApplicationEventPublisher接口定义了publishEvent方法可以发布事件
public class UserLoginApplicationEvent extends ApplicationEvent {
public UserLoginApplicationEvent(Object source) {
super(source);
}
}
1、定义事件:先定义事件UserLoginApplicationEvent,它要继承ApplicationEvent抽象类
@SpringBootApplication
@Log4j2
public class SpringBootDemoApplication {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
ConfigurableApplicationContext applicationContext = SpringApplication.run(SpringBootDemoApplication.class, args);
applicationContext.publishEvent(new UserLoginApplicationEvent(applicationContext));
}
}
2、发布事件:调用publishEvent方法,参数是创建一个UserLoginApplicationEvent事件,source是applicationContext,当然可以在一个对象中注入ApplicationEventPublisher或者ApplicationContext接口然后调用publishEvent方法发布事件
@Component
@Log4j2
public class UserLoginApplicationListener implements ApplicationListener<UserLoginApplicationEvent> {
@Override
public void onApplicationEvent(UserLoginApplicationEvent event) {
log.info("UserLoginApplicationListener received event: " + event);
}
}
3、监听事件:第1种方式是定义一个类接收事件,必须注入到容器中,监听器必须实现ApplicationListener接口,指定泛型是UserLoginApplicationEvent事件,重写onApplicationEvent方法
@Component
@Log4j2
public class UserLoginListener {
@EventListener
public void receivedEvent(UserLoginApplicationEvent event) {
log.info("UserLoginListener received event: {}", event);
}
}
3、监听事件:第2种方式是创建一个public void方法,入参是事件,方法用注解@EventListener来监听事件
测试结果是启动服务会发布事件,两个监听器都会收到事件并输出,并且实现ApplicationListener接口的方式比方法用注解@EventListener来监听事件的方式先执行