IoC原理

Spring框架的IOC是基于Java反射机制实现的,那具体怎么实现的,下面研究一下

反射

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

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

package com.atguigu.reflect;

public class Car {

    //属性
    private String name;
    private int age;
    private String color;

    //无参数构造
    public Car() {
    }

    //有参数构造
    public Car(String name, int age, String color) {
        this.name = name;
        this.age = age;
        this.color = color;
    }

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

    //get和set方法
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getColor() {
        return color;
    }
    public void setColor(String color) {
        this.color = color;
    }

    @Override
    public String toString() {
        return "Car{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", color='" + color + '\'' +
                '}';
    }
}
package com.atguigu.reflect;

import org.junit.jupiter.api.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class TestCar {

    //1、获取Class对象多种方式
    @Test
    public void test01() throws Exception {
        //1 类名.class
        Class clazz1 = Car.class;

        //2 对象.getClass()
        Class clazz2 = new Car().getClass();

        //3 Class.forName("全路径")
        Class clazz3 = Class.forName("com.atguigu.reflect.Car");
        //实例化
        Car car = (Car)clazz3.getConstructor().newInstance();
        System.out.println(car);
    }
}
package com.atguigu.reflect;

import org.junit.jupiter.api.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class TestCar {

    //1、获取Class对象多种方式
    @Test
    public void test01() throws Exception {
        //1 类名.class
        Class clazz1 = Car.class;

        //2 对象.getClass()
        Class clazz2 = new Car().getClass();

        //3 Class.forName("全路径")
        Class clazz3 = Class.forName("com.atguigu.reflect.Car");

        int length = clazz1.getInterfaces().length;
        System.out.println(length);
        //实例化
        Car car = (Car)clazz3.getConstructor().newInstance();
        System.out.println(car);
    }

    //2、获取构造方法
    @Test
    public void test02() throws Exception {
        Class clazz = Car.class;
        //获取所有构造
        // getConstructors()获取所有public的构造方法
//        Constructor[] constructors = clazz.getConstructors();
         //getDeclaredConstructors()获取所有的构造方法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("捷达", 15, "白色");
        System.out.println(car2);
    }

    //3、获取属性
    @Test
    public void test03() throws Exception {
        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(car);
            }
            System.out.println(field.getName());
        }
    }

    //4、获取方法
    @Test
    public void test04() throws Exception {
        Car car = new Car("奔驰",10,"黑色");
        Class clazz = car.getClass();
        //1 public方法
        Method[] methods = clazz.getMethods();
        for (Method m1:methods) {
            //System.out.println(m1.getName());
            //执行方法 toString
            if(m1.getName().equals("toString")) {
                String invoke = (String)m1.invoke(car);
                //System.out.println("toString执行了:"+invoke);
            }
        }

        //2 private方法
        Method[] methodsAll = clazz.getDeclaredMethods();
        for (Method m:methodsAll) {
            //执行方法 run
            if(m.getName().equals("run")) {
                m.setAccessible(true);
                m.invoke(car);
            }
        }
    }
}

实现Spring中的IoC 

一:创建子模块 

二:创建测试类

package com.atguigu.service;

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

import com.atguigu.service.UserService;

public class UserServiceImpl  implements UserService {
    @Di
    private UserDao userDao;

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

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

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


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

三:创建两个注解(@Bean和@Di) 

package com.atguigu.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.atguigu.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 {
}

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

package com.atguigu.bean;

public interface ApplicationContext {

