第07篇:巧用Spring类型转换, ConverterFormatter知识点学习。

news2024/11/20 9:38:12

公众号: 西魏陶渊明

CSDN: https://springlearn.blog.csdn.net

天下代码一大抄, 抄来抄去有提高, 看你会抄不会抄!

文章目录

    • 一、前言
    • 1.1 类型转换
    • 1.2 格式化输出
    • 二、Converter 类型转换
    • 2.1 Converter
      • 2.1.1 接口定义
      • 2.1.2 接口功能
    • 2.2 ConverterFactory
      • 2.2.1 接口定义
      • 2.2.2 接口功能
    • 2.3 GenericConverter
      • 2.3.1 接口定义
      • 2.3.2 接口功能
    • 2.4 ConditionalGenericConverter
      • 2.4.1 接口定义
    • 2.5 Spring 实践
      • 2.5.1 ConversionService
      • 2.5.2 硬编码使用
      • 2.5.3 整合Spring
    • 三、Formatter 格式化输出
    • 3.1 自定义Formatter
    • 3.1 注解驱动Formatter

一、前言

本篇文章中的内容,非常的小众,虽然在实际开发中,基本上不会有使用的场景,但是在Spring中却无处不在的知识点。因为我们是学习Spring,所以我们最好了解一下。希望对你有用,最终能运用在Spring框架的扩展上。

本篇文章,主要学习两个东西。第一个是类型转换, 第二个是格式化输出(支持国际化)。

1.1 类型转换

类型转换,比如说Long类型转换Date、String类型转换Long类型。
在实际的开发中我们可能直接使用 BeanUtils.copy() 或者其他三方工具来实现,但其实Spring已经提供了这种的接口能力了。我们只需要下面这样就可以了。

如下演示,将Long类型转Date。

@SpringBootApplication
public class Application {

    // 注册一个转换器,目标由Long转Date
    public static class LongToDateConvert implements Converter<Long, Date> {
        @Override
        public Date convert(Long source) {
            return new Date(source);
        }
    }

    @Bean("customerConvert")
    public ConversionServiceFactoryBean customerConvert() {
        ConversionServiceFactoryBean conversionServiceFactoryBean = new ConversionServiceFactoryBean();
        conversionServiceFactoryBean.setConverters(Collections.singleton(new LongToDateConvert()));
        return conversionServiceFactoryBean;
    }
}
@SpringBootTest
@TestConfiguration
public class SpringConvertTest {

    @Autowired
    @Qualifier("customerConvert")
    private ConversionService conversionService;

    @Test
    public void test() {
        // 直接使用即可。
        Date convert = conversionService.convert(System.currentTimeMillis(), Date.class);
        // Mon Oct 17 21:38:07 CST 2022
        System.out.println(convert);
    }
}

举一反三,通过上面的接口能力,我们还能实现更多的使用场景。如上我们只实现了1:1的转换,其还可以1:N、N:N,更多的内容下面会讲。

1.2 格式化输出

什么是格式化输出,往往只针对的是文本类型。

  1. 对象类型转文本类型
  2. 文本类型转对象类型

所以格式化是围绕String进行的,在格式化这方面最典型的一个案例就是国际化。

同样的文本,针对不同国家地域展示为当地的语言类型。
下面我们看他的接口定义。

public interface Formatter<T> extends Printer<T>, Parser<T> {
}

@FunctionalInterface
public interface Printer<T> {
  // 对象类型,转换String类型,支持国际化
	String print(T object, Locale locale);
}
@FunctionalInterface
public interface Parser<T> {
  // String类型转换泛型,支持国际化
	T parse(String text, Locale locale) throws ParseException;
}

下面我们看详细的内容。

二、Converter 类型转换

Spring 3 引入了一个core.convert提供通用类型转换系统的包。系统定义了一个 SPI 来实现类型转换逻辑和一个 API 来在运行时执行类型转换。在 Spring 容器中,您可以使用此系统作为实现的替代PropertyEditor方案,将外部化的 bean 属性值字符串转换为所需的属性类型。您还可以在应用程序中需要类型转换的任何地方使用公共 API。

接口介绍
Converter单一的类型转换,从泛型 S -> T
ConverterFactory按照官方的描述是,具有层次的转换,从泛型 S -> 转换成 R 的子类,实现一对多个类型的转换
GenericConverter前面是一对多,一对一,这个是多对多
ConditionalGenericConverter在前者的基础上,添加上条件判断,符合条件才进行转换

