你自我介绍说很懂Spring配置类,那你怎么解释这个现象?(上)

简介: 你自我介绍说很懂Spring配置类,那你怎么解释这个现象?(上)

前言


各位小伙伴大家好,我是A哥。本专栏/系列讲解到这里,关于Spring的@Configuration配置类,应该是可以完成95%以上工作上的使用以及问题的解决。你也绝对算得上是一个“懂它”的Java Coder了,面试自然也就不在话下,基本可以实现“吊打面试官”。


建议刚“翻开”本专栏的同学去我公众号往前翻翻,前几篇文章能助你投入精力较少,收获大不一样


虽然你已经可以搞定95%的问题,但还剩5%呢?不要了麽?然而残酷的现实却是这样的,能解决那5%问题的才是真正的王者,他们的薪资往往能高出你一个甚至多个Level,并且在你眼中还好像还“不怎么干活”,不信你品,你细品…这就是不可替代性/稀缺性的价值…‘



如何提高自己的不可替代性?对于三无的我们,没有办法只能冲着那5%出发呗。对于钟爱于面向工资编程的我们,一般还是有更高追求的嘛,毕竟在趋同的程序员视界里,要的就是不一样,所以需要继续打怪升级。


接下来的两篇内容会比较深入,可能会让一些“初学者”感到不适(若感觉不适赶紧往前翻翻补课),希望坚持,毕竟这最终都会反应到你每个月的工资上,做难事必有所得嘛。


我粗浅的认为,对于大多数人来说,工资是衡量个人市场价值的唯一/最重要标准。工资20k和22k的两人可认为是差不多的,但40k的人肯定比前者价值高出一截


版本约定


本文内容若没做特殊说明,均基于以下版本:


  • JDK:1.8
  • Spring Framework:5.2.2.RELEASE


正文


如果说前面是些武功招式,那么接下来就到了内功修炼阶段了。走得多块得看你手脚是否能灵活会用,而走得多远是由你的体力(内功)决定的。下面我先以一个示例(可当面试题)开始本文的内容。


配置类在Full模式下的“能力”展示


配置类(标注有@Configuration注解,属于Full模式):


@Configuration
public class AppConfig {
}


case1:


先来个简单点的。


public static void main(String[] args) {
    ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
    AppConfig appConfig = context.getBean(AppConfig.class);
    System.out.println(appConfig.getClass());
    System.out.println(appConfig.getClass().getSuperclass() == AppConfig.class);
    System.out.println(AopUtils.isCglibProxy(appConfig));
}


结果输出:

class com.yourbatman.fullliteconfig.config.AppConfig$$EnhancerBySpringCGLIB$$d38ead10
true
false



结果解释:


Full模式的配置类被CGLIB增强了,所以最终放进容器内的实际是代理对象

代理类是由CGLIB生成的子类,所以父类必然就是目标类

这个为何是false???其实这个和AopUtils.isCglibProxy()的实现有关(建议你源码点进去瞄一眼一切都明白了),这个配置类仅仅是被CGLIB代理了,和AOP没毛关系

case2:


这个case会进阶一些。

public static void main(String[] args) throws IllegalAccessException {
    ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
    AppConfig appConfig = context.getBean(AppConfig.class);
    Field $$beanFactoryField = ReflectionUtils.findField(appConfig.getClass(), "$$beanFactory");
    BeanFactory beanFactory = (BeanFactory) $$beanFactoryField.get(appConfig);
    System.out.println(beanFactory == context.getAutowireCapableBeanFactory());
    System.out.println(beanFactory == context);
    System.out.println(appConfig instanceof BeanFactoryAware);
    System.out.println(appConfig.getClass().getInterfaces()[0]);
}


结果输出:

true
false
true
interface org.springframework.context.annotation.ConfigurationClassEnhancer$EnhancedConfigurati



结果解释:


  1. CGLIB字节码提升时,会自动给代理类新增一个名为$$beanFactory的字段/属性,在运行期间给其赋值。所以通过反射可以从代理实例里拿到这个属性值,并且值就是当前BeanFactory
  2. 小细节:一定只能写成(appConfig.getClass(), "$$beanFactory")而不能是(AppConfig.class, "$$beanFactory")哦,因为这个Field属于代理类而非目标类
  3. 这个结果是false,和配置类本身并无关系,考察的知识点是Spring上下文Bean工厂和内建Bean工程的区别,这里先混个脸熟,下个专栏会详解的
  4. 结果为true。你是否想动粗:“劳资”的AppConfig配置类明明就没有实现BeanFactoryAware接口,为毛你给我返回true呢?
  5. 解释同上


如果面试官通过这样的题目来考你(其实目的是想让你“降薪”),你是否招架得住,成为那5%呢?本文将带你一起继续深挖Spring @Configuration配置里面的“玄机”,看完后你再回来看这几个题目就会感叹了:so easy。


何时创建代理?


我们已然知道Full模式的配置类最终会被CGLIB字节码提升,从而最终放一个代理类对象到Spring容器里。那么我们先来弄清楚创建代理的时机在哪儿~


Spring容器在refresh()启动步骤的AbstractApplicationContext#invokeBeanFactoryPostProcessors这一步会执行所有的BeanFactoryPostProcessor处理器,而此时BeanFactory才刚刚准备好,容器内除了ConfigurationClassPostProcessor之外,并无任何其它BeanFactoryPostProcessor,截图示例如下:


image.png



可能你会问:既然这么早期,那这个处理器是什么时候放进去的呢?我只能回答:在Bean容器“开山阶段”同几个开山鼻祖一起放进去的。如果你继续追问很多为什么的话,那我只能回答:这不是本专栏讲解的重点所在,放在下个专栏详解,请关注我公众号即可


