Spring4-IoC3-手写IoC

news2024/9/22 5:31:25

Spring框架的IoC是基于Java反射机制实现的

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

要想解剖一个类,必须先要获取到该类的Class对象,而剖析一个类或用反射解决具体的问题就是使用相关API(1)java.lang.Class(2)java.lang.reflect,所以,Class对象是反射的根源

package com.qcby;

import org.junit.Test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class TestCar {
    //1.获取Class对象多种方式
    @Test
    public void test01() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //1)类名.class
        Class<Car> clazz1 = Car.class;
        //2)对象.getClass()
        Class clazz2 = new Car().getClass();
        //3.Class.forName("全路径")
        Class clazz3 = Class.forName("com.qcby.Car");
        
        //实例化
        Car car = (Car) clazz3.getDeclaredConstructor().newInstance();
        System.out.println(car);
    }
    
    //2.获取构造方法
    @Test
    public void test02() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        Class<Car> clazz = Car.class;
        //获取所有public的构造方法
//        Constructor[] constructors = clazz.getConstructors();
        //获取所有构造方法(public、private)
        Constructor[] constructors = clazz.getDeclaredConstructors();
        for (Constructor c : constructors) {
            System.out.println("名称:"+c.getName()+" 参数个数:"+c.getParameterCount());
        }

        //指定有参构造创造对象
        //1)构造方法是public
//        Constructor c1 = clazz.getConstructor(String.class, int.class, String.class);
//        Car car1 = (Car) c1.newInstance("奥迪", 10, "黑色");
//        System.out.println(car1);
        //2)构造private
        Constructor c2 = clazz.getDeclaredConstructor(String.class, int.class, String.class);
        c2.setAccessible(true); //允许访问私有构造
        Car car2 = (Car) c2.newInstance("宝马", 10, "白色");
        System.out.println(car2);
    }
    //3.获取属性
    @Test
    public void test03() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        Class clazz = Car.class;
        Car car = (Car) clazz.getDeclaredConstructor().newInstance();
        //获取所有public属性
//        Field[] fields = clazz.getFields();
        //获取所有属性(包含私有属性)
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            if(field.getName().equals("name")){
                //设置允许访问
                field.setAccessible(true);
                field.set(car,"五菱宏光");
            }
            System.out.println(field.getName());
            System.out.println(car);
        }
    }
    //4.获取方法
    @Test
    public void test04() throws InvocationTargetException, IllegalAccessException {
        Car car = new Car("奔驰",10,"黑色");
        Class clazz = car.getClass();
        //1)public方法
        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
//            System.out.println(method.getName());
            //执行方法
            if(method.getName().equals("toString")){
                String invoke = (String) method.invoke(car);
                System.out.println("toString执行了:"+invoke);
            }
        }
        //2)private方法
        Method[] methods1 = clazz.getDeclaredMethods();
        for (Method method : methods1) {
            //执行方法
            if(method.getName().equals("run")){
                method.setAccessible(true);
                method.invoke(car);
            }
        }
    }

}

手写IoC

1.创建子模块spring-ioc

2.创建测试类 service dao

3.创建两个注解

  • @Bean 创建对象
  • @Di 属性注入

4.创建bean容器接口 ApplicationContext

  • 定义方法
  • 返回对象

5.实现bean容器接口

  • 返回对象
  • 根据包规则加载bean

扫描com.qcby这个包和它的子包里面的所有类,看类上面是否有@Bean注解,如果有则把这个类通过反射实例化

package com.qcby.dao;

public interface UserDao {
    void add();
}
package com.qcby.dao.impl;

import com.qcby.anno.Bean;
import com.qcby.dao.UserDao;
import org.springframework.stereotype.Repository;


@Bean
public class UserDaoImpl implements UserDao {
    @Override
    public void add() {
        System.out.println("dao...");
    }
}
package com.qcby.service;

public interface UserService {
    void add();
}
package com.qcby.service.impl;

import com.qcby.anno.Bean;
import com.qcby.anno.Di;
import com.qcby.dao.UserDao;
import com.qcby.service.UserService;
import org.springframework.stereotype.Service;

@Bean
public class UserServiceImpl implements UserService {

    @Di
    private UserDao userDao;

    @Override
    public void add() {
        System.out.println("service...");
        //调用dao的方法
        userDao.add();
    }
}
package com.qcby.bean;

public interface ApplicationContext {
    Object getBean(Class clazz);
}
package com.qcby.anno;


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 {
}
package com.qcby.anno;


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 {
}
package com.qcby.bean;

import com.qcby.anno.Bean;
import com.qcby.anno.Di;

import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
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;

public class AnnotationApplicationContext implements ApplicationContext{

