【源码解析】SpringBoot自定义条件及@Conditional源码解析

news2025/2/27 14:24:04

自定义注解

自定义条件类

public class MessageCondition extends SpringBootCondition {
    @Override
    public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
        MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Message.class.getName());
        if (attrs != null) {
            for (Object value : attrs.get("value")) {
                if (!StringUtils.hasText(context.getEnvironment().getProperty("message")) || context.getEnvironment().getProperty("message").equals("message")) {
                    return ConditionOutcome.match(ConditionMessage.forCondition(Message.class)
                            .because("match"));
                }
            }
            return ConditionOutcome.noMatch(ConditionMessage.forCondition(Message.class)
                    .because("not match"));
        }
        return ConditionOutcome.match(ConditionMessage.forCondition(Message.class)
                .because("match"));
    }
}

自定义注解

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(MessageCondition.class)
public @interface Message {

    String[] value();
}

当条件Message满足的时候,生成ABean对象。

@Configuration
public class MessageConfiguration {

    @Message("abc")
    public ABean aBean() {
        return new ABean();
    }
}

标题复制10行,并且每行大于10个字符【源码解析】SpringBoot自定义条件及@Conditional源码解析】SpringBoot接口参数校验原理标题复制10行,并且每行大于10个字符【源码解析】SpringBoot自定义条件及@Conditional源码解析

源码解析

在spring启动过程中,会调用AbstractApplicationContext#invokeBeanFactoryPostProcessors,触发所有的BeanFactoryPostProcessorBeanDefinitionRegistryPostProcessorBeanFactoryPostProcessor其中的一种,会优先执行。而最熟悉的ConfigurationClassPostProcessor是最关键的BeanDefinitionRegistryPostProcessor

类扫描

ConfigurationClassParser#doProcessConfigurationClass,挨个处理注解,对需要的bean进行扫描。

	protected final SourceClass doProcessConfigurationClass(
			ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
			throws IOException {

		if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
			// Recursively process any member (nested) classes first
			processMemberClasses(configClass, sourceClass, filter);
		}

		// Process any @PropertySource annotations
		for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), PropertySources.class,
				org.springframework.context.annotation.PropertySource.class)) {
			if (this.environment instanceof ConfigurableEnvironment) {
				processPropertySource(propertySource);
			}
			else {
				logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
						"]. Reason: Environment must implement ConfigurableEnvironment");
			}
		}

		// Process any @ComponentScan annotations
		Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
		if (!componentScans.isEmpty() &&
				!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
			for (AnnotationAttributes componentScan : componentScans) {
				// The config class is annotated with @ComponentScan -> perform the scan immediately
				Set<BeanDefinitionHolder> scannedBeanDefinitions =
						this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
				// Check the set of scanned definitions for any further config classes and parse recursively if needed
				for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
					BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
					if (bdCand == null) {
						bdCand = holder.getBeanDefinition();
					}
					if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
						parse(bdCand.getBeanClassName(), holder.getBeanName());
					}
				}
			}
		}

		// Process any @Import annotations
		processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

		// Process any @ImportResource annotations
		AnnotationAttributes importResource =
				AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
		if (importResource != null) {
			String[] resources = importResource.getStringArray("locations");
			Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
			for (String resource : resources) {
				String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
				configClass.addImportedResource(resolvedResource, readerClass);
			}
		}

		// Process individual @Bean methods
		Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
		for (MethodMetadata methodMetadata : beanMethods) {
			configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
		}

		// Process default methods on interfaces
		processInterfaces(configClass, sourceClass);

		// Process superclass, if any
		if (sourceClass.getMetadata().hasSuperClass()) {
			String superclass = sourceClass.getMetadata().getSuperClassName();
			if (superclass != null && !superclass.startsWith("java") &&
					!this.knownSuperclasses.containsKey(superclass)) {
				this.knownSuperclasses.put(superclass, configClass);
				// Superclass found, return its annotation metadata and recurse
				return sourceClass.getSuperClass();
			}
		}

		// No superclass -> processing is complete
		return null;
	}

ClassPathScanningCandidateComponentProvider#isConditionMatch,在类路径扫描的时候,会进行条件判断。

	private boolean isConditionMatch(MetadataReader metadataReader) {
		if (this.conditionEvaluator == null) {
			this.conditionEvaluator =
					new ConditionEvaluator(getRegistry(), this.environment, this.resourcePatternResolver);
		}
		return !this.conditionEvaluator.shouldSkip(metadataReader.getAnnotationMetadata());
	}

ConditionEvaluator#shouldSkip(),获取指定类上的Conditional的value,配的是Condition,加载到条件集合中,进行挨个判断。

	public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) {
		if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) {
			return false;
		}

		if (phase == null) {
			if (metadata instanceof AnnotationMetadata &&
					ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) {
				return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION);
			}
			return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN);
		}

		List<Condition> conditions = new ArrayList<>();
		for (String[] conditionClasses : getConditionClasses(metadata)) {
			for (String conditionClass : conditionClasses) {
				Condition condition = getCondition(conditionClass, this.context.getClassLoader());
				conditions.add(condition);
			}
		}

		AnnotationAwareOrderComparator.sort(conditions);

		for (Condition condition : conditions) {
			ConfigurationPhase requiredPhase = null;
			if (condition instanceof ConfigurationCondition) {
				requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();
			}
			if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) {
				return true;
			}
		}

		return false;
	}

	private List<String[]> getConditionClasses(AnnotatedTypeMetadata metadata) {
		MultiValueMap<String, Object> attributes = metadata.getAllAnnotationAttributes(Conditional.class.getName(), true);
		Object values = (attributes != null ? attributes.get("value") : null);
		return (List<String[]>) (values != null ? values : Collections.emptyList());
	}

	private Condition getCondition(String conditionClassName, @Nullable ClassLoader classloader) {
		Class<?> conditionClass = ClassUtils.resolveClassName(conditionClassName, classloader);
		return (Condition) BeanUtils.instantiateClass(conditionClass);
	}
标题复制10行,并且每行大于10个字符【源码解析】SpringBoot自定义条件及@Conditional源码解析】SpringBoot接口参数校验原理标题复制10行,并且每行大于10个字符【源码解析】SpringBoot自定义条件及@Conditional源码解析

自动装配

AutoConfigurationImportSelector#getAutoConfigurationEntry自动装配处理的时候,也会处理SpringBootCondition

  • OnBeanCondition
  • OnClassCondition
  • OnWebApplicationCondition

均继承了FilteringSpringBootCondition,该类实现了FilteringSpringBootCondition

AutoConfigurationImportSelector#getConfigurationClassFilter,加载AutoConfigurationImportFilter,使用的是Springboot的SPI。

	private ConfigurationClassFilter getConfigurationClassFilter() {
		if (this.configurationClassFilter == null) {
			List<AutoConfigurationImportFilter> filters = getAutoConfigurationImportFilters();
			for (AutoConfigurationImportFilter filter : filters) {
				invokeAwareMethods(filter);
			}
			this.configurationClassFilter = new ConfigurationClassFilter(this.beanClassLoader, filters);
		}
		return this.configurationClassFilter;
	}
标题复制10行,并且每行大于10个字符【源码解析】SpringBoot自定义条件及@Conditional源码解析】SpringBoot接口参数校验原理标题复制10行,并且每行大于10个字符【源码解析】SpringBoot自定义条件及@Conditional源码解析

OnPropertyCondition

OnPropertyCondition的判断条件是OnPropertyCondition.Spec#collectProperties,结果存放到noMatchmatch

@Order(Ordered.HIGHEST_PRECEDENCE + 40)
class OnPropertyCondition extends SpringBootCondition {

	@Override
	public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
		List<AnnotationAttributes> allAnnotationAttributes = metadata.getAnnotations()
				.stream(ConditionalOnProperty.class.getName())
				.filter(MergedAnnotationPredicates.unique(MergedAnnotation::getMetaTypes))
				.map(MergedAnnotation::asAnnotationAttributes).collect(Collectors.toList());
		List<ConditionMessage> noMatch = new ArrayList<>();
		List<ConditionMessage> match = new ArrayList<>();
		for (AnnotationAttributes annotationAttributes : allAnnotationAttributes) {
			ConditionOutcome outcome = determineOutcome(annotationAttributes, context.getEnvironment());
			(outcome.isMatch() ? match : noMatch).add(outcome.getConditionMessage());
		}
		if (!noMatch.isEmpty()) {
			return ConditionOutcome.noMatch(ConditionMessage.of(noMatch));
		}
		return ConditionOutcome.match(ConditionMessage.of(match));
	}

	private ConditionOutcome determineOutcome(AnnotationAttributes annotationAttributes, PropertyResolver resolver) {
		Spec spec = new Spec(annotationAttributes);
		List<String> missingProperties = new ArrayList<>();
		List<String> nonMatchingProperties = new ArrayList<>();
		spec.collectProperties(resolver, missingProperties, nonMatchingProperties);
		if (!missingProperties.isEmpty()) {
			return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnProperty.class, spec)
					.didNotFind("property", "properties").items(Style.QUOTE, missingProperties));
		}
		if (!nonMatchingProperties.isEmpty()) {
			return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnProperty.class, spec)
					.found("different value in property", "different value in properties")
					.items(Style.QUOTE, nonMatchingProperties));
		}
		return ConditionOutcome
				.match(ConditionMessage.forCondition(ConditionalOnProperty.class, spec).because("matched"));
	}

	private static class Spec {

		private final String prefix;

		private final String havingValue;

		private final String[] names;

		private final boolean matchIfMissing;

		Spec(AnnotationAttributes annotationAttributes) {
			String prefix = annotationAttributes.getString("prefix").trim();
			if (StringUtils.hasText(prefix) && !prefix.endsWith(".")) {
				prefix = prefix + ".";
			}
			this.prefix = prefix;
			this.havingValue = annotationAttributes.getString("havingValue");
			this.names = getNames(annotationAttributes);
			this.matchIfMissing = annotationAttributes.getBoolean("matchIfMissing");
		}

		private String[] getNames(Map<String, Object> annotationAttributes) {
			String[] value = (String[]) annotationAttributes.get("value");
			String[] name = (String[]) annotationAttributes.get("name");
			Assert.state(value.length > 0 || name.length > 0,
					"The name or value attribute of @ConditionalOnProperty must be specified");
			Assert.state(value.length == 0 || name.length == 0,
					"The name and value attributes of @ConditionalOnProperty are exclusive");
			return (value.length > 0) ? value : name;
		}

		private void collectProperties(PropertyResolver resolver, List<String> missing, List<String> nonMatching) {
			for (String name : this.names) {
				String key = this.prefix + name;
				if (resolver.containsProperty(key)) {
					if (!isMatch(resolver.getProperty(key), this.havingValue)) {
						nonMatching.add(name);
					}
				}
				else {
					if (!this.matchIfMissing) {
						missing.add(name);
					}
				}
			}
		}

		private boolean isMatch(String value, String requiredValue) {
			if (StringUtils.hasLength(requiredValue)) {
				return requiredValue.equalsIgnoreCase(value);
			}
			return !"false".equalsIgnoreCase(value);
		}

		@Override
		public String toString() {
			StringBuilder result = new StringBuilder();
			result.append("(");
			result.append(this.prefix);
			if (this.names.length == 1) {
				result.append(this.names[0]);
			}
			else {
				result.append("[");
				result.append(StringUtils.arrayToCommaDelimitedString(this.names));
				result.append("]");
			}
			if (StringUtils.hasLength(this.havingValue)) {
				result.append("=").append(this.havingValue);
			}
			result.append(")");
			return result.toString();
		}

	}

}

标题复制10行,并且每行大于10个字符【源码解析】SpringBoot自定义条件及@Conditional源码解析】SpringBoot接口参数校验原理标题复制10行,并且每行大于10个字符【源码解析】SpringBoot自定义条件及@Conditional源码解析

@Profile

@Profile也是一个条件注解

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(ProfileCondition.class)
public @interface Profile {

	/**
	 * The set of profiles for which the annotated component should be registered.
	 */
	String[] value();

}

ProfileCondition的条件判断。

class ProfileCondition implements Condition {

	@Override
	public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
		MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
		if (attrs != null) {
			for (Object value : attrs.get("value")) {
				if (context.getEnvironment().acceptsProfiles(Profiles.of((String[]) value))) {
					return true;
				}
			}
			return false;
		}
		return true;
	}

}
标题复制10行,并且每行大于10个字符【源码解析】SpringBoot自定义条件及@Conditional源码解析】SpringBoot接口参数校验原理标题复制10行,并且每行大于10个字符【源码解析】SpringBoot自定义条件及@Conditional源码解析

@ConditionalOnExpression

注解使用OnExpressionCondition

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
@Conditional({OnExpressionCondition.class})
public @interface ConditionalOnExpression {
    String value() default "true";
}

OnExpressionCondition,封装value值,从context中获取ConfigurableListableBeanFactory,再从beanFactory获取BeanExpressionResolver,执行resolver.evaluate(expression, expressionContext);,得到结果。

@Order(Ordered.LOWEST_PRECEDENCE - 20)
class OnExpressionCondition extends SpringBootCondition {

	@Override
	public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
		String expression = (String) metadata.getAnnotationAttributes(ConditionalOnExpression.class.getName())
				.get("value");
		expression = wrapIfNecessary(expression);
		ConditionMessage.Builder messageBuilder = ConditionMessage.forCondition(ConditionalOnExpression.class,
				"(" + expression + ")");
		expression = context.getEnvironment().resolvePlaceholders(expression);
		ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
		if (beanFactory != null) {
			boolean result = evaluateExpression(beanFactory, expression);
			return new ConditionOutcome(result, messageBuilder.resultedIn(result));
		}
		return ConditionOutcome.noMatch(messageBuilder.because("no BeanFactory available."));
	}

	private boolean evaluateExpression(ConfigurableListableBeanFactory beanFactory, String expression) {
		BeanExpressionResolver resolver = beanFactory.getBeanExpressionResolver();
		if (resolver == null) {
			resolver = new StandardBeanExpressionResolver();
		}
		BeanExpressionContext expressionContext = new BeanExpressionContext(beanFactory, null);
		Object result = resolver.evaluate(expression, expressionContext);
		return (result != null && (boolean) result);
	}

	/**
	 * Allow user to provide bare expression with no '#{}' wrapper.
	 * @param expression source expression
	 * @return wrapped expression
	 */
	private String wrapIfNecessary(String expression) {
		if (!expression.startsWith("#{")) {
			return "#{" + expression + "}";
		}
		return expression;
	}

}

在这里插入图片描述

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

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

相关文章

成年人的崩溃只在一瞬间,程序员凌晨三点写的代码竟被女友删了...

对于恋爱中的情侣来说&#xff0c;吵架是很正常的事情&#xff0c;就算是再怎么亲密&#xff0c;也难免会出现意见不合的时候。 吵架不可怕&#xff0c;可怕的是&#xff0c;受吵架情绪的影响&#xff0c;做出一些比较“极端”的事情。 之前某社交平台上一位女生吐槽自己的男…

Ubuntu 本地部署 Stable Diffusion web UI

Ubuntu 本地部署 Stable Diffusion web UI 0. 什么是 Stable Diffusion1. 什么是 Stable Diffusion web UI2. Github 地址3. 安装 Miniconda34. 创建虚拟环境5. 安装 Stable Diffusion web UI6. 启动 Stable Diffusion web UI7. 访问 Stable Diffusion web UI8. 其他 0. 什么是…

我这里取出来的数据(最后边的excel)有点问题,我没有要取性别的数据,但是表里有...

点击上方“Python爬虫与数据挖掘”&#xff0c;进行关注 回复“书籍”即可获赠Python从入门到进阶共10本电子书 今 日 鸡 汤 上穷碧落下黄泉&#xff0c;两处茫茫皆不见。 大家好&#xff0c;我是皮皮。 一、前言 前几天在Python钻石群【不争】问了一个Python自动化办公的问题&…

PyCharm安装教程,图文教程(超详细)

「作者简介」&#xff1a;CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 「推荐专栏」&#xff1a;对网络安全感兴趣的小伙伴可以关注专栏《网络安全入门到精通》 PyCharm 一、PyCharm下载安装二、Python下载安装三、创建项目四、安装模块1、pip安装2、P…

商城系统商品属性的数据库设计思路

京东商城的数据库是如何搭建的&#xff0c;那么多商品&#xff0c;每种商品的参数各不相同&#xff0c;是怎样设计数据库的&#xff1f; 在提及这种设计思路前&#xff0c;首先得了解数据表可以分为两种结构: 1\横表,也就是我们经常用到的表结构&#xff0c; 2\纵表,这种结构…

ASEMI代理LT8609AJDDM#WTRPBF原装ADI车规级芯片

编辑&#xff1a;ll ASEMI代理LT8609AJDDM#WTRPBF原装ADI车规级芯片 型号&#xff1a;LT8609AJDDM#WTRPBF 品牌&#xff1a;ADI /亚德诺 封装&#xff1a;DFN-10 批号&#xff1a;2023 安装类型&#xff1a;表面贴装型 引脚数量&#xff1a;10 工作温度:-40C~125C 类型…

MySQL学习---13、存储过程与存储函数

1、存储过程概述 MySQL从5.0版本开始支持存储过程和函数。存储过程和函数能够将负杂的SQL逻辑封装在一起&#xff0c;应用程序无序关注存储过程和函数内部复杂的SQL逻辑&#xff0c;而只需要简单的调用存储过程和函数就可以。 1.1 理解 含义&#xff1a;存储过程的英文是Sto…

ASEMI代理LTC2954CTS8-2#TRMPBF原装ADI车规级芯片

编辑&#xff1a;ll ASEMI代理LTC2954CTS8-2#TRMPBF原装ADI车规级芯片 型号&#xff1a;LTC2954CTS8-2#TRMPBF 品牌&#xff1a;ADI/亚德诺 封装&#xff1a;TSOT-23-8 批号&#xff1a;2023 引脚数量&#xff1a;23 工作温度&#xff1a;-40C~85C 安装类型&#xff1a…

