Study Notes: Shang Silicon Valley Spring 6 Advanced Chapter

Spring6

  1. Study notes: Spring 6 basics_ljtxy.love’s blog-CSDN blog
  2. Study Notes: Spring 6 Advanced Chapter_ljtxy.love’s Blog-CSDN Blog

Article directory

7. Unit testing: JUnit

Summary of notes:

  1. Overview: JUnit is a unit testing framework for the Java programming language

  2. Basic use case:

    Step 1: Introduce dependencies: Junit support dependencies, Junit5 tests

    Step 2: Configuration file: configure basic class scanning

    Step 3: Demonstration: Use the **@SpringJUnitConfig(locations = “classpath:beans.xml”)** annotation to automatically load the configuration file and create the class

7.1 Overview

JUnit is a unit testing framework for the Java programming language. It was created by Erich Gamma and Kent Beck and has gradually become one of the standard unit testing frameworks for the Java language. JUnit provides some annotations and assertion methods to help developers write and run test cases. It can also integrate with build tools such as Maven and Gradle to facilitate continuous integration and automated testing

7.2 Basic use cases

Step 1: Introduce dependencies

<!--spring对junit的支持相关依赖-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>6.0.2</version>
</dependency>

<!--junit5测试-->
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-api</artifactId>
    <version>5.9.0</version>
</dependency>

Step 2: Configuration file

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <context:component-scan base-package="com.atguigu.spring6.bean"/>
</beans>

illustrate:

​Configuration class scanning for automatically assembling beans

Step 3: Create the class

@Component
public class User {
    
    

    public User() {
    
    
        System.out.println("run user");
    }
}

Step 4: Demonstrate

@SpringJUnitConfig(locations = "classpath:beans.xml")
public class SpringJUnit5Test {
    
    

    @Autowired
    private User user;

    @Test
    public void testUser(){
    
    
        System.out.println(user);
    }
}

illustrate:

  • The @SpringJUnitConfig annotation is used to specify Spring's configuration file. Before the test method is run, Spring will first create the required beans based on the configuration file and then inject them into the test class. In this way, you can directly use Spring's beans in the test class for testing.

  • The class is:

    ApplicationContext context = new ClassPathXmlApplicationContext("xxx.xml");
    Xxxx xxx = context.getBean(Xxxx.class);
    
  • This code is loaded through ApplicationContextthe classpath xml context of the interfaceClassPathXmlApplicationContext

8. Affairs

Summary of notes:

  1. Overview:
    1. Definition: A transaction is one or more data operations that either succeed or fail
    2. characteristic:
      • Atomicity : either all succeed or all fail
      • Consistency : After the transaction execution fails, the data in the database does not change
      • Isolation : Multiple transactions are isolated from each other
      • Durability : After the transaction is completed, the data in the database should be stored normally and persistently
    3. Programmatic transactions: use code to process transactions
    4. Declarative transactions: Use annotations to process transactions. Annotation **@Transactional**
  2. Annotation-based declarative transactions: please read this section in detail
  3. XML-based declarative transactions (understanding): defining and applying aspects through XML

8.1 Overview

8.1.1 Definition

​A transaction refers to a logical unit of work consisting of one or more sequences of operations. These operations either all succeed or all fail and are rolled back. In the database, a transaction contains one or more data operations, such as adding, deleting, modifying, etc. At the same time, these operations must either all succeed or all fail. Only some operations cannot succeed or fail.

8.1.2 Features

  1. Atomicity: A transaction is an indivisible unit of work that either all succeeds or all fails. Partial success and partial failure are not allowed.
  2. Consistency: The state of the database should remain consistent before and after a transaction is executed. If a transaction fails to execute, the database should be restored to the state before execution.
  3. Isolation: Multiple transactions should be isolated from each other, and transactions cannot interfere with each other to avoid problems such as dirty reads, non-repeatable reads, and phantom reads.
  4. Durability: After the transaction is completed, the modifications to the database should be persisted. Even if the system fails or crashes, the data should not be lost.

8.2.3 Programmatic Transactions

​ Programmatic transactions are implemented by writing transaction management code in the code. It is necessary to manually control the start, submission and rollback of transactions. It is necessary to write relevant code in each method that requires transaction management, so that the code coupling is high and The transaction management code appears repeatedly and is inconvenient to maintain.

Connection conn = ...;
    
try {
    
    
    
    // 开启事务:关闭事务的自动提交
    conn.setAutoCommit(false);
    
    // 核心操作
    
    // 提交事务
    conn.commit();
    
}catch(Exception e){
    
    
    
    // 回滚事务
    conn.rollBack();
    
}finally{
    
    
    
    // 释放数据库连接
    conn.close();
    
}

8.2.4 Declarative transactions

​ Declarative transactions are implemented through AOP, which separates transaction management code from business logic. By declaring transaction management in the configuration file, automatic management of transactions is achieved. Developers only need to add methods that require transaction management. Just annotate or configure it, which greatly simplifies code writing and maintenance work.

public interface UserService {
    
    
    void updateUser(User user);
}

@Service
@Transactional
public class UserServiceImpl implements UserService {
    
    
    @Autowired
    private UserDao userDao;

    @Override
    public void updateUser(User user) {
    
    
        userDao.update(user);
    }
}

illustrate:

  • In this example, @Transactionalannotations are used at the class level of the service implementation class. This means that when updateUserthe method is called, Spring will create a transaction, and when the method execution ends, the transaction will be committed or rolled back (if an exception occurs)
  • In this way, when calling updateUserthe method, we do not need to explicitly open and commit the transaction, the Spring framework will automatically handle the transaction submission and rollback

8.2 Annotation-based declarative transactions

Note section:

  1. Basic use case:

    Step 1: Add tx namespace , add transaction manager , and enable transaction annotation driver

    Step 2: Add transaction annotation : @Transactional

  2. Transaction attributes:

    1. Read-only : @Transactional( readOnly = true), tells the database that this operation can only be read and not rewritten.
    2. Timeout : @Transactional( timeout = 3), when this operation times out, prompt
    3. Rollback strategy :
      • rollbackFor attribute: When the transaction method throws an exception of the specified type, the transaction will be rolled back
      • rollbackForClassName attribute: similar to the rollbackFor attribute, but uses a string when specifying the exception type
      • noRollbackFor attribute: Specifies that the transaction will not be rolled back when the transaction method throws an exception of the specified type.
      • noRollbackForClassName attribute: Similar to the noRollbackFor attribute, but uses a string when specifying the exception type
    4. Isolation level :
      • READ_UNCOMMITTED: Indicates that a transaction can read data from another uncommitted transaction. Dirty reads, non-repeatable reads, and phantom reads may occur at this level, and are generally not recommended.
      • READ_COMMITTED: Indicates that a transaction can only read the data of another committed transaction, which can avoid dirty read problems, but non- repeatable read and phantom read problems may still occur.
      • REPEATABLE_READ: Indicates that a transaction can read the same row of data multiple times during execution, which can avoid dirty read and non-repeatable read problems, but phantom read problems may still occur .
      • SERIALIZABLE: Indicates that a transaction locks all data involved during execution, avoiding the problems of dirty reads, non-repeatable reads, and phantom reads, but the concurrency performance is very poor .
    5. Propagation behavior : The propagation behavior of a transaction refers to the rules that control how transactions propagate and affect each other's behavior when multiple transaction methods call each other.
    6. Full annotation configuration transaction : @EnableTransactionManagement turns on annotation transaction management

8.2.1 Basic use case-implementing annotated declarative transactions

Step 1: Add the tx namespace and configuration in the configuration file

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context
                           http://www.springframework.org/schema/context/spring-context.xsd
                           http://www.springframework.org/schema/tx
                           http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!--事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="druidDataSource"/>
    </bean>

    <!--
		开启事务的注解驱动。通过注解@Transactional所标识的方法或标识的类中所有的方法,都会被事务管理器管理事务-->
    <!-- transaction-manager属性的默认值是transactionManager,如果事务管理器bean的id正好就是这个默认值,则可以省略这个属性 -->
    <tx:annotation-driven transaction-manager="transactionManager" />
</beans>

illustrate:

  1. Add tx namespace
  2. Add transaction manager
  3. Enable transaction annotation driver

Step 2: Add transaction annotations