    //创建一个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) throws IOException, ClassNotFoundException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
        //1.把.替换成\
        String packagePath = basePackage.replaceAll("\\.", "\\\\");
        //2.获取包的绝对路径
        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());
            //包扫描
            loadBean(new File(filePath));
        }
        //属性注入
        loadDi();
    }



    //包扫描过程,实例化
    private void loadBean(File file) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //1.判断当前是否是文件夹
        if(file.isDirectory()){
            //2.获取文件夹里的所有内容
            File[] childrenFiles = file.listFiles();
            //3.判断文件夹里面为空,直接返回
            if(childrenFiles == null || childrenFiles.length == 0){
                return;
            }
            //4.如果文件夹不为空,遍历文件夹所有内容
            for (File child : childrenFiles) {
                //1)遍历得到每个File对象,继续判断,如果还是文件夹,递归
                if(child.isDirectory()){
                    loadBean(child);
                }else {
                    //2)遍历得到File对象不是文件夹,是文件
                    //3)得到包路径+类名称部分  字符串截取
                    String pathWithClass = child.getAbsolutePath().substring(rootPath.length() - 1);
                    //4)判断当前文件类型是否是.class
                    if(pathWithClass.contains(".class")){
                        //5)如果是.class类型,把路径\替换成.,把.class去掉
                        String allName = pathWithClass.replaceAll("\\\\", ".").replace(".class", "");
                        //6)判断类上是否有注解@Bean,如果有,实例化过程
                        //6.1获取类的Class
                        Class<?> clazz = Class.forName(allName);
                        //6.2判断不是接口,实例化
                        if(!clazz.isInterface()){
                            //6.3判断类上面是否有注解@Bean
                            Bean annotation = clazz.getAnnotation(Bean.class);
                            if(annotation != null){
                                //6.4实例化
                                Object instance = clazz.getConstructor().newInstance();
                                //7)把对象实例化之后,放到map集合beanFactory
                                //7.1判断当前类如果有接口,让接口class作为map的key
                                if (clazz.getInterfaces().length>0){
                                    beanFactory.put(clazz.getInterfaces()[0],instance);
                                }else {
                                    beanFactory.put(clazz,instance);
                                }
                            }
                        }
                    }
                }
            }
        }
    }

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


}

测试:

package com.qcby;

import com.qcby.bean.AnnotationApplicationContext;
import com.qcby.bean.ApplicationContext;
import com.qcby.service.UserService;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;

public class TestUser {
    public static void main(String[] args) throws IOException, ClassNotFoundException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
        ApplicationContext context =
                new AnnotationApplicationContext("com.qcby");
        UserService userService = (UserService) context.getBean(UserService.class);
        System.out.println(userService);
        userService.add();
    }
}

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

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

相关文章

【AI学习笔记】初学机器学习西瓜书的知识点概要记录

初学机器学习西瓜书的知识点概要记录 1.1 机器学习1.2 典型的机器学习过程1.2 机器学习理论1.3 基本术语1.4 归纳偏好1.5 NFL定理2.1 泛化能力2.2 过拟合和欠拟合2.3 三大问题2.4 评估方法2.5 调参与验证集2.6 性能度量2.7 比较检验 以下内容出自周志华老师亲讲西瓜书 1.1 机器…

复习:数组

目录 数组名 一般性理解 下标引用与间接访问 例外 一维数组 声明与初始化 下标引用 内存分配 长度计算 二维数组 内存分配 长度计算 声明与初始化 数组指针 引入 数组指针 一级指针 引入 一级指针 章尾问题 数组名 一般性理解 数组名是一个指向&#x…

DockerLinux安装DockerDocker基础

Linux软件安装 yum命令安装 通过yum命令安装软件,是直接把软件安装到Linux系统中 安装和卸载都比较麻烦,因为软件和系统是强关联的 Docker docker是一种容器技术,可以解决软件和系统强关联关系,使得软件的安装和卸载更方便,它可以将我们的应用以及依赖进行打包,制作出一个镜…

算法:TopK问题

题目 有10亿个数字&#xff0c;需要找出其中的前k大个数字。 为了方便讲解&#xff0c;这里令k为5。 思路分析&#xff08;以找前k大个数字为例&#xff09; 很容易想到&#xff0c;进行排序&#xff0c;然后取前k个数字即可。 但是&#xff0c;难点在于&#xff0c;10亿个数…

人工智能GPT____豆包使用的一些初步探索步骤 体验不一样的工作

豆包工具是我使用比较频繁的一款软件&#xff0c;其集合了很多功能。对话 图像 AI搜索 伴读等等使用都非常不错。电脑端安装集合了很多功能。 官网直达&#xff1a;豆包 使用我的文案创作能力&#xff0c;您可以注意以下几个技巧&#xff1a; 明确需求&#xff1a; 尽可能具…

Linux进阶 把用户加入和移除用户组

1、Linux 单用户多任务,多用户多任务概念 Linux 是一个多用户、多任务的操作系统。 单用户多任务、多用户多任务 概念; Linux 的 单用户、多任务以 beinan 登录系统,进入系统后,我要打开gedit 来写文档,但在写文档的过程中,我感觉少点音乐,所以又打开xmms 来点音乐;当…

C语言:联合和枚举

一. 联合体 1.联合体的声明 1. 像结构体一样&#xff0c;联合体也是由一个或者多个成员构成&#xff0c;这些成员可以不同的类型。 union { 成员1&#xff1b; 成员2&#xff1b; ........ }; //联合体类型 union S {char c;int i; }; 2.联合体的特点和大小计算 像结构体一…