下面我们来以此看下,每个接口的

2.1 Converter

2.1.1 接口定义

package org.springframework.core.convert.converter;

public interface Converter<S, T> {

    T convert(S source);
}

这个接口非常的简单,没什么好解释的。我们要创建自己的转换器,只用实现Converter接口就可以了。

2.1.2 接口功能

实现从 S,向 T 的泛型转换,Spring提供了很多内置的转换,如下示例。

Spring默认提供了很多的默认实现,下面我们看一个简单的实现。看下面的源码,感觉Spring是真的用心呀。

  • on、true、1、yes 都会转换成 true
  • off、false、0、no 都会转换成 false
final class StringToBooleanConverter implements Converter<String, Boolean> {
    private static final Set<String> trueValues = new HashSet(8);
    private static final Set<String> falseValues = new HashSet(8);

    StringToBooleanConverter() {
    }

    @Nullable
    public Boolean convert(String source) {
        String value = source.trim();
        if (value.isEmpty()) {
            return null;
        } else {
            value = value.toLowerCase();
            if (trueValues.contains(value)) {
                return Boolean.TRUE;
            } else if (falseValues.contains(value)) {
                return Boolean.FALSE;
            } else {
                throw new IllegalArgumentException("Invalid boolean value '" + source + "'");
            }
        }
    }

    static {
        trueValues.add("true");
        trueValues.add("on");
        trueValues.add("yes");
        trueValues.add("1");
        falseValues.add("false");
        falseValues.add("off");
        falseValues.add("no");
        falseValues.add("0");
    }
}

2.2 ConverterFactory

ConverterFactory 跟 Converter的区别在于, ConverterFactory 提供一个泛化的接口。根据泛型获取自己的转换类。但是前提是Converter要具备能处理返回接口的能力。以此来处理 1 对 N的转换。

2.2.1 接口定义

package org.springframework.core.convert.converter;

public interface ConverterFactory<S, R> {

    <T extends R> Converter<S, T> getConverter(Class<T> targetType);
}

可以看到泛型是从 S -> R, getConverter 泛型方法允许 <T extends R> 返回 T

2.2.2 接口功能

package org.springframework.core.convert.support;

final class StringToEnumConverterFactory implements ConverterFactory<String, Enum> {

    public <T extends Enum> Converter<String, T> getConverter(Class<T> targetType) {
        return new StringToEnumConverter(targetType);
    }

    private final class StringToEnumConverter<T extends Enum> implements Converter<String, T> {

        private Class<T> enumType;

        public StringToEnumConverter(Class<T> enumType) {
            this.enumType = enumType;
        }
        // 将泛化类型
        public T convert(String source) {
            return (T) Enum.valueOf(this.enumType, source.trim());
        }
    }
}

通过 <T exends R> 的限定, 最终实现 1 : N 的转换。

2.3 GenericConverter

  • Converter 处理 1:1的转换
  • ConverterFactory 处理 1:N的转换
  • GenericConverter 处理里 N: N的转换

下面我们看接口

2.3.1 接口定义

  • getConvertibleTypes 返回了一个集合,而集合中每个key都是一个键值对。就支持 N:N 了。
package org.springframework.core.convert.converter;

public interface GenericConverter {

    public Set<ConvertiblePair> getConvertibleTypes();

    Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
}

getConvertibleTypes() 是一个Set集合。可以看到ConvertiblePair是成对的,只要转换双方是包含在这set结合中,都会调用这个进行转换。

final class ConvertiblePair {
		private final Class<?> sourceType;
		private final Class<?> targetType;
}  

意味着一个转换器可以处理多种类型的转换。

2.3.2 接口功能

