SpringBoot源码系列(一)


写在前面

    入坑Java已经快两年的时间了(之前在C#坑中混迹六七年),工作之余也喜欢钻研技术,一直想积累些学习成果,但是由于能力一般水平有限,迟迟未曾着手,现在终于下定决心写些技术博客,结果7天能憋出6个字。。。好了,废话不多说,开始进入正题吧,至于文字功底,大家当做批改小学生作文就好。至于文章中有哪些不对的地方,还望大家多多指正。

前言

        相信现在好多单位都已经在使用SpringBoot进行开发了,开发过程中,肯定会有很多的疑问,比如常听说的IOC、AOP都是什么;为什么我们controller使用service时,不需要实例化,直接就可以调用方法;很多的注解都是什么意思,作用是什么,什么时候被使用了;等等一系列的问题,这些问题都会在我接下来的源码专题中得到解答。

        我在学习源码的过程中,也试着画了代码的详细讲解图,不知道在博客园上怎么上传文件,有需要的加我QQ找我要吧。

        

    

整体流程

    在正式阅读源码之前,我们需要先了解启动的整体流程,这样有助于我们对源码的理解。

    SpringBoot核心就是IOC和AOP,其中最最重要的核心点就是IOC。对于这二者的概念、控制反转、依赖注入、切面编程这些大家肯定也都在网上有所了解了,这里不再赘述,接下来简单的学习下IOC的过程,举个例子:你到了需要女朋友的年龄,然后自己去按照要求找女朋友,过程很复杂,如果国家现在规定,成立一个婚姻分配局,婚姻分配局把女朋友都放到一个房子里,你提出来了自己的寻找女朋友的要求,“肤白貌美大长腿”,婚姻分配局就会按照你的要求给你分配一个女朋友,省去了你自己寻找女朋友的时间,是不是很方便(不要纠结女朋友是全局的,大家共有的问题吧... ...)。这个婚姻分配局做的事就是一个IOC思想,那个房子就是一个IOC容器。

    在之前的开发中,我们需要使用哪个类,就new一个对象,然后拿来使用,这样是比较笨拙的,整个项目中,我们也会增加了很多的代码,new了很多的对象,先不说这些对象对内存的占用问题,单单编写代码时就会增加了不少代码量,那有没有一种方式让我们可以不再自己创建对象呢,比如说我们把需要用到的对象放到一个箱子(容器)里面,用的时候直接到里面拿取,答案是肯定的。

    使用过spring或者springBoot开发的朋友都知道,我们是怎么实例化、初始化我们需要的类呢,可以通过xml、注解或者配置类的方式来完成。但是框架又是怎么完成实例化和初始化的呢?如果我们有一些额外的需求,框架有没有给我们留下什么可扩展的地方呢?

    单单靠语言描述,可能无法直观的描述清楚这个过程,我画个图大家看一下:

 图1

 

    1 首先BeanDefinitionReader接口的实现类会把类的信息加载成BeanDefinition;

    2 beanFactory接口通过反射的方式,针对每一个BeanDefinition完成实例化,来看看这个接口:

public interface BeanFactory {
    String FACTORY_BEAN_PREFIX = "&";

    Object getBean(String var1) throws BeansException;

    <T> T getBean(String var1, @Nullable Class<T> var2) throws BeansException;

    Object getBean(String var1, Object... var2) throws BeansException;

    <T> T getBean(Class<T> var1) throws BeansException;

    <T> T getBean(Class<T> var1, Object... var2) throws BeansException;

    boolean containsBean(String var1);

    boolean isSingleton(String var1) throws NoSuchBeanDefinitionException;

    boolean isPrototype(String var1) throws NoSuchBeanDefinitionException;

    boolean isTypeMatch(String var1, ResolvableType var2) throws NoSuchBeanDefinitionException;

    boolean isTypeMatch(String var1, @Nullable Class<?> var2) throws NoSuchBeanDefinitionException;

    @Nullable
    Class<?> getType(String var1) throws NoSuchBeanDefinitionException;

    String[] getAliases(String var1);
}

    beanFactory接口里面有多个geBean,这个就是用来查找创建对象的方法,(如果此时有读过源码的朋友会知道,按照getBean->doGetBean->createBean->doCreateBean->createBeanInstance这样一层层方法跟踪,就会看到创建的过程,后续源码学习中会一起来研究这里)。

    2.1 在实例化之前,框架给我们预留了一个非常重要的接口,就是BeanFactoryPostProcessor,我们可以通过实现这个接口的方式,完成额外信息的添加,如:

@Service public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor { public MyBeanFactoryPostProcessor() { super(); System.out.println("这是BeanFactoryPostProcessor实现类构造器!!"); } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory arg0) throws BeansException { System.out.println("BeanFactoryPostProcessor调用postProcessBeanFactory方法"); BeanDefinition bd = arg0.getBeanDefinition("person2"); bd.getPropertyValues().addPropertyValue("phone", "110"); } }

     我们看一下执行了这个接口方法之后的效果:

 图2

    执行之后,我们的person2里面已经包含了一个phone的属性,值为110,(我知道看到这里,大家肯定会有疑问,MyBeanFactoryPostProcssor是什么时候调用,别急这篇文章先有个印象,后续代码研读中会有详细的讲解)。

    3 完成实例化及初始化的工作

    3.1及3.2 框架为我们预留了另外一个非常重要的接口类:beanPostprocessor,里面包含了两个方法:

public interface BeanPostProcessor { @Nullable default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } @Nullable default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; } }

    这两个方法分别如上图所示,before在初始化之前执行,after在初始化之后执行;

    先看下我测试的代码:

    继承beanPostProcessor接口的类代码:

@Component public class MyBeanPostProcessor implements BeanPostProcessor { public MyBeanPostProcessor() { super(); System.out.println("这是BeanPostProcessor实现类构造器!!"); // TODO Auto-generated constructor stub
 } @Override public Object postProcessBeforeInitialization(Object arg0, String arg1) throws BeansException { System.out.println("BeanPostProcessor接口方法postProcessBeforeInitialization对属性进行更改!"); //当程序执行到myTestPostprocessor这个类的时候 看看效果
        if(arg1.contains("myTestPostprocessor")){ MyTestPostprocessor myTestPostprocessor = (MyTestPostprocessor)arg0; System.out.println("加载MmyTestPostprocessor -- before : mytestController.controllerName = "+ myTestPostprocessor.getName()); } return arg0; } @Override public Object postProcessAfterInitialization(Object arg0, String arg1) throws BeansException { System.out.println("BeanPostProcessor接口方法postProcessAfterInitialization对属性进行更改 当前beanName="+arg1); //当程序执行到myTestPostprocessor这个类的时候 看看效果
        if(arg1.contains("myTestPostprocessor")){ MyTestPostprocessor myTestPostprocessor = (MyTestPostprocessor)arg0; System.out.println("加载MmyTestPostprocessor -- after : mytestController.controllerName = "+ myTestPostprocessor.getName()); } return arg0; } }

    上面代码中的MyTestPostProcessor类的代码:

@Service
public class MyTestPostprocessor{
public String name;
public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

//初始化方法 给name属性赋值
@PostConstruct
public void init() {
this.setName("月夜星空-测试");
System.out.println("MyTestPostprocessor --- 执行初始化方法 init");
}

}

    执行之后我看一下执行的结果:

 

    首先执行了Before方法,此时name属性还没有值,然后执行了初始化方法init,name属性赋值了,然后执行了after方法,可以看出我们取到的myTestPostProcessor的name属性已经有值了--“月夜星空-测试”。

    试想SpringBoot的框架为什么这么流行,就是因为它的肆意妄为的扩展性,可以让你在任何的阶段进行扩展,除了上面提到的这些可扩展的接口以外,如果我们想在其他的某些阶段做一些特殊的处理,应该怎么做呢 -- 监听器。SpringBoot框架对外提供了很多的监听器,可以方便我们的扩展,这里我先举一个例子,我们可以通过监听器来监听当前在线的用户,不废话了,上代码吧:

    先自定义一个监听器,当session发生变化时,会进入到sessionCreated方法,count++,记录当前新登录的人数:

@Component public class MyHttpSessionListener implements HttpSessionListener { public Integer count=0; @Override public void sessionCreated(HttpSessionEvent httpSessionEvent) { System.out.println("新用户上线"); count++; httpSessionEvent.getSession().getServletContext().setAttribute("count",count); } @Override public void sessionDestroyed(HttpSessionEvent httpSessionEvent) { System.out.println("新用户下线"); count--; httpSessionEvent.getSession().getServletContext().setAttribute("count",count); } }

    创建一个测试类:

@RestController @RequestMapping("/TestSessionListener") public class TestSessionListener { @GetMapping(value = "/login") public String login(HttpServletRequest request) { Integer count = (Integer) request.getSession().getServletContext().getAttribute("count"); System.out.println("在线人数:" + count); return "在线人数:" + count; } }

    我们测试一下:

 

 

 

 

 

     我们还可以通过自定义事件和监听器的方式来扩展,这里先不举例了。

    这篇文章主要介绍了IOC的过程,并且提到了两个重要的接口,并没有真正的研读代码,包括我们上面提到的这两个接口的调用的时机、如何调用等问题都还没有解答,这些都会在后续的文章中详细学习,通过本文的学习,需要对框架启动、IOC的过程有一个大致的了解。

    我知道看完这篇文章之后会有很多朋友一脑子的问号,“框架如何通过xml或者注解来读取类信息的”、“BeanDefinition是啥,包括啥”、“beanFactoryPostProcessor接口何时执行、如何执行的”、“beanFactory如何创建对象的”等等,今天我们主要学习的是流程和思想,真正的源码不多,请大家整理好这些问题,后面会有详细的源码解读,等到详细学习源码时,这些问题都会得到一一讲解。


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM