(a)Spring注解式开发,注册组件的@Repository,@Service,@Controller,@Component使用及说明

news2024/11/25 14:20:31

注解扫描原理

通过反射机制获取注解

@Target(value = {ElementType.TYPE})// 设置Component注解可以出现的位置,以上代表表示Component注解只能用在类和接口上
@Retention(value = RetentionPolicy.RUNTIME)// 设置Component注解的保持性策略,以上代表Component注解可以被反射机制读取
public @interface Component {
    // 注解中的一个属性, 该属性类型String,属性名是value
    String value();
}

假设我们现在只知道一个包名, 这个包下有多少个Bean我们不知道, Bean上有没有注解也不知道,如何通过程序自动将类上有注解的Bean实例化

@Component(value = "userBean")// 语法格式:@注解类型名(属性名=属性值, 属性名=属性值, 属性名=属性值......)
public class User {
}
// 没有注解的Bean	
public class Vip {
}
public class ComponentScan {
    public static void main(String[] args){
        // 存放Bean的Map集合,key存储beanId,value存储Bean
        Map<String,Object> beanMap = new HashMap<>();
        // 通过包的名字扫描这个包下所有的类,当这个类上有@Component注解的时候实例化该对象,然后放到Map集合中
        String packageName = "com.powernode.bean";
        // 开始写扫描程序,将包名换成路径获取目录下的所有文件
        // 在正则表达式中"."属于通配符代表任意字符,使用"\."代表一个普通的"."字符(java中"\"表示转义字符所以需要使用"\\.")
        String packagePath = packageName.replaceAll("\\.", "/");
        String packagePath = packageName.replaceAll("\\.", "/");
        // 从类的根路径下加载资源,自动返回一个URL类型的对象(路径)
        URL url = ClassLoader.getSystemClassLoader().getResource(packagePath);
        // 获去扫描的类所在的绝对路径
        String path = url.getPath();

        // 获取一个绝对路径下的所有文件
        File file = new File(path);
        // 遍历这个路径下的所有文件,每个文件都是File对象,最后存到一个File类型的数组中
        File[] files = file.listFiles();
        Arrays.stream(files).forEach(f -> {
            try {
                // f.getName()获取com.powernode.bean包下的文件名User.class和Vip.class
                // f.getName().split("\\.")[0],先通过"."对文件名进行拆分,然后取数组的第一个元素即文件的简类名User和Vip
                // 拼接字符串得到文件的全类名com.powernode.bean.User和com.powernode.bean.Vip
                String className = packageName+ "." + f.getName().split("\\.")[0];

                // 通过全类名获取类的字节码对象
                Class<?> aClass = Class.forName(className);

                // 判断类上是否有Component注解
                if (aClass.isAnnotationPresent(Component.class)) {
                    // 获取Component注解
                    Component annotation = aClass.getAnnotation(Component.class);
                    // 获取Component注解的属性值及即bean的id
                    String id = annotation.value();
                    // 有Component注解的都要创建对象
                    Object obj = aClass.newInstance();
                    // 将创建的对象放入Map集合当中
                    beanMap.put(id, obj);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        // Map集合中只有User对象,因为Vip类上没有Component注解 
        System.out.println(beanMap);
    }
}

注册组件的四个注解

注解的存在主要是为了简化XML的配置, Spring6倡导全注解开发,所以只要使用了Spring的注解就要使用包扫描机制

  • 使用注解一般加入的是自己写的组件 , 使用bean标签配置加入的是别人的写的组件 , 开发中常用注解和bean配置相结合的方式
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <context:component-scan base-package="com.powernode.spring6.bean4"/>
</beans>

标识Bean的四个注解

使用标识Bean的四个注解和使用XML配置的方式将组件加入到容器中后组件的默认行为都是一样的 , 组件都有id和默认作用域

  • 四个注解都只有一个value属性用来指定bean的id,bean的名字默认是组件的简单类名首字母小写后得到的字符串 , 作用域默认就是单例的
  • 优点: 可读性比较好简化了bean的声明, 更符合MVC的设计理念, 这种声明bean的方式是目前企业中较为常见的bean的声明方式
  • 缺点: 没有任何一个地方可以查阅整体信息,只有当程序运行起来才能感知到加载了多少个bean

实际这四个注解用哪个都可以 , 它们的功能都是只能起到标识的作用,Spring并没有能力识别一个组件到底是不是它所标记的MVC架构类型

  • 即使将@Respository注解用在一个表述层控制器组件上面也不会产生任何错误
  • 所以@Respository、@Service、@Controller这几个注解仅仅是为了让开发人员自己明确当前的组件扮演的角色,增强程序的可读性
注解名功能
@Repository标识一个受Spring IOC容器管理的持久化层组件,给数据库层 (持久化层 , dao层)的组件添加这个注解
@Service标识一个受Spring IOC容器管理的业务逻辑层组件,推荐给业务逻辑层的组件添加这个注解
@Controller标识一个受Spring IOC容器管理的表述层控制器组件,推荐给控制层也就是Servlet包下的这些组件加这个注解
@Component标识一个受Spring IOC容器管理的普通组件,给不属于以上几层的组件添加这个注解
@Scope指定加入的组件是多实例的还是单实例的,默认是单实例的 , prototype属性表示指定的bean是多实例的

四个注解的源码

@Component源码: @Controller、@Service、@Repository这三个注解都是@Component注解的别名,使用别名的方式可读性更好

@Target(value = {ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Component {
    String value();
}

@Repository , @Controller ,@Service注解源码

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
    // 别名
    @AliasFor(
        annotation = Component.class
    )
    String value() default "";
}

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
    // 别名
    @AliasFor(
        annotation = Component.class
    )
    String value() default "";
}

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {
    // 别名
    @AliasFor(
        annotation = Component.class
    )
    String value() default "";
}

使用Spring的IoC注解

使用步骤

第一步: 引入aop的依赖才支持注解模式(如果加入spring-context依赖之后会关联加入aop的依赖)

在这里插入图片描述

第二步:使用context:component-scan标签让Spring扫描去指定包中加了注解的组件,并将这些组件实例化后加入到IoC容器中,Spring负责管理这些bean对象

  • 在xmlns头部信息中添加context命名空间的配置信息:xmlns:context="http://www.springframework.org/schema/context"
  • 在xsi约束文件中添加约束信息:http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd

context:component-scan标签的属性

属性名功能
base-package指定一个需要扫描的基类包,默认Spring容器会扫描这个指定的基类包及其子包中的所有类
resource-pattern扫描基类包下特定的类,如仅希望扫描基类包下特定的类而非所有类(*表示匹配任意个字符)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <!--告诉Spring框架要扫描哪些包中的类-->
    <context:component-scan base-package="com.powernode.spring6.bean"> </context:component-scan>
    <!--过滤特定的类-->
    <context:component-scan base-package="com.powernode.spring6" resource-pattern="bean/*"/>
</beans>

第三步:在Bean类上使用注解, 虽然Component注解换成其它三个注解照样创建bean对象,但为了可读性应该根据属于MVC架构模式的哪一层加对应类型注解

//@Component(value = "userBean")
@Component("userBean")
public class User {
}

public class AnnotationTest {
    @Test
    public void testBean(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        User userBean = applicationContext.getBean("userBean", User.class);
        System.out.println(userBean);
    }
}

使用细节

如果IoC注解没有设置value属性,Spring会为bean自动取名默认是bean首字母小写后的类名

@Repository// 等价于<bean id="bankDao" class="com.powernode.spring6.bean.BankDao"></bean>
public class BankDao {
}

public class AnnotationTest {
    @Test
    public void testBean(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        BankDao bankDao = applicationContext.getBean("bankDao", BankDao.class);
        System.out.println(bankDao);
    }
}

扫描多个基类包下加了注解的类

  • 第一种: 在配置文件中指定多个基类包使用逗号隔开
  • 第二种: 指定多个基类包的共同父包,但是这样扫描范围就大了肯定要牺牲一部分效率
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <!--使用逗号隔开要扫描的多个基类包-->
    <context:component-scan base-package="com.powernode.spring6.bean,com.powernode.spring6.bean2"/>
    
    <!--扫描多个基类包共同的父包,那么这些包都会扫描进来-->
    <context:component-scan base-package="com.powernode.spring6"/>
</beans>

选择性实例化包下的Bean

假设在某个包下很多类上都标注了不同类型的注解,现在由于特殊业务的需要只允许其中所有的加了Controller注解的类参与Bean管理,其他的都不实例化

@Component
public class A {
    public A() {
        System.out.println("A的无参数构造方法执行");
    }
}

@Controller
class B {
    public B() {
        System.out.println("B的无参数构造方法执行");
    }
}

@Service
class C {
    public C() {
        System.out.println("C的无参数构造方法执行");
    }
}

@Repository
class D {
    public D() {
        System.out.println("D的无参数构造方法执行");
    }
}

context:component-scan标签的属性

属性值功能
use-default-filters=“true”(默认)只要扫描的bean上有Component、Controller、Service、Repository中的任意一个注解都会进行实例化
use-default-filters=“false”不再使用spring默认实例化规则, 让所有bean上的Component、Controller、Service、Repository注解全部失效

context:component-scan标签的子标签(指定包含与排除基类包下的哪些类)

标签名功能
context:exclude-filter指定扫描基类包时要排除在外的目标类,type属性指定排除的规则 , 默认全部扫描进来并实例化
context:include-filter指定扫描基类包时要包含在内的目标类,扫描时一定要禁用默认的过滤规则(默认全都扫描) , type属性指定排除规则

context:exclude-filter标签和context:include-filter标签type属性和expression属性的值

Type属性的值expression属性的值说明
annotation要过滤的注解的全类名按照bean上的注解类型进行过滤 (常用)
assignable要过滤的类的全类名按照类的全类名过滤指定类和它的子类
aspectjcom.atguigu.*Service根据AspectJ表达式进行过滤 (常用) , 过滤所有类名是以Service结束的类或其子类
regexcom.atguigu.anno.*根据正则表达式匹配到的类名进行过滤 , 过滤com.atguigu.anno包下的所有类
customcom.atguigu.XxxTypeFilter使用XxxTypeFilter类通过编码的方式自定义过滤规则
该类必须实现org.springframework.core.type.filter.TypeFilter接口

spring的配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <!--第一种方案:false表示该包下的所有的带有声明Bean的注解全部失效,组件都不再实例化-->
    <context:component-scan base-package="com.powernode.spring6.bean" use-default-filters="false">
        <!--只有带有Controller注解的组件被包含进来,其他注解全部失效-->
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

    <!--第二种方案:true表示该包下的所有的带有声明Bean注解的组件全部都会实例化-->
    <context:component-scan base-package="com.powernode.spring6.bean" use-default-filters="true">
        <!--将带有Repository,Service,Component注解的组件全部排除在外-->
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Component"/>
    </context:component-scan>
</beans>

Type其他属性的测试

<!--默认是该基类包下所有的类全部扫描进来,根据规则单独指定要排除基类包下的哪些类不扫描进来-->
<context:component-scan base-package="com.powernode.spring6.bean">
    <!--根据全类名不扫描基类包下的指定类和它的子类-->
    <context:exclude-filter type="assignable" expression="com.powernode.spring6.bean.A">             
</context:component-scan>
        
        
<!--禁用默认的过滤规则,该包下所有的类都不会扫描进来,单独指定要将该包下的哪些类要扫描进来-->       
<context:component-scan base-package="com.atguigu" use-default-filters="false">
    <!--根据全类名扫描基类包下的指定类和它的子类-->
    <context:include-filter type="assignable" expression="com.powernode.spring6.bean.A">             
</context:component-scan>

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

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

相关文章

社区团购美团和多多买菜小程序购物车

概述 微信小程序购物车列表demo 详细 需求 显示食物名称、价格、数量。 点击相应商品增加按钮,购买数量增加1,点击食物减少按钮,购买数量减一 显示购买总数和总金额 查看当前购买的商品 效果图(数据来自本地模拟) 目录结构 实现过程 主要wxml <view classfoods>…

工具篇 | H2数据库的使用和入门

引言 1.1 H2数据库概述 1.1.1 定义和特点 H2数据库是一款以 Java编写的轻量级关系型数据库。由于其小巧、灵活并且易于集成&#xff0c;H2经常被用作开发和测试环境中的便利数据库解决方案。除此之外&#xff0c;H2也适合作为生产环境中的嵌入式数据库。它不仅支持标准的SQL…

饮料生产线Modbus协议转换网关的应用介绍

在饮料生产线设备数据采集和控制系统中&#xff0c;MODBUS网关是一种非常重要的设备。它可以将不同设备之间的通讯协议转换为统一的MODBUS协议&#xff0c;从而实现数据采集和指令下达。在本文中&#xff0c;我们将介绍如何使用MODBUS网关采集饮料生产线设备数据并下达指令。 在…

知识库搭建保姆级教程,如何从0到1完成知识库搭建

在这个信息爆炸的时代&#xff0c;如何获取、整理和应用知识成为了我们个体价值和企业核心竞争力打造的重要表现&#xff0c;搭建一个高效的知识库可以提升我们企业的竞争力&#xff0c;必要时还能快速切换赛道&#xff0c;开展一个新的领域。 今天我们将结合HelpLook 来与你一…

基于C++实现的3D野外赛车驾驶游戏源码+项目文档+汇报PPT

项目介绍&#xff1a;本项目实现了一个户外场景下的赛车游戏&#xff0c;可以通过键盘控制赛车的移动&#xff0c;视角为第二人称视角。场景中有汽车&#xff0c;建筑&#xff0c;道路&#xff0c;天空等物体&#xff0c;拥有光照和阴影的效果。通过粒子系统模拟尾气效果&#…

Kubernetes组件和架构简介

目录 一.概念简介 1.含义&#xff1a; 2.主要功能&#xff1a; 3.相关概念&#xff1a; 二.组件和架构介绍 1.master&#xff1a;集群的控制平面&#xff0c;管理集群 2.node&#xff1a;集群的数据平面&#xff0c;为容器提供工作环境 3.kubernetes简单架构图解 一.概…

Windows迁移文件的快速方法

文章目录 1.简单比较2.传输方法介绍&#xff1a;有线&#xff08;直连网络&#xff09;3.传输方法介绍&#xff1a;无线热点传输4. 共享文件夹的设置5.挂载共享文件夹 1.简单比较 方法传输速度有线传输接近900Mb无线热点传输接近500MbU盘传输基本上不超过100Mb 2.传输方法介绍…

小程序-uniapp:URL Link / 适用于在移动端 从短信、邮件、微信外网页 等场景打开小程序任意页面

一、背景介绍 小程序URL Scheme、URL Link是微信小程序后台生成的一种地址&#xff0c;适用于从短信、邮件、微信外网页 等场景打开小程序任意页面。所以&#xff0c;适用性极强。可与微信扫码携带参数跳转到小程序指定页面技术互补 若在微信外打开&#xff0c;用户可以在浏览…

【C++】C++ 类中的 this 指针用法 ③ ( 全局函数 与 成员函数 相互转化 | 有参构造函数设置默认参数值 | 返回匿名对象与返回引用 )

文章目录 一、全局函数 与 成员函数 相互转化1、成员函数转为全局函数 - 多了一个参数2、全局函数转为成员函数 - 通过 this 指针隐藏操作数 二、有参构造函数设置默认参数值三、返回匿名对象与返回引用四、完整代码示例 一、全局函数 与 成员函数 相互转化 1、成员函数转为全局…

2023-Chrome插件推荐

Chrome插件推荐 一键管理扩展 链接 https://chromewebstore.google.com/detail/lboblnfejcmcaplhnbkkfcienhlhpnni 介绍 一键开启、禁用Chrome插件。 Checker Plus for Gmail™ 链接 https://jasonsavard.com/zh-CN/Checker-Plus-for-Gmail https://chromewebstore.goo…

ElementUI之首页导航与左侧菜单

目录 一、Mock 1.1 什么是Mock.js 1.2 安装与配置 1.2.1 安装mock.js 1.2.2 引入mock.js 1.3 mock.js使用 1.3.1 定义测试数据文件 1.3.2 mock拦截Ajax请求 1.3.3 界面代码优化 二、总线 2.1 定义 2.2 类型分类 2.3 前期准备 2.4 配置组件与路由关系 2.4.1 配置…

计算机毕业设计 基于微信小程序的校园商铺系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

2023 “华为杯” 中国研究生数学建模竞赛(A题)深度剖析|数学建模完整代码+建模过程全解全析

华为杯数学建模A题 当大家面临着复杂的数学建模问题时&#xff0c;你是否曾经感到茫然无措&#xff1f;作为2021年美国大学生数学建模比赛的O奖得主&#xff0c;我为大家提供了一套优秀的解题思路&#xff0c;让你轻松应对各种难题。 让我们一起看看研赛的A题呀&#xff01; …

络安全开发和音视频开发哪个方向更有前景?

网络安全开发和音视频开发哪个方向更有前景&#xff1f; 随着互联网和移动互联网的飞速发展&#xff0c;音视频应用已经成为人们日常生活和工作中不可或缺的一部分。 从视频会议、在线教育、直播、短视频到游戏&#xff0c;音视频技术的应用场景越来越广泛&#xff0c;市场需求…

面试必杀技:Jmeter性能测试攻略大全(第二弹)

1. JMeter介绍与安装 JMeter介绍 JMeter是Apache组织开发的基于Java的压力测试工具。具有开源免费、框架灵活、多平台支持等优势。除了压力测试外&#xff0c;JMeter也可以应用的接口测试上。JMeter下载、安装及启动 下载&#xff1a; 访问JMeter官网&#xff1a;https://j…

机器学习——seaborn实用画图方法简介

0、seaborn简介: 前言:下面的总结只是介绍seaborn有哪些方法和属性,至于具体使用,通过下面给出的名称稍作查找即可。重点应该关注本文介绍的seaborn的使用方法seaborn与机器学习的关系: 知识图谱 0.1、了解即可的知识: seaborn:在matplotlib的基础上画一些更好看的图,在…

快速排序与代码

快速排序&#xff08;Quicksort&#xff09;是一种常用的排序算法&#xff0c;它基于分治的思想。 时间复杂度&#xff1a;O&#xff08;nlogn&#xff09; 空间复杂度&#xff1a;O&#xff08;logn&#xff09; 快速排序的基本思想如下&#xff1a; 选择一个元素作为基准&a…

Android Jetpack组件架构 :LiveData的使用和原理

Android Jetpack组件架构&#xff1a; LiveDate的使用和原理 导言 继Lifecycle组件之后我们接下来要介绍的就是LiveDate组件&#xff0c;所谓LiveDate字面意思上就是有声明的数据&#xff0c;当数据有改动时该组件可以感知到这个操作并将该事件通知到其观察者&#xff0c;这样…

STM32单片机入门学习(四)-蜂鸣器

蜂鸣器接线 低平蜂鸣器&#xff0c;低电平发声&#xff0c;高电平不发声&#xff0c; 三个排针&#xff0c;VCC接3.3v&#xff0c;GND接地&#xff0c;I/O接A0口&#xff0c;如图&#xff1a; 蜂鸣器代码&#xff1a;响一秒停半秒 #include "stm32f10x.h" #includ…

SQL server 创建存储过程

SQL Server如何创建存储过程 存储过程&#xff1a; 可以理解为完成特定功能的一组 SQL 语句集&#xff0c;存储在数据库中&#xff0c;经过第一次编译&#xff0c;之后的运行不需要再次编译&#xff0c;用户通过指定存储过程的名字并给出参数&#xff08;如果该存储过程带有参数…