手动开发--简单的 Spring 基于注解配置的程序

news2024/11/15 8:06:26

目录

手动开发--简单的 Spring 基于注解配置的程序

需求说明

思路分析+程序结构

2) 程序框架图

● 应用实例

创建ComponentScan.java注解

创建WyxSpringConfig

创建WyxSpringApplicationContext

 作用

注意

获取全类名的步骤

Class.forName和Class.loadClass的区别


手动开发--简单的 Spring 基于注解配置的程序

需求说明

自己写一个简单的Spring 容器 , 通过读取类的解(@Component @Controller @Service @Reponsitory),将对象注入到 IOC 容器

也就是说,不使用 Spring 原生框架,我们自己使用 IO+Annotaion+反射+集合 技术实现

思路分析+程序结构

1) 我们使用注解方式完成, 这里不用 xml 来配置

2) 程序框架图

 

● 应用实例

1. 手动实现注解的方式来配置 Controller / Service / Respository / Component

2. 我们使用自定义注解来完成.

创建ComponentScan.java注解

作用

可以指定要扫描包 

类似于component--scan 扫描

1. @Target(ElementType.TYPE)指定我们的ComponentScan注解可以修饰 Type程序元素
2. @Retention(RetentionPolicy.RUNTIME) 指定ComponentScan注解 保留范围
3. String value() default ""; 表示ComponentScan 可以传入 value

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ComponentScan {
    String value() default "";
}

创建WyxSpringConfig

这是一个配置类, 作用类似我们原生spring的 beans.xml 容器配置文件

@ComponentScan(value = "com.spring.component")
public class WyxSpringConfig {
}

创建WyxSpringApplicationContext

 作用

Spring容器

通过WyxSpringConfig 进行初始化

在这个容器中会创建ComponentScan注解指定的包并生成对象

public class WyxSpringApplicationContext {
    private Class configClass;
    //ioc我存放的就是通过反射创建的对象(基于注解方式)
    private final ConcurrentHashMap<String, Object> ioc =
            new ConcurrentHashMap<>();

