【小家Spring】Spring Framework提供的实用纯Java工具类大合集(一)(上)

简介: 【小家Spring】Spring Framework提供的实用纯Java工具类大合集(一)(上)

前言

在Spring Framework里的spring-core核心包里面,有个org.springframework.util里面有不少非常实用的工具类。


该工具包里面的工具类虽然是被定义在Spring下面的,但是由于Spring框架目前几乎成了JavaEE实际的标准了,因此我们直接使用也是无妨的,很多时候能够大大的提高我们的生产力。本文主要介绍一些个人认为还非常实用的工具类,仅仅代表个人意见哦~


image.png


此处提到的工具类为纯工具类。与Spring的Bean没有关系,是最为共用的工具类


IdGenerator 唯一键生成器 UUID


UUID除了生成的字符串比较长以外,几乎没有缺点(当然用字符串做主键,也算一个小缺点吧)


Spring给我提供了接口:IdGenerator 来生成id代表唯一箭,它内置提供了三个实现:


image.png


JdkIdGenerator


JDK的工具类包util包中就为我们提供了一个很好的工具类,即UUID。UUID(Universally Unique Identifier)通用唯一识别码。

  @Override
  public UUID generateId() {
    return UUID.randomUUID();
  }


底层字节调用JDK的UUID方法,因此不做过多介绍了


AlternativeJdkIdGenerator