@Transactional
public void buyBook(Integer bookId, Integer userId) {
    
    
    //查询图书的价格
    Integer price = bookDao.getPriceByBookId(bookId);
    //更新图书的库存
    bookDao.updateStock(bookId);
    //更新用户的余额
    bookDao.updateBalance(userId, price);
    //System.out.println(1/0);
}

illustrate:

  • The Service layer represents the business logic layer. Annotations are usually added to the functions of the business layer to achieve the purpose of transaction management.
  • The @Transactional annotation marked on the method will only affect the method to be managed by the transaction management tool. The @Transactional annotation is marked on a class, which will affect all methods in the class to be managed by the transaction management tool.

Step 3: Demo

illustrate:

​ If an error occurs during the operation transaction, the Spring framework will help us automatically roll back the data

8.2.2 Transaction attributes: read-only

For a query operation, if we set it to read-only, we can clearly tell the database that this operation does not involve write operations. This allows the database to be optimized for query operations

@Transactional(readOnly = true)
public void buyBook(Integer bookId, Integer userId) {
    
    
    //查询图书的价格
    Integer price = bookDao.getPriceByBookId(bookId);
    //更新图书的库存
    bookDao.updateStock(bookId);
    //更新用户的余额
    bookDao.updateBalance(userId, price);
    //System.out.println(1/0);
}

illustrate:

  1. At this time, the transaction read-only attribute has been added to the transaction, so adding, deleting, and modifying operations will throw an exception:

  2. Caused by: java.sql.SQLException: Connection is read-only. Queries leading to data modification are not allowed
    

8.2.3 Transaction attributes: timeout

During the execution of a transaction, certain problems may be encountered, causing the program to get stuck, thus occupying database resources for a long time. If resources are occupied for a long time, it is most likely because there is a problem with the program running (it may be a Java program or a MySQL database or a network connection, etc.). At this time, the program that is likely to have problems should be rolled back, the operations it has done should be undone, the transaction should be ended, and the resources should be released so that other normal programs can be executed.

//超时时间单位秒
@Transactional(timeout = 3)
public void buyBook(Integer bookId, Integer userId) {
    
    
    try {
    
    
        TimeUnit.SECONDS.sleep(5);
    } catch (InterruptedException e) {
    
    
        e.printStackTrace();
    }
    //查询图书的价格
    Integer price = bookDao.getPriceByBookId(bookId);
    //更新图书的库存
    bookDao.updateStock(bookId);
    //更新用户的余额
    bookDao.updateBalance(userId, price);
    //System.out.println(1/0);
}

illustrate:

  1. At this time, the transaction timeout attribute has been added to the transaction, so if the check and modification operation exceeds the unit time, an exception will be thrown:

  2. org.springframework.transaction.TransactionTimedOutException: Transaction timed out: deadline was Fri Jun 04 16:25:39 CST 2022
    

8.2.4 Transaction attributes: rollback strategy

The rollback strategy in the transaction attributes refers to how to handle the commit or rollback of the transaction when an exception occurs in the transaction

Attribute classification:

  1. rollbackFor attribute: When the transaction method throws an exception of the specified type, the transaction will be rolled back

    @Transactional(rollbackFor = {
          
          SQLException.class, IOException.class})
    
  2. rollbackForClassName attribute: similar to the rollbackFor attribute, but uses a string when specifying the exception type

    @Transactional(rollbackForClassName = {
          
          "java.sql.SQLException", "java.io.IOException"})
    
  3. noRollbackFor attribute: Specifies that the transaction will not be rolled back when the transaction method throws an exception of the specified type.

    @Transactional(noRollbackFor = {
          
          NullPointerException.class, IllegalArgumentException.class})
    
  4. noRollbackForClassName attribute: Similar to the noRollbackFor attribute, but uses a string when specifying the exception type

    @Transactional(noRollbackForClassName = {
          
          "java.lang.NullPointerException", "java.lang.IllegalArgumentException"})
    

Basic use case:

@Transactional(noRollbackFor = ArithmeticException.class)
//@Transactional(noRollbackForClassName = "java.lang.ArithmeticException")
public void buyBook(Integer bookId, Integer userId) {
    
    
    //查询图书的价格
    Integer price = bookDao.getPriceByBookId(bookId);
    //更新图书的库存
    bookDao.updateStock(bookId);
    //更新用户的余额
    bookDao.updateBalance(userId, price);
    System.out.println(1/0);
}

illustrate:

At this time, the attribute configured by the @Transactional annotation is noRollbackFor, so when ArithmeticException.classsuch an exception occurs, the transaction will not be rolled back

8.2.5 Transaction attributes: isolation level

​ Transaction isolation level refers to an isolation mechanism adopted by the database in order to ensure data consistency between transactions when multiple transactions are executed concurrently.

isolation level dirty read non-repeatable read phantom reading
READ UNCOMMITTED have have have
READ COMMITTED none have have
REPEATABLE READ none none have
SERIALIZABLE none none none

illustrate:

  • READ_UNCOMMITTED: Indicates that a transaction can read data from another uncommitted transaction. Dirty reads, non-repeatable reads, and phantom reads may occur at this level, and are generally not recommended.
  • READ_COMMITTED: Indicates that a transaction can only read the data of another committed transaction, which can avoid dirty read problems, but non-repeatable read and phantom read problems may still occur.
  • REPEATABLE_READ: Indicates that a transaction can read the same row of data multiple times during execution, which can avoid dirty read and non-repeatable read problems, but phantom read problems may still occur.
  • SERIALIZABLE: Indicates that a transaction locks all data involved during execution, avoiding the problems of dirty reads, non-repeatable reads, and phantom reads, but the concurrency performance is very poor.

Basic usage

@Transactional(isolation = Isolation.DEFAULT)//使用数据库默认的隔离级别
@Transactional(isolation = Isolation.READ_UNCOMMITTED)//读未提交
@Transactional(isolation = Isolation.READ_COMMITTED)//读已提交
@Transactional(isolation = Isolation.REPEATABLE_READ)//可重复读
@Transactional(isolation = Isolation.SERIALIZABLE)//串行化

8.2.6 Transaction attributes: propagation behavior

Transaction propagation behavior refers to the rules that control how transactions propagate and affect each other's behavior when multiple transaction methods call each other. In the Spring framework, transaction propagation behavior Propagationis defined by enumeration classes. Commonly used propagation behaviors include the following:

  1. REQUIRED (default): If a transaction currently exists, join the transaction; if there is no current transaction, create a new transaction. In other words, if it doesn’t exist, create a new one, and if it exists, add it.
  2. SUPPORTS: If there is currently a transaction, join the transaction; if there is no transaction, continue running in a non-transactional manner. In other words, if you have it, join it; if you don’t have it, leave it alone.
  3. MANDATORY: If there is currently a transaction, join the transaction; if there is no transaction, throw an exception. In other words, if it exists, add it, if not, throw an exception.
  4. REQUIRES_NEW: Create a new transaction. If a transaction currently exists, suspend the current transaction. In other words, whether there is or not, a new transaction is opened directly. There is no nesting relationship between the new transaction and the previous transaction, and the previous transaction is suspended.
  5. NOT_SUPPORTED: Run in non-transactional mode. If a transaction currently exists, the current transaction will be suspended. In other words, transactions are not supported and will be suspended if they exist.
  6. NEVER: Run in non-transactional mode and throw an exception if a transaction currently exists. In other words, if transactions are not supported, an exception will be thrown if they exist.
  7. NESTED: If there is currently a transaction, it will be executed in a nested transaction; if there is no current transaction, it will be executed according to PROPAGATION_REQUIRED. In other words, if there is a transaction, a completely independent transaction can be nested in this transaction, and the nested transaction can be committed and rolled back independently. No transaction is the same as REQUIRED
@Transactional(propagation = Propagation.REQUIRED)
public void transactionalMethod() {
    
    
    // ...
}

8.2.7 Full annotation configuration transactions (key points)

Step 1: Add configuration class

package com.atguigu.spring6.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;

@Configuration // 表示该类为配置类
@ComponentScan("com.atguigu.spring6") // 开启组件扫描,扫描com.atguigu.spring6包下的组件
@EnableTransactionManagement // 开启注解式事务管理
public class SpringConfig {
    
    

