ssm框架之spring:浅聊IOC

news2024/11/16 13:59:02

IOC

前面体验了spring,不过其运用了IOC,至于IOC( Inverse Of Controll—控制反转 )

看一下百度百科解释:

控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。

从这个里面可以看出三个重要的词汇:控制,反转,依赖注入。

  • 控制:既然提到控制,那么就是谁控制谁,也就行前提需要有两个对象,不然哪里来的控制。

    一般创建对象的时候,通过new来创建一个对象。但是现在又有个问题了,IOC既然是控制,那么其如何生成对象呢?既然是解耦减少new来创建对象。前面对于例子初体验的时候,看出控制对象的时候没有通过new来创建对象。

  • 反转: 既然叫做反转,那自然就有转了,不然就没有反转一说了。当然没有正转这个词汇,简单说就是一般用法就是正转。

    比如一个对象控制另一个对象的时候,会在这个类中创建这个被调用的对象。

    class A{
        
    }
    class B{
         public static void main(String[] args) {
             // 所谓的正转调用 因为需要调用A的方法所以需要创建一个A的对象
             A a=new A();
     
         }
    }
    

    而反转却没有看见通过new来创建对象。详情看初体验的例子

    可以看出没有通过new来创建对象,那么如何创建对象呢?肯定是Spring帮我们创建了对象。其通过配置文件就创建了对象,而这个帮忙创建对象的好人就是被称为IOC容器。而IOC容器帮我们查找以及注入依赖对象,而作为操作者的对象只能被动的接受依赖对象。 所以可以看出不是手动去创建对象,而根据配置文件Spring通过IOC容器进行依赖注入,然后对对象进行创建,销毁。所以是说控制对象生存周期的不再是引用它的对象,而是 Spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被 Spring 控制,所以这叫控制反转。

  • 依赖注入(DI):依赖注入和IOC两者其实可以说IOC是一种编程思维,而依赖注入是具体实现这个编程思维的方式。

    其实最常用的两种注入方法是:set注入,构造注入,当然这个在spring中听过xml或者注解进行体现。

    通过依赖注入机制 只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。

    当然组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中

    当然依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。

其实在Spring中的IOC容器使用了工厂模式,以及反射,如果没有反射也就没有必要进行配置信息。

现在有两个问题了,Spring中是通过那个类进行处理这个配置信息的,毕竟配置信息可以是xml或者注解,而这个类必然要进行判断之后才能处理的。

创建bean容器

Spring的IOC容器就是IOC思想的一种实现,而在IOC容器(IOC容器存放着bean 所以被叫做 Spring bean容器)的创建,需要看一个接口BeanFactory,这个是创建Spring bean容器的根接口,这个不是我说的而是源码:

在这里插入图片描述

但是BeanFactory这个是Spring内部使用的接口,面向Spring本身,不是给开发人员用的。一般使用其子接口ApplicationContext,而这个接口在前面例子中很多体现,现在可以看下其关系:

在这里插入图片描述

常用的ApplicationContext实现类或接口:

类/接口描述
ClassPathXmlApplicationContext通过读取类路径下的XML格式配置文件创建的IOC容器对象
FileSystemXmlApplicationContext通过文件系统路径下XML格式配置文件创建的IOC容器对象
ConfigurableApplicationContextApplicationContext的子接口,包含了一些扩展方法比如close(),refresh等。
AnnotationConfigApplicationContext完全注解的时候,用来加载带有配置注解的类。
WebApplicationContext为web应用准备,是基于web环境开发创建IOC容器对象,并将对象存入ServletContext域中。

得到bean信息

其实这个需要一个接口:BeanDefinition (Definition的英文意思是解释,释义。 不得说母语英语真是友好,看名知其意,还是需要学英语的。)

然后看一下其源码是如果解释的:

在这里插入图片描述

翻译如下:

BeanDefinition 描述了一个实例信息,其拥有的属性只,构造方法中带有的参数以及具体实现去其它更多信息。

当然这个类加载信息,需要通过配置文件或者注解才可以,而这个配置文件或者注解也有一定的标准,不然呈现也不能读取这些配置的信息。具体源码就不再此篇聊了。

还有在spring中也不可能只有一个bean的信息,所以在spring中用一个BeanDefinitionMap进行保存信息。

可以用一个图来看一下这个IOC创建容器的大概过程:

在这里插入图片描述

图中还有缓存这个概念,毕竟生存的bean有的时候会被重复使用,如果调用某个bean的时候先判断是否被保存,如果有就直接调用,如果没有就在返回查看BeanDefinitionMap中需要的bean的配置信息。图中既然写了一级缓存那就是spring有多级缓存了,这个后面有机会再聊吧。

自己写一个依注释实现IOC注入的代码

代码的结构如下:

在这里插入图片描述

代码直接能用,可以复制在自己环境内就可以运行。而且每步带有注解。

  • 首先实现两个接口:Di和Bean

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Bean {
    
    }
    
    
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Di {
    //    无法使用Object作为注解参数
     String value();
    }
    
    
  • 自己定义一个容器接口:MyApplicationContext

//创建一个自己Spring容器的接口 这个将BeanFactory和ApplicationContext融为一个方便写不然需要写父接口和子接口
public interface MyApplicationContext {
    Object getBean(Class clazz);
}

  • 实现容器接口的类:MyAnnotionAoolicationContext

    public class MyAnnotionAoolicationContext implements MyApplicationContext {
        private static Annotation beanAnnotation;
        //    用一个map存储bean的信息 模仿 BeanDefinitionMap
        private static Map<Class, Object> MyBeanDefinitionMap = new HashMap<Class, Object>();
        //     作为一个加载扫描其下包或者类的根目录
        private static String rootFile;
    
        @Override
        public Object getBean(Class clazz) {
            return MyBeanDefinitionMap.get(clazz);
        }
    
        //    创建构造方法,加载配置文件或者带有自定义注解的
    //   这个直接使用的是通过注解进行创建容器,不是通过xml配置文件进行配置
        public MyAnnotionAoolicationContext(String packagename) {
    
            try {
                // 因为传递过来包名路径是. 转换为路径符合
                String packageFile = packagename.replace(".", File.separator);
    
    //        得到绝对路径 因为会部署在不同的电脑上,目的是遍历其下的文件中是否都有注解
                URL url = Thread.currentThread().getContextClassLoader().getResource(packageFile);
                String fileString = URLDecoder.decode(url.getFile(), "utf-8");
                rootFile = fileString.substring(1, fileString.length() - packageFile.length());
    
    //            System.out.println(fileString);
                loadFile(fileString);
    
            } catch (Exception e) {
                System.out.println(e);
                throw new RuntimeException(e);
            }
            loadDi();
        }
    
        //    遍历根路径下的文件中带有注解的文件
        private static void loadFile(String fileString)   {
            try {
                File file = new File(fileString);
    //        首先判断是否是文件夹
                if (file.isDirectory()) {
                    File[] childFileArr = file.listFiles();
        //            判断文件夹是否为空,如果为空就直接跳出即可
                    if (childFileArr.length == 0 || childFileArr == null) {
                        return;
                    } else {
        //                遍历所有的文件判断是文件还是文件夹
                        for (File childFile : childFileArr) {
                            if (childFile.isFile()) {
    //                            System.out.println(childFile);
        //                         通过路径得到反射所需要的包路径+类名
        //                         这样得到的文件不是以.java 结束,而是以.class
                                String childFileString = childFile.toString();
                                String forName = childFileString.substring(rootFile.length(), childFileString.length() - ".class".length()).replace("\\",".");
                                Class clazz= Class.forName(forName);
    
    //                            自己写的注解一般作用在类上而不是接口上,所以将接口,和注解类排除
                                if(!clazz.isAnnotation() && !clazz.isInterface()){
    //                                判断类上是否有bean注解,如果有就实例化  getAnnotation针对的是类上的注解
    //                                不过一般如果类的实例化上都没有注解,那么方法上带注解实现ioc 也就没有多少意义了
                                    Annotation beanAnnotation= clazz.getAnnotation(Bean.class);
    
    //                                Class s=Class.forName("com.xzd.myannotion.Bean");
    //                                System.out.println(s.getFields());
    
                                    if(beanAnnotation!=null){
    //                                    为了方便暂时使用空构造方法
                                        Object bean= clazz.newInstance();
                                        System.out.println(forName);
                                        System.out.println(clazz);
    //                                    因为一般针对的是接口,所以保存MyBeanDefinitionMap中如果有接口就以接口作为主键
                                        if(clazz.getInterfaces().length>0) {
    //                                    默认使用第一个接口吧
                                            MyBeanDefinitionMap.put(clazz.getInterfaces()[0], bean);
                                        }else {
                                            MyBeanDefinitionMap.put(clazz, bean);
                                        }
                                    }
    
                                }
    
    
                            } else {
                                loadFile(String.valueOf(childFile));
    
                            }
    
                        }
                    }
    
    
                }
            } catch ( Exception e) {
                System.out.println(e);
                throw new RuntimeException(e);
            }
    
    
        }
    
    //     前面的实例对象,还可以为属性进行注入值
      private void loadDi(){
          try {
    
    //       一般类上带有注解的才会在属性上带有ioc注入,所以就不便利所有的类,直接从MyBeanDefinitionMap获取即可
              Set<Map.Entry<Class, Object>> set= MyBeanDefinitionMap.entrySet();
              Iterator<Map.Entry<Class, Object>>  iterator= set.iterator();
              while(iterator.hasNext()){
                  Map.Entry<Class, Object> entry =iterator.next();
                  Class clazz= entry.getKey();
                  Object bean= entry.getValue();
    //              System.out.println(bean+"111");
       //            得到属性,从属性判断是否有注入数据
    
                  Field[] fields=  clazz.getDeclaredFields();
                  System.out.println(fields.length);
                  for(Field field:fields){
    
                      Annotation annotation= field.getAnnotation(Di.class);
    
                      if(annotation!=null){
                          Class fieldClass= field.getType();
                          fieldClass.getName();
                          System.out.println(fieldClass.getName()+"111");
                          String fieldName= field.getName();
                          Object value= ((Di) annotation).value();
                          Constructor fieldconstructor=fieldClass.getConstructor(String.class);
                          field.setAccessible(true);
                          field.set(bean, fieldconstructor.newInstance(value));
                      };
                  }
    
    
              }
          } catch (Exception e) {
              throw new RuntimeException(e);
    
          }
      }
    
    }
    
    
  • 为了方便直接在一个bean上进行注解

    @Bean
    public class Student  {
        @Di("12")
    
        Integer age;
        @Di("王五")
        String name;
    
        @Override
        public String toString() {
            return "Student{" +
                    "age=" + age +
                    ", name='" + name + '\'' +
                    '}';
        }
    }
    
  • 测试方便调用:test

    public class testSpring {
        public static void main(String[] args) {
            // 直接从目录上开始加载 
            MyApplicationContext myApplicationContext= new MyAnnotionAoolicationContext("com.xzd");
            Student student= (Student) myApplicationContext.getBean(Student.class);
            System.out.println(student);
        }
    }
    
    

