壹影博客.
我在下午4点钟开始想你
手写Spring底层源码 启动扫描、依赖注入解析分享
  • 2023-11-19日
  • 4评论
  • 1798围观

 

手写Spring底层源码 启动扫描、依赖注入解析分享

1.通过手写模拟,了解Spring的底层源码启动过程
2.通过手写模拟,了解BeanDefinition、BeanPostProcessor的概念
3.通过手写模拟,了解Spring解析配置类等底层源码工作流程
4.通过手写模拟,了解依赖注入,Aware回调等底层源码工作流程

在手写Spring之前我们先看看 Spring一般启动都是怎么怎么对Bean进行创建的,下面的代码是正常实例化一个Spring的ApplicationContext的类 传入 Config类进去

//Test.java

public class Test1 {
   public static void main(String[] args) {
      ApplicationContext applicationContext = new ApplicationContext(Config.class);
      UserService userService = (UserService) applicationContext.getBean("userService");
      userService.test(); //执行 userService 的 text方法
   }
}
//Appconfig.java

//指定要扫描的路径
@ComponentScan("com.yyge.service")
public class AppConfig {
}
// service/UserService.java

@Component("userService") //给当前Bean去取一个名字 并且为单例 Bean
@Scope("prototype") //配置为原型Bean 也就是多例Bean
public class UserService {

  @Autowired
  private OrderService orderService;

  public void test(){
      System.out.printf("输出测试");
  }
}

如上代码都是我们引入了Spring之后可以直接进行的操作 让Spring帮我们去实例化Bean对象,并且可以使用这些注解,下面我们来手写模拟Spring的一些操作

一、手写ApplicationContext

我这里命名为YygeApplicationContext.java 由于代码文件有一些多不方便展示 我这里就只展示核心的YygeApplicationContext.java文件内容

/**
 * 模拟spring启动类
 * @author 壹影
 */
public class YygeApplicationContext {
    private Class configClass;  // 用于接收外面传进来的配置类

    private Map<String,BeanDefinition> beanDefinitionMap = new HashMap<>();
    private Map<String,Object> singletonObjects = new HashMap<>();

    private List<BeanPostProcessor> beanPostProcessorList=new ArrayList<>(); //用于存储BeanPostProcessor

    public YygeApplicationContext(Class configClass) {
        this.configClass=configClass;

        //扫描
        scan(configClass);

        //扫描完毕之后 就回去创建单例Bean
        for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()) {
            String beanName = entry.getKey();
            BeanDefinition beanDefinition=entry.getValue();

            //找出所有的单例Bean
            if(beanDefinition.getScope().equals("singleton")){
               //找到之后去创建
               Object bean =createBean(beanName,beanDefinition);
               singletonObjects.put(beanName,bean); // 保存单例Bean
            }
        }
    }

    /**
     * 创建Bean
     * @param beanName bean的名称
     * @param beanDefinition beanDefinition 对象
     * @return 返回Object
     */
    private Object createBean(String beanName, BeanDefinition beanDefinition) {
        Class<?> clazz = beanDefinition.getType();
        Object instance = null;
        try {
            instance = clazz.getConstructor().newInstance();//获取到无参构造方法 并且去实例化

            //实现依赖注入
            //获取这个对象里面所有的属性 getDeclaredFields()  并且遍历
            for (Field field : clazz.getDeclaredFields()) {
                //判断属性上面是否存在Autowired注解
                if (field.isAnnotationPresent(Autowired.class)) {
                    //如果存在
                    field.setAccessible(true); //设置可以通过反射 访问和操作类的 私有成员
                    //field.getName() 获取当前属性的名字 为这个字段设置值
                    field.set(instance,getBean(field.getName()));
                }
            }

            // 初始化前方法
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                instance = beanPostProcessor.postProcessBeforeInitialization(instance,beanName);
            }


            //初始化的实现
            //判断当前实例 是否实现了InitializingBean这个接口
            if (instance instanceof InitializingBean){
                //如果实现了就进行强制转换 并执行afterPropertiesSet 方法
                ((InitializingBean) instance).afterPropertiesSet();
            }

            // 初始化后方法
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                instance = beanPostProcessor.postProcessAfterInitialization(instance,beanName);
            }


        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }

        return instance;

    }

    /**
     * 扫描类
     * @param configClass
     */
    private void scan(Class configClass) {
        //扫描
        if (configClass.isAnnotationPresent(ComponentScan.class)) {
            //拿到类上面的ComponentScan注解
            ComponentScan componentScanAnnotaion = (ComponentScan) configClass.getAnnotation(ComponentScan.class);//拿到注解对象

            //拿到注解后 我们就可以拿到里面的扫描路径
            String path = componentScanAnnotaion.value();
            path = path.replace(".","/");
            System.out.println("扫描路径--> "+path);

            //获取当前类的类加载
            ClassLoader classLoader = YygeApplicationContext.class.getClassLoader();

            //获取类加载的 Resource 路径
            URL resource = classLoader.getResource(path);

            //定义File 传入路径 File可以是 文件 也可以表示文件夹
            File file =new File(resource.getFile());

            //如果是文件夹的话 就遍历他
            if(file.isDirectory()){
                for (File f : file.listFiles()) {
                    //获取 文件的绝对路径
                    String absolutePath = f.getAbsolutePath();
                    //System.out.println(absolutePath); // 打印测试看看

                    //将:C:\Users\Administrator\Desktop\project\spring\target\classes\com\yyge\service\OrderService.class
                    //截取为这样com.yyge.service.OrderService.class
                    absolutePath = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"));
                    absolutePath = absolutePath.replace("\\",".");
                    //判断:这些文件里面有没有Component 注解
                    //方法 将class文件加载为class对象
                    try {
                        Class<?> clazz = classLoader.loadClass(absolutePath);

                        // 判断每一个类上面有没有 Component 注解
                        if (clazz.isAnnotationPresent(Component.class)) {

                            //判断当前类是否实现了BeanPostProcessor这个接口
                            if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
                                BeanPostProcessor newInstance = (BeanPostProcessor) clazz.getConstructor().newInstance();
                                beanPostProcessorList.add(newInstance);
                            }

                            Component annotation = clazz.getAnnotation(Component.class); //取出来Component 注解
                            String beanName = annotation.value(); //获取annotation 注解中的值 也就是Bean的名字
                            //如果拿出来的名字为"" 那么就默认生成一个名字
                            if("".equals(beanName)){
                                //默认生成名字
                              beanName = Introspector.decapitalize(clazz.getSimpleName());
                            }

                            BeanDefinition beanDefinition=new BeanDefinition();
                            beanDefinition.setType(clazz); //记录Bean的类型

                            //判断Scope 注解
                            if (clazz.isAnnotationPresent(Scope.class)) {
                              //如果有的话
                                Scope scope = clazz.getAnnotation(Scope.class);
                                String value = scope.value(); // 然后拿到里面配置的内容
                                beanDefinition.setScope(value); // 设置作用域
                            } else {
                                // 如果没有Scope注解 默认就是单例 singleton
                                beanDefinition.setScope("singleton");
                            }

                            beanDefinitionMap.put(beanName,beanDefinition);
                           // 创建Bean
                        }
                    } catch (ClassNotFoundException e) {
                        throw new RuntimeException(e);
                    } catch (InvocationTargetException e) {
                        throw new RuntimeException(e);
                    } catch (InstantiationException e) {
                        throw new RuntimeException(e);
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException(e);
                    } catch (NoSuchMethodException e) {
                        throw new RuntimeException(e);
                    }

                }
            }
        }
    }

    public Object getBean(String beanName){
      if(!beanDefinitionMap.containsKey(beanName)){
        throw new RuntimeException("容器不存在的Bean:"+beanName);
      }

        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if (beanDefinition.getScope().equals("singleton")) {
            Object singletonBean = singletonObjects.get(beanName);

            //因为在依赖注入的时候 也要调用getBean 如果说singletonBean 取出来是null 说明没有创建
            //所以直接创建就可以了
            if(singletonBean == null){
                singletonBean = createBean(beanName, beanDefinition);
                singletonObjects.put(beanName,singletonBean);
            }
            //如果是单例 则从单例池中去取值
            return singletonBean;
        }else{
          //原型bean
          return createBean(beanName,beanDefinition);
        }
    }
}