    @Bean // 声明一个Bean对象
    public DataSource getDataSource(){
    
    
        DruidDataSource dataSource = new DruidDataSource(); // 创建Druid连接池
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); // 配置驱动类名
        dataSource.setUrl("jdbc:mysql://localhost:3306/spring?characterEncoding=utf8&useSSL=false"); // 配置数据库URL
        dataSource.setUsername("root"); // 配置数据库用户名
        dataSource.setPassword("root"); // 配置数据库密码
        return dataSource; // 返回配置好的数据源对象
    }

    @Bean(name = "jdbcTemplate") // 声明一个Bean对象并命名为"jdbcTemplate"
    public JdbcTemplate getJdbcTemplate(DataSource dataSource){
    
    
        JdbcTemplate jdbcTemplate = new JdbcTemplate(); // 创建JdbcTemplate对象
        jdbcTemplate.setDataSource(dataSource); // 设置JdbcTemplate的数据源
        return jdbcTemplate; // 返回配置好的JdbcTemplate对象
    }

    @Bean // 声明一个Bean对象
    public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource){
    
    
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(); // 创建DataSourceTransactionManager对象
        dataSourceTransactionManager.setDataSource(dataSource); // 设置数据源
        return dataSourceTransactionManager; // 返回配置好的DataSourceTransactionManager对象
    }
}

illustrate:

​ When using full annotations to configure transactions, you need to declare a transaction manager and use the annotation @EnableTransactionManagement to enable the transaction manager

Step 2: Demonstration

@Test
public void testTxAllAnnotation(){
    
    
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
    BookController accountService = applicationContext.getBean("bookController", BookController.class);
    accountService.buyBook(1, 1);
}

8.2.8 Underlying principles

In Java, using @Transactionalannotations to implement method-level transaction management is based on the characteristics of the Spring framework and the principles of AOP (aspect-oriented programming).

The underlying principle is as follows:

  1. @TransactionalSpring intercepts the execution of methods with annotations through AOP functionality .
  2. When a method is called, Spring creates a proxy object for the method at runtime.
  3. The proxy object will insert transaction-related logic before and after method execution.
  4. At the beginning of the method, the transaction manager starts a new database transaction.
  5. If the method executes successfully (no exception is thrown), the transaction manager will commit the transaction and persist the modifications to the database.
  6. If an exception occurs during method execution, the transaction manager will roll back the transaction, undo modifications to the database, and restore to the state before the transaction started.
  7. After the method execution is completed, the transaction manager closes the transaction.

Through AOP, Spring can control transactions before and after method execution. It starts a transaction before the method is executed and commits or rolls back the transaction after the method is executed, thus ensuring the consistency and integrity of the data.

image-20230624175741508

​ It should be noted that @Transactionalthe effectiveness of annotations also depends on the configuration of the transaction manager and the support of the usage environment. The Spring framework provides a variety of transaction manager implementations, such as JDBC-based transaction managers and JTA-based transaction managers. You can choose the appropriate transaction manager according to specific needs. At the same time, Spring needs to enable the transaction manager in the configuration file and ensure that @Transactionalthe method modified by the annotation is called through the proxy object obtained by the Spring container. Only in this way can the transaction annotations take effect and the transaction management functions can be applied correctly.

8.3 XML-based declarative transactions (understanding)

<aop:config>
    <!-- 配置事务通知和切入点表达式 -->
    <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.atguigu.spring.tx.xml.service.impl.*.*(..))"></aop:advisor>
</aop:config>
<!-- tx:advice标签:配置事务通知 -->
<!-- id属性:给事务通知标签设置唯一标识,便于引用 -->
<!-- transaction-manager属性:关联事务管理器 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <!-- tx:method标签:配置具体的事务方法 -->
        <!-- name属性:指定方法名,可以使用星号代表多个字符 -->
        <tx:method name="get*" read-only="true"/>
        <tx:method name="query*" read-only="true"/>
        <tx:method name="find*" read-only="true"/>
    
        <!-- read-only属性:设置只读属性 -->
        <!-- rollback-for属性:设置回滚的异常 -->
        <!-- no-rollback-for属性:设置不回滚的异常 -->
        <!-- isolation属性:设置事务的隔离级别 -->
        <!-- timeout属性:设置事务的超时属性 -->
        <!-- propagation属性:设置事务的传播行为 -->
        <tx:method name="save*" read-only="false" rollback-for="java.lang.Exception" propagation="REQUIRES_NEW"/>
        <tx:method name="update*" read-only="false" rollback-for="java.lang.Exception" propagation="REQUIRES_NEW"/>
        <tx:method name="delete*" read-only="false" rollback-for="java.lang.Exception" propagation="REQUIRES_NEW"/>
    </tx:attributes>
</tx:advice>

Note: Declarative transactions implemented based on xml must introduce aspectJ dependencies

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>6.0.2</version>
</dependency>

9. Resource operations: Resources

Summary of notes:

  1. Overview: In the Spring framework, org.springframework.core.io.Resourceinterfaces and their implementation classes are abstraction layers used to handle resources (such as files, classpath resources, URLs, etc.)
  2. Resources interface: The Resource interface is an abstraction of the Spring resource access strategy. It does not provide any resource access implementation itself, and the specific resource access is completed by the implementation class of the interface . The Resource interface is used to abstract access to low-level resources
  3. Resources implementation class:
    • UrlResource: This class is used to represent URL type resources, which can be used to access network resources.
    • ClassPathResource : ClassPath Resource is used to represent resources under the class path and can be instantiated by specifying the relative path or absolute path of the resource.
    • FileSystemResource : FileSystem Resource class is used to access file system resources
    • ServletContextResource:slightly
    • InputStreamResource:slightly
    • ByteArrayResource:slightly
  4. ResourceLoader interface: It defines a unified access method for loading resources.
  5. ResourceLoader implementation class:
    • DefaultResourceLoader: The default resource loader, which can be used to load classpath, file system and URL resources .
    • FileSystemResourceLoader: Used to load resources in the file system .
    • ClassPathResourceLoader: Used to load resources under the classpath .
    • ServletContextResourceLoader: Used to load resources in the context of a web application .
  6. ResourceLoaderAware interface: used to inject ResourceLoader instances into Beans that implement this interface.
  7. Dynamically obtain Resource resources: directly use dependency injection to simplify Spring resource access to the greatest extent
  8. Determine the resource access path:
    • Implement class-specified access path
      • ClassPathXML ApplicationContext: Corresponds to using ClassPathResource for resource access.
      • FileSystemXml ApplicationContext: Corresponds to using FileSystemResource for resource access.
      • XmlWeb ApplicationContext: Corresponds to using ServletContextResource for resource access.
    • The prefix specifies the access path:
      1. classpath prefix
      2. classpath wildcard
      3. Other uses of wildcards

9.1 Overview

In the Spring framework, org.springframework.core.io.Resourceinterfaces and their implementation classes are abstraction layers used to handle resources (such as files, classpath resources, URLs, etc.). It provides a unified way to access and manipulate different types of resources, whether they are accessed in the file system, under the classpath, or through URLs

9.2Resources interface

9.2.1 Overview

​ Spring's Resource interface is located org.springframework.core.ioin . Intended to be a more powerful interface for abstracting access to low-level resources.

​InterfacesResource do inherit InputStreamSourceinterfaces in Spring and provide more methods. The interface has only InputStreamSourceone method

9.2.2 Common methods

  • boolean exists(): Check whether the resource exists.
  • boolean isReadable(): Check whether the resource is readable.
  • boolean isOpen(): Check whether the resource is open.
  • URL getURL(): Get the URL of the resource.
  • URI getURI(): Get the URI of the resource.
  • File getFile(): Get the file corresponding to the resource.
  • long contentLength(): Get the length of the resource.
  • long lastModified(): Get the last modification time of the resource.
  • Resource createRelative(String relativePath): Create a relative resource relative to the current resource.
  • String getFilename(): Get the file name of the resource.
  • String getDescription(): Get the description information of the resource.
  • InputStream getInputStream(): Get the input stream of the resource.

9.3Resources implementation class

9.3.1 Overview

The Resource interface is an abstraction of Spring's resource access strategy. It does not itself provide any resource access implementation. Specific resource access is completed by the implementation class of this interface. Each implementation class represents a resource access strategy. Resource generally includes these implementation classes:UrlResource、ClassPathResource、FileSystemResource、ServletContextResource、InputStreamResource、ByteArrayResource

image-20230516133813254

9.3.2UrlResource class

This class is used to represent URL type resources, which can be instantiated through the URL path of URLthe object or type and used to access network resources.String

Basic use case:

public class UrlResourceDemo {
    
    