在这里插入图片描述

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

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

相关文章

训练自己的GPT2-Chinese模型

文章目录效果抢先看准备工作环境搭建创建虚拟环境训练&预测项目结构模型预测续写训练模型遇到的问题及解决办法显存不足生成的内容一样文末效果抢先看 准备工作 从GitHub上拉去项目到本地&#xff0c;准备已训练好的模型百度网盘&#xff1a;提取码【9dvu】。 gpt2对联训…

又一个开源第一!飞桨联合百舸,Stable Diffusion推理速度遥遥领先

AIGC(AI Generated Content)&#xff0c;即通过人工智能方法生成内容&#xff0c;是当前深度学习最热门的方向之一。其在绘画、写作等场景的应用也一直层出不穷&#xff0c;其中&#xff0c;AI绘画是大家关注和体验较多的方向。 Diffusion系列文生图模型可以实现AI绘画应用&…

八股总结(一)C++语言特性、基础语法、类与模板、内存管理、拷贝控制、STL及C++11新特性

layout: post title: 八股总结&#xff08;一&#xff09;C语言特性、基础语法、类与模板、内存管理、拷贝控制、STL及C11新特性 description: 八股总结&#xff08;一&#xff09;C语言特性、基础语法、类与模板、内存管理、拷贝控制、STL及C11新特性 tag: 八股总结 总结的大部…