下面举一个例子

 @Data
    public static class SourceOne {
        private String name;
    }

    @Data
    public static class TargetOne {
        private String name;
    }

    @Data
    public static class TargetTwo {
        private String name;
    }


    @Test
    public void test() {
        ApplicationConversionService applicationConversionService = new ApplicationConversionService();
        GenericConverter genericConverter = new GenericConverter() {
            @Override
            public Set<ConvertiblePair> getConvertibleTypes() {
                Set<ConvertiblePair> paris = new HashSet<>();
                paris.add(new ConvertiblePair(SourceOne.class, TargetOne.class));
                paris.add(new ConvertiblePair(SourceOne.class, TargetTwo.class));

                return paris;
            }

            @Override
            public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
                if (sourceType.getObjectType().equals(SourceOne.class) && targetType.getObjectType().equals(TargetOne.class)) {
                    TargetOne targetOne = new TargetOne();
                    targetOne.setName(((SourceOne) source).getName() + "-> TargetOne");
                    return targetOne;
                }
                if (sourceType.getObjectType().equals(SourceOne.class) && targetType.getObjectType().equals(TargetTwo.class)) {
                    TargetTwo TargetTwo = new TargetTwo();
                    TargetTwo.setName(((SourceOne) source).getName() + "-> TargetTwo");
                    return TargetTwo;
                }
                return null;
            }
        };
        applicationConversionService.addConverter(genericConverter);
        SourceOne sourceOne = new SourceOne();
        sourceOne.setName("Jay");
        System.out.println(applicationConversionService.convert(sourceOne, TargetOne.class));
        System.out.println(applicationConversionService.convert(sourceOne, TargetTwo.class));

    }

2.4 ConditionalGenericConverter

有时,你希望 Converter只有在特定条件成立时才运行,此时可以实现这个接口。这个接口是实现了 GenericConverterConditionalConverter

2.4.1 接口定义

public interface ConditionalConverter {

    boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);
}

public interface ConditionalGenericConverter extends GenericConverter, ConditionalConverter {
}

2.5 Spring 实践

ConversionService定义了一个统一的 API,用于在运行时执行类型转换逻辑。

2.5.1 ConversionService

package org.springframework.core.convert;

public interface ConversionService {
    // 如果sourceType的对象可以转换为targetType ,则返回true 
    boolean canConvert(Class<?> sourceType, Class<?> targetType);
    // 将给定的source转换为指定的targetType 。
    <T> T convert(Object source, Class<T> targetType);
    // 如果sourceType的对象可以转换为targetType ,则返回true
    boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType);
    // 将给定的source转换为指定的targetType 。 TypeDescriptors 提供有关将发生转换的源和目标位置的附加上下文,通常是对象字段或属性位置。
    Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
}

2.5.2 硬编码使用

如果转换失败会抛出 org.springframework.core.convert.ConversionFailedException

public class ConvertTest {

    @Test
    public void test(){
        ConversionService sharedInstance = DefaultConversionService.getSharedInstance();
        System.out.println(sharedInstance.convert("1", Boolean.class));
        System.out.println(sharedInstance.convert("123", Long.class));
        System.out.println(sharedInstance.convert("1234", Integer.class));
        System.out.println(sharedInstance.convert("1235", int.class));
    }

}

2.5.3 整合Spring

@SpringBootApplication
public class Application {


    public static class LongToDateConvert implements Converter<Long, Date> {
        @Override
        public Date convert(Long source) {
            return new Date(source);
        }
    }

    @Bean("customerConvert")
    public ConversionServiceFactoryBean customerConvert() {
        ConversionServiceFactoryBean conversionServiceFactoryBean = new ConversionServiceFactoryBean();
        conversionServiceFactoryBean.setConverters(Collections.singleton(new LongToDateConvert()));
        return conversionServiceFactoryBean;
    }
}
    
@SpringBootTest
@TestConfiguration
public class SpringConvertTest {

    @Autowired
    @Qualifier("customerConvert")
    private ConversionService conversionService;

    @Test
    public void test() {
        Date convert = conversionService.convert(System.currentTimeMillis(), Date.class);
        System.out.println(convert);
    }
}

三、Formatter 格式化输出

core.convert 是一个通用的类型转换系统。它提供了一个统一的ConversionServiceAPI 以及一个强类型的ConverterSPI,用于实现从一种类型到另一种类型的转换逻辑。

现在考虑典型客户端环境的类型转换要求,例如 Web 或桌面应用程序。在这样的环境中,您通常转换 fromString 以支持客户端回发过程,以及转换回String以支持视图呈现过程。

此外,您经常需要本地化String值(国际化)。core.convert Converter 不直接解决此类格式要求。为了直接解决这些问题,Spring 3 引入了一个方便的SPI,它为客户端环境的实现Formatter提供了一个简单而健壮的替代方案。PropertyEditor。

3.1 自定义Formatter

要想自定义Formatter我们只用实现 Formatter 接口即可。下面我们看他们的接口定义,就能看到。
Formatter 跟Convert的区别是什么。

  • Formatter 只支持String和对象的双向转换,适合文本格式化、国际化的处理。
  • Converter 支持任意类型的转换