    public static void loadAndReadUrlResource(String path){
    
    
        // 创建一个 Resource 对象
        UrlResource url = null;
        try {
    
    
            url = new UrlResource(path);
            // 获取资源名
            System.out.println(url.getFilename());
            System.out.println(url.getURI());
            // 获取资源描述
            System.out.println(url.getDescription());
            //获取资源内容
            System.out.println(url.getInputStream().read());
        } catch (Exception e) {
    
    
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
    
    
        //1 访问网络资源
        //loadAndReadUrlResource("http://www.atguigu.com");

        //2 访问文件系统资源
        loadAndReadUrlResource("file:atguigu.txt");
    }
}

illustrate:

  • When accessing file system resources, the file needs to be read based on the root path of the current project.

    image-20230516140223559

Replenish:

  • http:------This prefix is ​​used to access network resources based on HTTP protocol
  • ftp:------This prefix is ​​used to access network resources based on FTP protocol
  • file: ------This prefix is ​​used to read resources from the file system

9.3.3ClassPathResource class

​ ClassPathResource is used to represent resources under the class path, and can be instantiated by specifying the relative path or absolute path of the resource. Compared with other Resource implementation classes, its main advantage is to facilitate access to resources in the class loading path, especially for Web applications. , ClassPathResource can automatically search for resource files under classes without using absolute path access.

Basic use case:

public class ClassPathResourceDemo {
    
    

    public static void loadAndReadUrlResource(String path) throws Exception{
    
    
        // 创建一个 Resource 对象
        ClassPathResource resource = new ClassPathResource(path);
        // 获取文件名
        System.out.println("resource.getFileName = " + resource.getFilename());
        // 获取文件描述
        System.out.println("resource.getDescription = "+ resource.getDescription());
        //获取文件内容
        InputStream in = resource.getInputStream();
        byte[] b = new byte[1024];
        while(in.read(b)!=-1) {
    
    
            System.out.println(new String(b));
        }
    }

    public static void main(String[] args) throws Exception {
    
    
        loadAndReadUrlResource("atguigu.txt");
    }
}

illustrate:

  • When accessing the file resource system, the file needs to be read based on the class path of the current project.

    image-20230516140413107

9.3.4FileSystemResource class

The FileSystemResource class provided by Spring is used to access file system resources. There is not much advantage in using FileSystemResource to access file system resources, because the File class provided by Java can also be used to access file system resources.

public class FileSystemResourceDemo {
    
    

    public static void loadAndReadUrlResource(String path) throws Exception{
    
    
        //相对路径
        FileSystemResource resource = new FileSystemResource("atguigu.txt");
        //绝对路径
        //FileSystemResource resource = new FileSystemResource("C:\\atguigu.txt");
        // 获取文件名
        System.out.println("resource.getFileName = " + resource.getFilename());
        // 获取文件描述
        System.out.println("resource.getDescription = "+ resource.getDescription());
        //获取文件内容
        InputStream in = resource.getInputStream();
        byte[] b = new byte[1024];
        while(in.read(b)!=-1) {
    
    
            System.out.println(new String(b));
        }
    }

    public static void main(String[] args) throws Exception {
    
    
        loadAndReadUrlResource("atguigu.txt");
    }
}

9.3.5ServletContextResource class

​ This is ServletContextthe Resource implementation of the resource, which interprets relative paths in the root directory of the associated web application. It always supports stream access and URL access, but only allows java.io.File access if the web application archive is extended and the resource is actually on the file system. Whether it's expanded on the file system or accessed directly from a JAR or elsewhere (like a database) is really dependent on the Servlet container.

9.3.6InputStreamResource class

​IsInputStreamResource the Resource implementation of the given input stream. Its usage scenario is used when there is no specific resource implementation (it feels very similar to the applicable scenario of @Component). Compared to other Resource implementations, this is a descriptor of the opened resource. Therefore, its isOpen()methods return true. Do not use it if you need to keep the resource descriptor somewhere or if you need to read the stream multiple times.

9.6.7ByteArrayResource class

​ Resource implementation class of byte array. Creates one from the given array ByteArrayInputStream. It's useful for loading content from any given byte array without having to resort to single-use ones InputStreamResource.

9.4ResourceLoader interface

9.4.1 Overview

​InterfaceResourceLoader is a core interface in the Spring framework, which defines a unified access method for loading resources. It provides a unified method to load various types of resources, such as files, classpath resources, URL resources, etc.

9.4.2 Common methods

  • Resource getResource(String location)Resource: Get an object based on the given resource location (location) . A resource location can be a file path, class path, URL, etc. The specific resource loading strategy ResourceLoaderis determined by the specific implementation class.
  • ClassLoader getClassLoader(): Gets the object used to load the class ClassLoader. This is useful for loading resources on the classpath.

9.4.3 Implementation class

The Spring framework provides several ResourceLoaderclasses that implement interfaces, including:

  • DefaultResourceLoader: The default resource loader, which can be used to load classpath, file system and URL resources.
  • FileSystemResourceLoader: Used to load resources from the file system.
  • ClassPathResourceLoader: Used to load resources under the classpath.
  • ServletContextResourceLoader: Used to load resources in the context of a web application.

9.4.4 Basic use cases

public static void main(String[] args) {
    
    
    // 创建Spring应用上下文,从类路径中加载配置文件
    ApplicationContext ctx = new ClassPathXmlApplicationContext();
    //        通过ApplicationContext访问资源
    //        ApplicationContext实例获取Resource实例时,
    //        默认采用与ApplicationContext相同的资源访问策略
    Resource res = ctx.getResource("atguigu.txt");
    System.out.println(res.getFilename());
}

illustrate:

Spring will use the same strategy as ApplicationContext to access resources. In other words, if ApplicationContext is ClassPathXmlApplicationContext, res is the ClassPathResource instance

public static void main(String[] args) {
    
    
    // 创建Spring应用上下文,指定文件系统路径加载配置文件
    ApplicationContext ctx = new FileSystemXmlApplicationContext();
    Resource res = ctx.getResource("atguigu.txt");
    System.out.println(res.getFilename());
}

illustrate:

Spring will use the same strategy as ApplicationContext to access resources. In other words, if ApplicationContext is FileSystemXmlApplicationContext, res is a FileSystemResource instance;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.Resource;

public class ResourceLoaderExample {
    
    

    public static void main(String[] args) {
    
    
        // 创建一个ApplicationContext容器对象,该对象会读取classpath(类路径)下的名为applicationContext.xml的配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

        // 使用 classpath: 前缀指定使用 ClassPathResource 实现类
        Resource resource1 = context.getResource("classpath:config.properties");
        System.out.println("Resource 1: " + resource1.getClass().getSimpleName());

        // 使用 file: 前缀指定使用 FileSystemResource 实现类
        Resource resource2 = context.getResource("file:/path/to/file.txt");
        System.out.println("Resource 2: " + resource2.getClass().getSimpleName());

        // 使用 http: 前缀指定使用 UrlResource 实现类
        Resource resource3 = context.getResource("http://www.example.com");
        System.out.println("Resource 3: " + resource3.getClass().getSimpleName());
    }
}

illustrate:

​ Use the getResource() method of ApplicationContext to obtain three different types of resources, and use different prefixes to specify the use of different Resource implementation classes.

9.5ResourceLoaderAware interface

9.5.1 Overview

9.5.1.1 Meaning

​ ResourceLoaderAware is a Spring Bean interface used to inject ResourceLoader instances into beans that implement this interface. It defines a setResourceLoader(ResourceLoader resourceLoader) method to which the Spring container passes a ResourceLoader instance as a parameter at startup.

9.5.1.2 Function

​ Classes that implement the ResourceLoaderAware interface can obtain the ResourceLoader instance of the Spring container, thereby utilizing the resource loading function provided by the Spring container.

9.5.2 Basic use cases

Step 1: Create Bean

package com.atguigu.spring6.resouceloader;

import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.ResourceLoader;

public class TestBean implements ResourceLoaderAware {
    
    

    private ResourceLoader resourceLoader;

    //实现ResourceLoaderAware接口必须实现的方法
	//如果把该Bean部署在Spring容器中,该方法将会有Spring容器负责调用。
	//SPring容器调用该方法时,Spring会将自身作为参数传给该方法。
    public void setResourceLoader(ResourceLoader resourceLoader) {
    
    
        this.resourceLoader = resourceLoader;
    }

