手写IOC

news2025/1/13 10:03:50

IOC原理(手写IOC)

Spring框架的IOC是基于反射机制实现的。

反射回顾

Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制,简单来说,反射机制就是程序在运行时能够获取自身的信息。

示例

实体类Car

package com.louis.reflect;

/**
 * @author XRY
 * @date 2023年06月26日8:59
 */
public class Car {

    private String bind;
    private int lifeTime;
    private String color;

    public Car() {
    }

    public Car(String bind, int lifeTime, String color) {
        this.bind = bind;
        this.lifeTime = lifeTime;
        this.color = color;
    }

    //普通方法
    private void use(){
        System.out.println("私有方法..........");
    }

    public String getBind() {
        return bind;
    }

    public void setBind(String bind) {
        this.bind = bind;
    }

    public int getLifeTime() {
        return lifeTime;
    }

    public void setLifeTime(int lifeTime) {
        this.lifeTime = lifeTime;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    @Override
    public String toString() {
        return "Car{" +
                "bind='" + bind + '\'' +
                ", lifeTime=" + lifeTime +
                ", color='" + color + '\'' +
                '}';
    }
}

1、获取class对象

@Test
public void testGetClass() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
    //1、类名.class
    Class<Car> clazz01 = Car.class;
    //2、对象.getClass()
    Class<? extends Car> clazz02 = new Car().getClass();
    //3、Class.forName("全路径")
    Class<?> clazz03 = Class.forName("com.louis.reflect.Car");
    //实例化
    Car car = (Car)clazz03.getDeclaredConstructor().newInstance();
    logger.info("car" + car);
}

/*[2023-06-26 09:16:13:036] [INFO] - com.louis.reflect.TestCar.testGetClass(TestCar.java:28) - carcom.louis.reflect.Car@2445445a*/

2、获取构造方法

@Test
public void testCacheConstructor() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
    Class<Car> clazz = Car.class;
    //获取所有构造
    //getConstructors针对public方法,如果是private则不能够使用这种方法获取,如果构造方法中包含私有的方法,则需要使用getDeclaredConstructors
    Constructor<?>[] constructors = clazz.getConstructors();
    for (Constructor<?> constructor : constructors) {
        logger.info("constructor" + constructor.getName() + "参数个数" + constructor.getParameterCount());
    /*
    * [2023-06-26 09:57:16:855] [INFO] - com.louis.reflect.TestCar.testCacheConstructor(TestCar.java:38) - constructorcom.louis.reflect.Car参数个数0
      [2023-06-26 09:57:16:858] [INFO] - com.louis.reflect.TestCar.testCacheConstructor(TestCar.java:38) - constructorcom.louis.reflect.Car参数个数3
    * */
    }
    //指定有参数的构造去创建对象
    //1、构造是public,如果目标对象是private则会报错
    /*Constructor<Car> haveParameterPub = clazz.getConstructor(String.class, int.class, String.class);
    Car car = haveParameterPub.newInstance("野马", 1, "blue");
    /*[2023-06-26 10:07:47:947] [INFO] - com.louis.reflect.TestCar.testCacheConstructor(TestCar.java:39) - constructorcom.louis.reflect.Car参数个数0
      [2023-06-26 10:07:47:950] [INFO] - com.louis.reflect.TestCar.testCacheConstructor(TestCar.java:39) - constructorcom.louis.reflect.Car参数个数3
      汽车Car{bind='野马', lifeTime=1, color='blue'}*/
    //2、构造是private
    Constructor<Car> haveParameterPri = clazz.getDeclaredConstructor(String.class, int.class, String.class);
    haveParameterPri.setAccessible(true);//设置访问权限,如果为false不能够访问
    Car car1 = haveParameterPri.newInstance("悍马", 2, "yellow");
    System.out.println("car1 = " + car1);
    /*
    [2023-06-26 10:13:58:492] [INFO] - com.louis.reflect.TestCar.testCacheConstructor(TestCar.java:39) - constructorcom.louis.reflect.Car参数个数0
    [2023-06-26 10:13:58:496] [INFO] - com.louis.reflect.TestCar.testCacheConstructor(TestCar.java:39) - constructorcom.louis.reflect.Car参数个数3
    car1 = Car{bind='悍马', lifeTime=2, color='yellow'}
    * */
}

