Java Spring的高级装配
1.profile与bean
1.1 profile
如果我们在配置类中装配一个bean,但是这个bean与环境相关怎么办?
比如有一个类,它在开发环境采取一种模式,但是到了生产环境,有需要使用另一种环境。
当然,你可能会说,写两个类。但是实际情况可能不允许。这个时候,需要根据环境的不同生产不同的bean。它使用的方法是@Profile
举一个例子:
@Configuration
@Profile("dev")
public class DevProfileConfig{
@Bean
public A a{...}
@Bean
public B b{...}
}
只有在dev环境中,这个配置类下面的bean才能生成
@Profile不仅仅可以用在类上,也可用在方法中,代表这个类只有满足环境要求才能生产bean
@Configuration
public class DevProfileConfig{
@Bean
@Profile("dev1")
public A a{...}
@Bean
@Profile("dev2")
public B b{...}
@Bean
public B b{...}
}
我觉得不需要我解释上面的含义了。
但是需要注意。其他的bean指的是同一个类,如果其他bean没用指定相同的profile环境,那么这样的bean依然会生成。
同样,xml文件也能实现,这里就略过xml文件的讲解吧,都是一个逻辑。
1.2 激活profile
上面讲了profile掌握bean的生产条件,它与profile的环境相关。只要profile被激活,比如成为dev环境,那么相应的bean就会生效。如何让它激活?
它依赖于两个独立的属性:
(1).java.profile.active
(2).java.profile.default
它先查看active的值,如果处于激活状态,则取它的值;反之则取default,但是如果两个都没有激活,则profile控制的bean都无法生成。
那么如何设置这两个属性呢?
有很多办法。
其中常用的有
作为DispatcherServlet的初始化参数
作为web应用的上下文
作为环境变量
作为JVM的系统属性
在集成测试类上使用@ActiveProfile注解设置
还要其他很多,根据情况设置即可
2.条件化的bean
2.1 @Conditional
@Conditional
当满足某个条件的时候,该bean才会被创建。这种条件可能是某个库是否存在,其他某个bean是否被创建,某个特定的环境变量是否为特定的值。。。。。。
@Bean
@Conditional(MagicExistsCondition.class)
public MagicBean magicBean(){return new MagicBean();
}
上面的代码片段应该写在某个配置类中。它通过检查MagicExistsCondition这个类是否存在来决定要不要创建MagicBean。
它是通过Conditional这个接口实现来判断的,接口为:
public interface Conditional{boolean matches(Conditional ctxt, AnnotatedTypeMetadata metadata);
}
显式这个接口的类只需要实现它的matches方法即可,如果返回true,则代表条件满足,下面创建这个类来实现这个接口,以供条件判断
package com.example.demo.conditional;import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;public class MagicExistsConditional implements Condition {@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {Environment env = context.getEnvironment();return env.containsProperty("magic");}
}
通过ConditionContext类获取环境类,然后判断环境中有没有这个magic属性。
如果这么写肯定是返回false的,因为环境变量里面确实没有这个属性。
了解一下传入的两个参数很有必要
2.2 ConditionContext类和AnnotatedTypeMetadata类
ConditionContext类定义如下,它是一个接口,我们可以叫他条件上下文。
public interface ConditionContext {BeanDefinitionRegistry getRegistry();@NullableConfigurableListableBeanFactory getBeanFactory();Environment getEnvironment();ResourceLoader getResourceLoader();@NullableClassLoader getClassLoader();
}
getRegistry()返回的BeanDefinitionRegistry可以检查bean的定义。
getBeanFactory()返回的ConfigurableListableBeanFactory可以检查bean是否存在。
getEnvironment()返回的Environment可以检查环境变量属性以及值。
getResourceLoader()返回ResourceLoader可以读取加载的资源。
getClassLoader()返回ClassLoader可以判断类是否存在。
AnnotatedTypeMetadata接口如下
public interface AnnotatedTypeMetadata {MergedAnnotations getAnnotations();default boolean isAnnotated(String annotationName) {return this.getAnnotations().isPresent(annotationName);}@Nullabledefault Map<String, Object> getAnnotationAttributes(String annotationName) {return this.getAnnotationAttributes(annotationName, false);}@Nullabledefault Map<String, Object> getAnnotationAttributes(String annotationName, boolean classValuesAsString) {MergedAnnotation<Annotation> annotation = this.getAnnotations().get(annotationName, (Predicate)null, MergedAnnotationSelectors.firstDirectlyDeclared());return !annotation.isPresent() ? null : annotation.asAnnotationAttributes(Adapt.values(classValuesAsString, true));}@Nullabledefault MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName) {return this.getAllAnnotationAttributes(annotationName, false);}@Nullabledefault MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName, boolean classValuesAsString) {MergedAnnotation.Adapt[] adaptations = Adapt.values(classValuesAsString, true);return (MultiValueMap)this.getAnnotations().stream(annotationName).filter(MergedAnnotationPredicates.unique(MergedAnnotation::getMetaTypes)).map(MergedAnnotation::withNonMergedAttributes).collect(MergedAnnotationCollectors.toMultiValueMap((map) -> {return map.isEmpty() ? null : map;}, adaptations));}
}
这个接口显然都是检查注解的属性。就不详细解说了。
3. 处理自动装配的歧义性
3.1 @Qualifier
如果很多类都继承了同一个接口且都完成了bean的定义,那么在自动装配的时候,应该选哪个bean?比如下面这段代码:
//公共接口
public interface BaseClass{}
//下面三个类全都实现了公告接口@Component
public class OneClass implements BaseClass{}@Component
public class TwoClass implements BaseClass{}@Component
public class ThreeClass implements BaseClass{}//自动装配选择哪一个?
public class Test{@Autowiredprivate BaseClass b;
}
解决这个问题可以用以下几个方法
(1)@Primary
在@Bean下面注解,表示出现歧义的时候,优先使用这个bean。但是,同类型的只能在其中一个上使用。
(2)@Qualifier
它可以指定某一个bean
@Autowired
@Qualifier("id")
public void setData(BaseClass data){......}
这就相当于指定一个bean进行导入,这个bean的id是id
如果没有显式写id,一般bean的id就是类名的首字母小写,比如Oneclass的id就是oneClass
值得一提的是,id和类名紧耦合在一起可不是什么好事,因为变更以下名字,代码就需要修改了,不如直接给它限定一个id
比如
@Component
@Qualifier("one")
public class OneClass{......}
这样,OneClass的bean id就一直都是one了,通过这个名字可以索引到bean
如果相要写一个单元测试,就需要添加一个配置类,作为管理这三个bean的类。
3.2 自定义限定符
比如出现了一个大类都是数字类,但是下面又有了具体的One,Two,Three这样的小类,而我们想要有一个层次的限定符呢?
首先所有的数字类都可以使用@Qualifier("number")进行限定;
其次,为了对number下的各个小类进行限定
@Target({ElementType.CONSTRUCTOR, ElementType.FIFLD, ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPollicy.RUNTIME)
@Qualifier
publlic @interface Number()
上面的代码表示新建了一个Number注解,下面就可以使用@Number注解了。
然后Number注解可以缩小注解范围
@Number
@Qualifier("oneClass")
这代表限制对象的了两层限制。
4.bean的作用域
bean一般情况下是单例模式,但是它可以选择其他模型,比如:
(1)单例模式:每次都导入的都是同一个bean
(2)原型模式:每次创建的都是不同的bean
(3)会话模式:为每个会话创建一个bean
(4)请求模式:为每个请求创建一个bean
想要修改bean的模式,可以使用@Scope注解,比如修改bean的模式是原型模式
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
xml文件也差不多这样
这里不详细解释,只是简单说一下。
比如在购物时,每一个人都会有一个购物服务,每一个服务都需要一个购物车,那么我们希望服务每次都是新的,但是购物车都是同一个,不然每一次访问服务都创建一个新购物车,我们岂不是要到处翻上一次添加的购物记录吗?所有才会有会话的作用域。
5. 运行时值注入
我们希望在bean能够在运行时给自己的成员赋值,该如何做?
虽然之前提到在类里面配置,或者在xml种配置,但是这些值其实都是固定的,没办法更改。我们怎么在代码运行时让它自己去设置呢?
5.1 注入外部的值
在spring中,最简单的方法就是使用Environment来检索属性
比如在app.properties写入下面的代码
data.title=title
data.artist= artist
有一个类
public class BlackDisc implements CompactDisc{private String title;private String artist;public BlackDisc(String title, String artist) {this.title = title;this.artist = artist;}public void setTitle(String title) {this.title = title;}public void setArtist(String artist) {this.artist = artist;}@Overridepublic void play() {System.out.println("Playing " + title + " by " + artist);}}
通过配置类读取app.properties
import com.example.demo.project.BlackDisc;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;@Configuration
@PropertySource("classpath:app.properties")
public class Load {@AutowiredEnvironment env;@Beanpublic BlackDisc blackDisc(){return new BlackDisc(env.getProperty("data.title"),env.getProperty("data.artist"));}
}
测试输出
package com.example.demo.environment;import com.example.demo.autoSelectBean.BaseClass;
import com.example.demo.project.BlackDisc;
import com.example.demo.project.CDConfig;
import com.example.demo.project.CDPlayerConfig;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes= {Load.class})
public class Test3 {@Autowiredprivate BlackDisc blackDisc;@Testpublic void test() {blackDisc.play();}
}
总结:app.properties的信息会被Environment读取,我们可以使用getProperty来获取参数值
getProperty有多个重载
@Nullable String getProperty(String key);//返回字符串String getProperty(String key, String defaultValue);//返回字符串,但是如果key不存在,则返回默认值@Nullable <T> T getProperty(String key, Class<T> targetType);//可以返回指定的类型<T> T getProperty(String key, Class<T> targetType, T defaultValue);//可以返回指定的类型,如果key不存在,则返回默认值
因为我们的配置文件不一定都是String,也有可能是其他的类型。
Environment类还提供了其他的方法
boolean containsProperty(String key);//判断是否包含keyString getRequiredProperty(String key) throws IllegalStateException;//直接获取key对应的值,返回类型为String,但是如果key对应的值不存在,则抛出错误<T> T getRequiredProperty(String key, Class<T> targetType) throws IllegalStateException;//返回为指定类型
5.2 通过占位符获取属性值
也就是用${}
@Configuration
@PropertySource("classpath:app.properties")
public class Load {@AutowiredEnvironment env;@Value("${data.title}")String title;@Value("${data.artist}")String artist;@Beanpublic BlackDisc blackDisc(){return new BlackDisc(title , artist );}
}
比如上面的代码,就是从app文本中导入数据
5.3 使用Spring表达式语言进行装配
这里不做详细介绍,因为没有必要。在做web开发时,这东西到处都是。只需要看原理和几个案例。
Spring 表达式有很多特性,它可以通过下面的方法导入
(1)使用bean的id引用bean
(2)调用方法和访问对象属性
(3)对值进行运算
(4)正则表达式匹配
(5)集合操作
Spring表达式需要使用#{},也就是表达式要放在里面
比如通过某个类的方法
@Value("#{bean.getNumber()}")
Integer number;
相关文章:
Java Spring的高级装配
1.profile与bean 1.1 profile 如果我们在配置类中装配一个bean,但是这个bean与环境相关怎么办? 比如有一个类,它在开发环境采取一种模式,但是到了生产环境,有需要使用另一种环境。 当然,你可能会说&…...
分布式光伏发电系统电气一次部分设计(开题报告2)
毕业论文(设计)开题报告 题目 分布式光伏发电系统电气一次部分设计 题目类别 毕业设计 姓名 专业 班级 学号 一、选题背景及依据(简述国内外研究状况和相关领域中已有的研究成果(文献综述),选题目的、意义,列出主要参考文献) (一)选题背景与依据 选题背景与依据: …...
【设计模式-迪米特法则】
迪米特法则(Law of Demeter,LoD),也称为最少知识原则(Principle of Least Knowledge),是一种面向对象编程中的设计原则。它的核心思想是:一个对象应当尽可能少地了解其他对象&#x…...
Webpack安装
全局安装 npm install -g webpack webpack-cli安装后查看版本号: webpack -v初始化项目 npm init -yJS打包 webpack目录下创建配置文件webpack.config.js 以下配置的意思是:读取当前项目目录下src文件夹中的main.js(入口文件)…...
前端开发学习(一)VUE框架概述
一、MVC模式与MVVM模式 1.1mvc模式 MVC模式是移动端应用广泛的软件架构之一,MVC模式将应用程序划分为3部分:Model(数据模型)、View(用户界面视图)和Controller(控制器)。MVC模式的执行过程是将View层展示给用户,也就是通过 HTML页面接受用户动作&#…...
Linux操作系统的背景、发展历程及对比分析
1. UNIX发展历史 unix_百度百科 UNIX操作系统作为现代操作系统的奠基石,其发展历史可以追溯到20世纪60年代末。1969年,贝尔实验室的Ken Thompson、Dennis Ritchie以及他们的同事们为了实现一种多任务的、可移植的、简洁而高效的操作系统,开发…...
gaussdb 基础管理 数据库 表 用户 模式 权限 存储过程
数据库database #创建数据库,指定字符集UTF8,缺省情况下新数据库将通过复制标准系统数据库template0来创建,且仅支持使用template0来创建。 CREATE DATABASE devdb ENCODING UTF8 template template0; CREATE DATABASE testdb; 标识符的命名…...
i9-11900H+3070laptop+win10下的yolov5配置
参考博客:https://blog.csdn.net/qq_67105081/article/details/138232424 关键问题: 1、由之前装的CUDA11.6(有篇博客上可以换版本)CUDNN8.4.0Python3.9.13推后在指定的虚拟环境中装了Pytorch1.12.1(在AnnacondaPrompt下用pip命令…...
SpringBoot日常:封装redission starter组件
文章目录 逻辑实现POM.xmlRedissionConfigRedissionPropertiesRedissionUtilsspring.factories 功能测试application.yml配置POM.xmlTestController运行测试 本章内容主要介绍如何通过封装相关的redission连接配置和工具类,最终完成一个通用的redission starter。并…...
腾讯云技术深度解析:构建高效云原生应用与数据安全管理
腾讯云技术深度解析:构建高效云原生应用与数据安全管理 在当今快速发展的技术环境中,云计算已经成为企业数字化转型的关键驱动力。腾讯云作为中国领先的云服务提供商,凭借其卓越的技术和创新能力,为企业提供了高效、可扩展的云原…...
ACM与蓝桥杯竞赛指南 基本输入输出格式二
A B || 继续看第二个AB问题,A B || ,大家可以先自行读题,或者有经验直接看输入输出格式,发现依然是求AB,但它的输入数据为: 2 1 5 10 20 输出: 6 20 此题相比第一道而言,本…...
解决SolidWorks装配体无法更改透明度问题
这个问题是在零件上各个部件显示正常,且透明度可以更改,但是一到装配体上就出现问题都变成了灰色。更改透明度也不行。 解决方法: 1、因为该装配体里面存在过多的零部件层级的自定义外观(这些外观可能互相之前有了干扰࿰…...
2024_newstar_week1_crypto
baby_mod 题目 from Crypto.Util.number import * from enc import flagm bytes_to_long(flag) p getPrime(512) q getPrime(512) r getPrime(777) t getPrime(777) tmp getPrime(15) e 65537 n p*q print(f"c {pow(m,e,n)}") print(f"leak {p*r-q*…...
6.2 URDF集成Rviz基本流程
前面介绍过,URDF 不能单独使用,需要结合 Rviz 或 Gazebo,URDF 只是一个文件,需要在 Rviz 或 Gazebo 中渲染成图形化的机器人模型,当前,首先演示URDF与Rviz的集成使用,因为URDF与Rviz的集成较之于…...
双系统一体机电脑无法启动报错“Something has gone serously wrong: SBAT self-check failed: Security Policy Violation”
双系统一体机电脑无法启动 问题搜索解决办法解决开启时 F2 进入系统设置界面选择“疑难解答”选择“高级选项”选择“UEFI固件设置”进入“Start Menu”界面选择“Security”关闭相关选项 问题 在2024/8/14日Windows环境,系统更新了两个Windows更新项后,…...
八股面试2(自用)
mysql存储引擎 存储引擎:定义数据的存储方式,以及数据读取的实现逻辑 在以前数据库5.5默认MyISAM引擎,之后默认InnoDB引擎 MyISAM引擎的数据和索引是分开存储的,InnoDb将索引和文件存储在同一个文件。 MyISAM不支持事务&#…...
Leetcode 347 Top K Frequent Elements
题意: 求前k个出现频率最高的元素 首先得到一个频率图这是肯定的,下一步要考虑建立一个堆,堆中保存着前k个频率最大的数字,这个怎么做,可以用customized cmp来做,把数字存进去完事儿。注意这里不用 保存所有…...
[Linux网络编程]03-TCP协议
一.TCP协议数据通信的过程 TCP数据报如下,数据报中的标志位双端通信的关键。 三次握手: 1.客户端向服务端发送SYN标志位,请求建立连接,同时发送空包 2.服务端向客户端回发ACK标志位(即确认标志位,任何一端发送数据后都需要另一端…...
Windows和Linux在客户端/服务端在安全攻防方面的区别
Windows和Linux在客户端/服务端的安全攻防上存在一些显著区别,主要体现在系统架构、权限管理、安全工具、更新机制以及社区支持等方面。 一、系统架构与设计差异 1. 内核架构 Windows:Windows是一个闭源的操作系统,由微软开发和维护&#…...
VUE 仿神州租车-开放平台
项目背景: 神州租车是一家提供汽车租赁服务的公司,其API开放平台为开发者提供了访问神州租车相关服务和数据的接口。用VUE技术来仿照其开发平台。 成果展示: 首页: API文档: 关于我们:...
【JavaSE】绘图与事件入门学习笔记
-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角,以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向,距离坐标原点x个像素;第二个是y坐标,表示当前位置为垂直方向,距离坐标原点y个像素。 坐标体系-像素 …...
java高级——高阶函数、如何定义一个函数式接口类似stream流的filter
java高级——高阶函数、stream流 前情提要文章介绍一、函数伊始1.1 合格的函数1.2 有形的函数2. 函数对象2.1 函数对象——行为参数化2.2 函数对象——延迟执行 二、 函数编程语法1. 函数对象表现形式1.1 Lambda表达式1.2 方法引用(Math::max) 2 函数接口…...
Windows电脑能装鸿蒙吗_Windows电脑体验鸿蒙电脑操作系统教程
鸿蒙电脑版操作系统来了,很多小伙伴想体验鸿蒙电脑版操作系统,可惜,鸿蒙系统并不支持你正在使用的传统的电脑来安装。不过可以通过可以使用华为官方提供的虚拟机,来体验大家心心念念的鸿蒙系统啦!注意:虚拟…...
flow_controllers
关键点: 流控制器类型: 同步(Sync):发布操作会阻塞,直到数据被确认发送。异步(Async):发布操作非阻塞,数据发送由后台线程处理。纯同步(PureSync…...
js 设置3秒后执行
如何在JavaScript中延迟3秒执行操作 在JavaScript中,要设置一个操作在指定延迟后(例如3秒)执行,可以使用 setTimeout 函数。setTimeout 是JavaScript的核心计时器方法,它接受两个参数: 要执行的函数&…...
鸿蒙Navigation路由导航-基本使用介绍
1. Navigation介绍 Navigation组件是路由导航的根视图容器,一般作为Page页面的根容器使用,其内部默认包含了标题栏、内容区和工具栏,其中内容区默认首页显示导航内容(Navigation的子组件)或非首页显示(Nav…...
PLC入门【4】基本指令2(SET RST)
04 基本指令2 PLC编程第四课基本指令(2) 1、运用上接课所学的基本指令完成个简单的实例编程。 2、学习SET--置位指令 3、RST--复位指令 打开软件(FX-TRN-BEG-C),从 文件 - 主画面,“B: 让我们学习基本的”- “B-3.控制优先程序”。 点击“梯形图编辑”…...
项目进度管理软件是什么?项目进度管理软件有哪些核心功能?
无论是建筑施工、软件开发,还是市场营销活动,项目往往涉及多个团队、大量资源和严格的时间表。如果没有一个系统化的工具来跟踪和管理这些元素,项目很容易陷入混乱,导致进度延误、成本超支,甚至失败。 项目进度管理软…...
CentOS 7.9安装Nginx1.24.0时报 checking for LuaJIT 2.x ... not found
Nginx1.24编译时,报LuaJIT2.x错误, configuring additional modules adding module in /www/server/nginx/src/ngx_devel_kit ngx_devel_kit was configured adding module in /www/server/nginx/src/lua_nginx_module checking for LuaJIT 2.x ... not…...
使用 uv 工具快速部署并管理 vLLM 推理环境
uv:现代 Python 项目管理的高效助手 uv:Rust 驱动的 Python 包管理新时代 在部署大语言模型(LLM)推理服务时,vLLM 是一个备受关注的方案,具备高吞吐、低延迟和对 OpenAI API 的良好兼容性。为了提高部署效…...