    //返回ResourceLoader对象的应用
    public ResourceLoader getResourceLoader(){
    
    
        return this.resourceLoader;
    }

}

illustrate:

  • To implement the ResourceLoaderAware interface, you must implement setResourceLoadermethods,

Step 2: Configure Bean

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="testBean" class="com.atguigu.spring6.resouceloader.TestBean"></bean>
</beans>

Step 3: Demo

public static void main(String[] args) {
    
    
    //Spring容器会将一个ResourceLoader对象作为该方法的参数传入
    ApplicationContext ctx = new ClassPathXmlApplicationContext("bean.xml");
    TestBean testBean = ctx.getBean("testBean",TestBean.class);
    //获取ResourceLoader对象
    ResourceLoader resourceLoader = testBean.getResourceLoader();
    System.out.println("Spring容器将自身注入到ResourceLoaderAware Bean 中 ? :" + (resourceLoader == ctx));
    //加载其他资源
    Resource resource = resourceLoader.getResource("atguigu.txt");
    System.out.println(resource.getFilename());
    System.out.println(resource.getDescription());
}

illustrate:

​ Because ApplicationContextthe implementation class of the interface ClassPathXmlApplicationContextimplements ResourceLoaderthe interface, ClassPathXmlApplicationContextthe instance object also has the function of "ResourceLoader". Therefore, objects that implement ResourceLoaderAwarethis interface can use the resource loading function provided by the Spring container

9.6 Dynamically obtain Resource resources

9.6.1 Overview

9.6.1.1 Meaning

The Spring framework not only makes full use of the strategy pattern to simplify resource access, but also fully combines the strategy pattern with IoC to simplify Spring resource access to the greatest extent. When bean instances in the application need to access resources, Spring has a better solution: directly using dependency injection

9.6.1.2 Function

​ For obtaining Resource instances in code, when the program obtains Resource instances, it is always necessary to provide the location of the Resource, whether creating an instance through FileSystemResource, creating an instance through ClassPathResource, or obtaining an instance through the getResource() method of ApplicationContext. Provide resource location. This means: the physical location of the resource is coupled into the code, and if the location of the resource changes, the program must be rewritten. Therefore, it is generally recommended to adopt the method of dependency injection and let Spring inject resources for Bean instances .

9.6.2 Basic use cases

Step 1: Create a dependency injection class, define properties and methods

public class ResourceBean {
    
    
    
    private Resource res;
    
    public void setRes(Resource res) {
    
    
        this.res = res;
    }
    public Resource getRes() {
    
    
        return res;
    }
    
    public void parse(){
    
    
        System.out.println(res.getFilename());
        System.out.println(res.getDescription());
    }
}

Step 2: Create a spring configuration file and configure dependency injection

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="resourceBean" class="com.atguigu.spring6.resouceloader.ResourceBean" >
      <!-- 可以使用file:、http:、ftp:等前缀强制Spring采用对应的资源访问策略 -->
      <!-- 如果不采用任何前缀,则Spring将采用与该ApplicationContext相同的资源访问策略来访问资源 -->
        <property name="res" value="classpath:atguigu.txt"/>
    </bean>
</beans>

Step 3: Demo

public static void main(String[] args) {
    
    
    ApplicationContext ctx =
        new ClassPathXmlApplicationContext("bean.xml");
    ResourceBean resourceBean = ctx.getBean("resourceBean",ResourceBean.class);
    resourceBean.parse();
}

9.7 Determine resource access paths

9.7.1 Overview

No matter how you create an ApplicationContext instance, you need to specify a configuration file for the ApplicationContext. Spring allows the use of one or more XML configuration files. When a program creates an ApplicationContext instance, it usually accesses the configuration file in the form of Resource, so ApplicationContext fully supports resource access methods such as ClassPathResource, FileSystemResource, and ServletContextResource.

9.7.2 Implementing classes to specify access paths

(1) ClassPathXMLApplicationContext: Corresponds to using ClassPathResource for resource access.

(2) FileSystemXmlApplicationContext: Corresponds to using FileSystemResource for resource access.

(3) XmlWebApplicationContext: Corresponds to using ServletContextResource for resource access.

For detailed steps, please view the implementation class of Resources

9.7.3 Prefix specifies access path

9.7.3.1classpath prefix

public class Demo1 {
    
    

    public static void main(String[] args) {
    
    
        /*
         * 通过搜索文件系统路径下的xml文件创建ApplicationContext,
         * 但通过指定classpath:前缀强制搜索类加载路径
         * classpath:bean.xml
         * */
        ApplicationContext ctx =
                new ClassPathXmlApplicationContext("classpath:bean.xml");
        System.out.println(ctx);
        Resource resource = ctx.getResource("atguigu.txt");
        System.out.println(resource.getFilename());
        System.out.println(resource.getDescription());
    }
}

illustrate:

​ When using ApplicationContext, when specifying the path, use **classpath:** as the prefix

9.7.3.2classpath wildcard

ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath*:bean.xml");

illustrate:

​ When using the ApplicationContext, when specifying the path, use classpath* as the prefix, which means that Spring will search for all configuration files that meet this rule in the class loading path. For example: bean.xml, beans.xml

9.7.3.3 Other uses of wildcards

ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:bean*.xml");

illustrate:

​ How to load multiple configuration files at once: use wildcards when specifying configuration files

ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath*:bean*.xml");

illustrate:

​ Spring allows the combination of classpath*: prefixes and wildcards

10. Internationalization: i18n

Summary of notes:

  1. Overview: Designing software, applications, or websites to adapt to the needs of different languages , cultures, and regions

  2. Java internationalization:

    • ResourceBundle class: It is an internationalization tool class used to load and manage resource files in different language environments.

    • Configuration file naming rules: basename_language_country.properties, for examplemessages_en_CB.properties

    • Basic use case:

      1. Create resource configuration file

      2. Obtained through ResourceBundle class

        public static void main(String[] args) {
                   
                   
            ResourceBundle resourceBundle = ResourceBundle.getBundle("messages", new Locale("en", "GB"));
            String string = resourceBundle.getString("test");
            System.out.println(string);
        }
        
  3. Spring6 internationalization

    Step 1: Create properties resource configuration file

    Step 2: Create spring configuration file and configure MessageSource

    Step 3: Get the keys in the resource configuration file by injecting it into the Spring6 configuration file

    public static void main(String[] args) {
           
           
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        //传递动态参数,使用数组形式对应{0} {1}顺序
        Object[] objs = new Object[]{
           
           "atguigu",new Date().toString()};
        //www.atguigu.com为资源文件的key值,
        //objs为资源文件value值所需要的参数,Local.CHINA为国际化为语言
        String str=context.getMessage("www.atguigu.com", objs, Locale.CHINA);
        System.out.println(str);
    }
    

10.1 Overview

​Internationalization, often abbreviated as i18n (i + 18 letters + n), refers to the design of software, applications or websites to be able to adapt to the needs of different languages, cultures and regions. Generally speaking, internationalization in software is achieved through configuration files. If two languages ​​are to be supported, then two versions of configuration files are required.

10.2Java internationalization

10.2.1 createConstant method

image-20230525173916281

illustrate:

​ java.util.Locale is used to specify information such as the locale to which the current user belongs, and java.util.ResourceBundle is used to find the resource file corresponding to the binding. Locale contains language information and country information, the static method used by Locale to create the default locale object:

10.2.2ResourceBundle class

ResourceBundle is a tool class provided by Java for internationalization, which is used to load and manage resource files in different language environments.

10.2.3 Configuration file naming rules

basename_language_country.properties

illustrate:

​ The above naming rules must be followed for java to recognize it. Among them, basename is required, language and country are optional. There is a priority concept here. If both messages.properties and messages_zh_CN.propertes are provided at the same time, and if the locale provided conforms to en_CN, then the messages_en_CN.propertes configuration file will be searched first. If not found, the messages.properties configuration will be searched. document. Finally, as a reminder, all configuration files must be placed in the classpath, usually in the resources directory

10.3 Basic use cases

Step 1: In the resources resource file, create a configuration file

image-20230525175111010

illustrate:

​ Resource Bundle'messages'is automatically generated by the system, because the created properties files conform to the Java internationalization naming rules

Step 2: Write configuration files in different languages

test=123
// 
test=456

Step 3: Demo

public class ResourceBundleExample {
    
    

