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);// 获取单例对象singletonObjectsClass<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> {@Overridepublic void onApplicationEvent(UserLoginApplicationEvent event) {log.info("UserLoginApplicationListener received event: " + event);}
}
3、监听事件:第1种方式是定义一个类接收事件,必须注入到容器中,监听器必须实现ApplicationListener接口,指定泛型是UserLoginApplicationEvent事件,重写onApplicationEvent方法
@Component
@Log4j2
public class UserLoginListener {@EventListenerpublic void receivedEvent(UserLoginApplicationEvent event) {log.info("UserLoginListener received event: {}", event);}}
3、监听事件:第2种方式是创建一个public void方法,入参是事件,方法用注解@EventListener来监听事件
测试结果是启动服务会发布事件,两个监听器都会收到事件并输出,并且实现ApplicationListener接口的方式比方法用注解@EventListener来监听事件的方式先执行