public interface Formatter<T> extends Printer<T>, Parser<T> {}
@FunctionalInterface
public interface Printer<T> {
  // 对象转String
	String print(T object, Locale locale);
}
@FunctionalInterface
public interface Parser<T> {
	// String转对象
	T parse(String text, Locale locale) throws ParseException;
}

如下我们自定义一个时间的转换器

public class DateFormatterTest {

    @Test
    public void test() {
        DefaultFormattingConversionService defaultFormattingConversionService = new DefaultFormattingConversionService();
        defaultFormattingConversionService.addFormatter(new DateFormatter("yyyy-MM-dd"));
        Date convert = defaultFormattingConversionService.convert("2022-10-10", Date.class);
        System.out.println(convert);
    }

    public final class DateFormatter implements Formatter<Date> {
        private String pattern;
        public DateFormatter(String pattern) {
            this.pattern = pattern;
        }
        public String print(Date date, Locale locale) {
            if (date == null) {
                return "";
            }
            return getDateFormat(locale).format(date);
        }
        public Date parse(String formatted, Locale locale) throws ParseException {
            if (formatted.length() == 0) {
                return null;
            }
            return getDateFormat(locale).parse(formatted);
        }
        protected DateFormat getDateFormat(Locale locale) {
            DateFormat dateFormat = new SimpleDateFormat(this.pattern, locale);
            dateFormat.setLenient(false);
            return dateFormat;
        }
    }
}

3.1 注解驱动Formatter

在Spring中很多很多功能都是可以基于注解进行驱动的,开发者不用关心底层实现,直接使用注解。就能使用很强大的工具了。下面我们实现一个注解驱动的类型转换。

自定一个注解 @DatePattern , 将String类型,根据注解的配置最终给方法参数赋值。

    public String print(@DatePattern(pattern = "yyyy-MM-dd") Date date) {
        System.out.println(date.toString());
        return date.toString();
    }
  1. 首先我们要实现这个接口。
public interface AnnotationFormatterFactory<A extends Annotation> {

    Set<Class<?>> getFieldTypes();

    Printer<?> getPrinter(A annotation, Class<?> fieldType);

    Parser<?> getParser(A annotation, Class<?> fieldType);
}

注意这里一定要用 TypeDescriptor 构造的方式来处理,因为只有这样才会处理注解。

   @Test
    public void test() throws Exception {
        DefaultFormattingConversionService defaultFormattingConversionService = new DefaultFormattingConversionService();
        defaultFormattingConversionService.addFormatterForFieldAnnotation(new DatePatternFormatAnnotationFormatterFactory());
        Method print = getClass().getDeclaredMethod("print", Date.class);
        // 注意一定要使用 TypeDescriptor 构造的方式声明才会有注解信息
        for (Parameter parameter : print.getParameters()) {
            // true
            System.out.println(new TypeDescriptor(MethodParameter.forParameter(parameter)).hasAnnotation(DatePattern.class));
        }
        // 通过注解的方式实现解析
        Object convert = defaultFormattingConversionService.convert("2021-12-12", new TypeDescriptor(MethodParameter.forExecutable(print, 0)));
        System.out.println(convert);
    }

    public String print(@DatePattern(pattern = "yyyy-MM-dd") Date date) {
        System.out.println(date.toString());
        return date.toString();
    }

    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.FIELD, ElementType.PARAMETER})
    public @interface DatePattern {
        String pattern();
    }

    public final class DatePatternFormatAnnotationFormatterFactory
            implements AnnotationFormatterFactory<DatePattern> {
        public Set<Class<?>> getFieldTypes() {
            return new HashSet<Class<?>>(Arrays.asList(new Class<?>[]{Date.class}));
        }

        public Printer<Date> getPrinter(DatePattern annotation, Class<?> fieldType) {
            return configureFormatterFrom(annotation, fieldType);
        }

        public Parser<Date> getParser(DatePattern annotation, Class<?> fieldType) {
            return configureFormatterFrom(annotation, fieldType);
        }

        private Formatter<Date> configureFormatterFrom(DatePattern annotation, Class<?> fieldType) {
            return new DateFormatter(annotation.pattern());
        }
    }
    public final class DateFormatter implements Formatter<Date> {
        private String pattern;
        public DateFormatter(String pattern) {
            this.pattern = pattern;
        }
        public String print(Date date, Locale locale) {
            if (date == null) {
                return "";
            }
            return getDateFormat(locale).format(date);
        }
        public Date parse(String formatted, Locale locale) throws ParseException {
            if (formatted.length() == 0) {
                return null;
            }
            return getDateFormat(locale).parse(formatted);
        }
        protected DateFormat getDateFormat(Locale locale) {
            DateFormat dateFormat = new SimpleDateFormat(this.pattern, locale);
            dateFormat.setLenient(false);
            return dateFormat;
        }
    }