这是Spring提供给我们的重头戏,用它来取代JDK的UUID的生成。从它的javadoc说明:


 * An {@link IdGenerator} that uses {@link SecureRandom} for the initial seed and
 * {@link Random} thereafter, instead of calling {@link UUID#randomUUID()} every
 * time as {@link org.springframework.util.JdkIdGenerator JdkIdGenerator} does.
 * This provides a better balance between securely random ids and performance.

大意是:它使用了SecureRandom作为种子,来替换调用UUID#randomUUID()。它提供了一个更好、更高性能的表现(关于性能比较,下面会给出一个例子)


SimpleIdGenerator


类似于自增的Id生成器。每调用一次,自增1(一般比较少使用 指导就行了)


三者性能比较:


    public static void main(String[] args) {
        JdkIdGenerator jdkIdGenerator = new JdkIdGenerator();
        AlternativeJdkIdGenerator alternativeJdkIdGenerator = new AlternativeJdkIdGenerator();
        SimpleIdGenerator simpleIdGenerator = new SimpleIdGenerator();
        Instant start;
        Instant end;
        int count = 1000000;
        //jdkIdGenerator
        start = Instant.now();
        for (int i = 0; i < count; i++) {
            jdkIdGenerator.generateId();
        }
        end = Instant.now();
        System.out.println("jdkIdGenerator循环" + count + "次耗时:" + Duration.between(start, end).toMillis() + "ms");
        //alternativeJdkIdGenerator
        start = Instant.now();
        for (int i = 0; i < count; i++) {
            alternativeJdkIdGenerator.generateId();
        }
        end = Instant.now();
        System.out.println("alternativeJdkIdGenerator循环" + count + "次耗时:" + Duration.between(start, end).toMillis() + "ms");
        //simpleIdGenerator
        start = Instant.now();
        for (int i = 0; i < count; i++) {
            simpleIdGenerator.generateId();
        }
        end = Instant.now();
        System.out.println("simpleIdGenerator循环" + count + "次耗时:" + Duration.between(start, end).toMillis() + "ms");
    }



发现仅仅循环生成100万次,Spring提供的算法性能远远高于JDK的。因此建议大家以后使用AlternativeJdkIdGenerator去生成UUID,性能会更好一点


缺点是:还需要new对象才能使用,不能通过类名直接调用静态方法,当然我们可以二次封装。另外,一般输出串我们都会进一步这么处理:.toString().replace("-", "")


Assert 断言工具类


Assert断言工具类,通常用于数据合法性检查。Assert断言工具类,通常用于数据合法性检查.

if (message== null || message.equls("")) {  
    throw new IllegalArgumentException("输入信息错误!");  
} 

用Assert工具类上面的代码可以简化为:Assert.hasText((message, "输入信息错误!");

下面介绍常用的断言方法的使用:



Assert.notNull(Object object, "object is required")    -    对象非空 
Assert.isTrue(Object object, "object must be true")   -    对象必须为true   
Assert.notEmpty(Collection collection, "collection must not be empty")    -    集合非空  
Assert.hasLength(String text, "text must be specified")   -    字符不为null且字符长度不为0   
Assert.hasText(String text, "text must not be empty")    -     text 不为null且必须至少包含一个非空格的字符  
Assert.isInstanceOf(Class clazz, Object obj, "clazz must be of type [clazz]")    -    obj必须能被正确造型成为clazz 指定的类

junit也提供断言工具类,但是我们只能在单元测试中使用,而Spring提供的这个,哪儿都能使用,还是比较方便的


PathMatcher 路径匹配器


Spring提供的实现:AntPathMatcher Ant路径匹配规则

(1)SpringMVC的路径匹配规则是依照Ant的来的,实际上不只是SpringMVC,整个Spring框架的路径解析都是按照Ant的风格来的;

(2)AntPathMatcher不仅可以匹配Spring的@RequestMapping路径,也可以用来匹配各种字符串,包括文件路径等。


你是否曾今在你们的Filter里看过类似下面的代码?

image.png


这种所谓的白名单URL这样来匹配,可谓非常的不优雅,而且通过穷举法的扩展性不可为不差。因此下面举几个例子来介绍此匹配器的用法,以后建议使用它吧~


        PathMatcher pathMatcher = new AntPathMatcher();
        //这是我们的请求路径  需要被匹配(理解成匹配controller吧 就很容易理解了)
        String requestPath = "/user/list.htm?username=aaa&departmentid=2&pageNumber=1&pageSize=20";//请求路径
        //路径匹配模版
        String patternPath = "/user/list.htm**";
        assertTrue(pathMatcher.match(patternPath, requestPath));


NT方式的通配符有三种:

?(匹配任何单字符),*(匹配0或者任意数量的字符),**(匹配0或者更多的目录)

url路径匹配规则说明:


image.png


举一些常用案例:


    @Test
    public void fun1() {
        PathMatcher pathMatcher = new AntPathMatcher();
        // 精确匹配
        assertTrue(pathMatcher.match("/test", "/test"));
        assertFalse(pathMatcher.match("test", "/test"));
        //测试通配符?
        assertTrue(pathMatcher.match("t?st", "test"));
        assertTrue(pathMatcher.match("te??", "test"));
        assertFalse(pathMatcher.match("tes?", "tes"));
        assertFalse(pathMatcher.match("tes?", "testt"));
        //测试通配符*
        assertTrue(pathMatcher.match("*", "test"));
        assertTrue(pathMatcher.match("test*", "test"));
        assertTrue(pathMatcher.match("test/*", "test/Test"));
        assertTrue(pathMatcher.match("*.*", "test."));
        assertTrue(pathMatcher.match("*.*", "test.test.test"));
        assertFalse(pathMatcher.match("test*", "test/")); //注意这里是false 因为路径不能用*匹配
        assertFalse(pathMatcher.match("test*", "test/t")); //这同理
        assertFalse(pathMatcher.match("test*aaa", "testblaaab")); //这个是false 因为最后一个b无法匹配了 前面都是能匹配成功的
        //测试通配符** 匹配多级URL
        assertTrue(pathMatcher.match("/*/**", "/testing/testing"));
        assertTrue(pathMatcher.match("/**/*", "/testing/testing"));
        assertTrue(pathMatcher.match("/bla/**/bla", "/bla/testing/testing/bla/bla")); //这里也是true哦
        assertFalse(pathMatcher.match("/bla*bla/test", "/blaXXXbl/test"));
        assertFalse(pathMatcher.match("/????", "/bala/bla"));
        assertFalse(pathMatcher.match("/**/*bla", "/bla/bla/bla/bbb"));
        assertTrue(pathMatcher.match("/*bla*/**/bla/**", "/XXXblaXXXX/testing/testing/bla/testing/testing/"));
        assertTrue(pathMatcher.match("/*bla*/**/bla/*", "/XXXblaXXXX/testing/testing/bla/testing"));
        assertTrue(pathMatcher.match("/*bla*/**/bla/**", "/XXXblaXXXX/testing/testing/bla/testing/testing"));
        assertTrue(pathMatcher.match("/*bla*/**/bla/**", "/XXXblaXXXX/testing/testing/bla/testing/testing.jpg"));
        assertTrue(pathMatcher.match("/foo/bar/**", "/foo/bar"));
        //这个需要特别注意:{}里面的相当于Spring MVC里接受一个参数一样,所以任何东西都会匹配的
        assertTrue(pathMatcher.match("/{bla}.*", "/testing.html"));
        assertFalse(pathMatcher.match("/{bla}.htm", "/testing.html")); //这样就是false了
    }


注意事项:

1、AntPathMatcher不仅可以匹配URL路径,也可以匹配文件路径。但是需要注意AntPathMatcher也有有参构造,传递路径分隔符参数pathSeparator(若不传,默认值为/),对于文件路径的匹配来说,则需要根据不同的操作系统来传递各自的文件分隔符,以此防止匹配文件路径错误。


AntPathMatcher默认路径分隔符为“/”,而在匹配文件路径时,需要注意Windows下路径分隔符为“\”,Linux下为“/”。靠谱写法如下两种方式:

AntPathMatcher matcher = new AntPathMatcher(File.separator);
AntPathMatcher matcher = new AntPathMatcher(System.getProperty("file.separator"));

2、最长匹配规则(has more characters),即越精确的模式越会被优先匹配到。例如,URL请求/app/dir/file.jsp,现在存在两个路径匹配模式/**/.jsp和/app/dir/.jsp,那么会根据模式/app/dir/*.jsp来匹配。


ConcurrentReferenceHashMap


ConcurrentReferenceHashMap是自spring3.2后增加的一个同步的软(虚)引用Map。

这个工具类厉害了。我们知道java的引用类型一共分四种【小家java】引用类型(强引用、软引用、弱引用、虚引用),JDK也为我们提供了

WeakHashMap来使用。但是,但是它并不是线程安全的,因此刚好Spring给我们功提供这个工具类:ConcurrentReferenceHashMap满足了我们对线程安全的弱、软引用的需求。线面通过一个示例体验一把:

    @Test
    public void fun1() throws InterruptedException {
        String key = new String("key");
        String value = new String("val");
        Map<String, String> map = new ConcurrentReferenceHashMap<>(8, ConcurrentReferenceHashMap.ReferenceType.WEAK);
        map.put(key, value);
        System.out.println(map); //{key=val}
        key = null;
        System.gc();
        //等待一会 确保GC能够过来
        TimeUnit.SECONDS.sleep(5);
        System.out.println(map); //{}
    }


我们发现当我们吧key置为null后,垃圾回收器会回收掉这部分内存。这就是弱、虚引用的作用,主要用来防止OOM。


查看ConcurrentReferenceHashMap源码发现起底层实现依赖的是RefrenceQueue完成自动移除操作。时间有限就写到这里。有时间再进行完善。 当然还有保证线程安全的代码,也是很重要的

DefaultPropertiesPersister


这个类本身没有什么特别的,就是代理了JDK的Properties类而已。但写到此处是觉得Spring优秀就优秀在它强大的对扩展开放的原则体现。


比如如果我们需要对配置文件进行解密(比如数据库连接密码不能明文),这些操作通过复写这些扩展类的某些方法来做,将特别的优雅。

spring启动时,解密配置文件的密文


DigestUtils


可以对字节数组、InputStream流生成摘要。10禁止或者16进制都行

FastByteArrayOutputStream


可以说是Spring实现的加强版的ByteArrayOutputStream。为什么加强了呢?其实底层原理是Spring采用了一个LinkedList来作为缓冲区:

  // The buffers used to store the content bytes
  private final LinkedList<byte[]> buffers = new LinkedList<>();


ByteArrayOutputStream直接使用的字节数组。


protected byte buf[];

这样每一次扩容中分配一个数组的空间,并当该数据放入到List中。相当于批量的操作,而ByteArrayOutputStream内部实现为一个数组每一次扩容需要重新分配空间并将数据复制到新数组中。效率高下立判了~因此推荐使用


相关文章
|
6天前
|
缓存 前端开发 Java
【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)
Soring Boot的起步依赖、启动流程、自动装配、常用的注解、Spring MVC的执行流程、对MVC的理解、RestFull风格、为什么service层要写接口、MyBatis的缓存机制、$和#有什么区别、resultType和resultMap区别、cookie和session的区别是什么?session的工作原理
【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)
|
6天前
|
缓存 Java 数据库
【Java面试题汇总】Spring篇(2023版)
IoC、DI、aop、事务、为什么不建议@Transactional、事务传播级别、@Autowired和@Resource注解的区别、BeanFactory和FactoryBean的区别、Bean的作用域,以及默认的作用域、Bean的生命周期、循环依赖、三级缓存、
【Java面试题汇总】Spring篇(2023版)
|
7天前
|
Java 数据库连接 API
【Java笔记+踩坑】Spring Data JPA
从常用注解、实体类和各层编写方法入手,详细介绍JPA框架在增删改查等方面的基本用法,以及填充用户名日期、分页查询等高级用法。
【Java笔记+踩坑】Spring Data JPA
|
7天前
|
Java 数据库连接 数据格式
【Java笔记+踩坑】Spring基础2——IOC,DI注解开发、整合Mybatis,Junit
IOC/DI配置管理DruidDataSource和properties、核心容器的创建、获取bean的方式、spring注解开发、注解开发管理第三方bean、Spring整合Mybatis和Junit
【Java笔记+踩坑】Spring基础2——IOC,DI注解开发、整合Mybatis,Junit
|
25天前
|
Java
Java应用结构规范问题之在UnitConvertUtils工具类将千米转换为米的问题如何解决
Java应用结构规范问题之在UnitConvertUtils工具类将千米转换为米的问题如何解决
|
20天前
|
Java 前端开发 Apache
Apache Wicket与Spring MVC等Java Web框架大PK,究竟谁才是你的最佳拍档?点击揭秘!
【8月更文挑战第31天】在Java Web开发领域,众多框架各具特色。Apache Wicket以组件化开发和易用性脱颖而出,提高了代码的可维护性和可读性。相比之下,Spring MVC拥有强大的生态系统,但学习曲线较陡;JSF与Java EE紧密集成,但在性能和灵活性上略逊一筹;Struts2虽成熟,但在RESTful API支持上不足。选择框架时还需考虑社区支持和文档完善程度。希望本文能帮助开发者找到最适合自己的框架。
27 0
|
20天前
|
Java Spring 开发者
Java Web开发新潮流:Vaadin与Spring Boot强强联手,打造高效便捷的应用体验!
【8月更文挑战第31天】《Vaadin与Spring Boot集成:最佳实践指南》介绍了如何结合Vaadin和Spring Boot的优势进行高效Java Web开发。文章首先概述了集成的基本步骤,包括引入依赖和配置自动功能,然后通过示例展示了如何创建和使用Vaadin组件。相较于传统框架,这种集成方式简化了配置、提升了开发效率并便于部署。尽管可能存在性能和学习曲线方面的挑战,但合理的框架组合能显著提升应用开发的质量和速度。
27 0
|
22天前
|
安全 Java 开发者
Java反射:Spring Boot背后的魔法,让你的代码质量飞跃的神秘力量!
【8月更文挑战第29天】Java反射机制允许程序在运行时访问和修改类、接口、方法等属性,而Spring Boot则广泛应用反射实现依赖注入和自动配置。本文探讨如何利用反射机制提升Spring Boot应用的代码质量,包括动态类型处理、元数据访问及依赖注入等方面。通过实战示例展示动态调用方法和自定义注解处理,强调反射机制对代码灵活性与扩展性的贡献,同时提醒开发者注意性能和安全问题。
36 0
|
27天前
|
Java 开发工具 Spring
【Azure Spring Cloud】使用azure-spring-boot-starter-storage来上传文件报错: java.net.UnknownHostException: xxxxxxxx.blob.core.windows.net: Name or service not known
【Azure Spring Cloud】使用azure-spring-boot-starter-storage来上传文件报错: java.net.UnknownHostException: xxxxxxxx.blob.core.windows.net: Name or service not known
|
27天前
|
前端开发 JavaScript Java
【Azure 应用服务】App Service For Windows 中如何设置代理实现前端静态文件和后端Java Spring Boot Jar包
【Azure 应用服务】App Service For Windows 中如何设置代理实现前端静态文件和后端Java Spring Boot Jar包