    public static void main(String[] args) {
    
    
        // 获取名为 "messages" 的资源包,使用英国地区设置(Locale)
        ResourceBundle resourceBundle = ResourceBundle.getBundle("messages", new Locale("en", "GB"));
        
        // 从资源包中获取名为 "test" 的字符串
        String string = resourceBundle.getString("test");
        
        System.out.println(string);
    }
}

illustrate:

​ The ResourceBundle class is used to manage resource files in different language environments in internationalization, and Locale is used to specify the locale to which the current user belongs

10.4Spring6 internationalization

Step 1: Create resource files

Internationalization file naming format: basename_language_country.properties

Contents like {0},{1} are dynamic parameters.

image-20221207140024056

(1) Create atguigu_en_US.properties

www.atguigu.com=welcome {0},时间:{1}

(2) Create atguigu_zh_CN.properties

www.atguigu.com=欢迎 {0},时间:{1}

Step 2: Create a spring configuration file and configure MessageSource

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="messageSource"
          class="org.springframework.context.support.ResourceBundleMessageSource">
        <property name="basenames">
            <list>
                <value>atguigu</value>
            </list>
        </property>
        <property name="defaultEncoding">
            <value>utf-8</value>
        </property>
    </bean>
</beans>

illustrate:

In Spring 6, naming the id of ResourceBundleMessageSource as messageSource can reduce unnecessary configuration and explicit references and allow Spring 6 to achieve automatic injection.

Step 3: Create test class

package com.atguigu.spring6.javai18n;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.Date;
import java.util.Locale;

public class Demo2 {
    
    

    public static void main(String[] args) {
    
    
        
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        
        //传递动态参数,使用数组形式对应{0} {1}顺序
        Object[] objs = new Object[]{
    
    "atguigu",new Date().toString()};

        //www.atguigu.com为资源文件的key值,
        //objs为资源文件value值所需要的参数,Local.CHINA为国际化为语言
        String str=context.getMessage("www.atguigu.com", objs, Locale.CHINA);
        System.out.println(str);
    }
}

11. Data verification: Validation

Summary of notes:

  1. Overview: Data validation (Validation) is the process of validating user-entered data in an application to ensure that the data conforms to expected rules and constraints.

  2. Verification is implemented through the Validator interface:

    Step 1: Import hibernate-validatorand jakarta.eldependency

    Step 2: Create entity class

    Step 3: Implement the Validator interface

    Step 4: Implement DataBinderthe validator

  3. Implement verification through Bean Validation annotation (recommended):

    Step 1: Create configuration file class

    Step 2: Create an entity class and use annotations to define verification rules

    Step 3: Create a validator service (using @Autowired)

    Step 4: Verification

  4. Verification is implemented through methods:

    Step 1: Create configuration file class

    Step 2: Create an entity class and use annotations to define verification rules

    Step 3: Create the validator service (using @Valid)

  5. Pass custom verification (supplementary):

    Step 1: Customize verification annotations

    Step 2: Write the implemented verification class

  6. For more annotations: please refer to, spring-boot request parameter verification: use of annotation @Validated, manual verification, custom verification_validate annotation verification_sayyy's blog-CSDN blog

11.1 Overview

​ Data validation (Validation) is the process of validating user input data in the application to ensure that the data conforms to the expected rules and constraints. In Java, the commonly used data validation framework is Validation API, which is part of Java EE and also integrated and supported in the Spring framework.

11.2 Basic use case-verification through Validator interface

illustrate:

Implementing data verification through Validatorinterfaces is another way to use the Validation API for data verification. ValidatorThe interface provides a more flexible verification mechanism, and you can customize verification logic and error messages.

Step 1: Introduce relevant dependencies

<dependencies>
    <dependency>
        <groupId>org.hibernate.validator</groupId>
        <artifactId>hibernate-validator</artifactId>
        <version>7.0.5.Final</version>
    </dependency>

    <dependency>
        <groupId>org.glassfish</groupId>
        <artifactId>jakarta.el</artifactId>
        <version>4.0.1</version>
    </dependency>
</dependencies>

Step 2: Create entity class

package com.atguigu.spring6.validation.method1;

public class Person {
    
    
    private String name;
    private int age;

    public String getName() {
    
    
        return name;
    }
    public void setName(String name) {
    
    
        this.name = name;
    }
    public int getAge() {
    
    
        return age;
    }
    public void setAge(int age) {
    
    
        this.age = age;
    }
}

Step 3: Implement the Validator interface

public class PersonValidator implements Validator {
    
    

    // supports方法用来表示此校验用在哪个类型上
    @Override
    public boolean supports(Class<?> clazz) {
    
    
        return Person.class.equals(clazz);
    }

    // validate是设置校验逻辑的地点
    @Override
    public void validate(Object object, Errors errors) {
    
    
        ValidationUtils.rejectIfEmpty(errors, "name", "name.empty");
        Person p = (Person) object;
        if (p.getAge() < 0) {
    
    
            errors.rejectValue("age", "error value < 0");
        } else if (p.getAge() > 110) {
    
    
            errors.rejectValue("age", "error value too old");
        }
    }
}

illustrate:

  • Implement the Validator interface, rewrite the supports and validate methods, and implement the logic
  • ValidationUtils is a validation tool class encapsulated by Spring to help quickly implement validation

Step 4: Demonstrate

public static void main(String[] args) {
    
    
    // 创建person对象
    Person person = new Person();
    person.setName("lucy");
    person.setAge(-1);

    // 创建Person对应的DataBinder
    DataBinder binder = new DataBinder(person);

    // 绑定校验
    binder.setValidator(new PersonValidator());

    // 开始校验
    binder.validate();

    // 输出结果
    BindingResult results = binder.getBindingResult();
    System.out.println(results.getAllErrors());
}

illustrate:

​ By using DataBinderthe validator, you can easily perform data validation on the object and obtain the validation result

11.3 Basic use case-verification through Bean Validation annotation

illustrate:

​ Bean Validation is an annotation-based data validation specification in Java. Data validation rules can be defined by adding corresponding annotations to the properties of entity classes

Commonly used annotations:

  1. @NotNull: Check that the field value is not null.
  2. @NotEmpty: Checks that the value of a string, collection or array is not empty.
  3. @NotBlank: Checks that the value of a string is not empty or does not contain only whitespace characters.
  4. @Min(value): Checks that the field value is greater than or equal to the specified minimum value.
  5. @Max(value): Checks that the field value is less than or equal to the specified maximum value.
  6. @Size(max, min): Checks that the size of the field value is within the specified range.
  7. @Email: Checks that the field value meets the format requirements of the email.
  8. @Pattern(regex): Checks that the field value matches the specified regular expression.

Step 1: Create configuration file class

@Configuration
@ComponentScan("org.example")
public class ValidationConfig {
    
    

    @Bean
    public LocalValidatorFactoryBean validator() {
    
    
        return new LocalValidatorFactoryBean();
    }
}

illustrate:

  • LocalValidatorFactoryBeanIt is a class provided by the Spring framework that implements ValidatorFactorythe interface and is used to create and manage Validatorinstances.
  • Configuring ComponentScan scanning rules is to enable package scanning and facilitate Bean injection of the Spring framework.

Step 2: Create an entity class and use annotations to define verification rules

@Data
public class User {
    
    

    @NotNull
    private String name;

    @Min(0)
    @Max(120)
    private int age;
}

Notice:

​ @Max and @Min annotations require importing dependencies before data verification. For dependencies, please check through the Validator interface.

Step 3: Create the validator

1. Use jakarta.validation.Validator to verify

@Service
public class ValidatorServiceOne {
    
    

  // 导入 jakarta.validation.Validator
    @Autowired
    Validator validator;

    public Boolean vailAge(Person person) {
    
    
        // 使用Validator的vaildate方法进行数据验证
        Set<ConstraintViolation<Person>> validate = validator.validate(person);
        return validate.isEmpty();
    }
}

illustrate:

During the verification process, Validatoreach attribute of the user object will be checked in turn to verify whether it meets the specified constraints. If the value of an attribute does not meet the constraint conditions, an ConstraintViolationobject will be generated, which contains detailed information about the constraint violation, such as the attribute name, the type of constraint violated, error message, etc. Finally, these ConstraintViolationobjects are collected into a Setcollection and returned.

2. Use org.springframework.validation.Validator to verify

@Service
public class ValidatorServiceTwo {
    
    

     // 导入  org.springframework.validation.Validator;
    @Autowired
    private Validator validator; 