最后,都看到这里了,最后如果这篇文章,对你有所帮助,请点个关注,交个朋友。

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

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

相关文章

java8特性,lambda表达式,简写的演变及应用

&#x1f36c;博主介绍 &#x1f468;‍&#x1f393; 博主主页&#xff1a;chad_chang的主页 ✨主攻领域&#xff1a;【大数据】【java】【python】【面试分析】 文章目录lambda表达式1.1.简介1.1.1.什么是Lambda&#xff1f;1.1.2.为什么使用Lambda1.1.3.Lambda对接口的要求1…

【Linux】特别篇--SMBus 协议

【Linux】特别篇--SMBus 协议一、SMBus 简介二、SMBus 与 I2C 区别三、SMBus协议分析3.1 符号含义3.2 SMBus Quick Command3.3 SMBus Receive Byte3.4 SMBus Send Byte3.5 SMBus Read Byte3.6 SMBus Read Word3.7 SMBus Write Byte3.8 SMBus Write Word3.9 SMBus Block Read3.1…

672页21万字智慧高速数据中心大数据平台建设方案

目 录 第1章 设计总述 6 1.1 项目概述 6 1.1.1 项目名称 6 1.1.2 建设单位概况 6 1.1.3 公司具备的优势 6 1.2 对项目的理解分析 7 1.2.1 项目现状分析 7 1.2.2 建设目标分析 10 1.2.3 建设内容分析 13 1.2.4 项目建设重难点分析 19 1.2.5 项目风险分析 22 1.2.6 各中心职能需求…

python控制台颜色输出设置

python控制台颜色输出设置 控制台输出内容的颜色有前景色与背景色 控制台的展示效果有限&#xff0c;并不能像前端一样炫酷&#xff0c;只能做一些简单的设置 原理 python终端的字符颜色是用转义序列控制的&#xff0c;是文本模式下的系统显示功能&#xff0c;和具体的语言无…

【C++笔试强训】第十天

&#x1f387;C笔试强训 博客主页&#xff1a;一起去看日落吗分享博主的C刷题日常&#xff0c;大家一起学习博主的能力有限&#xff0c;出现错误希望大家不吝赐教分享给大家一句我很喜欢的话&#xff1a;夜色难免微凉&#xff0c;前方必有曙光 &#x1f31e;。 &#x1f4a6; &…

操作系统的地址、数据存储和大小端问题

文章目录基本概念什么是位&#xff0c;什么是字节&#xff1f;地址总线内存地址物理地址虚拟地址寻址空间存储单元CPU位数CPU寻址大端存储和小端存储原理为什么会有大小端模式的区分基本概念 什么是位&#xff0c;什么是字节&#xff1f; 位表示的是二进制位&#xff0c;一般…

C语言中用sizeof和strlen观察数组的变化2

目录 一.前言 二.用sizeof观察字符串数组的变化 代码 运行结果 结果解释 图解 三.用sizeof观察字符数组的变化 代码 注意 运行结果 图解 注意 证明 一.前言 数组名大部分情况下是首元素的地址&#xff0c;只有两种情况除外&#xff1a; sizeof&#xff08;数组名&…

哈希桶的基本操作 || HashMap和HashSet介绍(手撕源码)

目录 前言&#xff1a; 哈希表介绍 哈希冲突 闭散列 开散列&#xff08;哈希桶&#xff09; 模拟实现哈希桶中插入和获取方法 代码实现 HashMap介绍 手撕源码 HashSet介绍 手撕源码 小结&#xff1a; 前言&#xff1a; &#x1f388;一棵搜索树的最快查找数据的时间复杂度是O(…

Java------String类和正则表达式

String在设计时被定义成了final类&#xff08;最终类&#xff09;&#xff0c;所以不能定义String类的子类&#xff0c;String用来表示字符串常量&#xff0c;是引用数据类型&#xff0c;一旦String类对象的字符序列被定义&#xff0c;那么这个字符序列的值是不能再变化的。 创…

今年十八,喜欢SQL注入

