未央花博客 - spring
type: channel
link: http://www.pslanys.com/tag/spring/
items: Spring源码解析（二）Refresh()方法(一)
items: Spring源码解析（一）入门
items: spring和mybatis的日志

Spring源码解析（二）Refresh()方法(一)
type: item
date: 2020-12-22T21:31:00+08:00
description: 前言：spring系列第二篇文章，前面懒病犯了。（笑）本篇主要讲解context.refresh()方法0. 准备工作Test.Javapublic class Test { public static void main(String[] args) throws IOException { // 把spring所有的前提环境准备好(比如：bean容器、bean工厂等) AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); // 像容器注入配置类，后面来讲。 context.register(AppConfig.class); // 本篇主要讲解的内容 context.refresh(); AppConfig bean = context.getBean(AppConfig.class); System.out.println(bean); } }1. 源码阅读Refresh()方法方法处于AbstractApplicationContext类，方法里面做了很多的事情，首先我们点开这个方法:public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. // 准备工作包括：设置启动时间、是否激活标识位、初始化属性源（property source）配置 // 和主流程关系不太大 prepareRefresh(); // Tell the subclass to refresh the internal bean factory. // 得到beanFactory，然后对beanFactory进行设置 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. // 重点方法，准备beanFactory prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. // 空方法，当前版本没有代码 postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. /* * 在spring的环境中执行已经被注册的 factory processors * 设置执行自定义和spring内部的beanFactoryPostProcessor * 这里执行了了spring内部最关键的一个BeanFactoryPostProcessor：ConfigurationClassPostProcessor */ invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. // 注册bean的处理器来拦截bean的创建 registerBeanPostProcessors(beanFactory); // Initialize message source for this context. // 这个方法不太重要。国际化 initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. // 空壳方法 onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. // 实例化所有剩余的不是懒加载的单例bean，又一个重点方法。 // 何谓剩余？单例bean有两种：spring内置的、自己…

Spring源码解析（一）入门
type: item
date: 2020-11-03T23:51:00+08:00
description: 前言：一入spring深似海，呃，后面不知道怎么写了，那就开干吧。这篇是spring系列的第一篇文章，希望自己能坚持下去，愿阅读源码的各位都一同坚持下去。与君共勉！0. 准备工作首先应该准备相关的环境，我的环境是：IntelliJ IDEA 2020.2 (Ultimate Edition)JDK 1.8.*gradle-5.6.4spring版本：spring-framework-5.1.17.RELEASE可以使用我的环境，然后参考后面的步骤时，只需要修改对应的版本，当然你也可以使用这篇的文章的环境对应的版本。如果有不对的地方，欢迎留言，我会尽量提供帮助的。具体的步骤可以参考这篇文章 gradle+idea配置阅读Spring源码开发环境（解决jar包下载缓慢问题）,强烈推荐，良心博主，不做重复工作（不，其实是懒！）。但是好像有一个错误，就是设置gradle环境的地方，具体操作，参看下图。1. 源码阅读源码分析的部分主要是用JavaConfig技术启动spring环境，这也是spring官网推荐的方式。本系列文章是假设读者已经掌握了spring基本入门的应用。如未掌握，可以点击链接Java-based Container Configuration，前往官网学习入门，或者百度相关知识入门1.1 编写基本代码AppConfig.Java首先写一个配置类@Configuration @ComponentScan("com.example") public class AppConfig { }Test.Java编写一个测试类public class Test { public static void main(String[] args) throws IOException { // 把spring所有的前提环境准备好(比如：bean容器、bean工厂等) AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(AppConfig.class); context.refresh(); AppConfig bean = context.getBean(AppConfig.class); System.out.println(bean); } }如果成功打印出来，说明spring环境已经搭建成功，现在可以开心阅读源码了。1.2 AnnotationConfigApplicationContext类// 继承了GenericApplicationContext类 public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry首先在上面的main方法中，调用了AnnotationConfigApplicationContext类的无参构造方法，在其实例化之前，会调用父类 GenericApplicationContext类的无参构造，其构造方法如下：public GenericApplicationContext() { // 实例化了一个spring默认提供的BeanFactory ==> DefaultListableBeanFactory类 // 保存到了成员变量 beanFactory中 this.beanFactory = new DefaultListableBeanFactory(); }然后再调用本类的构造方法，如下：public AnnotationConfigApplicationContext() { // 创建一个读取注解的Bean定义读取器。Bean定义：BeanDefinition // 这个中完成了 this.reader = new AnnotatedBeanDefinitionReader(this); /* * 可以用来扫描包或类，然后转换成bd * 但是实际上我们扫描包工作不是scanner对象 */ // 扫描器，这是仅仅是为了程序员能够在外部地调用AnnotationConfigApplicationContext对象的scan方法 this.scanner = new ClassPathBeanDefinitionScanner(this); }下面主要介绍实例化 AnnotatedBeanDefinitionReader、ClassPathBeanDefinitionScanner两个类所做的事情1.2.1 AnnotatedBeanDefinitionReader类在…

spring和mybatis的日志
type: item
date: 2020-09-06T17:23:00+08:00
description: 前言本文主要介绍的是spring5、spring4、mybatis日志源码中是具体如何加载具体的日志类，看完后你可以根据源码的加载顺序使用自己需要使用的日志。如有错误，欢迎指出spring当中的日志log4jlogbacklog4j2julslf4jjcl各种日志技术的关系和作用通过源码来分析spring的日志技术分别分析spring5和spring4的使用日志的区别通过源码分析mybaits的日志技术spring和mybatis整合通过注解设置自定义的日志主流的log技术名词log4j log4j log4j 1.2.17 可以不需要依赖第三方的技术，直接记录日志jcljakartaCommonsLoggingImpl commons-logging commons-logging 1.1.1 jcl他不直接记录日志,他是通过第三方记录日志(jul)，它是jdk自带的如果使用jcl来记录日志,在没有log4j的依赖情况下,是用jul如果有了log4j则使用log4jjcl=Jakarta commons-logging ,是apache公司开发的一个抽象日志通用框架,本身不实现日志记录,但是提供了记录日志的抽象方法即接口(info,debug,error.......),底层通过一个数组存放具体的日志框架的类名,然后循环数组依次去匹配这些类名是否在app中被依赖了,如果找到被依赖的则直接使用,所以他有先后顺序。上图为jcl中存放日志技术类名的数组，默认有四个，后面两个可以忽略，因为都是jdk1.3以前的。上图81行就是通过一个类名去load一个class，如果load成功则直接new出来并且返回使用。如果没有load到class这循环第二个，直到找到为止。可以看到这里的循环条件必须满足result不为空，也就是如果没有找到具体的日志依赖则继续循环，如果找到则条件不成立，不进行循环了。总结：顺序log4j>juljuljava自带的一个日志记录的技术,直接使用log4j2slf4jslf4j他也不记录日志,通过绑定器绑定一个具体的日志记录来完成日志记录绑定器、桥接器问题slf4j -->通过绑定器-->jcl -->通过桥接器--> jcl ====> 会导致内存溢出logbacksimple-log各种日志技术的关系和作用spring日志技术分析spring5.*日志技术实现spring5使用的spring的jcl(spring改了jcl的代码)来记录日志的,但是jcl不能直接记录日志,默认是使用JUL记录，但是只要引入了log4j2、slf4j等，就会被重新赋值改变（在static静态块中）源代码展示org.apache.commons.logging.LogFactory类里面被调用的最多的方法：// 这个方法，调用本类的getLog()方法 public static Log getLog(Class<?> clazz) { return getLog(clazz.getName()); } public static Log getLog(String name) { return LogAdapter.createLog(name); }org.apache.commons.logging.LogAdapter类静态变量和静态代码块会在类的实例化之前被加载，具体是在类加载过程的初始化阶段，会在编译器生成的clinit方法中被调用，执行的顺序有源文件中出现的顺序一致/** * Create an actual {@link Log} instance for the selected API. * 这个方法就是返回具体的实例 */ public static Log createLog(String name) { // 通过静态变量logApi具体的值来加载日志 switch (logApi) { case LOG4J: return Log4jAdapter.createLog(name); case SLF4J_LAL: return Slf4jAdapter.createLocationAwareLog(name); case SLF4J: return Slf4jAdapter.createLog(name); default: return JavaUtilAdapter.createLog(name); } } // 核心代码 private static final String LOG4J_SPI = "org.apache.logging.log4j.spi.ExtendedLogger"; private static final String LOG4J_SLF4J_PROVIDER = "o…