    public boolean validaPersonByValidator(User user) {
    
    
        // 创建 BindException 对象,用于收集校验错误信息
        BindException bindException = new BindException(user, user.getName());
        
        // 使用 Validator 对象对用户对象进行校验,将校验结果收集到 bindException 中
        validator.validate(user, bindException);
        
        // 判断 bindException 中是否存在校验错误信息
        return bindException.hasErrors();
    }
}

illustrate:

BindException​is org.springframework.validationa class in the package, which inherits from org.springframework.validation.Errorsthe interface and is used to collect verification error information. By creating BindExceptionan object, the verification results can be collected into it to facilitate subsequent processing of error information.

Notice:

​ When using Validator, you need to import dependencies, the same as through the Validator interface.

Step 4: Demonstrate

1. Use jakarta.validation.Validator to verify

  @Test
    public void testMyService1() {
    
    
        ApplicationContext context = new AnnotationConfigApplicationContext(ValidationConfig.class);
        MyService1 myService = context.getBean(MyService1.class);
        User user = new User();
        user.setAge(-1);
        boolean validator = myService.validator(user);
        System.out.println(validator);
    }

2. Use org.springframework.validation.Validator to verify

    @Test
    public void testMyService2() {
    
    
        ApplicationContext context = new AnnotationConfigApplicationContext(ValidationConfig.class);
        MyService2 myService = context.getBean(MyService2.class);
        User user = new User();
        user.setName("lucy");
        user.setAge(130);
        user.setAge(-1);
        boolean validator = myService.validaPersonByValidator(user);
        System.out.println(validator);
    }

11.4 Basic use cases - verification through methods

illustrate:

Method-based verification refers to using verification annotations on a specific method to verify the method’s input parameters, return value, or status during method execution. Spring provides @Validatedannotations and validation annotations (eg @NotNull, @NotBlank, @Min, @Maxetc.) to implement method-based validation.

Step 1: Create configuration file class

@Configuration
@ComponentScan("org.example")
public class ValidationConfig {
    
    

    @Bean
    public MethodValidationPostProcessor validationPostProcessor() {
    
    
        return new MethodValidationPostProcessor();
    }
}

illustrate:

​isMethodValidationPostProcessor a post-processor provided by Spring for parameter verification when calling methods. It can automatically intercept @Validatedthe methods marked by annotations and verify the parameters of the methods.

Step 2: Create an entity class and use annotations to define verification rules

@Data
public class User {
    
    

    @NotNull
    private String name;

    @Min(0)
    @Max(120)
    private int age;