使用python求PLS-DA的方差贡献率

以鸢尾花数据集为例&#xff0c;实现PLS-DA降维&#xff0c;画出降维后数据的散点图并求其方差贡献率。 效果图 完整代码 # 导入所需库 import numpy as np from sklearn.cross_decomposition import PLSRegression from sklearn.datasets import load_iris from sklearn.pre…

synchronized原理mointor

Monitor对象头 在java中普通对象的对象头信息 Mark Word记录分代年龄、加锁的状态&#xff1b;Klass Word指向类对象的指针&#xff1b; 其中Mark Word结构 monitor执行原理 我们在加了重量级锁synchronize后&#xff0c;对象头的mark word会指向一个monitor&#xff0c;mon…

pandas库中的read_csv函数读取数据时候的路径问题详解(ValueError: embedded null character)

read_csv()函数不仅是R语言中的一个读取csv文件的函数&#xff0c;也是pandas库中的一个函数。pandas是一个用于数据分析和处理的python库。它的read_csv函数可以读取csv文件里的数据&#xff0c;并将其转化为pandas里面的DataFrame对象。它由很多参数可以设置&#xff0c;例如…

Express的详细教程

Express 文章目录Express初识ExpressExpress简介Express的基本使用安装创建基本的web服务器监听GET请求监听POST请求把内容响应给客户端获取URL中携带的查询参数获取URL中的动态参数托管静态资源express.static()托管多个静态资源挂载路径前缀nodemon为什么要使用nodemon安装no…

【专项训练】动态规划-1

动态规划 以上,并没有什么本质的不一样,很多时候,就是一些小的细节问题! 要循环,要递归,就是有重复性! 动态规划:动态递推 分治 + 最优子结构 会定义状态,把状态定义对 斐波那契数列 递归、记忆化搜索,比较符合人脑思维 递推:直接开始写for循环,开始递推 这里…

mysql无法启动服务及其他问题总结

文章目录1.安装后关于配置的问题显示【发生系统错误&#xff0c;拒绝访问】命令行Command Line Client闪退2.显示【MySQL服务无法启动】问题检查端口被占用删除data文件并初始化配置my.ini/.conf文件重新安装MySQL1.安装后关于配置的问题 显示【发生系统错误&#xff0c;拒绝访…

Apache Dubbo 存在反序列化漏洞(CVE-2023-23638)

漏洞描述 Apache Dubbo 是一款轻量级 Java RPC 框架 该项目受影响版本存在反序列化漏洞&#xff0c;由于Dubbo在序列化时检查不够全面&#xff0c;当攻击者可访问到dubbo服务时&#xff0c;可通过构造恶意请求绕过检查触发反序列化&#xff0c;执行恶意代码 漏洞名称Apache …

【Linux】 -- make/Makefile