emWin5的图片半透明之旅

文章目录 目标过程直接使用png (失败了)通过 BmpCvt.exe 转换一下&#xff08;成功了&#xff09;通过bmp转 &#xff08;半成功吧&#xff09; 补充工程结构整理 目标 显示半透明效果&#xff0c;类似png那种&#xff0c;能透过去&#xff0c;看到背景。 过程 直接使用png …

【STM32】单级与串级PID控制的C语言实现

【STM32】单级与串级PID的C语言实现 前言PID理论什么是PIDPID计算过程PID计算公式Pout、Iout、Dout的作用单级PID与串级PID PID应用单级PID串级PID 前言 笔者最近在学习PID控制器&#xff0c;本文基于Blog做以总结。CSDN上已有大量PID理论知识的优秀文章&#xff0c;因此本文将…

基于HPLC的低压电力采集方案

1. 组网部署 2. 组网部件 3. 原理

✔2848. 与车相交的点

代码实现&#xff1a; 方法一&#xff1a;哈希表 #define fmax(a, b) ((a) > (b) ? (a) : (b))int numberOfPoints(int **nums, int numsSize, int *numsColSize) {int hash[101] {0};int max 0;for (int i 0; i < numsSize; i) {max fmax(max, nums[i][1]);for …

基于SSM+Vue+MySQL的新生报到管理系统

系统展示 用户界面 管理员界面 系统背景 在当今高等教育日益普及的背景下&#xff0c;新生报到管理成为高校日常管理中的重要环节。为了提升报到效率、优化管理流程并确保数据的准确性与安全性&#xff0c;我们设计并实现了一个基于SSM&#xff08;SpringSpring MVCMyBatis&…

JavaScript高级——作用域和作用链

1、概念理解&#xff1a; —— 就是一块“地盘”&#xff0c;一个代码所在的区域 —— 静态的&#xff08;相对于上下文对象&#xff09;&#xff0c;在编写代码时就确定了 2、分类 ① 全局作用域 ② 函数作用域 ③ 没有块作用域&#xff08;ES6有了&#xff09; 3、作用 …

app抓包 chrome://inspect/#devices

一、前言&#xff1a; 1.首先不支持flutter框架&#xff0c;可支持ionic、taro 2.初次需要翻墙 3.app为debug包&#xff0c;非release 二、具体步骤 1.谷歌浏览器地址&#xff1a;chrome://inspect/#devices qq浏览器地址&#xff1a;qqbrowser://inspect/#devi…

C#开发基础之单例模式下的集合数据,解决并发访问读写冲突的问题

1. 前言 在C#中&#xff0c;使用单例模式管理集合数据时&#xff0c;如果多线程同时访问集合&#xff0c;容易产生并发访问的读写冲突问题。单例模式下集合数据的并发访问读写冲突是如何产生的&#xff1f; 单例模式确保一个类在整个应用运行期间只有一个实例&#xff0c;这使…

CSP-J 算法基础 图论

文章目录 前言图的简介1. **图的定义**2. **图的类型**3. **图的表示方法**a. **邻接矩阵&#xff08;Adjacency Matrix&#xff09;**b. **邻接表&#xff08;Adjacency List&#xff09;** 4. **图的基本操作**5. **图的遍历**6. **图的应用**7. **图的算法** 出度与入度1. *…

Android实现关机和重启功能

文章目录 需求场景需求场景经历 一、解决思路和方案实际困难点情况普遍思路用Shell 命令实现应用和系统联调遇到的问题 个人解决思路和方案 二、代码跟踪系统实现的关机、重启界面GlobalActionsDialogLite.java 创建关机、重启菜单createActionItems()shutdownAction GlobalAct…

时序数据库 TDengine 的入门体验和操作记录

时序数据库 TDengine 的学习和使用经验 什么是 TDengine &#xff1f;什么是时序数据 &#xff1f;使用RPM安装包部署默认的网络端口 TDengine 使用TDengine 命令行&#xff08;CLI&#xff09;taosBenchmark服务器内存需求删库跑路测试 使用体验文档纠错 什么是 TDengine &…

GEE:连续变化检测与分类(Continuous Change Detection and Classification, CCDC)教程

连续变化检测与分类&#xff08;Continuous Change Detection and Classification, CCDC&#xff09;是一种土地变化监测算法&#xff0c;旨在对卫星数据的时间序列进行操作&#xff0c;特别是Landsat数据。CCDC包括两个部分&#xff0c;其一是变化检测算法&#xff08;Change …

Mybatis中Like模糊查询三种处理方式

目录 Mybatis中Like模糊查询三种处理方式 1.通过单引号拼接${} 1&#xff09;mapper接口 2&#xff09;Mapper.xml 3&#xff09;测试代码 4) 测试结果 2.通过concat()函数拼接(个人推荐使用这种) 1&#xff09;mapper接口 2&#xff09;Mapper.xml 3&#xff09;测试代码 4) 测…