【Spring】——2、使用@ComponentScan自动扫描组件并指定扫描规则

news2024/11/15 15:54:09

在这里插入图片描述

📫作者简介:zhz小白
公众号:小白的Java进阶之路
专业技能:
1、Java基础,并精通多线程的开发,熟悉JVM原理
2、熟悉Java基础,并精通多线程的开发,熟悉JVM原理,具备⼀定的线上调优经验
3、熟悉MySQL数据库调优,索引原理等,⽇志原理等,并且有出过⼀篇专栏
4、了解计算机⽹络,对TCP协议,滑动窗⼝原理等有⼀定了解
5、熟悉Spring,Spring MVC,Mybatis,阅读过部分Spring源码
6、熟悉SpringCloud Alibaba体系,阅读过Nacos,Sentinel,Seata,Dubbo,Feign,Gateway核⼼源码与设计,⼆次开发能⼒
7、熟悉消息队列(Kafka,RocketMQ)的原理与设计
8、熟悉分库分表ShardingSphere,具有真实⽣产的数据迁移经验
9、熟悉分布式缓存中间件Redis,对其的核⼼数据结构,部署架构,⾼并发问题解决⽅案有⼀定的积累
10、熟悉常⽤设计模式,并运⽤于实践⼯作中
11、了解ElasticSearch,对其核⼼的原理有⼀定的了解
12、了解K8s,Jekins,GitLab
13、了解VUE,GO
14、⽬前有正在利⽤闲暇时间做互游游戏,开发、运维、运营、推销等

本人著作git项目:https://gitee.com/zhouzhz/star-jersey-platform,有兴趣的可以私聊博主一起编写,或者给颗star
领域:对支付(FMS,FUND,PAY),订单(OMS),出行行业等有相关的开发领域
🔥如果此文还不错的话,还请👍关注、点赞、收藏三连支持👍一下博主~

文章目录

  • 使用XML文件配置包扫描
    • BookDao
    • BookService
    • BookController
  • 使用注解配置包扫描(常用)
  • 关于@ComponentScan注解的源码解析
    • 扫描时排除注解标注的类(excludeFilters)
    • 扫描时只包含注解标注的类(includeFilters)
  • 重复注解(@Repeatable)
    • java8之后
    • Java8之前
  • 总结

在实际项目中,我们更多的是使用Spring的包扫描功能对项目中的包进行扫描,凡是在指定的包或其子包中的类上标注了**@Repository、@Service、@Controller、@Component**注解的类都会被扫描到,并将这个类注入到Spring容器中。

Spring包扫描功能可以使用XML配置文件进行配置,也可以直接使用**@ComponentScan**注解进行设置,使用@ComponentScan注解进行设置比使用XML配置文件来配置要简单的多。

使用XML文件配置包扫描

我们可以在Spring的XML配置文件中配置包的扫描,在配置包扫描时,需要在Spring的XML配置文件中的beans节点中引入context标签,如下所示。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:context="http://www.springframework.org/schema/context"
  xmlns:p="http://www.springframework.org/schema/p"
  xmlns:aop="http://www.springframework.org/schema/aop"
  xmlns:tx="http://www.springframework.org/schema/tx"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
  http://www.springframework.org/schema/context
  http://www.springframework.org/schema/context/spring-context-4.2.xsd">
  <!-- 包扫描:只要是标注了我们熟悉的@Controller、@Service、@Repository、@Component这四个注解中的任何一个的组件,它就会被自动扫描,并加进容器中 -->
  <context:component-scan base-package="com.zhz"/>
  <!-- 注册组件 -->
  <bean id="person" class="com.zhz.bean.Person">
    <property name="age" value="18"/>
    <property name="name" value="zhz"/>
  </bean>
</beans>

这样配置以后,只要在com.zhz包下,或者com.zhz的子包下标注了**@Repository、@Service、@Controller、@Component注解的类都会被扫描到,并自动注入到Spring容器中。
此时,我们分别创建
BookDao、BookService以及BookController这三个类,并在这三个类中分别添加@Repository、@Service、@Controller**注解,如下所示。

BookDao

package com.zhz.dao;

import org.springframework.stereotype.Repository;

/**
 * @author zhouhengzhe
 * @description: todo
 * @date 2022/11/4 10:56
 * @since v1
 */
@Repository
public class BookDao {
}

BookService

package com.zhz.service;

import org.springframework.stereotype.Service;

