【SpringBoot:详解Bean装配】

🏡Java码农探花

 🔥 推荐专栏:<springboot学习>

🛸学无止境,不骄不躁,知行合一


文章目录
  • 前言
  • 一、IoC容器的简介
  • BeanFactory接口源码
  • 二、Bean装配
  • 扫描装配
  • 探索启动类
  • 条件装配
  • 自定义Bean
  • 总结


前言

IoC((Inversion of Control,控制反转)容器是 Spring 的核心,可以说 Spring 是一种基于 IoC容器编程的框架。因为Spring Boot 是基于注解的开发 Spring IoC, 所以我们就从全注解的方式来讲诉Bean装配。


一、IoC容器的简介

Spring IoC 容器是一个管理 Bean 的容器,在 Spring 的定义中,它要求所有的 IoC 容器都需要实现接口 BeanFactory,它是一个顶级容器接口。 我们从源码讲诉。

BeanFactory接口源码

package org.springframework.beans.factory;import org.springframework.beans.BeansException;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;public interface BeanFactory {// 前缀String FACTORY_BEAN_PREFIX = "&";// 多个getBean方法Object getBean(String name) throws BeansException;<T> T getBean(String name, Class<T> requiredType) throws BeansException;Object getBean(String name, Object... args) throws BeansException;<T> T getBean(Class<T> requiredType) throws BeansException;<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);<T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);// 是否包含Beanboolean containsBean(String name);//是否单例boolean isSingleton(String name) throws NoSuchBeanDefinitionException;// 是否原型boolean isPrototype(String name) throws NoSuchBeanDefinitionException;// 是否类型匹配boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;// 获取Bean的类型@NullableClass<?> getType(String name) throws NoSuchBeanDefinitionException;// 获取Bean的别名@NullableClass<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException;String[] getAliases(String name);
}

分析:

  • 上诉源码中加入了中文注释,通过它们就可以理解这些方法的含义。
  • 这里值得注意的是接口中的几个方法:
    • 首先我们看到了多个getBean 方法,这也是IoC 容器最重要的方法之一, 它的意义是从IoC 容器中获取Bean而从多个getBean方法中可以看到有按类型(bytype)获取Bean 的,也有按名称(by name)获取 Bean 的,这就意味着在 Spring IoC 容器中,允许我们按类型或者名称获取 Bean。这对理解后面将讲到的Spring 的依赖注入(Dependency Injection, DI) 是十分重要的。
    • isSingleton 方法则判断 Bean 是否在 Spring IoC 中为单例。这里需要记住的是在 Spring IoC 容器中,默认的情况下, Bean 都是以单例存在的,也就是使用 getBean 方法返回的都是同一个对象。与isSingleton 方法相反的是 isPrototype 方法,如果它返回的是 true,那么当我们使用 getBean 方法获取Bean 的时候, Spring IoC 容器就会创建一个新的 Bean 返回给调用者。

由于BeanFactory 的功能还不够强大,因此 Spring 在 BeanFactory 的基础上, 还设计了一个更为高级的接口 ApplicationContext。 它是 BeanFactory 的子接口之一, 在 Spring 的体系中 BeanFactory 和ApplicationContext 是最为重要的接口设计,在现实中我们使用的大部分 Spring IoC 容器是ApplicationContext 接口的实现类。

在这里插入图片描述

  • 在图中可以看到, ApplicationContext 接口通过继承上级接口,进而继承 BeanFactory 接口, 但是在BeanFactory 的基础上,扩展了消息国际化接口(MessageSource)、环境可配置接口 (EnvironmentCapable)、应用事件发布接口(ApplicationEventPublish巳r) 和资源模式解析接口(ResourcePatternResolver),所以它的功能会更为强大
  • 在Spring Boot 当中我们主要是通过注解来装配Bean到 Spring IoC 容器中,为了贴近 SpringBoot 的需要, 这里不再介绍与 XML 相关的 IoC 容器,而主要介绍一个基于注解的 IoC 容器,它就是AnnotationConfigApplicationContext,从名称就可以看出它是一个基于注解的 IoC 容器。 之所以研究它, 是因为Spring Boot 装配和获取 Bean 的方法与它如出一辙。

例:创建一个User类,然后使用AnnotationConfigApplicationContext构建IoC容器

public class User {private Long id; private String userName; 
/**setter and getter **/ 
}

import org.springframework.context.annotation.Bean; 
import org.springframework.context.annotation.Con f工guration ;
import com.springboot.chapter3.po] o.User;
@Configuration 
public class AppConfig { @Bean(name =”user” } public User ini tUser () { User user= new User (); user. set Id (1L) ; user.setUserName (”aa”); return user;}
}

@Configuration 代表这是一个 Java 配置文件, Spring 的容器会根据它来生成IoC 容器去装配Bean;@Bean 代表将 initUser 方法返回的 POJO 装配到 IoC 容器中,而其属性name 定义这个 Bean 的名称如果没有配置它,则将方法名称“initUser”作为 Bean 的名称保存到Spring IoC 容器中。

import org.apache. log4j .Logger; 
import org. springframework.context.ApplicationContext; 
import org. springframework.context annotation.AnnotationConfigApplicat工onContext;
import com.springboot.chapter3.po] o .User;
public class IoCTest { private static Logger log= Logger.getLogger(IoCTest.class); publ工c static 飞roid main (String [] args) { ApplicationContext ctx = new AnnotationConfigAppl丰cationContext(AppConfig. class);User user= ctx.getBean(User.class); }
}

代码中将Java 配置文件 AppConfig 传递给 AnnotationConfigApplicationContext 的构造方法,这样它就能够读取配置了。然后将配置里面的Bean装配到IoC容器中,于是可以使用 getBean方法获取对应的POJO。

二、Bean装配

扫描装配

上诉讲诉的User对象装配就是使用@Bean装配。但是如果一个个的 Bean 使用注解@Bean 注入 Spring loC 容器中,那将是一件很麻烦的事情。好在Spring 还允许我们进行扫描装配 Bean 到 loC 容器中,对于扫描装配而言使用的注解是@Component和@ComponentScan@Component 是标明l哪个类被扫描进入 Spring IoC 容器,而ComponentScan则是标明采用何种策略去扫描装配Bean

@Component(”user") 
public class User { @Value("1") private Long id; @Value("aa"} private String userName; 
/**setter and getter **/ 
}

这里的注解@Component表明这个类将被SpringIoC 容器扫描装配,其中配置的“user"则是作为Bean 的名称,当然你也可以不配置这个字符串,那么IoC容器就会把类名第一个字母作为小写其他不变作为Bean 名称放入到IoC 容器中注解@Value则是指定具体的值,使得Spring IoC给予对应的属性注入对应的值。为了让SpringIoC 容器装配这个类, 需要改造类AppConfig:

import org.springframework.context.annotat工on.ComponentScan;
import org.springframework.context.annotation Configuration; 
@Configuration 
@ComponentScan 
public class AppConfig {
}

这里加入了@ComponentScan,意味着它会进行扫描,但是它只会扫描类AppConfig所在的当前包和其子包。也就是@ComponentScan默认扫描当前类所在包及其子包。 所以User类的位置要注意。

测试:

Applicat工onContext ctx = new AnnotationConfigApplicationContext{AppConfig.class) ;
User user= ctx.getBean(User.class); 
log. info(user.getid());

为了更加合理,@ComponentScan还允许我们自定义扫描的包,我们看一下源码:

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 {};//Bean name生成器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 {};}
}

分析:

  • 首先可以通过配置项basePackages定义扫描的包名,在没有定义的情况下,它只会扫描当前包和其子包下的路径:还可以通过basePackageClasses 定义扫描的类;
  • 其中还有 includeFilters 和 excludeFilters, includeFilters 是定义满足过滤器(Filter)条件的 Bean 才去扫描, excludeFilters 则是排除过滤器条件的 Bean,它们都需要通过一个注解@Filter 去定义,它有一个type 类型,这里可以定义为注解或者正则式等类型。 classes定义注解类, pattern 定义正则式类

所以得出三个扫描路径表示:

@ComponentScan ("com.springboot.example.* ")
@ComponentScan(basePackages = {"com.springboot.example.pojo"})
@ComponentScan(basePackageClasses = {User.class} ) 

以及排除扫描包或类,让其不被装配:

//扫描example下所有包除了@Service装配的类
//这样,由于加入了 excludeFilters 的配置,使标注了@Service 的类将不被 IoC 容器扫描注入,这样就可以把它类排除到 Spring IoC容器中了。
@ComponentScan(basePackages = {"com.dragon.restart"},excludeFilters = {@ComponentScan.Filter(classes = Service.class)})
探索启动类

事实上,之前在 Spring Boot 的注解@SpringBootApplication 也注入了@ComponentScan,这里不妨探索其源码:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
//自定义排除的扫描类
@ComponentScan(excludeFilters = {@Filter(type = FilterType.CUSTOM,classes = {TypeExcludeFilter.class}
), @Filter(type = FilterType.CUSTOM,classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {//通过类型排除自动配置@AliasFor(annotation = EnableAutoConfiguration.class)Class<?>[] exclude() default {};//通过名称排除自动配置类@AliasFor(annotation = EnableAutoConfiguration.class)String[] excludeName() default {};//定义扫描包@AliasFor(annotation = ComponentScan.class,attribute = "basePackages")String[] scanBasePackages() default {};//定义被扫描的类@AliasFor(annotation = ComponentScan.class,attribute = "basePackageClasses")Class<?>[] scanBasePackageClasses() default {};

显然,通过它就能够定义扫描哪些包。但是这里需要特别注意的是,它提供的exclude和excludeName两个方法是对于其内部的自动配置类才会生效的。为了能够排除其他类,还可以再加入@ComponentScan以达到我们的目的。

条件装配

  • 例如在数据库连接池的配置中漏掉一些配置会造成数据源不能连接上。 在这样的情况下, IoC容器如果还进行数据源的装配, 则系统将会抛出异常,导致应用无法继续。这时倒是希望IoC容器不去装配数据源。
  • 为了处理这样的场景, Spring 提供了@Conditional注解帮助我们,而它需要配合另外一个接口Condition(org.springframework.context.annotation.Condition )来完成对应的功能。

装配的Bean:

@Bean(name = "dataSource", destroyMethod = "close" ) 
@Conditional(DatabaseConditional.class)
public DataSource getDataSource ( 
@Value("${database.driverName}") String driver, 
@Value("${database.url}") String url, 
@Value("${database.username}") String username, 
@Value("{database.password}") String password 
){ Properties props= new Properties(); props.setProperty("driver", driver); props setProperty("url", url); props.setProperty("username", username); props setProperty("password", password); DataSource dataSource = null; try { dataSource = BasicDataSourceFactory.createDataSource(props) ; ) catch (Exception e) { e.printStackTrace();}return dataSource;
}

自定义DatabaseConditional类:

public class DatabaseConditional implements Condition { 
/**
* 数据库装配条件
* 
* @param context 条件上下文
* @param metadata 注释类型的元数据
* @return true 装配 Bean,否则不装配
*/
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { //取出环境配置Environment env = context.getEnvironment(); //判断属性文件是否存在对应的数据库配置return env.containsProperty(”database.driverName” ) && env.containsProperty(”database.url”) && env.containsProperty(” database.username”) && env.containsProperty (”database.password");

matches 方法首先读取其上下文环境, 然后判定是否已经配置了对应的数据库信息。这样,当这些都己经配置好后则返回true。这个时候Spring会装配数据库连接池的Bean,否则是不装配的。

自定义Bean

  • 现实的Java 的应用往往需要引入许多来自第三方的包, 并且很有可能希望把第三方包的类对象也放入到Spring IoC 容器中,这时@Bean注解就可以发挥作用了。
  • 例如,要引入一个DBCP数据源,我们先在pom.xml上加入项目所需要DBCP包和数据库MySQL驱动程序的依赖。
<dependency> <groupid>org.apache.commons</groupid> <artifactid>commons-dbcp2</artifactid> 
</dependency> 
<dependency> <groupid>mysql</groupid> <artifactid>mysql-connector-ava</artifactid>
</dependency>

这样 DBCP 和数据库驱动就被加入到了项目中,接着将使用它提供的机制来生成数据源:

@Bean(name = "dataSource") 
@Conditional(DatabaseConditional.class)
public DataSource getDataSource (){ Properties props= new Properties(); props.setProperty("driver", driver); props setProperty("url", url); props.setProperty("username", username); props setProperty("password", password); DataSource dataSource = null; try { dataSource = BasicDataSourceFactory.createDataSource(props) ; ) catch (Exception e) { e.printStackTrace();}return dataSource;
}

这里通过@Bean 定义了其配置项 name 为“dataSource“,那么 Spring 就会把它返回的对象用名称“dataSource” 保存在 loC 容器中。当然, 你也可以不填写这个名称,那么它就会用你的方法名称作为Bean 名称保存到 IoC 容器中。通过这样,就可以将第三方包的类装配到SpringIoC容器中了。


总结

以上就是SpringBoot的Bean装配的详细讲解。

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

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

相关文章

Storm详细配置

一、认识Storm Apache Storm是个实时数据处理的“大能”&#xff0c;它可以实时接收、处理并转发大量数据流&#xff0c;就像一个高速运转的物流中心&#xff0c;确保数据及时、准确地到达目的地。我们要做的&#xff0c;就是把这个物流中心搭建起来&#xff0c;并且根据我们的…

版本管理|Git -目前最好的版本管理工具

文章目录 什么是版本为什么需要版本管理Git -目前最好的版本管理工具Git的关键特点和概念初始化Git保存代码改变git addgit commit -m ""git statusgit log branch分支管理git branch & git chekoutHEAD的本质git merge 合并fast forward merge 本地与远程如何解…

【一刷《剑指Offer》】面试题 3:二维数组中的查找

力扣对应题目链接&#xff1a;240. 搜索二维矩阵 II - 力扣&#xff08;LeetCode&#xff09; 核心考点&#xff1a;数组相关&#xff0c;特性观察&#xff0c;时间复杂度把握。 一、《剑指Offer》对应内容 二、分析题目 正常查找的过程本质就是排除的过程&#xff0c;谁排除…

【YOLOv9】完胜V8的SOTA模型Yolov9(论文阅读笔记)

官方论文地址&#xff1a; 论文地址点击即可跳转 官方代码地址&#xff1a; GitCode - 开发者的代码家园 官方代码地址点击即可跳转 1 总述 当输入数据经过各层的特征提取和变换的时候&#xff0c;都会丢失一定的信息。针对这一问题&#xff1a; 论文中提出的可编程梯度信息…

PYTHON 爬取彼岸图片升级版

import requests from lxml import etree import time import os from concurrent.futures import ThreadPoolExecutordate_dict {动漫: dongman,游戏: youxi,美女: meinv,风景: fengjing,影视: yingshi,汽车: qiche,人物: renwu,动物: dongwu,宗教: zongjiao,北京: beijing }…

2024.4.14力扣每日一题——设计哈希集合

2024.4.14 题目来源我的题解方法一 链表数组 题目来源 力扣每日一题&#xff1b;题序&#xff1a;705 我的题解 方法一 链表数组 由于给定限制次数为10000&#xff0c;所以构造一个长度为10001的链表数组。对于add操作先看数组对应的位置是否为null或者为空&#xff0c;若是…

免费的 ChatGPT 网站(六个)

&#x1f525;博客主页&#xff1a; 小羊失眠啦. &#x1f3a5;系列专栏&#xff1a;《C语言》 《数据结构》 《C》 《Linux》 《Cpolar》 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 文章目录 一、insCode二、讯飞星火三、豆包四、文心一言五、通义千问六、360智脑 现在智能…

【窃取网络游戏中虚拟物品的行为定性】

虚拟物品随着网络游戏产业的不断发展越来越走入人们的生活。与此同时&#xff0c;窃取网络游戏中虚拟装备、货币等侵害网络游戏虚拟物品的行为屡见不鲜&#xff0c;对网络游戏产业环境造成破坏的同时对游戏参与者的权益也造成了一定的侵害。对于窃取网络游戏虚拟物品的行为&…

2024-04-13 问AI: 什么是h5py文件?

文心一言 h5py文件是HDF5文件的一种Python接口表示形式。HDF5&#xff08;Hierarchical Data Format 5&#xff09;是一种用于存储和组织大量科学数据的文件格式。h5py是Python中的一个库&#xff0c;提供了对HDF5文件的高级封装&#xff0c;使得在Python中处理HDF5文件变得更…

Docker - Nginx

博文目录 文章目录 说明命令 说明 Docker Hub Nginx 数据卷数据卷印射在容器内的路径nginx.conf/etc/nginxnginx.html/usr/share/nginx/htmlnginx.log/var/log/nginx 容器内的路径说明/etc/nginx/nginx.conf配置文件/etc/nginx/conf.d配置目录/usr/share/nginx/html静态目录/…

【漏洞复现】WordPress LayerSlider插件SQL注入漏洞复现

声明&#xff1a;亲爱的读者&#xff0c;我们诚挚地提醒您&#xff0c;Aniya网络安全的技术文章仅供个人研究学习参考。任何因传播或利用本实验室提供的信息而造成的直接或间接后果及损失&#xff0c;均由使用者自行承担责任。Aniya网络安全及作者对此概不负责。如有侵权&#…

C数据结构:双向链表(带头循环)

前言 链表分多种&#xff0c;分别为 不带头不循环单向链表、不带头循环单向链表、带头循环单向链表、带头不循环单向链表 不带头不循环双向链表、不带头循环双向链表、带头循环双向链表、带头不循环双向链表 一共八种 在前一篇博客中完成的单链表即为不带头不循环单向链表…

杰发科技AC7840——CAN通信简介(5)_可变波特率设置

0. 简介 设置可变波特率时候&#xff0c;遇到2个坑&#xff0c;在此记录下来 使用该函数即可 can_time_segment_t bitrate2 s_canBitrate[CAN_BITRATE_250K]; CAN_DRV_SetBitrate(instance, &bitrate2); 1. 波特率指针注意不要空 查看设置波特率的接口&#xff0c;发现…

儿童学编程的好处

儿童学习编程是当今社会中越来越受到关注的话题。随着科技的发展&#xff0c;编程已经成为一种基本的技能&#xff0c;而儿童正是未来的希望。因此&#xff0c;让儿童学习编程具有诸多好处。 首先&#xff0c;学习编程可以培养儿童的逻辑思维能力。编程是一门需要严密思维和逻…

nodejs安装常用命令

安装 Node.js 后&#xff0c;你可以在命令行中使用以下常用命令&#xff1a; node&#xff1a;启动 Node.js 的交互式解释器&#xff0c;可以直接在命令行中执行 JavaScript 代码。 npm install <package-name>&#xff1a;安装一个 Node.js 模块&#xff0c;<packag…

吉他弹唱谱怎么制作 Guitar Pro 怎么写弹简谱 Guitar Pro8.02简谱

学习如何制作吉他弹唱谱是提升音乐创作和表现能力的重要一环。借助专业的软件工具如Guitar Pro&#xff0c;可以轻松地将音乐创意转化为可视化的乐谱&#xff0c;使演奏和分享变得更加便捷和高效。下面我们来看看吉他弹唱谱怎么制作&#xff0c;Guitar Pro 怎么写弹简谱的相关内…

el-date-picker调用回车事件

elementui的el-date-picker想要调用回车事件&#xff1a; <el-date-pickerv-model"state.date"type"date"value-format"YYYY-MM-DD HH:mm:ss"placeholder"选择日期"clearablekeydown.enter"handleDown"></el-date-…

threejs--01整体api整理

文章目录 threejs学习01 threejs的基本组成1. threejs的坐标系2. threejs的场景3. threejs相机的类型4. threejs光4.1 threejs的光4.2 threejs的阴影 5. threejs的物体5.1 threejs的曲线5.2 threejs的几何体5.3 threejs的材质5.3 threejs的纹理 6. threejs的动画6.1 threejs的动…

Llama 3下月正式发布,继续开源!

4月10日&#xff0c;Techcrunch消息&#xff0c;Meta在本周伦敦举办的一场活动中确定&#xff0c;下个月将正式发布Llama 3并且继续开源。 Meta全球事务总裁Nick Clegg表示&#xff0c;我们希望在下个月&#xff0c;甚至更短的时间内&#xff0c;正式推出新一代基础模型Llama …

云服务器宝塔ssh:tabby 部署SpringBoot项目

阿里云服务器 ----》 linux 云服务器 &#xff0c; ip , root 密码 tabby -----> ssh 连接工具 &#xff0c;用这个连接云服务器 去操作 云服务器 宝塔 -------》 可视化工具&#xff0c;快速部署 云服务器的 安装环境 &#xff0c;部署项目。 tabby :Tabby Terminal 下载…