spring底层学习笔记(一) BeanFactory与ApplicationContext
课程:某站视频黑马程序员Spring视频教程,深度讲解spring5底层原理
0.环境准备-springboot项目搭建
这里用了aliyunstart使用git直接拉取demo代码
git clone "https://start.aliyun.com/type=maven-project&language=java&architecture=none&bootVersion=2.6.13&baseDir=demo&groupId=com.example&artifactId=demo&name=demo&description=Demo%20project%20for%20Spring%20Boot&packageName=com.example.demo&packaging=jar&javaVersion=1.8/demo.git" demo
1.BeanFactory与ApplicationContext的关系
在加有@SpringBootApplication注解的引导类的main方法中,SpringBootApplication.run()方法存在返回值(spring容器-ConfigurableApplicationContext)
@SpringBootApplication
public class Application {
public static void main(String[] args) {
//SpringBootApplication.run()方法存在返回值(spring容器-ConfigurableApplicationContext)
ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
}
}
对ConfigurableApplicationContext 使用快捷键ctrl+alt+u,查看类图
可以看出二者的关系:ApplicationContext间接继承了BeanFactory,提供了功能上的扩展
2.BeanFactoryg功能
问题1.什么是BeanFactory?
ApplicationContext的父接口,它才是spring的核心容器,主要的ApplicationContext实现都组合了它的功能,BeanFactory是ApplicationContext的一个成员变量
这个context有getBean的方法,但实际还是使用的BeanFactory的getBean方法
问题2:BeanFactory什么用处?
表面上只有getBean,实际上控制反转、基本的依赖注入,直至Bean的什么周期的各种功能,都由它的实现类(DefaultListableBeanFactory)提供。
也就是说,DefaultListableBeanFactory才是真正实现了容器接口并可以独立使用的类,是IOC的核心
DefaultListableBeanFactory是一个重要的类,它可以管理所有bean,以及单例对象。
(选中DefaultSingletonBeanRegistry按F4跳转)
然后我们进入到DefaultSingletonBeanRegistry类中。
Spring的bean其实都是在这个类中被管理,二级缓存、三级缓存之类的其实是一个个不同功能的map。
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
/** Maximum number of suppressed exceptions to preserve. */
private static final int SUPPRESSED_EXCEPTIONS_LIMIT = 100;
/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
/** Set of registered singletons, containing the bean names in registration order. */
private final Set<String> registeredSingletons = new LinkedHashSet<>(256);
/** Names of beans that are currently in creation. */
private final Set<String> singletonsCurrentlyInCreation =
Collections.newSetFromMap(new ConcurrentHashMap<>(16));
/** Names of beans currently excluded from in creation checks. */
private final Set<String> inCreationCheckExclusions =
Collections.newSetFromMap(new ConcurrentHashMap<>(16));
/** Collection of suppressed Exceptions, available for associating related causes. */
@Nullable
private Set<Exception> suppressedExceptions;
/** Flag that indicates whether we're currently within destroySingletons. */
private boolean singletonsCurrentlyInDestruction = false;
/** Disposable bean instances: bean name to disposable instance. */
private final Map<String, Object> disposableBeans = new LinkedHashMap<>();
/** Map between containing bean names: bean name to Set of bean names that the bean contains. */
private final Map<String, Set<String>> containedBeanMap = new ConcurrentHashMap<>(16);
/** Map between dependent bean names: bean name to Set of dependent bean names. */
private final Map<String, Set<String>> dependentBeanMap = new ConcurrentHashMap<>(64);
/** Map between depending bean names: bean name to Set of bean names for the bean's dependencies. */
private final Map<String, Set<String>> dependenciesForBeanMap = new ConcurrentHashMap<>(64);
}
这里我们回到启动类,通过反射看一下singletonObjects这个map(包含所有的单例bean)
@SpringBootApplication
public class Application {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
//SpringBootApplication.run()方法存在返回值(spring容器-ConfigurableApplicationContext)
ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
//1.拿到私有的成员变量对象
Field singletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");
//2.设置允许通过反射访问私有变量
singletonObjects.setAccessible(true);
//3.获取beanFactory-DefaultListableBeanFactory
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//4.反射调用-field.get(obj)返回指定对象obj上此field标识字段的值
//也就是获取beanFactory中的singletonObjects对象
Map<String, Object> map = (Map<String, Object>) singletonObjects.get(beanFactory);
map.forEach((k,v)->{
//这里输出的就是所有的单例bean
System.out.println(k+"="+v);
});
}
}
这里context.getBeanFactory()获取到的其实是实现了ConfigurableListableBeanFactory接口的DefaultListableBeanFactory对象,而DefaultListableBeanFactory是DefaultSingletonBeanRegistry的子类,所以包含了父类的几个map,其中有我们需要的singletonObjects
debug看下这个map,里面就是所有的单例bean了。
3.ApplicationContext功能
还是这张图,拓展的四个接口
/*
2.ApplicationContext比BeanFactory多点啥
*/
//1.国际化 实际应用中Locale这个参数是通过请求头带过来的
System.out.println(context.getMessage("hi",null, Locale.CHINA));
//2.获取资源
Resource resource = context.getResource("classpath:application.yaml");
System.out.println(resource);
//使用通配符在所有jar包中找
Resource[] resources = context.getResources("classpath*:META-INF/spring.factories");
for(Resource a : resources){
System.out.println(a);
}
//3.获取配置信息,环境变量或配置文件
System.out.println(context.getEnvironment().getProperty("java_home"));
System.out.println(context.getEnvironment().getProperty("server.port"));
//4.发布事件,spring自带mq,但只适用于单体应用,必须是同一个jvm,一个服务内部需要解耦的场景
//中间件mq通常是跨服务的
//这里应该是同步的,异步需要在方法上使用@Async注解,在启动类上要加@EnableAsync注解
//context.publishEvent(new UserRegisterEvent(context));
context.getBean(Component1.class).register();
配合@EventListener注解使用
@Component
public class Component2 {
private static final Logger log = LoggerFactory.getLogger(Component2.class);
//spring中的任何组件都可以作为监听器,入参的类型以发布事件类型为准
@EventListener
public void aaa(UserRegisterEvent event){
log.debug("{}",event);
log.debug("发送短信");
}
}
总结:
(1)BeanFactory与ApplicationContext并不仅仅是简单接口继承的关系,ApplicationContext组合并扩展了BeanFactory的功能
(2)新一种代码间解耦途径