/**
 * @author zhouhengzhe
 * @description: todo
 * @date 2022/11/4 10:56
 * @since v1
 */
@Service
public class BookService {
}

BookController

package com.zhz.controller;

import org.springframework.stereotype.Controller;

/**
 * @author zhouhengzhe
 * @description: todo
 * @date 2022/11/4 10:57
 * @since v1
 */
@Controller
public class BookController {
}

我们写一个测试类,在src/test/java/com/zhz/test下,建立一个IOCTest测试类

package com.zhz.test;

import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author zhouhengzhe
 * @description: todo
 * @date 2022/11/4 10:58
 * @since v1
 */
public class IOCTest {

    @SuppressWarnings("resource")
    @Test
    public void test() {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-bean.xml");
        // 我们现在就来看一下IOC容器中有哪些bean,即容器中所有bean定义的名字
        String[] definitionNames = applicationContext.getBeanDefinitionNames();
        for (String name : definitionNames) {
            System.out.println(name);
        }
    }
}

我们可以清晰的发现,扫到的包中包含我们写的bean
在这里插入图片描述

使用注解配置包扫描(常用)

我们只需要在MainConfig中添加

package com.zhz.config;

import com.zhz.bean.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

/**
 * @author zhouhengzhe
 * @description: todo
 * @date 2022/11/4 10:27
 * @since v1
 */
@ComponentScan("com.zhz")
@Configuration
public class MainConfig {

    /**
     * @Bean注解是给IOC容器中注册一个bean,类型自然就是返回值的类型,id默认是用方法名作为id
      */
    @Bean(name = "person")
    public Person person1(){
        return new Person("zhz", 20);
    }
}

然后我们改造一下IOCTest

package com.zhz.test;

import com.zhz.config.MainConfig;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author zhouhengzhe
 * @description: todo
 * @date 2022/11/4 10:58
 * @since v1
 */
public class IOCTest {

    @SuppressWarnings("resource")
    @Test
    public void test() {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
        // 我们现在就来看一下IOC容器中有哪些bean,即容器中所有bean定义的名字
        String[] definitionNames = applicationContext.getBeanDefinitionNames();
        for (String name : definitionNames) {
            System.out.println(name);
        }
    }
}

我们就会发现他跟XML文件配置方法的输出结果是一样的
在这里插入图片描述

关于@ComponentScan注解的源码解析

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.context.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.core.annotation.AliasFor;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
    @AliasFor("basePackages")
    String[] value() default {};

    @AliasFor("value")
    String[] basePackages() default {};

    Class<?>[] basePackageClasses() default {};

    Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;

    Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;

    ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;

    String resourcePattern() default "**/*.class";

    boolean useDefaultFilters() default true;

    Filter[] includeFilters() default {};

    Filter[] excludeFilters() default {};

    boolean lazyInit() default false;

    @Retention(RetentionPolicy.RUNTIME)
    @Target({})
    public @interface Filter {
        FilterType type() default FilterType.ANNOTATION;

        @AliasFor("classes")
        Class<?>[] value() default {};

        @AliasFor("value")
        Class<?>[] classes() default {};

        String[] pattern() default {};
    }
}

我们可以发现他又两个比较核心的方法:

  • Filter[] includeFilters() default {}:方法指定Spring扫描的时候按照什么规则只需要包含哪些组件
  • Filter[] excludeFilters() default {}:指定Spring扫描的时候按照什么规则排除哪些组件

扫描时排除注解标注的类(excludeFilters)

假设我们要排除@Controller包下的Bean,那么我们要怎么实现呢?
我们只需要在MainConfig类中添加如下

package com.zhz.config;

import com.zhz.bean.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;

/**
 * @author zhouhengzhe
 * @description: todo
 * @date 2022/11/4 10:27
 * @since v1
 */
@ComponentScan(value = {"com.zhz"},excludeFilters = {
        /*
		 * type:指定你要排除的规则,是按照注解进行排除,还是按照给定的类型进行排除,还是按照正则表达式进行排除,等等
		 * classes:除了@Controller标注的组件之外,IOC容器中剩下的组件我都要,即相当于是我要排除@Controller这俩注解标注的组件。
		 */
        @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class})
})
@Configuration
public class MainConfig {

    /**
     * @Bean注解是给IOC容器中注册一个bean,类型自然就是返回值的类型,id默认是用方法名作为id
      */
    @Bean(name = "person")
    public Person person1(){
        return new Person("zhz", 20);
    }
}