3、获取属性

@Test
public void getAttribute() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
    //获得类的字节码文件,类、对象和class.forName
    Class<?> carClass = Class.forName("com.louis.reflect.Car");
    //实例化
    Car car = (Car)carClass.getDeclaredConstructor().newInstance();
    //获取其中所有的public方法
    Field[] fields = carClass.getFields();
    //获取所有的属性,包括私有的属性
    Field[] declaredFields = carClass.getDeclaredFields();
    for (Field declaredField : declaredFields) {
        System.out.println("declaredField.getName() = " + declaredField.getName());
        //给属性赋值
        if(declaredField.getName().equals("bind")){
            //设置允许访问
            declaredField.setAccessible(true);
            //传入对象和属性值
            declaredField.set(car,"野马");
        }
        System.out.println("car" + car);
    }
    /*
    * declaredField.getName() = bind
      carCar{bind='野马', lifeTime=0, color='null'}
      declaredField.getName() = lifeTime
      carCar{bind='野马', lifeTime=0, color='null'}
      declaredField.getName() = color
      carCar{bind='野马', lifeTime=0, color='null'}

    * */
}

4、获取方法

@Test
public void getMethod() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
    Car car = new Car("Benz", 10, "black");
    Class<? extends Car> clazz = car.getClass();
    //1、public方法,不会取到私有
    Method[] methods = clazz.getMethods();
    for (Method method : methods) {
//            System.out.println(method.getName());
        //执行方法toString
        if(method.getName().equals("toString")){
            String invoke = (String)method.invoke(car);
            System.out.println("toString执行了" + invoke);
            /*toString执行了Car{bind='Benz', lifeTime=10, color='black'}*/
        }
    }
    //2、private方法
    Method[] methodsAll = clazz.getDeclaredMethods();
    for (Method methodA : methodsAll) {
        //执行私有方法
        if(methodA.getName().equals("use")){
            methodA.setAccessible(true);
            methodA.invoke(car);
        }
    }
    /*私有方法..........*/
}

实现Spring的IoC

实现过程

1、创建模块spring-ioc

在这里插入图片描述

2、创建测试类service、dao

在这里插入图片描述

接口实现

@Bean
public class UserServiceImpl implements UserService {
    @Di
    private UserDao userDao;
    public void add(){
        System.out.println("service......");
        //调用dao的方法
        userDao.show();
    }
}

3、创建两个注解@Bean(创建对象)、 @Di(属性注入)

@Bean

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

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Bean {
    //用于创建对象
}

@Di

package com.louis.annotation;

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

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Di {
    //用于注入属性
}

4、创建bean容器接口ApplicationContext,定义方法、返回对象

public interface ApplicationContext {
    //在IOC中BeanFactory返回的是一个工厂
    Object getBean(Class clazz);
}

5、实现bean容器接口,创建实现类

(1)返回对象
(2)根据包规则加载bean(扫描路径下包含@Bean注解的类,并将这些类通过反射实例化)

package com.louis.bean.impl;

import com.louis.annotation.Bean;
import com.louis.annotation.Di;
import com.louis.bean.ApplicationContext;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * @author XRY
 * @date 2023年06月26日16:39
 */
public class AnnotationApplicationContext implements ApplicationContext {
    //模拟IOC,创建map集合,放置bean对象
    private Map<Class, Object> beanFactory = new HashMap<>();
    private static String rootPath;

    //返回对象
    @Override
    public Object getBean(Class clazz) {
        return beanFactory.get(clazz);
    }

