美文网首页
Spring源码阅读整体介绍

Spring源码阅读整体介绍

作者: 逍遥白亦 | 来源:发表于2020-11-26 20:39 被阅读0次

本系列开篇第一文,从整体梳理Spring的脉络。

1. 闲聊创建对象

首先先来看,如果没有Spring这个框架,在Java里如何创建对象。

   Class A = new Class();

基本有Java基础的人可以很简单的写出上面代码,那么当我们希望调用其他对象的时候,比如有一个ClassA,里边有一个成员变量name,在对该类初始化的时候,对name属性进行赋值,最终希望可以获得这个name。

public class ClassA {
    private String name;

    public ClassA(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

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

调用类

public class AppClass {

    public static void main(String[] args) {
        ClassA classA = new ClassA("Spring");
        System.out.println(classA.getName());

    }

}

如此就可以在AppClass里调用ClassA的方法了。

1.1 如果类很多呢?

上边的代码如果只是简单的写写Demo那还可以忍受,但是学代码可不只是为了写Demo,我们是有理想有抱负的,要面向工资....哦不,要使代码整体简洁一点,要解耦。

那么我们就希望在调用类里,将ClassA变为一个成员变量,那么不实例化的情况下,怎么可以获取ClassA的所有属性呢?

稍微学过Java基础的朋友,会想到反射。

1.2 用反射获取对象

举一个日常搬砖的常用例子,有一个UserController类,在这个类会接收所有的用户服务的请求,并调用UserService类,对请求的参数做处理,比如去数据库查询用户信息。那么代码会写成如下这样:

UserController类

package controller;

import service.UserService;

public class UserController {

    private UserService userService;

    public UserService getUserService() {
        return userService;
    }

    public void setUserService(UserService userService) {
        this.userService = userService;
    }
}


UserService类

package service;

public class UserService {

}

测试类

import controller.UserController;
import org.junit.Test;
import service.UserService;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class AppClass {

    @Test
    public void reflectionTest() throws Exception{
        UserController userController = new UserController();

        Class<? extends  UserController> clazz = userController.getClass();

        //创建对象
        UserService userService = new UserService();

        //获取所有的属性
        Field serviceField = clazz.getDeclaredField("userService");

        serviceField.setAccessible(true);

        //只有通过方法才能够设置具体的属性值
        String name = serviceField.getName();

        //拼接方法的名称
        name = name.substring(0,1).toUpperCase() + name.substring(1, name.length());
        String setMethodName = "set" + name;
        //通过方法注入属性的对象
        Method method = clazz.getMethod(setMethodName, UserService.class);
        //反射
        method.invoke(userController,userService);
        System.out.println(userController.getUserService());
    }
}

1.3 加注解实现

上述代码通过反射创建了UserService对象,但是实际情况一般是用Spring里的一个@Autowired注解来实现,那么接下来,就自己实现一个该注解。

注解

package com.demo;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface AutoWired {
}

UserController类

package com.demo.controller;

import com.demo.service.UserService;
import com.demo.AutoWired;

public class UserController {

    @AutoWired
    private UserService userService;

    public UserService getUserService() {
        return userService;
    }

    public void setUserService(UserService userService) {
        this.userService = userService;
    }
}

UserService类不变,测试类

package com.demo;

import com.demo.controller.UserController;
import org.junit.Test;

import java.util.stream.Stream;

public class AppClass {

    @Test
    public void reflectionTest() throws Exception{
        UserController userController = new UserController();

        Class<? extends UserController> clazz = userController.getClass();

        Stream.of(clazz.getDeclaredFields()).forEach( field -> {
            String name = field.getName();
            AutoWired annotation = field.getAnnotation(AutoWired.class);
            if (annotation != null){
                field.setAccessible(true);
                //获取属性的类型
                Class<?> type = field.getType();
                try {
                    Object o= type.newInstance();
                    field.set(userController, o);
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        });
        System.out.println(userController.getUserService());
    }


}

这样,我们可以不用写死UserService类,可以通过注解,注入多个类。

2. Spring做了什么

我们在使用Spring的时候,注入Bean会通过注解、XML文件以及配置类等方式,最终将类注入到IOC容器中,在容易中对类进行实例化。那么如果我们不看源码的话,这些功能怎么实现呢?

2.1 读取XML文件、注解以及配置类(BeanDefinitionReader)

首先我们会先抽象出一层,将这些来源的类读取出来,所以会先抽象出一层来对这些类进行读取、处理,这在Spring里边是由BeanDefinitionReader实现的。

2.2 创建对象的工厂

提到创建对象,就会想到一个很熟悉的思想,就是工厂类,用工厂类创建对象,Spring提供了BeanFactory进行对象的创建。

2.2.1 BeanFactory与FactoryBean

我们想下如果所有的对象只能通过一种方式创建,而用户自己无法更改,是一种很不好的设计。举个例子,比如远古传说中有女娲造人,女娲把地上的泥土都按照一种方式捏出来,变成人,这种方式就是BeanFactory,但是久而久之,所有人都一样,那么就无法进行区分,这时女娲想到,可以创造出不同的作坊,再给这些作坊制定一个负责人,由负责人自己进行创造,可以捏出男人、女人、高的人、矮的人等等。这些作坊的思想就是FactoryBean。

用户可以自己定义一个FactoryBean类,最终通过该类的getObjcet方法,返回一个用户自定义的实例,增强了扩展性,大名鼎鼎的FeignClient就是通过这种方式创建的。

2.3 各种后置处理器

当这些Bean在进入容器之前或者进入容器之后时,我们需要对类进行一些扩展,所以就有了BeanFactoryPostProcessor以及BeanPostProcessor后置处理器。大名鼎鼎的AOP就是通过BeanPostProcessor进行扩展的。

2.4 bean的生命周期

在IOC容器里,bean会经过实例化、填充属性、初始化以及各种后置处理之后,放入对象池里,这其中就有一个bean的生命周期的概念。

生命周期大体是这样(后续看源码的时候,会对其进行验证):

  1. 实例化Bean对象,这个时候Bean的对象是非常低级的,基本不能够被我们使用,因为连最基本的属性都没有设置,可以理解为 连Autowired注解都是没有解析的;
  2. 填充属性,当做完这一步,Bean对象基本是完整的了,可以理解为Autowired注解已经解析完毕,依赖注入完成了;
  3. 如果Bean实现了BeanNameAware接口,则调用setBeanName方法;
  4. 如果Bean实现了BeanClassLoaderAware接口,则调用setBeanClassLoader方法;
  5. 如果Bean实现了BeanFactoryAware接口,则调用setBeanFactory方法;
  6. 调用BeanPostProcessor的postProcessBeforeInitialization方法;
  7. 如果Bean实现了InitializingBean接口,调用afterPropertiesSet方法;
  8. 如果Bean定义了init-method方法,则调用Bean的init-method方法;
  9. 调用BeanPostProcessor的postProcessAfterInitialization方法;当进行到这一步,Bean已经被准备就绪了,一直停留在应用的 上下文中,直到被销毁;
  10. 如果应用的上下文被销毁了,如果Bean实现了DisposableBean接口,则调用destroy方法,如果Bean定义了destory-method 声明了销毁方法也会被调用。

2.5 监听器

这里边因为会在bean对象的不同阶段对其进行处理,所以还会用到一个很重要的思想,叫观察者模式,Spring里还有许多监听器。

2.6 环境

我们在使用Spring时,还有一些配置文件,注入xx.properties或者xx.yml等,会通过Environment类加载到容器中。

3. 结语

至此,Spring整体的代码以及设计思路,就简单的分析完了,下篇开始进入撸源码阶段。

最后附上整体结构图


image

相关文章

网友评论

      本文标题:Spring源码阅读整体介绍

      本文链接:https://www.haomeiwen.com/subject/opbziktx.html