我们可以观察到运行结果中并没有@Controller注释修饰的Bean
在这里插入图片描述

扫描时只包含注解标注的类(includeFilters)

跟上面哪个相反,代表只会扫描注解下的包,其他不会扫描,如下,我们只要@Controller修饰的包

package com.zhz.config;

import com.zhz.bean.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;

/**
 * @author zhouhengzhe
 * @description: todo
 * @date 2022/11/4 10:27
 * @since v1
 */
@ComponentScan(value = {"com.zhz"},includeFilters = {
        /*
		 * type:指定你要排除的规则,是按照注解进行排除,还是按照给定的类型进行排除,还是按照正则表达式进行排除,等等
		 * classes:除了@Controller标注的组件之外,IOC容器中剩下的组件我都不要,即相当于是我要排除@Controller注解以外的标注的组件。
		 */
        @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class})
},useDefaultFilters = false)
@Configuration
public class MainConfig {

    /**
     * @Bean注解是给IOC容器中注册一个bean,类型自然就是返回值的类型,id默认是用方法名作为id
      */
    @Bean(name = "person")
    public Person person1(){
        return new Person("zhz", 20);
    }
}

我们可以发现运行结果中是没有**@Repository、@Service**注解修饰的Bean,具体演示效果如下:
在这里插入图片描述

重复注解(@Repeatable)

java8之后

我们发现@ComponentScan注解下面是有这样的一个注解的:@Repeatable
在这里插入图片描述

然后我们可以进入他括号里面的类ComponentScans,可以发现他是一个数组注解,因此我们可知@Repeatable代表重复注解,也就是说我们可以在一个类上重复使用这个注解
在这里插入图片描述

让我们来演示一下具体的demo

package com.zhz.config;

import com.zhz.bean.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;

/**
 * @author zhouhengzhe
 * @description: todo
 * @date 2022/11/4 10:27
 * @since v1
 */
@ComponentScan(value = {"com.zhz"},includeFilters = {
        /*
		 * type:指定你要排除的规则,是按照注解进行排除,还是按照给定的类型进行排除,还是按照正则表达式进行排除,等等
		 * classes:除了@Controller标注的组件之外,IOC容器中剩下的组件我都不要,即相当于是我要排除@Controller注解以外的标注的组件。
		 */
        @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class})
},useDefaultFilters = false)
@ComponentScan(value = {"com.zhz"},includeFilters = {
        /*
		 * type:指定你要排除的规则,是按照注解进行排除,还是按照给定的类型进行排除,还是按照正则表达式进行排除,等等
		 * classes:除了@Service标注的组件之外,IOC容器中剩下的组件我都不要,即相当于是我要排除@Service注解以外的标注的组件。
		 */
        @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Service.class})
},useDefaultFilters = false)
@Configuration
public class MainConfig {

    /**
     * @Bean注解是给IOC容器中注册一个bean,类型自然就是返回值的类型,id默认是用方法名作为id
      */
    @Bean(name = "person")
    public Person person1(){
        return new Person("zhz", 20);
    }
}

从下图运行效果中,我们可以发现他是两个@ComponentScan注解都生效了。
在这里插入图片描述

Java8之前

如果是Java8之前,我们要实现上面那种方式,需要改成如下

package com.zhz.config;

import com.zhz.bean.Person;
import org.springframework.context.annotation.*;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;

/**
 * @author zhouhengzhe
 * @description: todo
 * @date 2022/11/4 10:27
 * @since v1
 */
@ComponentScans({
        @ComponentScan(value = {"com.zhz"},includeFilters = {
                /*
                 * type:指定你要排除的规则,是按照注解进行排除,还是按照给定的类型进行排除,还是按照正则表达式进行排除,等等
                 * classes:除了@Controller标注的组件之外,IOC容器中剩下的组件我都要,即相当于是我要排除@Controller和@Service这俩注解标注的组件。
                 */
                @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class})
        },useDefaultFilters = false),
        @ComponentScan(value = {"com.zhz"},includeFilters = {
                /*
                 * type:指定你要排除的规则,是按照注解进行排除,还是按照给定的类型进行排除,还是按照正则表达式进行排除,等等
                 * classes:除了@Controller标注的组件之外,IOC容器中剩下的组件我都要,即相当于是我要排除@Controller和@Service这俩注解标注的组件。
                 */
                @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Service.class})
        },useDefaultFilters = false)
})
@Configuration
public class MainConfig {