    //设置包扫描规则
    //当前包及其子包,将带有@Bean注解的类通过反射实现实例化
    public AnnotationApplicationContext(String basePackage){
        //扫描路径
        //1、将.替换成\
        String packagePath = basePackage.replaceAll("\\.", "\\\\");
        //2、获取包的绝对路径,编译之后的路径
        try {
            Enumeration<URL> urls = Thread.currentThread().getContextClassLoader().getResources(packagePath);
            while(urls.hasMoreElements()){
                URL url = urls.nextElement();
                //转码
                String filePath = URLDecoder.decode(url.getFile(), "utf-8");
                //获取包前面路劲部分,字符串截取
                rootPath = filePath.substring(0, filePath.length() - packagePath.length());
//                System.out.println("filePath = " + filePath);
                //根据获得的路径进行包扫描
                loadBean(new File(filePath));
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        //属性注入
        loadDi();
    }

    /**
     * 包扫描过程,进行实例化
     * @param file
     */
    private void loadBean(File file) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //1、判断当前路径下的东西是否是文件夹,如果不是就不需要继续往下查找
        if(file.isDirectory()){
            //2、获取文件夹里面所有内容
            File[] childFiles = file.listFiles();
            //3、判断文件夹里面为空,直接返回
            if(childFiles == null || childFiles.length == 0){
                return;
            }
            //4、如果文件夹里面不为空,遍历文件夹中所有内容
            for(File child:childFiles){
                //4.1遍历得到每个File对象,继续判断,如果是文件夹,递归
                if(child.isDirectory()){
                    //递归
                    loadBean(child);
                }else{
                    //4.2遍历得到的File对象不是文件夹
                    //4.3得到包路径+类名称部分
                    String packagePath = child.getAbsolutePath().substring(rootPath.length() - 1);
                    //4.4判断当前文件类型是否为.class,如果是,将路径中的\替换为.并将.class文件去掉
                    if(packagePath.contains(".class")){
                        String allName = packagePath.replaceAll("\\\\", "\\.").replaceAll(".class", "");
                        //4.5判断类上面是否有注解@Bean,如果有进行实例化过程
                        //4.5.1获取类的class
                        Class<?> clazz = Class.forName(allName);
                        //4.5.2判断不是interface
                        if(!clazz.isInterface()){
                            Bean annotation = clazz.getAnnotation(Bean.class);
                            if(annotation != null){
                                //4.5.3实例化
                                Object instance = clazz.getConstructor().newInstance();
                                //4.7把对象实例化之后,放到map集合beanFactory
                                //4.7.1判断当前类如果有接口,让接口class作为map的key,如果没有接口将自己的class作为key
                                if(clazz.getInterfaces().length > 0){
                                    beanFactory.put(clazz.getInterfaces()[0], instance);
                                }else{
                                    beanFactory.put(clazz,instance);
                                }
                            }
                        }
                    }
                }
            }
        }
    }

//    public static void main(String[] args) {
//        new AnnotationApplicationContext("com.louis");
//    }

    //属性注入
    private void loadDi(){
        //实例化的对象都在beanFactory的map集合里
        //1、遍历beanFactory的map集合,entrySet()用来获取到对象的集合
        Set<Map.Entry<Class, Object>> entries = beanFactory.entrySet();
        for (Map.Entry<Class, Object> entry : entries) {
            //2、获取map集合中每个对象(value),每个对象获取到属性
            Object value = entry.getValue();
            //获取对象Class
            Class<?> clazz = value.getClass();
            //获取每个对象中的属性
            Field[] declaredFields = clazz.getDeclaredFields();
            //3、遍历得到的每个对象属性的数组,得到每个属性
            for (Field field : declaredFields) {
                //4、判断属性上是否有@Di注解,
                Di annotation = field.getAnnotation(Di.class);
                if(annotation != null){
                    //如果是私有属性可以设置一个值
                    field.setAccessible(true);
                    //有Di注解就把对象进行设置(注入)
                    try {
                        field.set(value, beanFactory.get(field.getType()));
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
    }
}

6、测试

public class TestBean {
  public static void main(String[] args){
      ApplicationContext context = new AnnotationApplicationContext("com.louis");
      UserService userService = (UserService) context.getBean(UserService.class);
//      UserService userService1 = new UserServiceImpl();
      userService.add();
  }
}

/*
* service......
UserDao................
* */

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/769886.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

开放式蓝牙耳机推荐哪款?开放式蓝牙耳机排行榜推荐

​说到开放式耳机&#xff0c;想必很多人听着还是陌生&#xff0c;普通耳机久戴&#xff0c;会出现耳朵疼痛问题&#xff0c;而开放式蓝牙耳机没有&#xff0c;不入耳的设计更加的干净&#xff0c;不会对耳道造成的伤害&#xff0c;下面我给大家推荐几款很不错的开放式耳机&…

C++学习——类和对象(二)

紧接着我们上一部分类和对象的讲解之后&#xff0c;我们再来学习一下类当中的几大特点&#xff0c;以及使用方法。 一&#xff1a;实例化对象赋初值 首先我们需要学习的就是该如何为我们实例化出的对象赋初值。 1.外部赋值 对于对象赋初值我们有很多的形式&#xff0c;我们甚至…

【lesson1】Linux发展史

文章目录 推动技术进步的基本模式为什么科技一直在进步&#xff1f;科技进步动力是什么&#xff1f; 理解操作系统的发展计算机的发明操作系统的发展Linux的发展 开源为什么有人愿意参加开源项目呢&#xff1f;开源的好处 Linux的应用为什么Windows更好用&#xff1f;OS(操作系…

CGT Asia嘉年华|2023第四届亚洲细胞与基因治疗 创新峰会(广州站)10月升级启航

近年来&#xff0c;全球CGT发展突飞猛进&#xff0c;为遗传罕见病、难治性慢性病和肿瘤患者带来了新的希望&#xff0c;也成为整个国际领域科技竞争的未来焦点。国家发改委发布的《“十四五”生物经济发展规划》明确指出要重点发展基因诊疗、干细胞治疗、免疫细胞治疗等新技术&…

ROS学习——通信机制(常用命令)

2.4 常用命令 Autolabor-ROS机器人入门课程《ROS理论与实践》零基础教程088常用命令简介_Chapter2-ROS通信机制_哔哩哔哩_bilibili 机器人系统中启动的节点少则几个&#xff0c;多则十几个、几十个&#xff0c;不同的节点名称各异&#xff0c;通信时使用话题、服务、消息、参…

Leetcode刷题4

⼆叉树、BFS、堆、Top K、⼆叉搜索树、模拟、图算法 一、二叉树 二叉树的前序中序后序 二叉树节点定义 为了方便演示&#xff0c;我们先定义一个二叉树节点类。 class TreeNode:def __init__(self, val0, leftNone, rightNone):self.val valself.left leftself.right r…

keil5汇编实现1-100累加

.text 用于声明下面的代码都存放在.text文本段 .globl _start 声明_start代码内容属于全局内容_start: 汇编语言的标签&#xff0c;类似于c语言中的函数mov r0,#0 总和mov r1,#1 比较累加的值&#xff0c;次数bl fun 跳转&#xff0c;下一个指令地址保存到lr中cmp r1,#…

LLM微调 | LoRA: Low-Rank Adaptation of Large Language Models

&#x1f525; 发表于论文&#xff1a;(2021) LoRA: Low-Rank Adaptation of Large Language Models &#x1f604; 目的&#xff1a;大模型预训练微调范式&#xff0c;微调成本高。LoRA只微调新增的小部分参数。 文章目录 1、背景2、动机3、LoRA原理4、总结 1、背景 adapter…

【UE4 塔防游戏系列】11-多种类型敌人

目录 效果 前言 步骤 一、创建多种不同类型敌人 二、创建波次 &#xff08;修改游戏模式&#xff09; 效果 前言 我们之前使用的敌人都是同一种敌人&#xff0c;都是名为“Crossbowman”敌人 这里我们根据&#xff08;【UE4 塔防游戏系列】03-创建第一个敌人&#xff09…

学会在重装系统前如何备份软件,再也不怕失去珍贵的应用!

​Windows系统是电脑的重要组成部分&#xff0c;它不仅提供了友好的用户界面&#xff0c;还承担着许多关键的功能和任务&#xff0c;为我们提供了一个稳定、安全和效率的工作环境&#xff0c;使我们能够充分发挥电脑的潜力&#xff0c;优化工作效率和生活品质。 随着系统使…

软件测试面试及笔试题

1、什么是软件测试&#xff1f; 【要点】 在规定条件下对程序进行操作&#xff0c;以发现错误&#xff0c;对软件质量进行评估&#xff0c;包括对软件形成过程的文档、数据以及程序进行测试。 【详解】 软件测试就是在软件投入运行前对软件需求分析、软件设计规格说明书和软…

网络安全/黑客技术—学习笔记

一、什么是网络安全 网络安全可以基于攻击和防御视角来分类&#xff0c;我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术&#xff0c;而“蓝队”、“安全运营”、“安全运维”则研究防御技术。 无论网络、Web、移动、桌面、云等哪个领域&#xff0c;都有攻与防两面…

【产品经理】小型团队通用工作流程SOP方案

&#xff1a;所谓SOP&#xff0c;即标准作业程序&#xff0c;指将某一事件的标准操作步骤和要求以统一的格式描述出来&#xff0c;用于指导和规范日常的工作。实际执行过程中sop核心是符合本企业并可执行&#xff0c;不流于形式。 一、跨部门工作流程 跨部门流程及职能如下图展…

2.6Checkbutton勾选项

2.6Checkbutton勾选项 运行之后的效果将会像下面的图片一样&#xff0c;此时不作任何操作. 如果只选中第一个选项&#xff0c;即图中的python, 效果就会如下. 如果只选中第二个选项&#xff0c;即图中的c, 效果就会如下. 如果两个选项都选中, 效果就会如下. Checkbutton部…

2023年7月22日(星期六):骑行海囗

2023年7月22日(星期六)&#xff1a;骑行海囗&#xff0c;早8:30到9:00&#xff0c; 大观公园门囗集合&#xff0c;9:30点准时出发 【因迟到者&#xff0c;骑行速度快者&#xff0c;可自行追赶偶遇。】 偶遇地点: 大观公园门囗集合&#xff0c;家住南&#xff0c;东&#xff0…

【第11天】面向对象程序设计_对象的创建,使用,继承

对象的创建及应用 对象的创建 对象可以认为是在一类事物中抽象出某一个特定通过这个特例来处理这类事物出现的问题。在程序语言中通过new关键字来创建对象。前文在讲解构造函数时介绍过每实例化一个对象就会自动调用一次构造函数&#xff0c;实质上这个过程就是创建对象的过程…

实例018 类似windows xp的程序界面

实例说明 在Windows XP环境下打开控制面板&#xff0c;会发现左侧的导航界面很实用。双击展开按钮&#xff0c;导航栏功能显示出来&#xff0c;双击收缩按钮&#xff0c;导航按钮收缩。下面通过实例介绍此种主窗体的设计方法。运行本例&#xff0c;效果如图1.18所示。 ​编辑…

C++ vector容器注意事项

容量&#xff08;capacity&#xff09;和大小&#xff08;size&#xff09;的区别 vector 容器的容量&#xff08;用 capacity 表示&#xff09;&#xff0c;指的是在不分配更多内存的情况下&#xff0c;容器可以保存的最多元素个数&#xff1b;而 vector 容器的大小&#xff…

如何提升环境、生态、水文、土地、土壤、农业、大气等领域的数据分析能力

专题一、空间数据获取与制图 1.1 软件安装与应用讲解 1.2 空间数据介绍 1.3海量空间数据下载 1.4 ArcGIS软件快速入门 1.5 Geodatabase地理数据库 专题二、ArcGIS专题地图制作 2.1专题地图制作规范 2.2 空间数据的准备与处理 2.3 空间数据可视化&#xff1a;地图符号与…

zabbix监控linux主机、监控windows10主机

目录 一、环境准备 1、关闭防火墙 2、准备三台服务器、添加主机声明 3、修改主机名 4、此篇接着上一篇zabbix监控自己的环境下操作&#xff0c;server&#xff08;192.168.147.135&#xff09;已经配置好 二、源码安装zabbix 1、下载包、安装依赖包、联网同步清华时间 2…