我们先来看看BeanFactory与ApplicationContext的区别:
在springboot启动类中有个启动方法SpringApplication.run(SpringPrincipleApplication.class, args);
它的返回值 ConfigurableApplicationContext context就是spring容器。
ConfigurableApplicationContext context =SpringApplication.run(SpringPrincipleApplication.class, args);
而context属于ConfigurableApplicationContext是可配置的ApplicationContext,是子接口;ApplicationContext是父接口。
按control+alt+u可看类图,选Java Classes
图中可知ApplicationContext是ConfigurableApplicationContext的父接口,而beanFactory是顶层的父接口,所以ApplicationContext的功能也比beanFactory的功能多。
beanFactory:是ApplicationContext的父接口,是spring的核心容器,主要的ApplicationContext实现都需要它的功能。ConfigurableApplicationContext context的getBean(“”)获取bean的方法 就是由beanFactory提供的功能。
其中spring中还有很多单例bean,就是存放在beanFactory中的singletonObjects中。
所以我们先看看顶层父接口beanFactory的功能:
我们先看看beanFactory中的所有方法,先按control+o,输入beanFactory,找到该类,然后在按control+7,查看其中的所有方法。
表面上有用的只有getBean,实际上 控制反转、基本的bean注入、直至bean的生命周期的各种功能,都由它的实现类提供。它的实现类为:DefaultListableBeanFactory(该实现类实现了很多接口,beanFactory只是其中的一个小接口)
DefaultListableBeanFactory能管理所有的bean,它的父类DefaultSingleBeanFactory管理单例对象。
双击看它的源码:
其中的singletonObjects Map集合中放的就是单例对象。因为这方法是私有的,debug的话实例太多不好看,可以通过反射拿到该map集合,看看其中的单例
@SpringBootApplication
public class a01Application {public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {ConfigurableApplicationContext context = SpringApplication.run(a01Application.class, args);//beanFactory功能://通过反射查看DefaultSingletonBeanRegistry中的单例Field singletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");//表示要取消 Java 语言对访问权限的检查机制,使得即使在常规情况下不具备访问权限的代码位置,也能够去访问对应的成员。singletonObjects.setAccessible(true);ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();Map<String,Object> map = (Map<String, Object>) singletonObjects.get(beanFactory);/*map.forEach((k,v)->{System.out.println(k + "=" + v);});*///过滤,仅看component开头的beanmap.entrySet().stream().filter(e->e.getKey().startsWith("component")).forEach(e->{System.out.println(e.getKey()+ "=" + e.getValue());});
}
我们可以写俩个@Component来测试,然后查找
@Component
public class Component1 {private static final Logger log = LoggerFactory.getLogger(Component1.class);
}
@Component
public class Component2 {private static final Logger log = LoggerFactory.getLogger(Component2.class);
}
启动该springboot项目后在返回结果中按control+F查找Component1:
然后我们在看ApplicationContext比BeanFactory多了哪些功能:
ApplicationContext多了四个接口
MessageSource:处理国际化资源的能力,支持程序多种语言的翻译能力。
ResourcePatternResolver: 路径中的通配符匹配多种资源的能力
ApplicationEventPublisher:发布事件对象
EnvironmentCapable:spring中环境信息,读取系统环境变量、pom文件、yaml文件等等。
下面我来演示一下:
1、MessageSource:处理国际化资源的能力,支持程序多种语言的翻译能力。
同一文本的不同语言的翻译是需要我们自己准备的:
我们先准备不同语言的翻译资源,创建文件:
结果:
直接在resources目录下创建就行,不用专门创建Resource Bundle ‘messages’目录,文件名以messages开头,其中en表示英文,zh表示中文。messages.properties文件不用创建。
资源准备好了,下面用代码获取:
下面代码都是写在springboot启动类中的
//MessageSourceSystem.out.println(context.getMessage("hi", null, Locale.CHINA));System.out.println(context.getMessage("hi", null, Locale.ENGLISH));//根据key找到不同语言的翻译资源结果。这些资源名结果一般都以messages打头
2、ResourcePatternResolver: 路径中的通配符匹配多种资源的能力
//可以根据有通配符的路径,获取多个资源//classpath: 为类路径下去查找(不去jar包中找)。classpath*: 就可以到jar包中查找。Resource[] resources = context.getResources("classpath:application.properties");for (Resource resource : resources) {System.out.println(resource);}
匹配的是resources目录下的springboot配置文件
3、 ApplicationEventPublisher:发布事件对象
例子:我们在register方法中完成了用户的注册功能后,还要求发个短信通知用户一下,我们就可以在register方法中把发短信的需求由事件发送给别的类去完成。
下面是由Component1发送事件,Component2处理事件
@Component
public class Component1 {private static final Logger log = LoggerFactory.getLogger(Component1.class);@Autowiredprivate ApplicationEventPublisher context;public void register(){log.info("用户注册");//通过发送事件,完成对用户注册和发短信通知事务的解藕context.publishEvent(new UserRegisteredEvent(this));}
}
@Component
public class Component2 {private static final Logger log = LoggerFactory.getLogger(Component2.class);@EventListener //(表名该事件为监听事件)public void aaa(UserRegisteredEvent event){log.info("{}",event);log.info("发送短信");}
}
启动代码:在springboot启动类中:
context.getBean(Component1.class).register();
4、EnvironmentCapable:spring中环境信息,读取系统环境变量、pom文件、yaml文件等等。
//配置信息,不同的配置信息来源可以不同,这里是查看关于java_home的配置信息,Linux区别大小写,windows不区分System.out.println(context.getEnvironment().getProperty("java_home"));System.out.println(context.getEnvironment().getProperty("server.port"));