目录 Linux项目自动化构建工具 – make/Makefile 背景 依赖关系和依赖方法 多文件编译 项目清理 make原理 Linux项目自动化构建工具 – make/Makefile 背景 一个工程的源文件不计其数 按照其类型、功能、模块分别放在若干个目录当中 Makefile定义了一系列的规则来指定&…

DJ1-3 计算机网络和因特网

目录 一、物理介质 1. 双绞线 2. 同轴电缆 3. 光纤线缆 4. 无线电磁波 二、端系统上的 Internet 服务 1. 面向连接的服务 TCP&#xff08;Transmission Control Protocol&#xff09; 2. 无连接的服务 UDP&#xff08;User Datagram Protocol&#xff09; TCP 和 UD…

TypeScript(四)接口

目录 前言 定义 用法 基本用法 约定规则 属性控制 任意属性 可选属性 只读属性 定义函数 冒号定义 箭头定义 接口类型 函数接口 索引接口 继承接口 类接口 总结 前言 在介绍TS对象类型中&#xff0c;为了让数组每一项更具体&#xff0c;我们使用 string [ ]…

C++面向对象编程之四:成员变量和成员函数分开存储、this指针、const修饰成员和对象

在C中&#xff0c;成员变量和成员函数是分开存储的&#xff0c;只有非静态成员变量才存储在类中或类的对象上。通过该类创建的所有对象都共享同一个函数#include <iostream> using namespace std;class Monster {public://成员函数不占对象空间&#xff0c;所有对象共享同…

数据库基本功之复杂查询的子查询

子查询返回的值可以被外部查询使用,这样的复合查询等效与执行两个连续的查询. 1. 单行单列子查询 (>,<,,<>,>,<)内部SELECT子句只返回一行结果 2.多行单列子查询 (all, any, in,not in) all (>大于最大的,<小于最小的) SQL> select ename, sal from…

Linux创建并挂载NAS

1 目标 在Linux服务器1上创建nas服务器&#xff0c;并指定可读写目录在Linux服务器2上挂载上述nas磁盘在Linux服务器2上设置开机自动挂载nas磁盘 2 搭建环境 两台Linux系统服务器&#xff0c;如下&#xff1a; 服务器1 IP为192.168.31.101 服务器2 IP为192.168.31.102 3 在服…

自动化测试——selenium多浏览器处理

这里写目录标题一、背景二、pytes hook函数1、conftest.py2、测试用例3、执行测试用例一、背景 用户使用的浏览器(frefox,chrome,IE 等) web应用应该能在任何浏览器上正常的工作&#xff0c;这样能吸引更多的用户来使用。 是跨不同浏览器组合验证网站或web应用程序功能的过程 …

Python高频面试题——生成器(最通俗的讲解)

生成器定义在 Python 中&#xff0c;使用了 yield 的函数被称为生成器&#xff08;generator&#xff09;。跟普通函数不同的是&#xff0c;生成器是一个返回迭代器的函数&#xff0c;只能用于迭代操作&#xff0c;更简单点理解生成器就是一个迭代器。 在调用生成器运行的过程中…

Ubuntu系统开机自动挂载NTFS硬盘【超实用】

由于跑深度学习实验(图像分割)f非常消耗内存&#xff0c;系统盘sda1内存小&#xff0c;配置了一个大容量得出NTFS机械盘&#xff0c;网上招了一些资料如何挂在&#xff0c;但是每次开机得手动挂载一遍才能使用硬盘&#xff0c;非常不方便&#xff0c;还容易造成数据丢失。 Step…

Elasticsearch使用系列-ES增删查改基本操作+ik分词

一、安装可视化工具KibanaES是一个NoSql数据库应用。和其他数据库一样&#xff0c;我们为了方便操作查看它&#xff0c;需要安装一个可视化工具 Kibana。官网&#xff1a;https://www.elastic.co/cn/downloads/kibana和前面安装ES一样&#xff0c;选中对应的环境下载&#xff0…