    Object getBean(Class clazz);
}

五:实现bean容器接口,返回对象,根据包扫描规则加载bean 

package com.atguigu.bean;

import com.atguigu.anno.Bean;
import com.atguigu.anno.Di;

import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
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) {
        // com.atguigu
        try {
            //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));
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        //属性注入
        loadDi();
    }



    //包扫描过程,实例化
    private void loadBean(File file) throws Exception {
        //1 判断当前是否文件夹
        if(file.isDirectory()) {
            //2 获取文件夹里面所有内容
            File[] childrenFiles = file.listFiles();
            
            //3 判断文件夹里面为空,直接返回
            if(childrenFiles == null || childrenFiles.length == 0) {
                return;
            }

            //4 如果文件夹里面不为空,遍历文件夹所有内容
            for(File child : childrenFiles) {
                //4.1 遍历得到每个File对象,继续判断,如果还是文件夹,递归
                if(child.isDirectory()) {
                    //递归
                    loadBean(child);
                } else {
                    //4.2 遍历得到File对象不是文件夹,是文件,
                    //4.3 得到包路径+类名称部分-字符串截取
                    String pathWithClass =
                            child.getAbsolutePath().substring(rootPath.length() - 1);

                    //4.4 判断当前文件类型是否.class
                    if(pathWithClass.contains(".class")) {

                        //4.5 如果是.class类型,把路径\替换成.  把.class去掉
                        // com.atguigu.service.UserServiceImpl
                        String allName = pathWithClass.replaceAll("\\\\", ".")
                                .replace(".class", "");

                        //4.6 判断类上面是否有注解 @Bean,如果有实例化过程
                        //4.6.1 获取类的Class
                        Class<?> clazz = Class.forName(allName);
                        //4.6.2 判断不是接口
                        if(!clazz.isInterface()) {
                            //4.6.3 判断类上面是否有注解 @Bean
                            Bean annotation = clazz.getAnnotation(Bean.class);
                            if(annotation != null) {
                                //4.6.4 实例化
                                Object instance = clazz.getConstructor().newInstance();
                                //4.7 把对象实例化之后,放到map集合beanFactory
                                //4.7.1 判断当前类如果有接口,让接口class作为map的key
                                if(clazz.getInterfaces().length>0) {
                                    beanFactory.put(clazz.getInterfaces()[0],instance);
                                } else {
                                    beanFactory.put(clazz,instance);
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    //属性注入
    private void loadDi() {
        //实例化对象在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注解,把对象进行设置(注入)
                    try {
                        field.set(obj,beanFactory.get(field.getType()));
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
    }
}

最终测试
 

package com.atguigu;

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

public class TestUser {

    public static void main(String[] args) {
        ApplicationContext context =
                new AnnotationApplicationContext("com.atguigu");
        UserService userService = (UserService)context.getBean(UserService.class);
        System.out.println(userService);
        userService.add();
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

JenkinsGitLab完成自动化构建部署

关于GitLab安装:GitLab安装-CSDN博客 Docker中安装GitLab:Docker下安装GitLab-CSDN博客 安装JenKins Jenkins官网:Jenkins 中文版:Jenkins 安装时候中文页面的war包下不来 在英文页面 记得装JDK8以上 JenKins使用java写的 运行JenKins需要JDK环境 我这里已经装好了 将下…

网神 SecGate 3600 防火墙 route_ispinfo_import_save 文件上传漏洞

免责声明&#xff1a;文章来源互联网收集整理&#xff0c;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;所产生的一切不良后果与文章作者无关。该…

问题:重热现象可使多级汽轮机的理想焓降增加,重热系数越大,多级汽轮机的内效率就越低。 #学习方法#微信#媒体

问题&#xff1a;重热现象可使多级汽轮机的理想焓降增加&#xff0c;重热系数越大&#xff0c;多级汽轮机的内效率就越低。 参考答案如图所示

代码随想录算法训练营第二十五天 |216.组合总和III,17.电话号码的字母组合(已补充)

剪枝操作讲解&#xff1a;&#xff08;已观看&#xff09; 带你学透回溯算法-组合问题的剪枝操作&#xff08;对应力扣题目&#xff1a;77.组合&#xff09;| 回溯法精讲&#xff01;_哔哩哔哩_bilibili 216.组合总和III&#xff08;已观看&#xff09; 1、题目链接&#xf…

Pinia介绍和使用

1. pinia是什么 Pinia 是一个基于 Vue.js 的状态管理库&#xff0c;用于管理应用程序的数据。它提供了一种简单、直观且可扩展的方式来组织和访问应用程序的状态&#xff0c;下面是详细介绍 基于 Vue 3&#xff1a;Pinia 是专门为 Vue 3 开发的状态管理库&#xff0c;充分利用…

推理系统学习笔记

一些学习资料 最近对MLsys比较感兴趣&#xff0c;遂找些资料开始学习一下 https://fazzie-key.cool/2023/02/21/MLsys/https://qiankunli.github.io/2023/12/16/llm_inference.htmlhttps://dlsyscourse.orghttps://github.com/chenzomi12/DeepLearningSystem/tree/main/04Infe…

算法——前缀和算法

1. 什么是前缀和算法 前缀和算法&#xff08;Prefix Sum&#xff09;是一种用于快速计算数组元素之和的技术。它通过预先计算数组中每个位置前所有元素的累加和&#xff0c;将这些部分和存储在一个新的数组中&#xff0c;从而在需要计算某个区间的和时&#xff0c;可以通过简单…

《Git 简易速速上手小册》第3章:分支管理(2024 最新版)

文章目录 3.1 创建与合并分支3.1.1 基础知识讲解3.1.2 重点案例&#xff1a;为 Python 项目添加新功能3.1.3 拓展案例 1&#xff1a;使用 Pull Requests (PRs) 在团队中合作3.1.4 拓展案例 2&#xff1a;解决合并冲突 3.2 分支策略的最佳实践3.2.1 基础知识讲解3.2.2 重点案例&…

【动态规划】【前缀和】【数学】2338. 统计理想数组的数目

作者推荐 【动态规划】【前缀和】【C算法】LCP 57. 打地鼠 本文涉及知识点 动态规划汇总 C算法&#xff1a;前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频 LeetCode:2338. 统计理想数组的数目 给你两个整数 n 和 maxValue &#xff0c;用于描述一个 理想…

手把手教你玩转蓝牙模块(原理+驱动)

head: title: 手把手教你玩转蓝牙模块&#xff08;原理驱动&#xff09; description: 手把手教你玩转蓝牙模块&#xff08;原理驱动&#xff09; 作为嵌入式开发工程师&#xff0c;蓝牙模块怎能少呢&#xff1f; 蓝牙模块广泛应用在各种电子器件&#xff0c;比如手机、蓝牙耳…

2 月 3 日算法练习-数论

简单数论 思路&#xff1a;各个相邻数的差值求最大公约数得到 d&#xff0c;然后就能求出最少项数。 c17用gcd&#xff0c;c11 用_gcd #include<bits/stdc.h> using namespace std; using ll long long; const int N 1e5 10; ll a[N]; int n; int main( ){cin>>…

【网站项目】030小学生课外知识学习网站

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

Vue3.3新特新和Vue3-Pinia

文章目录 1.Vue3.3新特性 - defineOptionsVue3.3新特性 - defineModel3.Pinia快速入门4.手动添加Pinia到Vue项目5.Vue3 - Pinia的基本语法6.action的异步实现7.Vue3-Pinia-storeToRefs方法8.Pinia持久化插件安装用法 1.Vue3.3新特性 - defineOptions 背景说明 有<script se…

ELAdmin后台启动

版本选择 ELAdmin官网地址&#xff1a;https://eladmin.vip/ 有 JPA 和 MyBatis两个版本&#xff0c;之前只有 JPA&#xff0c;考虑到国内复杂的业务情况增加了 MyBatis 版本。我最终也选择了使用 MyBatis版本。 代码 仓库地址&#xff1a;https://gitee.com/elunez/eladmin…

Python环境下基于辛几何模态分解的信号分解方法

基于辛几何的分析方法是一种保护相空间几何结构的新型分析方法&#xff0c;主要用于求解动力学和控制系统中矩阵或Hamilton矩阵的特征值问题&#xff0c;用来解决在动力学和控制系统理论的2n2n矩阵或哈密顿矩阵的特征值问题&#xff0c;已应用到结构损伤信号、奇异微分方程等系…

【C#】.net core 6.0 创建默认Web应用,以及默认结构讲解,适合初学者

欢迎来到《小5讲堂》 大家好&#xff0c;我是全栈小5。 这是《C#》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解&#xff0c; 特别是针对知识点的概念进行叙说&#xff0c;大部分文章将会对这些概念进行实际例子验证&#xff0c;以此达到加深对知识点的理解和掌握。…

Zoho Mail 2023:回顾过去,展望未来

当我们告别又一个非凡的一年时&#xff0c;我们想回顾一下Zoho Mail如何融合传统与创新。我们迎来了成立15周年&#xff0c;这是一个由客户、合作伙伴和我们的敬业团队共同庆祝的里程碑。与我们一起回顾这段旅程&#xff0c;探索定义Zoho Mail历史篇章的敏捷性、精确性和创新性…

HiveSQL——条件判断语句嵌套windows子句的应用

注&#xff1a;参考文章&#xff1a; SQL条件判断语句嵌套window子句的应用【易错点】--HiveSql面试题25_sql剁成嵌套判断-CSDN博客文章浏览阅读920次&#xff0c;点赞4次&#xff0c;收藏4次。0 需求分析需求&#xff1a;表如下user_idgood_namegoods_typerk1hadoop1011hive1…

MQTT 服务器(emqx)搭建及使用

推荐阅读&#xff1a; MQTT 服务器(emqx)搭建及使用 - 哔哩哔哩 (bilibili.com) 一、EMQX 服务器搭建 1、下载EMQX https://www.emqx.com/zh/try?productbroker 官方中文手册&#xff1a; EMQX Docs 2、安装使用 1、该软件为绿色免安装版本&#xff0c;解压缩后即安装完…

spring boot打完jar包后使用命令行启动,提示xxx.jar 中没有主清单属性

在对springBoot接口中间件开发完毕后&#xff0c;本地启动没有任何问题&#xff0c;在使用package命令打包也没异常&#xff0c;打完包后使用命令行&#xff1a;java -jar xxx.jar启动发现报异常&#xff1a;xxx.jar 中没有主清单属性&#xff0c;具体解决方法如下&#xff1a;…