首页 > 编程语言 >用Java手搓一个依赖注入框架

用Java手搓一个依赖注入框架

时间:2024-08-02 18:06:26浏览次数:9  
标签:依赖 Java 框架 object class field new clazz method

1、bean容器

public class Container {

    private final static Logger log = Logger.getLogger(Container.class.getSimpleName());

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

    private List<SuspendBean> suspendBeans = new ArrayList<>();

    private List<SuspendBeanMethod> suspendBeanMethods = new ArrayList<>();

    public Container(Class<?> mainClass) {
        String packageName = mainClass.getPackageName();
        init(packageName);
    }

    @SuppressWarnings("unchecked")
    public <T> Optional<T> getBeanByType(Class<T> clazz) {
        return (Optional<T>) context.values().stream()
                .filter(object -> clazz.isAssignableFrom(object.getClass()))
                .findFirst();
    }

    private void init(String... packages) {
        List<Class<?>> classes = Packages.scanClasses(packages);
        System.out.println("扫描到:" + classes.stream().map(Class::getSimpleName).toList());
        for (Class<?> clazz : classes) {
            if (clazz.isAnnotationPresent(Singleton.class)) {
                try {
                    Object object;
                    if (clazz.isRecord()) {
                        createRecordInstance(clazz);
                    } else {
                        object = clazz.getDeclaredConstructor().newInstance();
                        createMethodBean(object);
                        String beanName = getBeanName(clazz);
                        injectField(object);
                        addBean(beanName, object);
                    }
                } catch (InstantiationException | IllegalAccessException | InvocationTargetException |
                         NoSuchMethodException e) {
                    throw new RuntimeException(e);
                }
            }
        }
        initSuspendField();
        initSuspendMethod();
    }