若依框架(RuoYI)项目打包(jar)方法,部署到 Linux 服务器

序言 在若依框架的 bin 目录下&#xff0c;存在着三个 bat 文件&#xff0c;一个是清除之前的依赖的自动化 bat 脚本&#xff08;clean.bat&#xff09;&#xff0c;一个是自动化项目打包的 bat 脚本&#xff08;package.bat&#xff09;&#xff0c;一个是运行若依项目的脚本…

云原生CAx软件:技术约束

Pivotal公司的Matt Stine于2013年首次提出Cloud Native(云原生)的概念&#xff0c;从概念提出到技术落地&#xff0c;云原生还处于不断发展过程中&#xff0c;关于云原生的定义也不断在完善。 云原生为使用开源软件堆栈来创建容器化、动态编排和面向微服务的应用程序。 Heroku创…

YOLOv8 深度解析!一文看懂,快速上手实操(附实践代码)

关注并星标 从此不迷路 计算机视觉研究院 公众号ID&#xff5c;ComputerVisionGzq 学习群&#xff5c;扫码在主页获取加入方式 开源地址&#xff1a;https://github.com/ultralytics/ultralytics 计算机视觉研究院专栏 作者&#xff1a;Edison_G YOLOv8 是 ultralytics 公司在 …

消息队列测试场景和redis测试场景

一、消息队列测试场景 1、什么是消息队列。你是怎么测试的&#xff1f; 解题思路&#xff1a; 什么是消息队列。 消息队列应用场景。 消息队列测试点列举。 2、什么是消息队列 Broker&#xff1a;消息服务器&#xff0c;提供消息核心服务 Producer&#xff1a;消息生产者&a…

Python入门(八)字典(一)

字典&#xff08;一&#xff09; 1.字典概述2.一个简单的字典3.使用字典3.1 访问字典中的值3.2添加键值对3.3 先创建一个空字典3.4 修改字典中的值3.5 删除键值对 作者&#xff1a;xiou 1.字典概述 在本章中&#xff0c;你将学习能够将相关信息关联起来的Python字典&#xff…

二叉排序树查找成功和不成功的平均查找长度

理解二叉树的特性: 1)结点:包含一个数据元素及若干指向子树分支的信息。 2)结点的度:一个结点拥有子树的数据成为结点的度。 3)叶子结点:也称为终端结点,没有子树的结点或者度为零的结点。 4)分支结点:也称为非终端结点,度不为零的结点成为非终端结点。 5)结点…

视觉检测设备主要有哪几分类

在如今的工业4.0时代&#xff0c;整个行业都在加速智能制造的脚步&#xff0c;此时我们就不得不说其其中重要的环节——视觉检测&#xff0c;本身其只是属于机器视觉中一个应用的分支&#xff0c;但是因其应用广泛&#xff0c;久而久之很多工厂行业的从业者在遇到需要外观检测问…

GDB 的简单使用

GDB 的简单使用 一、启动调试二、常用调试命令1.list&#xff08;显示程序源代码&#xff09;2.break、tbreak、delete、disable、enable 和 info break&#xff08;断点操作&#xff09;3.run&#xff08;运行源程序&#xff09;4.next 和 step&#xff08;单步调试&#xff0…

大型语言模型 (LLM) 的两条发展路线:Finetune v.s. Prompt

前言 如果你对这篇文章感兴趣&#xff0c;可以点击「【访客必读 - 指引页】一文囊括主页内所有高质量博客」&#xff0c;查看完整博客分类与对应链接。 在大型语言模型的研究中&#xff0c;研究者对其有两种不同的期待&#xff0c;也可以理解为是两种不同的路线&#xff0c;具…

【LeetCode: 494. 目标和 | 暴力递归=>记忆化搜索=>动态规划 | 背包模型】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

【Spring框架三】——Spirng IOC和DI的实现

系列文章目录 【Spring框架一】——Spring框架简介 Spirng IOC和DI的实现 系列文章目录前言一、IDEA新建Spring 项目二、使用XML文件的方式实现IOC和DI1.创建XML文件2. 通过构造函数的方式进行注入运行结果3.setter方法进行注入 三、使用注解的方式实现IOC和DISpring 5中的常用…

Django SQL注入漏洞 CVE-2022-28346

漏洞简介 Django 在2022年发布的安全更新&#xff0c;修复了在 QuerySet 的 annotate()&#xff0c; aggregate()&#xff0c; extra() 等函数中存在的 SQL 注入漏洞。 影响版本 2.2< Django Django <2.2.283.2< Django Django <3.2.134.0< Django Django <4…