IoC容器的设计(利用反射、注解和工厂模式实现)

news2024/11/20 20:31:12

1.实验要求

  1. 利用注解、反射和工厂模式设计一个简单的IoC容器
  2. 该IoC容器包含3个注解和一个IoC容器类(AnnotationConfigApplicationContext),其定义如下:

注解:

注解含义
@Component标注Bean
@Autowired标注需要被注入的对象
@Configuration标注为配置类
@ComponentScan注解扫描器

IoC容器类:

        3.自定义两个业务类Group和User,创建一个测试类Test,对IoC容器进行测试:

2.实验思路

本实验要求我们通过反射和自定义注解的设计,模拟实现spring框架IoC容器类的基础功能,要实现的注解有@Component、@Autowired、@Configuration和@ComponentScan

首先我们需要明确这些注解在spring框架中本身的功能是什么:

  1. @Component:将实体类实例化到容器中
  2. @ComponentScan:配置包路径,到该路径下去寻找bean
  3. @Autowired:自动装配(定义一个变量接收注入的类)
  4. @Configuration:标识为配置类

所以在该实验中,我们需要将自定义四个注解,然后将Group和User类使用@Component注解,在User类中创建Group类的实例化对象并设置为自动装配,这样就能在User类中调用Group类的方法;

然后我们需要自己实现一个IoC容器类,处理自定义的注解的基本逻辑;

接下来在test类中实例化IoC容器,并从中取得bean,调用其方法

3.实验代码

pom文件依赖配置:

<dependencies>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.10.RELEASE</version>
  </dependency>

  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.14.RELEASE</version>
  </dependency>

  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
  </dependency>

jdk版本:1.8

开发工具:IDEA

3.1自定义注解

@Autowired:

//自定义注解:标注需要被注入的对象
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
}

@Component:

//自定义注解:标注Bean
@Target({ElementType.TYPE,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
    String value() default "";
}

@ComponentScan:

//自定义注解 注解扫描器
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ComponentScan {
    String value() default "";
}

@Configuration:

//自定义注解 标注为配置类
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Configuration {
}

3.2 定义实体类

Group:

@Component
public class Group {
    public String getBeanName()
    {
        return this.getClass().getSimpleName();
    }
}

User:

@Component
public class User {
    @Autowired
    private Group group;

    public void print()
    {
        System.out.println("group name as follow:");
        System.out.println(group.getBeanName());
    }

}

3.3 定义配置类

MyConfig:

@Configuration
@ComponentScan("org.example")
public class MyConfig {
}

3.4 定义IoC容器类

接口:ApplicationContext

public interface ApplicationContext {
    Object getBean(Class clazz);
}

实现类:AnnotationApplicationContext

public class AnnotationApplicationContext implements ApplicationContext {

    //定义Map,用于存放bean对象
    private Map<String, Object> beanFactory = new HashMap<>();

    //创建IOC容器类时执行的方法
    public AnnotationApplicationContext(Class configClass) throws InstantiationException, IllegalAccessException {
        //处理@Configuration的逻辑(没有实质性的内容,只是要求必须有配置类)
        try {
            //获取@Configuration类型的对象
            Configuration configuration = (Configuration) configClass.getAnnotation(Configuration.class);
            //如果对象为空,则引发异常
            if (configuration.annotationType().toGenericString().equals("")) {
                //没有具体逻辑
            }
        } catch (Exception e) {
            System.out.println("找不到配置类");
            System.exit(-1);
        }

        //处理@ComponentScan的逻辑
        /*
        总的来说,就是扫描某一路径下的所有类,如果有的类被Component注解,则创建其实例(一个bean),加入beanFactory中
        如果有的类中的对象(一个Field类型的数据)被@Autowired注解标注,则用beanFactory中同类型的bean替代该对象
        */
        try {
            ComponentScan componentScan = (ComponentScan) configClass.getAnnotation(ComponentScan.class);

            //根据componentScanValue的值来获取要扫描的路径(默认值处理的不是很好)
            String componentScanValue = componentScan.value().toString();
            String path = "";
            if (!componentScanValue.equals(""))
            {
                String[] splitValue = componentScanValue.split("\\.");
                for (int i = 0; i < splitValue.length; i++) {
                    path += splitValue[i] + "/";
                }
                path = "classpath*:" + path + "**/*.class";
            }
            else {
                path = "classpath*:org/example/**/*.class";
            }
            //扫描路径,获取所有的class
            PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
            Resource[] resources = resolver.getResources(path);
            for (Resource res : resources) {
                //获取类名
                String clsName = new SimpleMetadataReaderFactory().getMetadataReader(res).getClassMetadata().getClassName();
                //获取类对象
                Class thisClass = Class.forName(clsName);

                //判断当前类是否被Component注解标注
                Component component = (Component) thisClass.getAnnotation(Component.class);
                if (component != null) //说明该类被Component注解标注
                {
                    Constructor c = thisClass.getConstructor();
                    Object o = c.newInstance(); //创建该类的实例
                    beanFactory.put(o.getClass().getSimpleName(), o);//加入beanFactory中
                }

                //处理@Autowired注解
                for (Object bean : beanFactory.values()) { //查看beanFactory中所有的bean
                    Class beanClass = bean.getClass();
                    Field[] beanFields = beanClass.getDeclaredFields(); //获取bean的fields
                    for (Field field : beanFields) {
                        if (field.isAnnotationPresent(Autowired.class)) { //如果被Autowired注解标注
                            Object beanD = beanFactory.get(field.getType().getSimpleName()); //获取beanFactory中的bean
                            field.setAccessible(true); //关闭安全检查
                            field.set(bean, beanD); //用beanFactory中的bean来代替当前bean
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("没有ComponentScan注解");
            System.exit(-1);
        }
    }

    //getBean方法,获取某个bean(通过class)
    @Override
    public Object getBean(Class beanClass) {
        return beanFactory.get(beanClass.getSimpleName());
    }

}

3.5 测试类

public class test {
    public static void main(String[] args) throws InstantiationException, IllegalAccessException {
        //创建IOC容器,传入的参数是当前的配置类
        //因此可以根据@Configuration和@ComponentScan("org.example")来走接下来的逻辑
        AnnotationApplicationContext context = new AnnotationApplicationContext(MyConfig.class);
        //从容器中获得user对象
        User user = (User) context.getBean(User.class);
        //执行对象方法
        //可以看到通过user对象执行了group类的方法getBeanName()
        //但user类中,我们并没有实例化group对象,可以看到是通过注解来实现的
        user.print();
    }
}

4.实验结果

成功在user类中调用了group类的方法;

5.源码

上面的代码是完整的,下附工程文件:

https://download.csdn.net/download/qq_51235856/87944324

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

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

相关文章

如何写好一份企业直播主题策划?

写一份好的直播主题策划&#xff0c;需要考虑包括目标受众、目的、内容、形式、互动等&#xff0c;下面是写企业直播主题策划的一些关注点&#xff0c;希望能帮到您。 定位您直播的目标受众 明确你的直播主题适合的目标受众是谁&#xff0c;他们的兴趣、需求和期望是什么。了解…

OAuth2,jwt,springsecurity之间的区别和联系

OAuth 2.0、JWT (JSON Web Token) 和 Spring Security 是安全相关的概念和技术&#xff0c;它们有着不同的功能和用途。 OAuth 2.0&#xff08;开放授权&#xff09;&#xff1a; OAuth 2.0 是一种授权框架&#xff0c;用于授权第三方应用程序访问用户资源&#xff0c;而无需共…

【OpenCV DNN】Flask 视频监控目标检测教程 10

欢迎关注『OpenCV DNN Youcans』系列&#xff0c;持续更新中 【OpenCV DNN】Flask 视频监控目标检测教程 10 3.10 OpenCV DNNFlask实时监控目标检测1、加载MobileNet SSD模型2、导入分类名称文件3、处理视频帧进行目标检测4、新建一个Flask项目5、Python 程序文件6、视频流的网…

linux系统addr ip以及ifconfig查询不到ip地址解决方法,没有ens33

先看使用情况 网上一堆垃圾博文解决方案都是你抄我我抄你&#xff0c;一点用没有&#xff0c;都说使用 vi /etc/sysconfig/network-scripts/ifcfg-ens33 来更改配置ONBOOT为yes&#xff0c;改个屁&#xff0c;给你们看看我目前的配置&#xff0c;劳资本身就是yes&#xff0c;还…

Elasticsearch 基本使用(二)简单查询 嵌套查询

查询数据 简单查询按id查询单条记录查询所有数据设置排序filter 过滤查询数组内的值查询 嵌套查询查询一个外层字段 内的嵌套字段查询多个字段&#xff0c;其中有嵌套字段 简单查询 按id查询单条记录 GET bank/_doc/1查询所有数据 默认只查询10条记录 GET bank/_search {&q…

Linux任务调度、磁盘分区、挂载

一、任务调度介绍 任务调度是指系统在某个时间执行的特定的命令或程序 任务调度分为两类&#xff1a; 1.系统工作&#xff1a;有些重要的工作必须周而复始的执行&#xff0c;比如病毒扫描 2&#xff0c;个别用户工作&#xff1a;个别用户可能希望执行某些程序&#xff0c;比如…

canvas自定义绘制顺序解决遮挡问题

canvas自定义绘制顺序解决遮挡问题 1. 问题场景2. 解决思路3. 实现代码 1. 问题场景 使用canvas绘制进行要素叠加时&#xff0c;往往会出现不是按照先画的在下面&#xff0c;后画的在最上面这样的顺序进行叠加显示。原因就是由于图片大小不同导致绘制或加载的时间不一样&#…

合宙Air724UG Cat.1模块硬件设计指南--LCD专用SPI接口

概述 Air724UG支持一路LCD专用SPI接口&#xff0c;用于驱动SPI LCD屏幕&#xff0c;不能作为通用SPI使用 特性&#xff1a; 最大支持320240分辨率&#xff0c;30帧 内置图像处理单元GOUDA 支持格式&#xff1a; YUV4 : 2 : 0 ;YUV4 : 2 : 2;RGB565; ARGB8888 目前只支持4线8bi…

MySQL - 第8节 - MySQL复合查询

1.基本查询回顾 准备测试表&#xff1a; • 下面给出三张表&#xff0c;分别是员工表&#xff08;emp&#xff09;、部门表&#xff08;dept&#xff09;和工资等级表&#xff08;salgrade&#xff09;。 • 后续所要进行的查询操作都将以这三张表作为数据源&#xff0c;包括基…

【论文笔记】BEIT:BERT PRE-TRAINING OF IMAGE TRANSFORMERS

GitHub 1.介绍 1.1 挑战 视觉转换器的输入单元&#xff0c;即图像补丁&#xff0c;没有预先存在的词汇。预测遮罩面片的原始像素往往会在预训练短程依赖性和高频细节上浪费建模能力 1.2 回顾一下Bert的基本架构和流程 输入编码&#xff1a;通过tokenizer将输入的文本中的每…

gmpy2

简介 gmpy2是一个Python扩展模块&#xff0c;是对GMP的封装&#xff0c;它的前身是gmpy。 GMP&#xff08;即GNU高精度算术运算库&#xff09;&#xff0c;它是一个开源的高精度运算库&#xff0c;其中不但有普通的整数、实数、浮点数的高精度运算&#xff0c;还有随机数生成&a…

【promptulate专栏】使用ChatGPT和XMind快速构建思维导图

本文节选自笔者博客&#xff1a;https://www.blog.zeeland.cn/archives/ao302950h3j &#x1f496; 作者简介&#xff1a;大家好&#xff0c;我是Zeeland&#xff0c;全栈领域优质创作者。&#x1f4dd; CSDN主页&#xff1a;Zeeland&#x1f525;&#x1f4e3; 我的博客&#…

Go语言基础:标识符、关键字、变量、常量、iota

一、标识符 在编程语言中标识符就是程序员定义的具有特殊意义的词&#xff0c;比如变量名、常量名、函数名等等。 Go语言中标识符由字母数字和_(下划线&#xff09;组成&#xff0c;并且只能以字母和_开头。 二、关键字 关键字是指编程语言中预先定义好的具有特殊含义的标识符…

ThreadPoolExecutor的应用和源码分析

前面描述的线程池的创建方式大都是Executors类中创建出来&#xff0c;基于ThreadPoolExecutor去new出来实现的。 我们为什么要自定义线程池 在线程池ThreadPoolExecutor中提供了7个参数&#xff0c;都作为非常核心的属性&#xff0c;在线程池去执行任务的时候&#xff0c;每个…

【Docker】容器化和虚拟化基础

Docker发展史 Jail(监狱)时代 1979 年 贝尔实验室发明 chroot chroot的设计原理是&#xff1a;把一个进程的文件系统隔离起来。 ​ chroot 系统调用可以将进程及其子进程的根目录更改为文件系统中的新位置。隔离以后&#xff0c;该进程无法访问到外面的文件&#xff0c;因此这…

管理类联考——逻辑——知识篇——论证推理——二、加强——haimian

考点分析 加强 年度 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023题量213325356 主要问法 以下哪项如果为真&#xff0c;最能加强上述结论的说服力?以下哪项如果为真&#xff0c;最能支持题干的论证? 解题思路 阅读问题&#xff0c;确定是否为加强题型&…

进程参数编程

问题 execve(...) 的参数分别是什么&#xff1f;有什么意义&#xff1f; 第一个参数是程序路径&#xff0c;第二个参数是进程参数&#xff0c;第三个参数是环境变量 再论 execve(...) main 函数 (默认进程入口) int main(int argc, char* argv[]) argc - 命令行参数个数argv…

java——jdbc编程

文章目录 JDBC的概念JDBC的常用APIJDBC示例代码PreparedStatementCallableStatement JDBC&#xff08;Java Database Connectivity&#xff09;是Java的一种数据库访问标准&#xff0c;它提供了一套API&#xff0c;使得我们可以通过Java程序来访问和操作各种关系型数据库。 下面…

从零开始手搓一个STM32与机智云的小项目——GPIO模拟时序控制外设2

文章目录 前言模块简介硬件介绍硬件连接通信时序DHT11的数据帧格式信号时序1. 起始信号2.应答信号(响应信号)3.接收数据0与14.获取数据5结束信号 输入输出切换实际效果 总结 前言 在上一篇中介绍了&#xff0c;使用GPIO模拟WS2812B的控制时序来实现对RGB灯的控制&#xff0c;本…

【开源与项目实战:开源实战】84 | 开源实战四(上):剖析Spring框架中蕴含的经典设计思想或原则

在 Java 世界里&#xff0c;Spring 框架已经几乎成为项目开发的必备框架。作为如此优秀和受欢迎的开源项目&#xff0c;它是我们源码阅读的首选材料之一&#xff0c;不管是设计思想&#xff0c;还是代码实现&#xff0c;都有很多值得我们学习的地方。接下来&#xff0c;我们就详…