    //构造器
    public WyxSpringApplicationContext(Class configClass) {

        this.configClass = configClass;
        //System.out.println("this.configClass=" + this.configClass);
        //获取要扫描的包
        //1. 先得到WyxpringConfig配置的的@ComponentScan(value = "com.spring.component")
        ComponentScan componentScan =
                (ComponentScan) this.configClass.getDeclaredAnnotation(ComponentScan.class);
        //2. 通过componentScan的value=> 即要扫描的包
        String path = componentScan.value();
        System.out.println("要扫描的包= " + path);

        //得到要扫描的包下的所有资源(类 .class)
        //1.得到类的加载器
        ClassLoader classLoader =
                WyxApplicationContext.class.getClassLoader();
        //2. 通过类的加载器获取到要扫描的包的资源 url=》类似一个路径
        path = path.replace(".", "/");//一定要把. 替换成 /
        URL resource =
                classLoader.getResource(path);
        System.out.println("resource=" + resource);
        //3. 将要加载的资源(.class) 路径下的文件进行遍历=>io
        File file = new File(resource.getFile());
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File f : files) {
                System.out.println("=====================");
                System.out.println("=" + f.getAbsolutePath());
                //D:\xxx_spring\spring\out\production\spring\com\spring\component\UserService.class
                //获取到 com.spring.component.UserService
                String fileAbsolutePath = f.getAbsolutePath();

                //这里我们只处理.class文件
                if (fileAbsolutePath.endsWith(".class")) {

                    //1. 获取到类名
                    String className =
                            fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\") + 1, fileAbsolutePath.indexOf(".class"));
                    //System.out.println("className=" + className);
                    //2. 获取类的完整的路径(全类名)
                    //解读 path.replace("/",".") => com.spring.component.
                    String classFullName = path.replace("/", ".") + "." + className;
                    //System.out.println("classFullName=" + classFullName);

                    //3. 判断该类是不是需要注入容器, 就看该类是不是有注解 @Component @Service..
                    try {
                        //这时,我们就得到该类的Class对象
                        //Class clazz = Class.forName(classFullName)
                        //说一下
                        //1. Class clazz = Class.forName(classFullName) 可以反射加载类
                        //2. classLoader.loadClass(classFullName); 可以反射类的Class
                        //3. 区别是 : 上面方式后调用来类的静态方法, 下面方法不会
                        //4. aClass.isAnnotationPresent(Component.class) 判断该类是否有 @Component
                        Class<?> aClass = classLoader.loadClass(classFullName);
                        if (aClass.isAnnotationPresent(Component.class) ||
                                aClass.isAnnotationPresent(Controller.class) ||
                                aClass.isAnnotationPresent(Service.class) ||
                                aClass.isAnnotationPresent(Repository.class)) {

                            //这里演示一个Component注解指定value,分配id
                            //就是演示了一下机制.
                            if(aClass.isAnnotationPresent(Component.class)) {
                                //获取到该注解
                                Component component = aClass.getDeclaredAnnotation(Component.class);
                                String id = component.value();
                                if(!"".endsWith(id)) {
                                    className = id;//替换
                                }
                            }

                            //这时就可以反射对象,并放入到容器中
                            Class<?> clazz = Class.forName(classFullName);
                            Object instance = clazz.newInstance();
                            //放入到容器中, 将类名的首字母小写作为id
                            //StringUtils

                            ioc.put(StringUtils.uncapitalize(className) , instance);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }

    }

    //编写方法返回对容器中对象
    public Object getBean(String name) {
        return ioc.get(name);
    }
}

注意

获取全类名的步骤

1. 获取到类名

StringclassName=

fileAbsolutePath.substring

(fileAbsolutePath.lastIndexOf("\\")+1.fileAbsolutePath.indexOf(".class"));

2. 获取类的完整的路径(全类名)

解读 path.replace("/",".") => com.spring.component.
String classFullName = path.replace("/", ".") + "." + className;

3. 判断该类是不是需要注入容器, 就看该类是不是有注解 @Component @Service..

Class.forName和Class.loadClass的区别

1. Class clazz = Class.forName(classFullName) 可以反射加载类
2. classLoader.loadClass(classFullName); 可以反射类的Class
3. 区别是 : 上面方式后会调用 类的静态方法, 下面方法不会调用

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

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

相关文章

c++ 11标准模板(STL) std::vector (二)

定义于头文件 <vector> template< class T, class Allocator std::allocator<T> > class vector;(1)namespace pmr { template <class T> using vector std::vector<T, std::pmr::polymorphic_allocator<T>>; }(2)(C17…

马上五一了,带大家玩一下五子棋——C语言

五一祝福 因为这篇博文实在五一这天完成的&#xff0c;所以呢&#xff0c;在介绍五子棋之前&#xff0c;先祝各位支持小白的大佬都五一快乐&#xff01; 花了点时间下了个“五一快乐”的五子棋&#xff0c;哈哈哈哈哈哈&#xff0c;还不太熟练&#xff0c;所以写的有点丑&…

常用 Composition API【VUE3】

二、常用 Composition API 7. 计算属性与监视 7.1 computed函数 与Vue2.x中computed配置功能一致写法 <template><h1>一个人的信息</h1>姓&#xff1a;<input type"text" v-model"person.firstName"><br><br>名&a…

【ROS 开发神器 Visual Studio Code 的安装和设置】

【ROS 开发神器 Visual Studio Code 的安装和设置】 1. Visual Studio Code的安装1.1 点击deb文件下载1.2 安装VScode1.3 启动软件1.4 添加收藏夹 2. 导入工作空间2.1 熟悉Vscode基本界面2.2 添加工作空间 3. 安装简体中文语言4. 安装ROS插件5. 安装CMake插件6. 安装括号颜色插…

SpringBoot 中的加密模块

Spring Boot 是一款流行的 Java 开发框架&#xff0c;它提供了多种加密模块&#xff0c;用于保护数据的安全性。本文将介绍 Spring Boot 中的加密模块&#xff0c;包括对称加密、非对称加密和哈希加密等&#xff0c;同时还会提供相应的代码示例。 一、对称加密 对称加密是一种…

改进YOLOv8 | 即插即用篇 | 全维动态卷积 |《 OMNI-DIMENSIONAL DYNAMIC CONVOLUTION》

单个静态卷积核是现代卷积神经网络(CNNs)的常见训练范式。然而,最近的动态卷积研究表明,学习加权为其输入依赖注意力的n个卷积核的线性组合可以显著提高轻量级CNNs的准确性,同时保持高效的推理。然而,我们观察到现有的作品通过卷积核空间的一个维度(关于卷积核数量)赋予…

关于密码学的进一步答疑:SSL和TLS的区别、CA和CT的关系

《密码学&#xff1a;一文读懂常用加密技术原理及其逻辑与应用方法》一文一经发布后&#xff0c;后台收到了许多私信&#xff0c;承蒙喜爱&#xff0c;这篇文章将主要对后台收到的高频问题予以统一回应。 问题一: 在讨论加密解密的过程中&#xff0c;常常在同一语境下同时出现S…

设计模式之原型模式(深拷贝浅拷贝)

目录 1、什么是原型模式 2、前置知识&#xff08;深拷贝&浅拷贝&#xff09; 2.1 浅拷贝 2.2 深拷贝 3、代码实现 3.1 通过Object中的clone方法实现浅拷贝 3.2 通过对象流来实现深拷贝 4、原型模式总结 4.1 优缺点 4.2 使用场景 4.3 对比直接new对象有何不同 1、…

如何使用递归函数实现Excel列号转换列标

在Excel中&#xff0c;列标与列号转换是VBA开发过程中经常用到的功能&#xff0c;下面这篇博客为大家解释了多种方法。 【Excel列标与列号转换】 那么这篇博文的核心是“递归过程”&#xff0c;实现这个功能并不是必须使用递归过程&#xff0c;但是这也不失为一种实现方法&am…

【Android入门到项目实战-- 8.2】—— 使用HTTP协议访问网络

目录 一、使用HttpURLConnection 1、使用Android的HttpURLConnection步骤 1&#xff09;获取HttpURLConnection实例 2)设置HTTP请求使用的方法 3)定制HTTP请求&#xff0c;如连接超时、读取超时的毫秒数 4)调用getInputStream()方法获取返回的输入流 5)关闭HTTP连接 2、…

NXP - LPC1769与LPC1768的区别

文章目录 NXP - LPC1769与LPC1768的区别概述笔记General description验证结论END NXP - LPC1769与LPC1768的区别 概述 openpnp设备用到了冰沙主板. 冰沙主板的主控MCU用到了LPC1769, 想着研究一下. 订了OM13085UL, 遥遥无期… 买了LPC MCU的书, 里面提到了书的作者的网店, 居…

python+vue精品课程建设制作django服务网站系统

功能介绍通篇文章的撰写基础是实际的应用需要&#xff0c;然后在架构系统之前全面复习大学所修习的相关知识以及网络提供的技术应用教程&#xff0c;以视频建设制作服务的实际应用需要出发&#xff0c;架构系统来改善现视频建设制作服务工作流程繁琐等问题。不仅如此以操作者的…

kotlin在鸿蒙开发中的实践

先说一说kotlin 我们知道&#xff1a; kotlin目前是安卓首选的编程语言。 安卓逐渐抛弃java&#xff0c;拥抱kotlin这是大的趋势。 kotlin的最大优点就是与java的互操作性。 kotlin编译的产物和java一样是bytecode(不抬杠&#xff0c;本文只说面向jvm的kotlin)。 kotlin是一…

Cadence基础操作:Schematic编辑

本文转载自B站up主:_WithB&#xff0c;原文链接如下&#xff1a;https://www.bilibili.com/read/cv20414466 鼠标 左键单击 –> 选中或确定操作 按住左键 –> 选中区域内所有组件 左键双击&#xff0c;可以选择以特定操作模式和窗口类型进入对应组件的下一层一般我是ed…

Winform从入门到精通(36)—ColorDialog(史上最全)更新中

前言 当我们需要设置某个控件的颜色时,并且需要弹出一个可以选择颜色的对话框时,这时候就需要使用ColorDialog 一、属性 1、AllowFullOpen 该属性用于启用或者禁用“自定义颜色按钮”,该属性为true时,可以自定义颜色 2、AnyColor 实际测试该属性没什么作用 3、Colo…

请求与相应

从容器到Servlet 前面我们介绍了JSP的内置对象和Servlet的相关知识&#xff0c; 以及如何部署和开发一个Servlet。但是&#xff0c; 并没有详细介绍如何将Servlet与JSP结合起来使用。Web容器是JSP唯一可以识别的HTTP服务器&#xff0c; 所以必须了解Web容器如何生成请求和响应…

来上海一个月的记录、思考和感悟

作者 | gongyouliu 编辑 | gongyouliu 从4月3号早上来上海&#xff0c;到今天差不多整整一个月了&#xff0c;也是自己正式从杭州离职创业&#xff08;我更愿意称之为自由职业者&#xff0c;毕竟我没有招聘全职员工&#xff0c;有两个朋友业余时间在帮我&#xff09;的第一个月…

SAP UI5 之Bootstrap(引导)笔记二

文章目录 Setting up Visual Studio Code for UI5 development1.0 官网 Walkthrough学习-Bootstrap 引导加载1.0.1 在 index.html中新增script标签1.0.2 在webapp 下面新增index.js文件1.0.3启动UI5的服务 Setting up Visual Studio Code for UI5 development 学习链接 Setti…

如何正确部署Redisearch和Rejson(附*.so文件免费下载)

1 缘起 项目需要。 最近的一个项目需要做文本搜索,技术选型:Redis的两个组件Redisearch和ReJSON。 Redisearch和ReJSON是Redis的两个组件: RediSearch为Redis提供查询、二次索引和全文搜索。使用RediSearch,首先要在Redis数据上声明索引。然后使用RediSearch查询语言来查…

【电子通识】颜色的困惑:什么是国际通用Panone(潘通)

Pantone 是世界知名的色彩权威机构&#xff0c;也是色彩系统的供应商&#xff0c;为许多行业提供专业色彩选择。在 Pantone 之前&#xff0c;每个印刷公司都有自己的色彩指南。比如都是“黄色”&#xff0c;但由于印刷方式有所不同&#xff08;具体取决于每个油墨公司如何解释该…