    private void createRecordInstance(Class<?> clazz) throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
        Class<?>[] constructorParameterTypes = Arrays.stream(clazz.getDeclaredFields()).map(Field::getType).toArray(Class[]::new);
        Field[] fields = clazz.getDeclaredFields();
        Object[] constructorArgValues = new Object[constructorParameterTypes.length];
        for (int i = 0; i < fields.length; i++) {
            Optional<?> optional = this.getBeanByType(constructorParameterTypes[i]);
            if (optional.isPresent()) {
                constructorArgValues[i] = optional.get();
            } else {

            }
        }
        Object recordObject = clazz.getDeclaredConstructor(constructorParameterTypes).newInstance(constructorArgValues);
        String beanName = getBeanName(clazz);
        addBean(beanName, recordObject);
    }

    private static <T> String getBeanName(Class<T> clazz) {
        String beanName;
        if (clazz.isAnnotationPresent(Named.class)) {
            beanName = clazz.getAnnotation(Named.class).value();
        } else {
            beanName = clazz.getSimpleName().substring(0, 1).toLowerCase() + clazz.getSimpleName().substring(1);
        }
        return beanName;
    }

    private void initSuspendField() {
        // 对挂起的bean进行初始化
        log.info("开始对未初始化完的bean的field进行依赖注入:");
        for (SuspendBean suspendBean : suspendBeans) {
            Object object = suspendBean.beanObject();
            Field field = suspendBean.field();
            try {
                field.setAccessible(true);
                if (field.get(object) == null) {
                    String injectBeanName;
                    if (field.isAnnotationPresent(Named.class)) {
                        injectBeanName = field.getAnnotation(Named.class).value();
                    } else {
                        injectBeanName = field.getType().getSimpleName().substring(0, 1).toLowerCase() + field.getType().getSimpleName().substring(1);
                    }
                    field.set(object, context.get(injectBeanName));
                }
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }
    }

    private void initSuspendMethod() {
        // 对挂起的bean method进行初始化
        log.info("开始对未初始化完的bean的method进行依赖注入:");
        for (SuspendBeanMethod suspendBean : suspendBeanMethods) {
            Object object = suspendBean.beanObject();
            Method method = suspendBean.method();
            try {
                List<Object> parameterBeans = new ArrayList<>();
                boolean breakFlag = false;
                for (Parameter parameter : method.getParameters()) {
                    Optional<?> optional = this.getBeanByType(parameter.getType());
                    if (optional.isPresent()) {
                        parameterBeans.add(optional.get());
                    } else {
                        log.severe("无法创建实例:%s".formatted(method.getName()));
                        breakFlag = true;
                    }
                }
                if (breakFlag) {
                    continue;
                }
                Object methodBean = method.invoke(object, parameterBeans.toArray());
                String methodBeanName;
                if (method.isAnnotationPresent(Named.class)) {
                    methodBeanName = method.getAnnotation(Named.class).value();
                } else {
                    methodBeanName = method.getName();
                }
                this.addBean(methodBeanName, methodBean);
            } catch (IllegalAccessException | InvocationTargetException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public Container addBean(String name, Object object) {
        context.put(name, object);
        log.info("添加bean name: %s, bean objec: %s".formatted(name, object));
        return this;
    }

    private void injectField(Object object) {
        for (Field field : object.getClass().getDeclaredFields()) {
            if (field.isAnnotationPresent(Inject.class)) {
                try {
                    field.setAccessible(true);
                    String injectBeanName;
                    if (field.isAnnotationPresent(Named.class)) {
                        injectBeanName = field.getAnnotation(Named.class).value();
                    } else {
                        injectBeanName = field.getName();
                    }
                    Object injectBean = context.get(injectBeanName);
                    if (injectBean == null) {
                        suspendBeans.add(new SuspendBean(object, field));
                    }
                    field.set(object, injectBean);
                    field.setAccessible(false);
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

    private void createMethodBean(Object object) {
        Method[] methods = object.getClass().getDeclaredMethods();
        for (Method method : methods) {
            if (method.isAnnotationPresent(Singleton.class) && !method.getReturnType().isAssignableFrom(void.class)
                    && !method.getReturnType().isAssignableFrom(Void.class)) {
                try {
                    List<Object> parameterBeans = new ArrayList<>();
                    boolean skipFlag = false;
                    for (Parameter parameter : method.getParameters()) {
                        Optional<?> optional = this.getBeanByType(parameter.getType());
                        if (optional.isPresent()) {
                            parameterBeans.add(optional.get());
                        } else {
                            skipFlag = true;
                        }
                    }
                    if (skipFlag) {
                        suspendBeanMethods.add(new SuspendBeanMethod(object, method));
                        continue;
                    }
                    Object methodBean = method.invoke(object, parameterBeans.toArray());
                    String methodBeanName;
                    if (method.isAnnotationPresent(Named.class)) {
                        methodBeanName = method.getAnnotation(Named.class).value();
                    } else {
                        methodBeanName = method.getName();
                    }
                    this.addBean(methodBeanName, methodBean);
                } catch (IllegalAccessException | InvocationTargetException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

    @SuppressWarnings("unchecked")
    public <T> T getBean(String name, Class<T> clazz) {
        Object bean = context.get(name);
        if (bean != null && clazz.isAssignableFrom(bean.getClass())) {
            return (T) context.get(name);
        } else {
            throw new RuntimeException("no bean named '%s' with %s type".formatted(name, clazz.getSimpleName()));
        }
    }

    record SuspendBean(Object beanObject, Field field) {
    }

    record SuspendBeanMethod(Object beanObject, Method method) {
    }

}

2、支持

字段注入

@Singleton
@Named("myBananaService")
public class BananaService {

    @Inject
    private AppleService appleService;

    public void banana() {
        appleService.apple();
        System.out.println("banana");
    }

}

方法参数注入

@Singleton
public class AppConfig {

    @Singleton
    public HelloService helloService() {
        return new HelloService();
    }

    @Singleton
    public WorldService worldService(HelloService helloService) {
        System.out.println("开始初始化: worldService");
        return new WorldService(helloService);
    }

}

record注入

@Singleton
public record RecordService(HelloService helloService) {

    public void record() {
        System.out.println("record service");
        helloService.hello();
    }

}

2、使用

public class DemoApp {

    public static void main(String[] args) {
        Container container = new Container(DemoApp.class);
        AppleService appleService = container.getBean("appleService", AppleService.class);
        BananaService bananaService = container.getBean("myBananaService", BananaService.class);
        CherryService cherryService = container.getBean("cherryService", CherryService.class);
        WorldService worldService = container.getBean("worldService", WorldService.class);
        RecordService recordService = container.getBean("recordService", RecordService.class);
        appleService.apple();
        bananaService.banana();
        cherryService.cherry();
        worldService.world();
        recordService.record();
    }

}

标签:依赖,Java,框架,object,class,field,new,clazz,method
From: https://www.cnblogs.com/jiayuan2006/p/18339320

相关文章

  • Java集合
    单列集合思维导图遍历方式适用场景双列集合可变参数Collections工具类常用API......
  • JavaSE基础编程十题(数组和方法部分)
    写在前面继续昨天Java中的数组和方法部分的习题,今天写十题编程题,来看看你能写出来几题。答案也是仅供参考,如果有更好的解法欢迎在下面留言!题目展示1.数组查找操作:定义一个长度为10的一维字符串数组,在每一个元素存放一个单词;然后运行时从命令行输入一个单词,程序判断数组是否包......
  • Java中的抽象类
    目录抽象类抽象类为什么要抽象?抽象类的特征与用法抽象类的好处抽象类、实现类、接口的区别注意抽象类抽象就是从多个事物中将共性的,本质的内容抽取出来。抽象类Java中可以定义没有方法体的方法,该方法的具体实现由子类完成,该方法称为抽象方法,包含抽象方法的类就是抽象类。​......
  • [Java并发]AQS详解之二
    参考资料Java并发之AQS详解一、概述谈到并发,不得不谈ReentrantLock;而谈到ReentrantLock,不得不谈AbstractQueuedSynchronizer(AQS)!类如其名,抽象的队列式的同步器,AQS定义了一套多线程访问共享资源的同步器框架,许多同步类实现都依赖于它,如常用的ReentrantLock/Semaphore/Co......
  • maven项目pom文件查看架包依赖处理架包冲突问题
    在运行maven项目的时候由于依赖冲突出现如下问题 项目中依赖了多个不同版本的slf4j类库,我们可以使用dependency:tree命令:  找到冲突的依赖,向上找到引入依赖的根源,在对应的pom文件里,添加 <exclusions> 标签即可<dependency><groupId>com.baidu.aip</groupId>......
  • java集合的三种遍历方式
    一、迭代器遍历 在遍历过程中,想删除元素可以使用迭代器遍历ps:遍历过程中不能用集合的方法进行遍历,可以用指针的remove方法进行遍历二、增强for遍历idea快捷方式集合名字.for加回车。方法的底层也是利用迭代器三、lambda表达式遍历完整格式简单格式......
  • 程序员进阶架构知识体系、开发运维工具使用、Java体系知识扩展、前后端分离流程详解、
    场景作为一名开发者,势必经历过从入门到自学、从基础到进阶、从学习到强化的过程。当经历过几年企业级开发的磨炼,再回头看之前的开发过程、成长阶段发现确实是走了好多的弯路。作为一名终身学习的信奉者,秉承持续学习、持续优化的信念。不惜耗费无数个日日夜夜,耗费大量时间精力......
  • JAVA后端拉取gitee仓库代码项目并将该工程打包成jar包
    公司当前有一个系统用于导出项目,而每次导出的项目并不可以直接使用,需要手动从gitee代码仓库中获取一个模板代码然后将他们整合到一起它才是一个完整的项目,所以目前我的任务就是编写一个java程序可以自动地从gitee仓库拉取下来那个模板代码到指定地路径上去。并且我还要将这个ja......
  • 基于tp8框架开发野兔广告联盟系统源码定制
    这款程序是采用国内主流的PHP框架,最新版本thinkphp8.0.4,也是目前市面上功能相对比较强大,界面比较好看的一款全开源的广告联盟系统,程序支持任意二开商业,并且代码无任何加密处理。程序开发:PHP+MySQL ,PHP最低版本是PHP8.0程序开源:100%开源程序授权:无,支持任意二开商用主要功能模......
  • JAVA中实现队列和栈(Deque接口和ArrayDeque类)
    用什么来实现队列和栈首先JAVA中有一个Queue接口,用来实现队列。Deque其实就是双端队列,代表两端都可进可出的队列。ArrayDeque就是用数组来实现这个双端队列。(Deque由于是接口,只可以用于声明对象,但是没办法实例化,实例化还是要使用ArrayDeque类)这时可能就会产生疑惑,队列有了,......