其他文件源码附在了如下链接,下载项目打开参考

下载地址:点我跳转

提取码:评论后查看

此处内容已隐藏,评论后刷新即可查看!

我的学习

通过分析Spring的源码让我学习到了很多的内容具体有如下内容

//1.判断xxx类是否有xxxx注解
if (YygeClass.isAnnotationPresent(ComponentScan.class)) {
  //判断YygeClass类是否有ComponentScan注解
}

//2.获取注解对象 类.getAnnotation(xxx.class) 需要强转
ComponentScan componentScanAnnotaion = (ComponentScan) configClass.getAnnotation(ComponentScan.class);

//3.获取注解的属性 注解对象.属性()
componentScanAnnotaion.value();

//4.获取当前类的类加载对象 ClassLoader
ClassLoader classLoader = 当前类.class.getClassLoader();

//5.获取类加载的 Resource 路径 path可以为相对路径
URL resource = classLoader.getResource(path)

//6.通过URL对象实例化File对象 注意File可以是对象也可以是文件夹
File file =new File(resource.getFile());

//7.判断File对象是否是文件夹 返回布尔值
file.isDirectory()

//8.获取File对象的绝对路径
String absolutePath = file.getAbsolutePath()

//9.获取File对象 下所有的File对象(就是文件夹里面所有的文件 如果File是文件夹的话)
file.listFiles()

//10.判断某个类实现了某个接口
if (YygeClass.class.isAssignableFrom(BeanPostProcessor.class)) {
	//判断YygeClass类是否有BeanPostProcessor接口
}

//11.通过反射实例化某个对象 --实例化后拿到的是Object 可以强转为实例的那个类
YygeClass.class.getConstructor().newInstance()

 

发表评论

AlexisVar

Lv.1 @回复 #4

冠天下


https://xn--ghq10gmvi.com/

SteveaBili

Lv.1 @回复 地板

向新力運動彩券行



https://1688bet.tw/

Rodneycer

Lv.1 @回复 板凳

滿天星娛樂城



https://star168.tw/

WilliambrAsp

Lv.1 @回复 沙发

水微晶玻尿酸 - 八千代


https://yachiyo.com.tw/hyadermissmile-injection/

渝ICP备19011465号 | 渝ICP备19011465号-1