既然这样,那么接下来就会会ConfigurationClassPostProcessor这个后置处理器喽。


ConfigurationClassPostProcessor


用于引导处理@Configuration配置类。该后置处理器的优先级是较高的,属于PriorityOrdered分类。


说明:PriorityOrdered的优先级肯定比Order接口的高


// @since 3.0
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
    PriorityOrdered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {
    ...
}


它是一个BeanDefinitionRegistryPostProcessor处理器,所以在容器启动过程中会先后执行如下两个方法:


postProcessBeanDefinitionRegistry()


从注册进来的配置类(可能是Full模式,可能是Lite模式)里进一步派生bean定义。简而言之:收集到所有的BeanDefinition(后简称为bd)存储起来,包括@Import、@Component等等组件。并且做出标注:是Full模式的还是Lite模式的配置类(若非配置组件就不标注哦)。


ConfigurationClassPostProcessor:
  @Override
  public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    // 生成一个id,放置后面再重复执行
    int registryId = System.identityHashCode(registry);
    // 若重复执行  就抛出异常
    if (this.registriesPostProcessed.contains(registryId)) {
      throw new IllegalStateException("postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
    }
    if (this.factoriesPostProcessed.contains(registryId)) {
      throw new IllegalStateException("postProcessBeanFactory already called on this post-processor against " + registry);
    }
    // 表示此registry里的bd收集动作,已经做了  避免再重复收集此registry
    this.registriesPostProcessed.add(registryId);
    // 根据配置类,收集到所有的bd信息
    // 并且做出mark标注:是Full模式还是Lite模式,和很重要很重要
    processConfigBeanDefinitions(registry);
  }


执行完此方法,已经完成了bd的收集和标记,那接下来就是本文的主菜了:帮你解答上面case的结果


相关文章
|
15天前
|
XML Java 测试技术
Spring IOC—基于注解配置和管理Bean 万字详解(通俗易懂)
Spring 第三节 IOC——基于注解配置和管理Bean 万字详解!
98 26
|
2月前
|
Java Spring
【Spring】方法注解@Bean,配置类扫描路径
@Bean方法注解,如何在同一个类下面定义多个Bean对象,配置扫描路径
181 73
|
28天前
|
监控 Java 数据库连接
Spring c3p0配置详解
在Spring项目中配置C3P0数据源,可以显著提高数据库连接的效率和应用程序的性能。通过合理的配置和优化,可以充分发挥C3P0的优势,满足不同应用场景的需求。希望本文的详解和示例代码能为开发者提供清晰的指导,帮助实现高效的数据库连接管理。
47 10
|
2月前
|
Java Spring
【Spring配置相关】启动类为Current File,如何更改
问题场景:当我们切换类的界面的时候,重新启动的按钮是灰色的,不能使用,并且只有一个Current File 项目,下面介绍两种方法来解决这个问题。
|
2月前
|
Java Spring
【Spring配置】idea编码格式导致注解汉字无法保存
问题一:对于同一个项目,我们在使用idea的过程中,使用汉字注解完后,再打开该项目,汉字变成乱码问题二:本来a项目中,汉字注解调试好了,没有乱码了,但是创建出来的新的项目,写的注解又成乱码了。
|
2月前
|
Java Spring
【Spring配置】创建yml文件和properties或yml文件没有绿叶
本文主要针对,一个项目中怎么创建yml和properties两种不同文件,进行配置,和启动类没有绿叶标识进行解决。
|
XML 缓存 Java
模仿Spring实现一个类管理容器
项目的初衷是独立作出一个成熟的有特色的IOC容器,但由于过程参考Spring太多,而且也无法作出太多改进,于是目的变为以此项目作为理解Spring的一个跳板,与网上的一些模仿Spring的框架不同,本项目主要是针对注解形式
544 0
模仿Spring实现一个类管理容器
|
27天前
|
XML Java 应用服务中间件
Spring Boot 两种部署到服务器的方式
本文介绍了Spring Boot项目的两种部署方式:jar包和war包。Jar包方式使用内置Tomcat,只需配置JDK 1.8及以上环境,通过`nohup java -jar`命令后台运行,并开放服务器端口即可访问。War包则需将项目打包后放入外部Tomcat的webapps目录,修改启动类继承`SpringBootServletInitializer`并调整pom.xml中的打包类型为war,最后启动Tomcat访问应用。两者各有优劣,jar包更简单便捷,而war包适合传统部署场景。需要注意的是,war包部署时,内置Tomcat的端口配置不会生效。
202 17
Spring Boot 两种部署到服务器的方式
|
27天前
|
Dart 前端开发 JavaScript
springboot自动配置原理
Spring Boot 自动配置原理:通过 `@EnableAutoConfiguration` 开启自动配置,扫描 `META-INF/spring.factories` 下的配置类,省去手动编写配置文件。使用 `@ConditionalXXX` 注解判断配置类是否生效,导入对应的 starter 后自动配置生效。通过 `@EnableConfigurationProperties` 加载配置属性,默认值与配置文件中的值结合使用。总结来说,Spring Boot 通过这些机制简化了开发配置流程,提升了开发效率。
59 17
springboot自动配置原理
|
1月前
|
XML JavaScript Java
SpringBoot集成Shiro权限+Jwt认证
本文主要描述如何快速基于SpringBoot 2.5.X版本集成Shiro+JWT框架,让大家快速实现无状态登陆和接口权限认证主体框架,具体业务细节未实现,大家按照实际项目补充。
80 11