手撕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();

    }
}

执行结果

相关推荐
Theodore_10223 小时前
4 设计模式原则之接口隔离原则
java·开发语言·设计模式·java-ee·接口隔离原则·javaee
冰帝海岸4 小时前
01-spring security认证笔记
java·笔记·spring
世间万物皆对象5 小时前
Spring Boot核心概念:日志管理
java·spring boot·单元测试
没书读了5 小时前
ssm框架-spring-spring声明式事务
java·数据库·spring
小二·6 小时前
java基础面试题笔记(基础篇)
java·笔记·python
开心工作室_kaic6 小时前
ssm161基于web的资源共享平台的共享与开发+jsp(论文+源码)_kaic
java·开发语言·前端
懒洋洋大魔王6 小时前
RocketMQ的使⽤
java·rocketmq·java-rocketmq
武子康6 小时前
Java-06 深入浅出 MyBatis - 一对一模型 SqlMapConfig 与 Mapper 详细讲解测试
java·开发语言·数据仓库·sql·mybatis·springboot·springcloud
qq_17448285756 小时前
springboot基于微信小程序的旧衣回收系统的设计与实现
spring boot·后端·微信小程序
转世成为计算机大神7 小时前
易考八股文之Java中的设计模式?
java·开发语言·设计模式