    /**
     * @Bean注解是给IOC容器中注册一个bean,类型自然就是返回值的类型,id默认是用方法名作为id
      */
    @Bean(name = "person")
    public Person person1(){
        return new Person("zhz", 20);
    }
}

从下面演示效果上可以发现是跟Java8之后实现上是一样的
在这里插入图片描述

总结

我们可以使用@ComponentScan注解来指定Spring扫描哪些包,可以使用excludeFilters()方法来指定扫描时排除哪些组件,也可以使用includeFilters()方法来指定扫描时只包含哪些组件。当使用includeFilters()方法指定只包含哪些组件时,需要禁用掉默认的过滤规则。

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

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

相关文章

微信小程序开发(九):使用扩展组件库

前端开发中离不开各种组件库&#xff0c;我最先接触的组件库还是Bootstrap&#xff0c;后来工作中又陆续使用了inoic、ng-zorro等各种不同的库。 在微信小程序开发中也有多种组件库&#xff0c;这里记录其中几种不同组件库的使用方法。 WeUI 这是微信官方推出的一款和微信原…

使用Python,Open3D对点云散点投影到面上并可视化,使用3种方法计算面的法向量及与平均法向量的夹角

使用Python&#xff0c;Open3D对点云散点投影到面上并可视化&#xff0c;使用3种方法计算面的法向量及与平均法向量的夹角 写这篇博客源于博友的提问&#xff0c;他坚定了我继续坚持学习的心&#xff0c;带给了我充实与快乐。 将介绍以下5部分&#xff1a; 随机生成点云点投影…

LaTeX学习笔记

LaTeX学习笔记 文章目录LaTeX学习笔记1. 开始的尝试2.文档类与宏包3.标题与章节4.标注5.列表6.对齐7.插入代码块8.绘制表格9.插入图片10.数学公式10.1.基础公式10.2.复杂公式10.3 常用符号11.参考文献冲鸭&#xff01;&#xff01;&#xff01; 1. 开始的尝试 先开始试一下一个…

MySQL数据库索引和事务详解

目录 前言&#xff1a; 索引 查看索引 创建索引 删除索引 索引使用 底层数据结构分析 事务 事务引出 MySQL设计事务 事务四大特性 小结&#xff1a; 前言&#xff1a; 数据库索引和事务的存在&#xff0c;对于数据库的一些性能有了显著提升。我们需掌握其底层的实现…

NUMA那些事儿

NUMA——Non Uniform Memory Access&#xff0c;中文为非统一内存访问&#xff0c;在NUMA出现之前&#xff0c;内存的控制器是包含在北桥芯片中的&#xff0c;所有内存由北桥统一管理&#xff0c;因此可以保证访问内存的一致性。随着CPU架构的不断迭代和演进&#xff0c;核数越…

Elasticsearch与Kibana安装

现有环境 windows docker ubuntu Elasticsearch安装 安装包下载 ES不同平台、版本下载路径&#xff1a;Download Elasticsearch | Elastic 本文演示用linux # 启动ubuntu环境&#xff0c;开放端口9200、9300、5601 docker run -name es -p 9200:9200 -p 9300:9300 -p 5…

指夹式血氧饱和检测仪方案分析

指夹式心率血氧饱和度方案的测量原理是根据血红蛋白(Hb)和氧合血红蛋白 (HbO2)在红光和近红光区域的吸收光谱特性为依据&#xff0c;运用Lambert Beer定律建立数据处理经验公式&#xff0c;采用光电血氧检测技术结合光电容积脉搏波描记&#xff08;PPG&#xff09;技术&#xf…

化工制造行业数字化升级案例—基于HK-Domo商业智能分析工具

案例背景导读 世伟洛克&#xff08;Swagelok&#xff09;是全球领先的流体系统解决方案的开发商和制造商&#xff0c;为包括科研、仪表、制药、油气、电力、石化、代用燃料和半导体等在内的各个行业提供产品、组装和服务。世伟洛克通过独立的销售和服务中心网站进行运营&#x…

使用 Typescript 封装 Axios

对 axios 二次封装,更加的可配置化、扩展性更加强大灵活 通过 class 类实现&#xff0c;class 具备更强封装性(封装、继承、多态)&#xff0c;通过实例化类传入自定义的配置 创建 class 严格要求实例化时传入的配置&#xff0c;拥有更好的代码提示 /*** param {AxiosInstance…

C语言习题练习8--二进制操作符

