手撕spring框架(2)

相关系列

java中spring底层核心原理解析(1)-CSDN博客

java中spring底层核心原理解析(2)-CSDN博客

手撕spring框架(1)-CSDN博客

依赖注入原理

依赖注入(Dependency Injection,简称DI)是一种设计模式,它允许我们解藕组件之间的关系,使得代码更易于测试和维护。在Spring框架中,依赖注入被广泛使用来管理对象(称为beans)及相互之前的依赖关系。Spring容器负责创建对象,配置对象以及管理这些对象的生命周期。

手撕spring框架(1)-CSDN博客 中实现核心功能点,今天我们就在此基础上,来完成依赖注入。

创建Autowired.java

@Autowired是Spring框架提供的一个注解,用于实现依赖注入。我们简化的自定义实现。

功能

  • 自动装配:@Autowired注解可以让Spring容器自动为字段、构造器、setter方法等注入所需的bean。这意味着开发者不需要手动使用new操作符来创建依赖对象的实例,Spring容器会根据类型匹配(默认)或按名称匹配(通过@Qualifier配合使用)来自动提供这些依赖。
  • 默认行为:默认情况下,@Autowired按照类型匹配进行注入。如果存在多个相同类型的bean,Spring会尝试通过名称匹配或者使用@Primary标记的bean来解决歧义。如果没有找到匹配项,且没有配置为可选注入(通过@Autowired(required = false)),Spring将抛出异常。
  • 位置
    • 字段:直接标注在字段上,是最常见的使用方式。
    • 构造器:可以用来标识一个构造器来进行依赖注入,特别是当类有多个构造器或者依赖是必须的时候非常有用。
    • setter方法:适用于需要在对象创建之后设置依赖的情况。
  • 可选性:通过required属性可以声明依赖是否是必须的,默认为true表示必须注入,设为false则表示如果没有匹配的bean,也不会抛出异常。
java 复制代码
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Autowired {
    String value() default "";
}

字段注入

修改原有的UserService.java文件

java 复制代码
package com.dzend.service;


import com.spring.Autowired;
import com.spring.Component;

@Component(value = "userService")
public class UserService  {


    @Autowired
    private OrderService orderService;





    public OrderService getOrderService() {
        return orderService;
    }


}

调用DzendApplicationContext中文件createBean方法

则整个文件如下:

java 复制代码
package com.spring;

import java.beans.Introspector;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class DzendApplicationContext {

    private Class<?> configClass;

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

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

    private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();

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

        scan(configClass);

    }

    private void scan(Class configClass) {
        if(configClass.isAnnotationPresent(ComponentScan.class)){
            ComponentScan componentScan = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
            String path = componentScan.value();
            path = path.replace(".","/");


            ClassLoader classLoader = DzendApplicationContext.class.getClassLoader();
            URL resource = classLoader.getResource(path);

            try {
                path = URLDecoder.decode(path, "UTF-8");
            } catch (UnsupportedEncodingException e) {
                throw new RuntimeException(e);
            }
            File file = null;
            try {
                file = new File(  URLDecoder.decode(resource.getFile(), "UTF-8"));
            } catch (UnsupportedEncodingException e) {
                throw new RuntimeException(e);
            }



            if(file.isDirectory()){

                for (File f : file.listFiles()) {
                    String absolutePath = f.getAbsolutePath();

                    absolutePath = absolutePath.substring(absolutePath.indexOf("com"),absolutePath.indexOf(".class"));
                    absolutePath=absolutePath.replace("\\",".");


                    try {
                        Class<?>  clazz = classLoader.loadClass(absolutePath);

                        if (clazz.isAnnotationPresent(Component.class)) {


                            Component componentAnnotaion = clazz.getAnnotation(Component.class);
                            String beanName= componentAnnotaion.value();
                            if("".equals(beanName)){
                                beanName = Introspector.decapitalize(clazz.getSimpleName());
                            }

                           BeanDefinition beanDefinition = new BeanDefinition();
                            beanDefinition.setType(clazz);

                            if (clazz.isAnnotationPresent(Scope.class)) {
                                Scope scopeAnnotation = clazz.getAnnotation(Scope.class);
                                String value = scopeAnnotation.value();
                                beanDefinition.setScope(value);
                            }else{
                                beanDefinition.setScope("singleton");
                            }

                            beanDefinitionMap.put(beanName,beanDefinition);

                        }

                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    } 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 NullPointerException();
        }

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

            if(singletonObject == null){
                singletonObject = createBean(beanName,beanDefinition);
                singletonObjects.put(beanName,singletonObject);
            }
            return singletonObject;
        }else{
            //原型
            Object prototypeBean = createBean(beanName, beanDefinition);
            return prototypeBean;
        }

    }

    private Object createBean(String beanName, BeanDefinition beanDefinition) {
        Class clazz = beanDefinition.getType();

        Object instance = null;


        try {
            instance = clazz.getConstructor().newInstance();


            for (Field field : clazz.getDeclaredFields()) {
                if(field.isAnnotationPresent(Autowired.class)){
                    field.setAccessible(true);
                    field.set(instance,getBean(field.getName()));
                }

            }




        } 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;
    }
}

最后修改Test.java中的main方法

java 复制代码
package com.dzend;

import com.dzend.service.UserService;
import com.spring.DzendApplicationContext;

public class Test {

    public static void main(String[] args) {
        //扫描   ->创建对象
        DzendApplicationContext applicationContext = new DzendApplicationContext(AppConfig.class);

        UserService userService = (UserService) applicationContext.getBean("userService");
        userService.getOrderService().test();

    }
}

执行结果

相关推荐
YaHuiLiang7 分钟前
小微互联网公司与互联网创业公司 -- 学历之殇
前端·后端·面试
冬天的风滚草10 分钟前
Higress开源版 大规模 MCP Server 部署配置方案
后端
雨落倾城夏未凉10 分钟前
4.信号与槽
后端·qt
岁忧18 分钟前
(LeetCode 每日一题) 1865. 找出和为指定值的下标对 (哈希表)
java·c++·算法·leetcode·go·散列表
YuTaoShao21 分钟前
【LeetCode 热题 100】240. 搜索二维矩阵 II——排除法
java·算法·leetcode
考虑考虑1 小时前
JDK9中的dropWhile
java·后端·java ee
想躺平的咸鱼干1 小时前
Volatile解决指令重排和单例模式
java·开发语言·单例模式·线程·并发编程
hqxstudying2 小时前
java依赖注入方法
java·spring·log4j·ioc·依赖
·云扬·2 小时前
【Java源码阅读系列37】深度解读Java BufferedReader 源码
java·开发语言
春生野草2 小时前
关于SpringMVC的整理
spring