#1024程序员节&#xff5c;用代码&#xff0c;改变世界# 前言 &#x1f340;作者简介&#xff1a;被吉师散养、喜欢前端、学过后端、练过CTF、玩过DOS、不喜欢java的不知名学生。 &#x1f341;个人主页&#xff1a;被吉师散养的职业混子 &#x1fad2;每日emo&#xff1a;怀揣…

牛客竞赛每日俩题 - Day5

DP贪心 年终奖_牛客题霸_牛客网 思路&#xff1a; 定义f(i,j)表示从左上角走到坐标(i&#xff0c;j)处能获得的最大奖励。 搜索所有从左上角走到右下角的路径&#xff0c;找到最优路径。 f(i,j)分三种情况&#xff1a; 第一列&#xff1a;f(i, 0) f(i-1, 0) board(i, 0) 如果…

Java:企业应该用Java构建供应链软件吗?

很多公司使用Java编程语言&#xff0c;Java是最流行的编程语言之一&#xff0c;它帮助开发人员简化复杂企业解决方案的应用程序开发过程。 供应链是零售和制造业的重要组成部分&#xff0c;6%的零售业公司使用Java&#xff0c;另一方面&#xff0c;4%的制造公司使用编程语言&am…

基于莱维飞行扰动策略的麻雀搜索算法-附代码

基于莱维飞行扰动策略的麻雀搜索算法 文章目录基于莱维飞行扰动策略的麻雀搜索算法1.麻雀优化算法2. 改进麻雀算法2.1 莱维飞行搜索机制2.2 莱维飞行扰动策略2.3 初始化混沌搜索机制3.实验结果4.参考文献5.Matlab代码6.Python代码摘要&#xff1a;为了解决麻雀搜索算法存在迭代…

(02)Cartographer源码无死角解析-(04) 熟悉bag文件,配置.launch与.lua文件,初步调参

本人讲解关于slam一系列文章汇总链接:史上最全slam从零开始&#xff0c;针对于本栏目讲解(02)Cartographer源码无死角解析-接如下: (02)Cartographer源码无死角解析- (00)目录_最新无死角讲解&#xff1a;https://blog.csdn.net/weixin_43013761/article/details/127350885 文…

算法 | 详解斐波那契数列问题

14天阅读挑战赛 本篇是学习了《趣学算法&#xff08;第2版&#xff09;》 第一章之后总结的。 上一篇讲到了等比数列求和问题&#xff0c;求Sn122223...263&#xff1f;S_n 1 2 2^2 2^3 ... 2^{63} &#xff1f;Sn​122223...263&#xff1f;&#xff0c;该函数属于爆…

1024,我们节日

文章目录1024的前奏1024应该锻炼身体1024 &#xff1f;为何是1024其他的1024代码有Bug&#xff0c;世界却温暖特殊的一类人群&#xff08;一&#xff09;特殊的一类人群&#xff08;二&#xff09;写在最后1024的前奏 写博文的时间是23号&#xff0c;距离1024这个标志性的节日…

趣学算法14天阅读|Day2

14天阅读挑战赛 文章目录前言什么是算法&#xff1f;算法复杂度如何评定好算法案例案例一&#xff1a;棋盘的麦子案例二&#xff1a;兔子数列总结前言 &#x1f4eb; 作者简介&#xff1a;「六月暴雪飞梨花」&#xff0c;专注于研究Java&#xff0c;就职于科技型公司后端中级工…

@AutoConfigurationPackage注解详解

AutoConfigurationPackage是在springboot启动类注解SpringBootApplication下的EnableAutoConfiguration下。AutoConfigurationPackage作用是指定springboot扫描包&#xff0c;默认就是扫描启动类同包下的类。可以通过AutoConfigurationPackage来附加其他路径&#xff0c;然后sp…

asp.net园林绿化服务交易网站

本设计的目的是通过对园林绿化的初步了解&#xff0c;在巩固网站设计所学知识的基础上。运用于实践&#xff0c;在实践中强化理论知识&#xff0c;做到理论联系实践。在设计中学会搜集资料地方法&#xff0c;学会查阅设计规程。掌握设计步骤&#xff0c;同时培养创新能力&#…

【图像评价】基于matlab GUI图像评价系统【含Matlab源码 2185期】

一、图像质量评价 图像质量评价对图像处理算法的可行性分析以及某类算法达成效果的比较都起着非常重要的指导作用。随着图像处理技术的发展&#xff0c;对于图像质量评价的研究也日益深入&#xff0c;新的评价指标和评价算法不断涌现&#xff0c;共同推动了图像质量评价的进步…