IO型--从main函数开始写&#xff0c;要写输入、计算、输出 接口型--不需要写主函数&#xff0c;默认主函数是存在的&#xff0c;你只需要完成函数就行 一、二进制中1的个数 (12条消息) C语言丨关键字signed和unsigned 的使用与区别详解_Emily-C的博客-CSDN博客_signed unsi…

【笔记】samba shell 脚本 离线安装 - Ubuntu 20.04

前言 按照官网调试代码、网上各种步骤来走&#xff08;还收费&#xff09;都不行 结果发现是防火墙问题 公司服务器安装的ufw使用失效&#xff0c;导致端口号放行添加失败 换用firewall-cmd成功 现在免费放下代码&#xff0c;气死他们收费的 目录 ├── home│ ├── k…

linux备份mysql8.0数据库脚本

文章目录环境要求步骤1、创建一个.sh文件编写shell脚本2、添加定时任务环境要求 linux系统&#xff0c;安装了mysql8.0 步骤 1、创建一个.sh文件编写shell脚本 创建文件的命令&#xff1a; vim ***.shshell文件文件参考自文章 链接 export LANGen_US.UTF-8 #注意&#xf…

测试开发技术:Python测试框架Pytest的基础入门

Pytest简介 Pytest is a mature full-featured Python testing tool that helps you write better programs.The pytest framework makes it easy to write small tests, yet scales to support complex functional testing for applications and libraries. 通过官方网站介绍…

十五、Lua 协同程序(coroutine)的学习

Lua 协同程序(coroutine) 什么是协同(coroutine)&#xff1f; Lua 协同程序(coroutine)与线程比较类似&#xff1a;拥有独立的堆栈&#xff0c;独立的局部变量&#xff0c;独立的指令指针&#xff0c;同时又与其它协同程序共享全局变量和其它大部分东西。 协同是非常强大的功…

2646-61-9, 脯氨酰内肽酶(PEP)底物: Z-GPLGP-OH

编号: 160473中文名称: 脯氨酰内肽酶&#xff08;PEP&#xff09;底物&#xff1a;Z-Gly-Pro-Leu-Gly-ProCAS号: 2646-61-9单字母: Z-GPLGP-OH三字母: Cbz-Gly-Pro-Leu-Gly-Pro-COOH氨基酸个数: 5分子式: C28H39O8N5平均分子量: 573.64精确分子量: 573.28等电点(PI): -pH7.0时的…

Arduino程序设计(三) 光照采集 + 温度采集

光照采集 温度采集前言一、光敏电阻检测环境光二、DS18B20检测环境温度总结参考文献前言 本文主要介绍两种常见的传感器采集环境参数&#xff0c;即光照传感器和温度传感器。光照传感器采用光敏电阻GL3516&#xff08;5-10K&#xff09;检测环境光。温度传感器采用DS18B20检测…

2022Q3家电行业高增长细分市场分析(含热门品类数据)

2022年&#xff0c;在大环境的影响下&#xff0c;大众消费偏好更趋于理性化、追求高性价比&#xff0c;不少行业增速有所放缓&#xff0c;在此背景下&#xff0c;2022年Q3季度中&#xff0c;消费市场中仍有一些高增长概念涌现。 在家电行业中&#xff0c;我们发现了3个高增长品…

【重识云原生】第六章容器基础6.4.9.5节——端点切片(Endpoint Slices)

1 EndpointSlice特性 Kubernetes v1.21 [stable] 端点切片&#xff08;EndpointSlices&#xff09; 是一个新 API&#xff0c;它提供了 Endpoint API 可伸缩和可拓展的替代方案。EndpointSlice 会跟踪 Service Pod 的 IP 地址、端口、readiness 和拓扑信息。 在 Kubernetes v…

一文看懂页面置换算法

页面置换算法分为两类 1、局部页面置换算法 最优页面置换算法&#xff08;OPT、optimal&#xff09;先进先出算法&#xff08;FIFO&#xff09;最近最久未使用算法&#xff08;LRU,Least Recently Used&#xff09;时钟页面置换算法&#xff08;Clock&#xff09;最不常用算法…

【算法 | 实验18】在字符矩阵中查找给定字符串的所有匹配项

文章目录题目描述思路分析bug记录&#xff1a;"error: >> should be > > within a nested template argument list"代码题目描述 题目 在字符矩阵中查找给定字符串的所有匹配项 给定一个MN字符矩阵&#xff0c;以及一个字符串S&#xff0c;找到在矩阵中所…