    @Pattern(regexp = "^1(3|4|5|7|8)\\d{9}$",message = "手机号码格式错误")
    @NotBlank(message = "手机号码不能为空")
    private String phone;
}

Step 3: Create the validator

@Service
@Validated
public class MyService {
    
    
    //	使用 @Valid 注解标记了 User 参数,表示对该参数进行校验、使用 @NotNull 注解标记了 User 参数,表示该参数不能为空。
    public String testParams(@NotNull @Valid User user) {
    
    
        return user.toString();
    }
}

illustrate:

​ If the verification fails, a ConstraintViolationException will be thrown. If the verification passes, other business logic will be executed.

Step 4: Demonstrate

@Test
public void testMyService1() {
    
    
    ApplicationContext context = new AnnotationConfigApplicationContext(ValidationConfig.class);
    MyService myService = context.getBean(MyService.class);
    User user = new User();
    user.setAge(-1);
    myService.testParams(user);
}

11.5 Basic use case-pass custom verification

Step 1: Customize verification annotations

@Target({
    
    ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
// @Target 和 @Retention 来定义注解的使用范围和生命周期
@Documented
// @Constraint 标记该注解为校验注解,并指定对应的校验器类
@Constraint(validatedBy = {
    
    CannotBlankValidator.class})
public @interface CannotBlank {
    
    
    //默认错误消息
    String message() default "不能包含空格";

    //分组
    Class<?>[] groups() default {
    
    };

    //负载
    Class<? extends Payload>[] payload() default {
    
    };

    //指定多个时使用
    @Target({
    
    ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @interface List {
    
    
        CannotBlank[] value();
    }
}

illustrate:

For the content implementation of this annotation, you can refer to the part that comes with the existing annotation.

Replenish:

  • @TargetAnnotations are used to specify the usage scope of annotations, including methods, fields, annotation types, constructors, and parameters.
  • @RetentionAnnotations are used to specify the life cycle of annotations, that is, the annotation information is still retained during runtime.
  • @DocumentedAn annotation is used to indicate that the annotation should be included in the generated documentation.
  • @ConstraintThe annotation marks the annotation as a validation annotation and specifies the corresponding validator class CannotBlankValidator.
  • message()method defines a default error message to use when validation fails.
  • groups()The method is used for group verification, and you can specify which group the validator takes effect.
  • payload()Method specifies the payload type, which can be used in custom validators.
  • @ListAnnotation is used when specifying multiple @CannotBlankannotations, multiple annotations can be combined.

Notice:

​ The annotation itself does not implement specific verification logic, but CannotBlankValidatorimplements the specific verification logic through the validator class.

Step 2: Write the verification class

public class CannotBlankValidator implements ConstraintValidator<CannotBlank, String> {
    
    

    	// initialize() 方法用于初始化校验器,在校验之前进行一些初始化操作。可以获取注解中的属性值,并进行相应的处理
        @Override
        public void initialize(CannotBlank constraintAnnotation) {
    
    
        }

    	// isValid() 方法是校验的核心逻辑,用于判断被校验的值是否符合自定义的校验规则
        @Override
        public boolean isValid(String value, ConstraintValidatorContext context) {
    
    
                //null时不进行校验
                if (value != null && value.contains(" ")) {
    
    
                        //获取默认提示信息
                        String defaultConstraintMessageTemplate = context.getDefaultConstraintMessageTemplate();
                        System.out.println("default message :" + defaultConstraintMessageTemplate);
                        //禁用默认提示信息
                        context.disableDefaultConstraintViolation();
                        //设置提示语
                        context.buildConstraintViolationWithTemplate("can not contains blank").addConstraintViolation();
                        return false;
                }
                return true;
        }
}

illustrate:

  • Create a custom validator class: implement the ConstraintValidator interface, and specify the annotation type to be validated and the value type to be validated. In the verification logic, implement custom verification rules and return verification results as needed
  • In isValid()the method, there are two parameters:
    • value: The value to be verified, that is, the value of the field or method parameter that needs to be verified.
    • context: The context object of the validator, used to control some behaviors and settings during the validation process.
  • When implementing isValid()the method, according to the customized verification rules, you can use valuethe parameter to obtain the verified value, and verify it according to the business logic. If the verification is successful, it returns trueto indicate that the verification is passed; if the verification fails, it returns falseto indicate that the verification is not passed.

Step 3: Demo

public static void main(String[] args) {
    
    
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(ValidationConfig.class);
    ValidatorService bean = (ValidatorService) applicationContext.getBean("validatorService");
    Person person = new Person();
    person.setAge(12);
    person.setMessage("fd s  f");
    String s = bean.vailAge(person);
    System.out.println(s);
}

illustrate:

​ This step is only to implement custom rules through custom annotations for verification. How to implement verification also needs to refer to 实现the method of verification

12. Ahead of time compilation: AOT

Summary of notes:

  1. Overview:
    • Meaning: Ahead of Time Compilation (AOT) is a technology that compiles program code into machine code before it runs . Contrary to traditional just-in-time compilation (JIT), just-in-time compilation interprets program code line by line into machine code and executes it at runtime.
    • Graalvm: The AOT technology supported by Spring6, this GraalVM is the underlying support, and Spring also provides first-class support for GraalVM native images. GraalVM is a high-performance JDK designed to accelerate the execution of applications written in Java and other JVM languages, while also providing runtimes for JavaScript, Python, and many other popular languages.
    • Native Image: In addition to this solution of AOT in JVM, there is another way to implement Java AOT in the industry, which is to directly abandon the JVM and directly compile the code into machine code through a compiler like C/C++, and then run. This is undoubtedly an idea that directly subverts Java language design, that is GraalVM Native Image.
  2. Native Image building process
    • GraalVM installation
    • Install the C++ compilation environment
    • Write code and build Native Image

12.1 Overview

12.1.1 Meaning

Ahead of Time Compilation (AOT) is a technology that compiles program code into machine code before running . Contrary to traditional just-in-time compilation (JIT), just-in-time compilation interprets program code line by line into machine code and executes it at runtime.

AOT advantages

  1. Faster startup time : Since the code has been compiled into machine code, there is no need to compile and interpret it at runtime, so it can be executed directly, reducing startup time.
  2. Higher execution performance : AOT compilation can perform comprehensive static optimization, including code optimization, inlining, eliminating unnecessary checks, etc. These optimizations are performed at compile time and can provide higher execution performance .
  3. Better security : AOT compilation can compile source code into machine code, hiding the logic of the source code and providing better security.

Disadvantages of AOT

  1. Larger compilation output: AOT compilation completely compiles the source code or intermediate code into machine code, so the compilation output is usually larger than the source code or bytecode and takes up more storage space.
  2. Compilation time delay: AOT compilation needs to occur before the program can be run, thus increasing development and build time costs, especially for larger code bases or complex projects.
  3. Lack of runtime optimization: Since AOT compilation is performed before the program is run, it cannot be optimized based on the actual runtime context information and cannot dynamically adapt to the changing execution environment.

12.1.2 The difference between JIT and AOT

JIT, Just-in-time, dynamic (just-in-time) compilation, compilation while running;

When the program is running, the hot code is calculated based on the algorithm, and then JIT real-time compilation is performed. This method has high throughput, has a runtime performance bonus, can run faster, and can dynamically generate code , etc., but is relatively The startup speed is slow , and it takes a certain amount of time and calling frequency to trigger the JIT's layering mechanism. The disadvantage of JIT is that compilation needs to occupy runtime resources, which will cause the process to freeze.

AOT, Ahead Of Time, refers to pre-run compilation, pre-compilation;

​ AOT compilation can directly convert source code into machine code, with low memory usage and fast startup speed . It can run without runtime and directly statically link the runtime into the final program. However, there is no runtime performance bonus and it cannot be based on the running status of the program. For further optimization, the disadvantage of AOT is that compiling before the program is run will increase the program installation time.

In general: JIT just-in-time compilation refers to the process of converting bytecode into machine code that can be run directly on the hardware during the running process of the program and deploying it to the hosting environment. AOT compilation refers to the process of converting bytecode into machine code before the program is run.

image-20230526155600385

illustrate:

​ .java -> .class -> (using jaotc compilation tool) -> .so (program function library, that is, compiled code and data that can be used by other programs)

12.1.3Graalvm

​ GraalVM is the underlying support for the AOT technology supported by Spring 6. Spring also provides first-class support for GraalVM native images. GraalVM is a high-performance JDK designed to accelerate the execution of applications written in Java and other JVM languages, while also providing runtimes for JavaScript, Python, and many other popular languages. GraalVM provides two ways to run Java applications: using the Graal just-in-time (JIT) compiler on the HotSpot JVM or as a native executable compiled ahead of time (AOT). GraalVM's multilingual capabilities make it possible to mix multiple programming languages ​​in a single application while eliminating the cost of foreign language calls. GraalVM adds an advanced just-in-time (JIT) optimizing compiler written in Java to the HotSpot Java virtual machine.

GraalVM has the following features:

(1) An advanced optimizing compiler that generates faster, leaner code that requires fewer computing resources

(2) AOT native image compilation compiles Java applications into native binaries in advance, starts immediately, and achieves maximum performance without preheating

(3) Polyglot programming leverages the best features and libraries of popular languages ​​in a single application without additional overhead

(4) Advanced tools to debug, monitor, analyze and optimize resource consumption in Java and multiple languages

​ Generally speaking, the requirements for cloud native are not high. You can continue to use the 2.7.X version and JDK8 in the short term, but Spring has officially released Spring6.

12.1.4Native Image

​ At present, in the industry, in addition to this solution of performing AOT in the JVM, there is another way to implement Java AOT, which is to directly abandon the JVM , and directly compile the code into machine code through a compiler like C/C++, and then run it. This is undoubtedly an idea that directly subverts the design of the Java language, that is, GraalVM Native Image. It implements an ultra-miniature runtime component through C language - Substrate VM, which basically implements various features of the JVM, but is lightweight enough and can be easily embedded, which allows the Java language and engineering to get rid of the limitations of the JVM. It can truly realize the same AOT compilation as C/C++. After a long period of optimization and accumulation, this solution has achieved very good results and has basically become the first Java AOT solution officially recommended by Oracle.
​ Native Image is an innovative technology that compiles Java code into a stand-alone native executable or a native shared library . The Java bytecode processed during building a native executable includes all application classes, dependencies, third-party dependent libraries, and any required JDK classes. The resulting self-contained native executables are specific to each individual operating system and machine architecture that does not require a JVM.

12.2Native Image construction process

12.2.1GraalVM installation

Step 1: Download GraalVM

Enter the official website to download: https://www.graalvm.org/downloads/

image-20221207153944132

image-20221207152841304

Step 2: Configure environment variables

Add GRAALVM_HOME

image-20221207110539954

Change JAVA_HOME to the location of graalvm

image-20221207153724340

Change the Path to the bin location of graalvm

image-20221207153755732

Use the command to check whether the installation is successful

image-20221207153642253

Step 3: Install the native-image plugin

Use the command gu install native-image to download and install

image-20221207155009832

12.2.2 Install the C++ compilation environment

Step 1: Download the Visual Studio installation software

https://visualstudio.microsoft.com/zh-hans/downloads/

image-20221219112426052

Step 2: Install Visual Studio

image-20221207155726572

image-20221207155756512

Step 3: Add Visual Studio environment variables

Configure INCLUDE, LIB and Path

image-20221207110947997

image-20221207111012582

image-20221207111105569

Step 4: Open the tool and operate in the tool

image-20221207111206279

12.2.3 Write code and build Native Image

Step 1: Write Java code

public class Hello {
    
    

    public static void main(String[] args) {
    
    
        System.out.println("hello world");
    }
}

Step 2: Copy the file to the directory and perform compilation

image-20221207111420056

Step 3: Build Native Image

image-20221207111509837

image-20221207111609878

Step 4: View the built files

image-20221207111644950

Step 5: Execute the built file

image-20221207111731150

You can see that the size of the binary file finally packaged by Hello is 11M. This is the size after including various libraries of SVM and JDK. Although it is larger than the binary file of C/C++, it is still smaller than the complete JVM. It can be said that it is already very small.

Compared with running using JVM, Native Image is much faster and has lower CPU usage. It can also be seen from various official experimental data provided that Native Image greatly improves startup speed and memory usage. obviously:

image-20221207111947283

image-20221207112009852

knowledge gas station

1.The difference between ResourceLoader and Resource implementation classes

ResourceLoaderThe main differences from Resourcethe implementation class of the interface are as follows:

  1. ResourceLoaderThe implementation class is mainly used to load resources and provides methods for loading resources, such as ClassPathResourceLoaderloading resources under the class path, UrlResourceLoaderloading URL resources, FileSystemResourceLoaderloading resources in the file system, etc.
  2. ResourceThe implementation class is mainly used to access and operate resources, and provides specific operation methods for resources, such as ClassPathResourceaccessing resources under the class path, UrlResourceaccessing URL resources, FileSystemResourceaccessing resources in the file system, etc.
  3. ResourceLoaderThe implementation class is usually used internally by the Spring framework to provide resource loading functions and is not used directly in the application.
  4. ResourceThe implementation class of can be used in the application , through which the information and content of the resource can be obtained, read and manipulated.

In general, ResourceLoaderthe implementation class of is mainly responsible for loading resources, while Resourcethe implementation class of is mainly responsible for accessing and manipulating resources. They play different roles in the framework, but all are used to handle and manage resources in the application.

image-20230526220018997

illustrate:

​ The Resource interface is an abstraction for accessing resources in Spring. The Resource interface itself only provides regulations, and there are many implementation classes below. Both are resource descriptors abstracted from the actual system underlying resources. Generally speaking, in Spring, resources are described as resource addresses in URL format and Ant style with wildcards.

image-20230526220042075

illustrate:

​InterfaceResourceLoader , as the name suggests, is designed to quickly return (that is, load) the object of the Resource instance

Detailed reference document: Spring Framework Source Code Analysis (IoC): Relationship between Resource, ResourceLoader and Container - Tencent Cloud Developer Community - Tencent Cloud (tencent.com)

Guess you like

Origin blog.csdn.net/D_boj/article/details/132286524