写在前面(感慨):笔者曾在十一月份通过了字节跳动的三次面试, 但是最终因为疫情原因不能满足公司的入职时间要求, 没有拿到offer。近期也是投递了大量大厂的实习岗, 但是要么已读不回, 要么明确告诉我学历至少要硕士(天天被阿里cpu)。说实话一直以为自己能靠技术吃饭, 过去的几年里, 我学过很多技术, 从最开始的C/C++、Python到Php, Java, C#, 当然不只是语言层面, 也包括很多对应的框架, 像C++的cocos2dx、Python的Scrapy、C#的Unity等等, 最终还是选择了Java这条赛道。 我知道竞争很激烈, 我也从未停歇过, 一年里除去玩的时间可能也就两个月, 其他时间都是从早到晚的学(甚至五点多起床六点就到教室坐着看书), b站Java有关的视频我都看过, 也有自己买过培训班的课程, 相关的技术书籍更是看过少说二三十本, 有些还是来来回回看了几遍, 各种框架源码, 甚至于JVM源码也是常常钻研。但是这些好像不够, 至少说, 对于当下如此内卷, 本科生学历已经远远跟不上很多大厂的要求, 就连我之前通过字节三面的部门, leader也是让尽量捞研究生简历。有时候真的很累很想放弃, 我的志向并不是想做什么技术专家, 只是因为做技术钱多, 家里缺钱, 仅此而已。马上开学大三下了, 考研也就剩不到一年了, 未来何去何从呢, 暂且不知(三月再决定吧), 可能考研, 可能运气好拿到个大厂实习然后就这样到毕业。。。。。。
但不管怎么样, 接下来的时间笔者将与大家一起努力! 披星戴月路过山水万城,祝自己与温柔重逢。
1. BeanFactory与ApplicationContext等接口关系
BeanFactory
与ApplicationContext
并不仅仅只是继承关系,
BeanFactory
是ApplicationContext
的一个成员变量, 二者为组合关系。并在原有基础上进行了增强功能, 见2
DefaultSingletonBeanRegistry
管理了所有的单例Bean对象 – 存储在singletonObjects
这个map
↑ 实现
DefaultListableBeanFactory
是ApplicationContext
中实际的BeanFactory实现类
下面是获取DefaultSingletonBeanRegistry
中管理单例Bean对象的singletonObjects示例:
@SpringBootApplication
public class SpringSourceStudyApplication {
private static final Logger log = LoggerFactory.getLogger(SpringSourceStudyApplication.class);
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
ConfigurableApplicationContext context = SpringApplication.run(SpringSourceStudyApplication.class, args);
Field singletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");
singletonObjects.setAccessible(true);
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//一级缓存, 单列Bean对象的Map, 来自DefaultSingletonBeanRegistry
Map<String, Object> singletonObjectsMap = (Map<String, Object>) singletonObjects.get(beanFactory);
singletonObjectsMap.forEach((k,v)->{
System.out.println(k + "====" + v);
});
}
}
2. ApplicationContext实现的四个功能接口
MessageSource
: 具有处理国际化的能力
ResourcePatternResolver
: 具有处理资源通配符的能力
/* 示例 */
Resource[] resources1 = context.getResources("classpath:application.properties");
Resource[] resources2 = context.getResources("classpath*:META-INFO/spring.factories");//*号会将jar包也进行扫描
EnvironmentCapable
: 具有处理环境信息的能力
/* 示例 */
context.getEnvironment().getProperty("java_home"); //不区分大小写
context.getEnvironment().getProperty("server.port");
ApplicationEventPublisher
: 具有发布事件的能力
/* 示例 */
//先定义一个自定义事件
public class UserRegiseredEvent extends ApplicationEvent{
public UserRegisteredEvent(Object source){super(source);}
}
//发布事件
context.publishEvent(new UserRegisteredEvent(context));
//在类中定义要接收事件的方法
@Component
public class Componenet2{
private static final Logger log = LoggerFactory.getLogger(Component2.class);
@EventListener
public void aaa(UserRegisteredEvent event){
log.debug("{}", event);
}
}
3. BeanFactory的默认实现(DefaultListableBeanFactory)
BeanFactory
的默认实现是DefaultListaleBeanFactory
但是DefaultListableBeanFactory
是空的, 什么后置处理器都没有, 不会去解析@Configuration, ${}等等,
而ApplicationContext
则增加了对BeanFactory的这些流程处理
/* 示例 */
public class TestBeanFactory {
public static void main(String[] args){
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); //刚创建好没有任何的Bean和BeanDefinition
//要添加Bean的话必须通过添加BeanDefinition(Bean定义), 因为bean是beanFactory通过IOC控制反转创建的
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
//注册bd到BeanFactory
beanFactory.registerBeanDefinition("config", beanDefinition);
//以下for循环只会打印config, 说明Config这个Bean还没有被解析(如@Configuration, @Bean等注解的解析), 是由另外的类来提供解析功能的
for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) {
System.out.println(beanDefinitionName);
}
//给BeanFactory添加一些常用的后置处理器(BeanFactoryPostProcessor, BeanPostProcess), 就是对BeanFactory的扩展, 如解析@Configuration等注解
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
// org.springframework.context.annotation.internalConfigurationAnnotationProcessor(BeanFactoryPostProcessor) 解析@Configuration
// org.springframework.context.annotation.internalAutowiredAnnotationProcessor(BeanPostProcess) 解析@Autowired
// org.springframework.context.annotation.internalCommonAnnotationProcessor(BeanPostProcess) 解析@Resource
// org.springframework.context.event.internalEventListenerProcessor
// org.springframework.context.event.internalEventListenerFactory
for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) {
System.out.println(beanDefinitionName);
}
//执行beanFactory中的beanFactory后置处理器, 从而达到扩展BeanFactory功能的目的
Map<String, BeanFactoryPostProcessor> beanFactoryPostProcessorMap = beanFactory.getBeansOfType(BeanFactoryPostProcessor.class);
beanFactoryPostProcessorMap.values().stream().forEach(beanFactoryPostProcessor -> {
beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
});
System.out.println("=================");
for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) {
System.out.println(beanDefinitionName);
}
//此时依赖注入仍然没有, 可以看打印结果, bean1中没有成功注入bean2
// Bean1 bean = beanFactory.getBean(Bean1.class);
// System.out.println(bean.bean2);
//加入beanFactory中的beanFactory后置处理器, 从而达到扩展BeanFactory功能的目的
Map<String, BeanPostProcessor> beanPostProcessorMap = beanFactory.getBeansOfType(BeanPostProcessor.class);
beanPostProcessorMap.values().forEach(beanFactory::addBeanPostProcessor);
//再次获取Bean, getBean->doGetBean->createBean -> doCreateBean -> populateBean...会调用这些BeanPostProcessor, 完成依赖注入
Bean2 bean2 = beanFactory.getBean(Bean1.class).bean2;
System.out.println(bean2);
System.out.println("==================");
beanFactory.preInstantiateSingletons(); //准备好所有的单例Bean
}
@Configuration
static class Config{
@Bean
public Bean1 bean1(){return new Bean1();}
@Bean
public Bean2 bean2(){return new Bean2();}
}
static class Bean1{
private static Logger log = LoggerFactory.getLogger(Bean1.class);
@Autowired
Bean2 bean2;
public Bean1(){log.debug("构造Bean1()");}
}
static class Bean2{
private static Logger log = LoggerFactory.getLogger(Bean1.class);
public Bean2(){log.debug("构造Bean2()");}
}
}
4. ApplicationContext的多种实现
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebApplicationContext;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ApplicationContextImpl {
public static void main(String[] args) throws InterruptedException {
//testClassPathXmlApplicationContext();
//testFileSystemXmlApplicationContext();
//testAnnotationConfigApplicationContext();
//testAnnotationConfigServletWebServerApplicationContext();
}
//经典的Spring容器, 基于XML
private static void testClassPathXmlApplicationContext(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("aci.xml");
for (String beanDefinitionName : context.getBeanDefinitionNames()) {
System.out.println(beanDefinitionName);
}
System.out.println(context.getBean(Bean2.class).getBean1());
}
//基于磁盘路径下xml格式的配置文件来创建
private static void testFileSystemXmlApplicationContext(){
//FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext("D:\\java\\spring-source-study\\src\\main\\resources\\aci.xml");绝对路径
FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext("src\\main\\resources\\aci.xml"); //相对路径
for (String beanDefinitionName : context.getBeanDefinitionNames()) {
System.out.println(beanDefinitionName);
}
System.out.println(context.getBean(Bean2.class).getBean1());
}
static class Bean1{}
static class Bean2{
private Bean1 bean1;
public void setBean1(Bean1 bean1){
this.bean1 = bean1;
}
public Bean1 getBean1(){
return bean1;
}
}
//较为经典的容器, 基于java配置类来创建
private static void testAnnotationConfigApplicationContext(){
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
for (String beanDefinitionName : context.getBeanDefinitionNames()) {
System.out.println(beanDefinitionName);
}
System.out.println(context.getBean(Bean2.class).getBean1());
}
@Configuration
static class Config{
@Bean
public Bean1 bean1(){
return new Bean1();
}
@Bean
public Bean2 bean2(Bean1 bean1){
Bean2 bean2 = new Bean2();
bean2.setBean1(bean1);
return bean2;
}
}
// 基于Java配置类来创建, 用于web环境
private static void testAnnotationConfigServletWebServerApplicationContext(){
AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
}
//供testAnnotationConfigServletWebServerApplicationContext()测试用
@Configuration
static class WebConfig {
//由这个工厂负责创建servletWebServer
@Bean
public ServletWebServerFactory servletWebServerFactory() {
return new TomcatServletWebServerFactory();
}
//前端控制器
@Bean
public DispatcherServlet dispatcherServlet(){
return new DispatcherServlet();
}
//servlet是需要注册在tomcat上的, 还需要绑定注册一下
@Bean
public DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet){
return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
}
//添加一个控制器测试用, 以"/"开头则会被识别为访问路径
@Bean("/hello")
public Controller controller1(){
return (request, response) -> {
response.getWriter().print("hello");
return null;
};
}
}
}
附带xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="bean1" class="com.yadong.springsourcestudy.chapter1.ApplicationContextImpl.Bean1"/>
<bean id="bean2" class="com.yadong.springsourcestudy.chapter1.ApplicationContextImpl.Bean2">
<property name="bean1" ref="bean1"/>
</bean>
</beans>