Obtaining Parameters

You will not be able to obtain both @PathVariable and @RequestParam

if the last one is @PathVariable @RequestParam applicable to URL query parameters (? stockQuantity=100) or form data (form-urlencoded)。 but not applicable toput put needing pojo to pass

Parameter Cases
  • Array Parameters

For multi-select operations assets/JavaWeb/image-20230809102733219.png receive assets/JavaWeb/image-20230809102854144.png

array: request parameter name equals array variable name in the formal parameter, can be directly encapsulated

collection: request parameter name equals collection variable name, through @RequestParam binding parameter relationships

  • Date Parameters

use @DateTimeFormat(pattern= "") to specify the accepted parameter format

assets/JavaWeb/image-20230809104109364.png must be the same as the passed format assets/JavaWeb/image-20230809123944488.png

  • Json Parameters

use @RequestBody

assets/JavaWeb/image-20230809124901273.png

Json data key name and formal parameter object property name are the same

  • Path Parameters localhost:8080/path/1 localhost:8080/path/100 Request parameter (1,100) becomes part of the request path, this kind of parameter is called a path parameter

@RequestMapping("/path/{id}") use @pathVariable to obtain the parameter in the path and bind it to the formal parameter

assets/JavaWeb/image-20230809135426642.png assets/JavaWeb/image-20230809135805144.png

@DeleteMapping("/{ids}")
public Result delete(@PathVariable List<Integer> ids){

assets/JavaWeb/image-20230809140520042.png

Layered Decoupling

assets/JavaWeb/image-20240307154743209.png

File Upload

Accept file type: MultipartFile

common methods
String getOriginalFilename(); //obtain the original file name
void transferTo(File dest); //save the received file to a disk file
long getSize(); //obtain the file size, unit: bytes
byte[] getBytes(); //obtain the byte array of the file content
InputStream getInputStream(); //obtain the input stream of the received file content

assets/JavaWeb/image-20230828223003194.png

File Storage

  • springboot The default upload size for files is 1M
#configure the maximum upload size for a single file
spring.servlet.multipart.max-file-size=10MB
#configure the maximum upload size for a single request(one request can upload multiple files)
spring.servlet.multipart.max-request-size=100MB
Local Storage Method

    @PostMapping("/upload")
    Result upload(String username , Integer age , MultipartFile image) throws IOException {
        log.info("文件上传: {}, {}, {}", username, age, image);
        String originalFilename = image.getOriginalFilename();
        int index = originalFilename.lastIndexOf(".");//obtain the last.position of
        String extname = originalFilename.substring(index);//obtain the file extension
        String newFileName = UUID.randomUUID().toString()+extname;
        log.info("新的文件名: {}", newFileName);
        image.transferTo(new File("D:\\Users\\XCJY\\Desktop\\"+newFileName));
        return Result.success();
    }

Aliyun OSS

Dependency (Optional)

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
  • Aliyun
aliyun.oss.endpoint = https://oss-cn-beijing.aliyuncs.com  
aliyun.oss.bucketName = web-tlias1122233

assets/JavaWeb/image-20230829104400590.png

web-tlias1122233
AccessKey ID:LTAI5t5YvwjEkM88f6m8ExCS
AccessKey Secret:wO895xzzup9fBtouTWFZzs7z2Memym
Ssky:  
  alioss:  
    access-key-id: LTAI5t5YvwjEkM88f6m8ExCS  
    access-key-secret: wO895xzzup9fBtouTWFZzs7z2Memym  
    bucket-name: web-tlias1122233  
    endpoint: https://oss-cn-beijing.aliyuncs.com 


or set environment variables
OSS_ACCESS_KEY_ID=LTAI5t5YvwjEkM88f6m8ExCS
OSS_ACCESS_KEY_SECRET=wO895xzzup9fBtouTWFZzs7z2Memym
OSS_STS_ROLE_ARN=acs:ram::1547130400260530:role/xxx666

How to use OSSJavaSDK to complete common operations_Object Storage OSS - Aliyun Help Center (aliyun.com)

Alibaba Cloud storage method
public class UploadController {  
    @Autowired  
    AliOSSUtils aliOSSUtils;  
    
    @PostMapping("/upload")  
    Result upload(MultipartFile image) throws IOException {  
        log.info("文件上传, 文件名: {}", image.getOriginalFilename());  
        String url = aliOSSUtils.upload(image);  
        log.info("文件上传完成,文件访问的url", url);  
        return Result.success(url);  
    }  
}

/**
 * Alibaba Cloud OSS utility class
 */
@Component
public class AliOSSUtils {
    @Autowired
    private AliOSSProperties aliOSSProperties;

    EnvironmentVariableCredentialsProvider credentialsProvider;

    {
        try {
            credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
        } catch (ClientException e) {
            e.printStackTrace();
        }
    }

    /**
     * implement uploading images to OSS
     */
    public String upload(MultipartFile file) throws IOException {
        String endpoint = aliOSSProperties.getEndpoint();
        String bucketName = aliOSSProperties.getBucketName();

        // get the input stream of the uploaded file
        InputStream inputStream = file.getInputStream();

        // avoid file overwriting
        String originalFilename = file.getOriginalFilename();
        String fileName = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."));

        //upload file to OSS
        //OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
        OSS ossClient = new OSSClientBuilder().build(endpoint, credentialsProvider);
        ossClient.putObject(bucketName, fileName, inputStream);

        //file access path
        String url = endpoint.split("//")[0] + "//" + bucketName + "." + endpoint.split("//")[1] + "/" + fileName;
        // close ossClient
        ossClient.shutdown();
        return url;// return the upload to oss path
    }

}

Login verification technology

Since http and https are both stateless, and each access is independent, this makes it impossible on the server side to distinguish whether two requests come from the same browser and the same session using only the http protocol, so session tracking technology is needed

Session technology

Data sharing: very important, for example after logging in, other pages still obtain the login information; verification code: obtain the value of the verification code filled in by the user and the verification code generated in the previous request (two requests in one session share data)

But http supports cookie carrying, so data can be transmitted After logging in once, a session is established, and multiple requests can be sent within one session

  • Cookie Session tracking technology, stored on the client side, the client stores the entire cookie data, which is easy to forge
  • Session Session tracking technology, stored on the server side, the client cookie only stores key value, and goes to the server to look upcookiedata
  • Token technology session tracking technology: client requests no longer use cookie, but instead use token, stored on the client side, and the server side is only responsible for verification

assets/JavaWeb/image-20230902145151850.png

JWT token

is used to ensure that the transmitted content is reliable and has not been tampered with by a third party, so this untampered token can be used to log in; otherwise, even if the information is leaked, login will be rejected

Note: the JWT token itself doesnot encrypt, it only usessignatureto ensure itsintegrity, avoid forgery, and the third part is to avoid forgery. After the server obtains the JWT token, it uses the algorithm of the first part together with its own secret key to derive the digital signature. If it differs from the transmitted one, it is forged

The third part uses the algorithm from the first part, such as HMAC-SHA256 algorithm, passing in its own secret key, such as itheima, to perform signature calculation on the data of the second part and generate a digital signature. Since hackers do not know the secret key, they cannot create a legitimate digital signature. The transmitted digital signature can only be verified by itself, which solves the cluster authentication problem, and it can be decrypted using the same secret key

used by both communicating parties to json securely transmit information in data format. Due to the existence of the digital signature, this information is reliable

  • Encryption: The core goal isconfidentiality. Convert the original data (called plaintext) into an unreadable format (called ciphertext), and only the person who possesses the key can restore the ciphertext back to plaintext.

  • Signature: The core goal isintegrity and identity verification. It does not hide information, but proves the origin of the data and whether it has been tampered with by generating a digital signature attached to the data. JWT does not encrypt the information in the second part

JWT itself is stateless, the backend does not need to record information, it only needs to verify the third part; if the signature part is reliable, it means the information is reliable

  1. First part: Header (header): records the platform type (for example: JWT) and the signature algorithm (any HS 256), such as {"alg": "HS 256","type": "JWT"}
  2. Second part: payload (payload): carries some custom information, default information, etc. For example:{"id": "1","username": "Tom"}
  3. Third part: digital signature, Signature (signature), to prevent the Token from being tampered with and ensure security. Would header、payload, and add the specified secret key, through the first part of thesignature algorithmis calculated

Third part:.signWith(signatureAlgorithm,secretKey.getBytes(StandardCharsets.UTF_8))

assets/JavaWeb/image-20230902233328545.png The raw data is json format data, converted through base 64 encoding (not encryption) into the format below Base 64: it is an encoding method that represents binary data based on 64 printable characters (A-Z a-z 0-9 + /). eyJhbGciOiJIUzI1NiJ9 -> base 64 decode -> { "alg": "HS 256"}

eyJuYW1lIjoidG9tIiwiaWQiOjEsImV4cCI6MTY5MzY3NDQyN30 -> base64decode -> { "name": "tom".....}

Use jwts

Encryption and decryption are both to ensure claims the content has not been tampered with

Encryption uses builder

  1. content
  2. expiration time
  3. encryption algorithm plus key
  4. builder.compact();

Decrypt Get the token from http

  1. encryption algorithm plus key
  2. .parseClaimsJws(token).getBody(); Get theclaims
JWt

public class JwtUtil {  
    /**  
     * Generate jwt  
     * Use Hs256 algorithm, the private key uses a fixed secret key  
     *  
     * @param secretKey jwt secret key  
     * @param ttlMillis jwt expiration time(milliseconds)  
     * @param claims    the information that is set  
     * @return  
     */  
    public static String createJWT(String secretKey, long ttlMillis, Map<String, Object> claims) {  
        // the signature algorithm used when specifying the signature, that is header that part  
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;  
  
        // Generate JWT the time of
        long expMillis = System.currentTimeMillis() + ttlMillis;  
        Date exp = new Date(expMillis);  
  
        // Set jwt of body  
        JwtBuilder builder = Jwts.builder()  
                // if there is a private claim, you must set this self created private claim first, this is for builder of claim assign a value, once written after the standard claim assignment, those standard claims are overwritten
                .setClaims(claims)
                // Set the signature algorithm used for signing and the secret key used for signing  
                .signWith(signatureAlgorithm, secretKey.getBytes(StandardCharsets.UTF_8))  
                // Set the expiration time  
                .setExpiration(exp);  
  
        return builder.compact();  
    }  
  
    /**  
     * Token Decrypt  
     *  
     * @param secretKey jwt secret key this secret key must be kept well on the server side, cannot be exposed, otherwise sign it can be forged, if connecting to multiple clients it is recommended to transform into multiple  
     * @param token     the encrypted token  
     * @return  
     */  
    public static Claims parseJWT(String secretKey, String token) {  
        // obtain DefaultJwtParser  
        Claims claims = Jwts.parser()  
                // Set the secret key for the signature  
                .setSigningKey(secretKey.getBytes(StandardCharsets.UTF_8))  
                // Set the one that needs to be parsed jwt  
                .parseClaimsJws(token).getBody();  
        return claims;  
    }  
  
}

setSigningKey In order to verify JWT integrity and authenticity, although the claims can be obtained directly, the authenticity is unknown

eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoidG9tIiwiaWQiOjEsImV4cCI6MTY5MzY3NDQyN30.Z0Eac2WZQzHGkBIHDIZF8hjNDlvMOg9ijdSvlAj_u9g

token after one hour ->The first part and the second part are the same, but the third part is different, the error is expired Verify the user and whether it is valid through the third part

eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoidG9tIiwiaWQiOjEsImV4cCI6MTY5MzcxMjQ4N30.h3f-C46NdJMnlwnzv0uUk9zqW-K-0z7JsGGmv56BNT8

When the key is known, the key cannot be used to directly decrypt the signature part of the JWT. Instead, the key can be used to verify the signature of the JWT, to determine whether the JWT is trusted and complete.

the last digital signature cannot be decrypted

assets/JavaWeb/image-20230903000933091.png

Unified interception

Filter Filter

Concept:Filter A filter is one of the JavaWeb three major components (Servlet、Filter、Listener)one of

A filter can intercept requests for resources, thereby implementing some special functions such as: login verification, unified encoding processing, sensitive character processing, etc.

filter First execute the logic before release, after release filterChain.doFilter(servletRequest,servletResponse); the corresponding resource can be accessed, then return to the filter and execute the logic after release assets/JavaWeb/image-20230903194706743.png

When accessing againthe same or differentresources, the same steps are still executed

  • Filter chain (using multiple filters)

There are two ways to specify priority. When priority is not specified, it is based on the name

  1. For example@Order (2)The default is alphabetical order
  2. Use FilterRegistrationBean to set the priority of the filter
Define a Spring of Bean FilterRegistrationBean to wrap our custom Filter,so that Spring the container manages our filter

Similarly in Servlet there is also similar wrapping bean:ServletRegis registrationBean

@Configuration
public class FilterConfig {

    @Bean
    public FilterRegistrationBean<FirstFilter> firstFilterRegistration() {
        FilterRegistrationBean<FirstFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new FirstFilter());
        registrationBean.setOrder(1); // Set priority to 1
        return registrationBean;
    }

    @Bean
    public FilterRegistrationBean<SecondFilter> secondFilterRegistration() {
        FilterRegistrationBean<SecondFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new SecondFilter());
        registrationBean.setOrder(2); // Set priority to 2
        return registrationBean;
    }
}

assets/JavaWeb/image-20230903204940865.png

Attribute name Type Description
filterName String Specifies the name attribute of the filter, equivalent to
value String[] This attribute is equivalent to the urlPatterns attribute. However, the two should not be used at the same time.
urlPatterns String[] Specifies a set of URL matching patterns for the filter. Equivalent to the tag.
servletNames String[] Specifies which Servlets the filter will be applied to. The value is the value of the name attribute in @WebServlet, or the value in web.xml.
dispatcherTypes DispatcherType Specifies the forwarding mode of the filter. Specific values include: ASYNC, ERROR, FORWARD, INCLUDE, REQUEST.
initParams WebInitParam[] Specifies a set of filter initialization parameters, equivalent to the tag.
asyncSupported boolean Declares whether the filter supports asynchronous operation mode, equivalent to the tag.
description String The description information of the filter, equivalent to the tag.
displayName String The display name of the filter, usually used with tools, equivalent to the tag.
  • UseFilter Filter annotation @WebFilter(urlPatterns = { "/AddServlet","/qiantai/order.jsp","/RemoveServlet", "/UserOrderingServlet" })

  • Usage steps

  1. springboot Add annotation to startup class @ServletComponentScan (because WebListener/WebFilte The r annotation is not part of the Spring specification, so to recognize it, you need to add the annotation @ servletcomponentscan Spring Boot will automatically scan the specified package for Servlet、Filter and Listener implementation classes and register them in the Spring context. This means you do not need to manually register these components in the configuration file. (You can also skip it and manually use the related configuration classes with @component returned as ordinary beans)
  2. Create an implementation of the Filter interface, and add the @WebFilter(urlPatterns = "/*") annotation
  3. Override the method public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
  4. From servletRequest get the data, perform operations or judgments, filterChain.doFilter(servletRequest, servletResponse) proceed with release
FilterDemo


@WebFilter(urlPatterns = "/*")

public class DemoFileter implements Filter {

    @Override//initialization method,called only once
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("init 初始化方法执行了");
    }

    //the other two methods are not commonly used
    @Override//called after interception,called multiple times
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("Demo 拦截到了请求...放行前逻辑");
        //release
        filterChain.doFilter(servletRequest,servletResponse);
        System.out.println("Demo 拦截到了请求...放行后逻辑");
    }

    @Override
    public void destroy() {
        System.out.println("destroy 销毁方法执行了");
    }
}

title

@Slf4j
@WebFilter(urlPatterns = "/*")
public class LoginCheckFilter implements Filter {


    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) servletRequest;//request header data
        HttpServletResponse resp = (HttpServletResponse) servletResponse;

        //1. get the request url
        String url = req.getRequestURI().toString();

        log.info("请求的url:{}", url);

        if(url.contains("login")){
            log.info("登陆放行");
            filterChain.doFilter(servletRequest, servletResponse);//login release directly
            return;
        }

        String jwt = req.getHeader("token");
        if (!StringUtils.hasLength(jwt)){
            log.info("未登陆");
            Result error = Result.error("NOT_LOGIN");
            String notLogin = JSONObject.toJSONString(error);
            resp.getWriter().write(notLogin);
            return;
        }

        try {//if an exception occurs, it means validation did not pass,
            JwtUtils.parseJWT(jwt);
        } catch (Exception e) {
            e.printStackTrace();
            log.info("解析令牌失败, 返回未登录错误信息");
            Result error = Result.error("解析令牌失败");
            //manually convert the object--json --------> Alibaba fastJSON
            String notLogin = JSONObject.toJSONString(error);
            resp.getWriter().write(notLogin);
            return;
        }

        //6.release.
        log.info("令牌合法, 放行");
        filterChain.doFilter(servletRequest, servletResponse);//release

    }
}

Interceptor Interceptor

assets/JavaWeb/image-20240122155336203.png DispacherServlet -> HandlerMapping -> Interceptor -> HandlerAadapter ->preparation before invocation handleMethodArgumentResolver(argument resolver) and handleMethodReturnValueHandler(return value handler)

is a mechanism for dynamically intercepting method calls, similar to a filter. Provided in the Spring framework, it is used to dynamically intercept the execution of controller methods.

Function: intercept requests in the Spring environment, and before and after specified method calls, execute preset code according to business needs.

  1. Define the interceptor: implement the HandlerInterceptor interface and hand it to the IOC container for management (add @Component). The methods include pre/post/afterHandle methods, all of which return true to release
  2. Configure the interceptor: create a configuration class that implements WebMvcConfigurer , inject the interceptor just defined to make it effective,ctrl+o Override the addInterceptors method, and add the registry.addInterceptor(loginCheckInterceptor).addPathPatterns("").excludePathPatterns();//configure intercepted resources
  • interception path assets/JavaWeb/image-20230904091533033.png assets/JavaWeb/image-20230904091619895.png Is defining an interceptor prioritized over the configurer? If login is released in the definition, it will be released even if all paths are configured for interception. ^trzlo0
Introduction

Interceptor execution flow and differences from Filter assets/JavaWeb/image-20230904093429385.png Filter implements the Filter interface Interceptor implements the HandlerInterceptor interface

Interception scope differs: the filter Filter will intercept all resources, while the Interceptor will only intercept Spring resources in the environment. Tomcat does not recognize the written Controller program, but recognizes the servelet program, and this requires DiapatcherServlet, which will forward to Controller

assets/JavaWeb/image-20240826125221152.png assets/JavaWeb/image-20240826125238821.png

WebConfig
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private LoginCheckInterceptor loginCheckInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
		            registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/**").excludePathPatterns();//Configure intercepted resources
    }
}
  • Execution order of multiple interceptors
  1. If each interceptor's preHandle () all return true

At this point the execution order of multiple interceptors depends on the order in which the interceptors are configured in the SpringMVC configuration file:

preHandle()they execute in the configured order, whilepostHandle()andafterComplation()execute in the configuredreverse order

  1. If some interceptor's preHandle ()returns false

preHandle() The one that returns false and the interceptors before it, their preHandle() will all execute,postHandle() will not execute, and it returns the false of the interceptor before the interceptor that returns afterComplation() will execute (index)

Global exception handler

@RestControllerAdvice is @ControllerAdvice and @ResponseBody composite annotation

Exception handling

assets/JavaWeb/image-20230905103345610.png In the diagram, because the exception was not handled, it is ultimately thrown to the framework, and the returned json data assets/JavaWeb/image-20230905104525537.png assets/JavaWeb/image-20230905104540186.png

  • Usage ^x4m5fo
  1. Create a class, add the annotation @RestControllerAdvice
  2. Write a method, method annotation @ExceptionHandler(Exception type)
Global exception handler
@RestControllerAdvice//Represents that a global exception handler is defined
public class GlobeExceptionHandler {
	@ExceptionHandler(Exception.class)//Captured exception type:Exception.class All exceptions
    public Result ex(Exception ex){
        ex.printStackTrace();
        return Result.error("对不起,操作失败,请联系管理员");
    }
}
The global exception handler intercepts the throwing of custom exceptions

BaseException is the parent class of custom exception classes, and a custom exception class that inherits BaseException can be intercepted


@ExceptionHandler
public Result exceptionHandler(BaseException ex){  
    return Result.error(ex.getMessage());  
}

Transaction management

Spring transaction manager

Why not use AOP to implement transactions, because the tools used to develop databases are diverse (jdbc, mysql, hibernate), and the transaction code differs for each

  1. Spring Declarative transaction corresponding dependencies spring-tx: contains the basic specifications for implementing declarative transactions (transaction manager specification interface and transaction enhancement, etc.) spring-jdbc: contains DataSource the transaction manager implementation class of the approach DataSourceTransactionManager spring-orm: Contains transaction manager implementation classes for other persistence layer frameworks, for example:Hibernate/Jpa etc.

assets/JavaWeb/image-20240121211243948.png

The transaction manager we want to use now is org.springframework.jdbc.datasource.DataSourceTransactionManager, and in the future, integrating the JDBC approach,JdbcTemplate approach,Mybatis the transaction implementation of the approach!

DataSourceTransactionManager Main methods in the class: doBegin(): begin transaction doSuspend(): suspend transaction doResume(): resume the suspended transaction doCommit(): commit transaction doRollback(): roll back transaction

Overview

The underlying layer of transaction management is also AOP

springboot Startup fileEnable transaction management on it, add the annotation @EnableTransactionManagement

  • Use the annotation @Transactional
    • Location: the service layer's method, class, interface On
      • @Transactional annotated onmethods, it only affects that method
      • @Transactional annotated onclasses, it affects all methods in the class
    • Purpose: hand the current method over to Spring for transaction management; before the method executes, open a transaction; on successful completion, commit the transaction; when an exception occurs, roll back the transaction rollback ; by default only runtime exceptions are rolled back, through @Transactional(rollbackFor = Exception.class) you can specify which exceptions trigger rollback, Exception.class all exceptions will roll back)

For data modification and code that executes multiple times transaction management is needed; if only one step is executed, or no modification is needed, then transaction management is not needed

Declarative: implemented based on the AOP approach, the code is more concise, easier to use, and easier to understand Programmatic: very flexible, fully controlled by the user, allows the smallest granularity of transaction scope control, similar to locks, following theprinciple of using the smallest scope, especially programmatic transactions

@Transactional  
@Override  
public void delete(int id) {  
    deptMapper.delete(id);  
    empMapper.deleteByDeptId(id);  
}
  • Enable transaction management logging
logging:
  level:
    org.springframework.jdbc.support.JdbcTransactionManager: debug

Property

@Transactional property name Description
name When there are multiple TransactionManagers in the configuration file, this property can be used to specify which transaction manager to choose.
propagation The transaction propagation behavior, the default value is REQUIRED.
isolation The isolation level of the transaction, the default value uses DEFAULT.
timeout= The timeout time of the transaction, the default value is -1. If the transaction is not completed after exceeding this time limit, the transaction is automatically rolled back.
Timeout exceptionorg.springframework.transaction.**TransactionTimedOutException**: Transaction timed out: deadline was Fri Jun 04 16:25:39 CST 2022
readonly=false by default 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. In this way the database can optimize for the query operation. Setting read-only on add, delete, and update operations will throw the following exception:
Caused by: java.sql.SQLException: Connection is read-only. Queries leading to data modification are not allowed
rollbackfor Used to specify the exception types that can trigger transaction rollback. If multiple exception types need to be specified, each type
can be separated by commas.
no-rollback-for When the exception type specified by no-rollback-for is thrown, the transaction is not rolled back.
  • Rollback strategy rollbackfor

Declarative transactionsBy defaultonly forruntime exceptions RuntimeException and its subclassesrollback is performed, compile-time exceptions or Exception are not rolled back. ../../Java SE/assets/Java面试/image-20240507230430989.png

  • rollbackFor Property: need to set a Class type of object, the default is RuntimeException and Error only then can exceptions roll back
  • rollbackForClassNameProperty: need to set a fully qualified class name of string type
  • noRollbackFor Property: need to set a Class type object, specifies which exceptions will not roll back, by default none is specified, if specified, it should be within rollbackFor the range of
  • rollbackFor Property: need to set a fully qualified class name of string type

assets/JavaWeb/image-20230926104241294.png

  • Isolation level

Mysql/Mysql advanced > The four isolation levels in SQL

assets/JavaWeb/image-20230926105657853.png assets/JavaWeb/image-20230926105707646.png

@Transactional(isolation = Isolation.DEFAULT)//use the default isolation level of the database
@Transactional(isolation = Isolation.READ_UNCOMMITTED)//read uncommitted
@Transactional(isolation = Isolation.READ_COMMITTED)//Read committed
@Transactional(isolation = Isolation.REPEATABLE_READ)//Repeatable read
@Transactional(isolation = Isolation.SERIALIZABLE)//Serializable
  • Propagation behavior propagation

Transaction method a, transaction method b, at this time when b runs, does it join the transaction of method a or not join and create a new transaction? This involves the propagation behavior of transactions

Suppose there is method A and method B, the following is method B joining propagation the effect after

When A calls method B

  • required(默认): B needs a transaction A If there is a transaction, then B join A, A no transaction , B then create a transaction itself
  • required_new B needs a new transaction A If there is a transaction, suspend it,B The new transaction will prioritize ensuring its own execution
  • supports B does not care whether there is a transaction A Add it if it exists, it does not matter if it does not.
  • not_supports A calls transactionless B, if A has a transaction then B is suspended
  • never A calls B, A must also have no transaction, otherwise an exception occurs
  • mandatory A calls B, A must have a transaction, otherwise an exception occurs
  • nested A calls new transaction B, if A has a transaction it exists as a nested transaction, if A has no transaction it executes as a new transaction

assets/JavaWeb/image-20231130191610275.png assets/JavaWeb/image-20230926164605978.png

Propagation behavior

assets/JavaWeb/image-20230905144314809.png

assets/JavaWeb/image-20230905151145008.png

insert should be set to @Transactional(propagation = propagation.requires_new), so that insert in a new transaction, even if delete rolls back,insert it also does not roll back, when running the insert method, it will first suspend the transaction in the current environment, then run insert this transaction, even if the deletion fails, the log has already been recorded

  1. Only after the delete method transaction rolls back will it continue to execute the logic in the insert method.
  2. That is to say, transaction rollback is executed immediately when an exception occurs, and does not wait until the entire method has finished executing before rolling back.
  3. The insert method executes only after the delete method rolls back, so it is not affected.

REQUIRED : In most cases, using this propagation behavior is enough. REQUIRES_NEW : When we do not want transactions to affect each other, we can use this propagation behavior. For example: a log needs to be recorded before placing an order, and regardless of whether the order is saved successfully, the log record must be ensured to be recorded successfully.

When to use it?

  • When a basic business unit (such as an externally provided HTTP interface or service method) has>=2 data changes, it is necessary to consider whether partial updates succeed,and whether partial update failures will produce dirty data, and whether there will be an impact

If yes: then consider introducing a transaction If no: it is not recommended to add a transaction

  • If there is only one data change, but there is subsequent logic that strongly depends on this data, a transaction also needs to be added

For example, upon receiving a payment success message, I want to make a reconciliation background task My business logic is: first call a third party to verify whether it is true, then update the local status to success, and finally notify the order status change via MQ At this time we need to put the second step data change and the third step message notification in a transaction. As long as the notification fails, we still do not update the local status, avoiding the situation where our verification succeeds but the order remains unpaid

  • Usage recommendations to avoid large transactions Only add transactions where needed Pay attention to distributed transaction scenarios All operations should use indexes to avoid table locks Reduce scenarios involving range based and large batch data processing If business support allows, the isolation level can be lowered. First, it reduces the additional cost brought by the isolation level, and second, it can avoid deadlocks in certain scenarios

Failure situations

../SpringBoot/Spring总结#代理失效情况

  • May fail in multithreading assets/JavaWeb/file-20241020013148769.png

It is easy to understand why the above scenario does not take effect. The exception of the child thread will not be caught by the external thread, and the call to the testMultThread method does not throw an exception, so transaction rollback will not be triggered

AOP:Aspect Oriented Programming

AOP is essentially a simplification ofthe proxy patternsimplification of

Proxy pattern

Proxy pattern

assets/Spring/image-20230925222438847.png assets/JavaWeb/image-20240120222341002.png

Goal: place the core code, reducing calls to and interference with the target Proxy: place non-core functionality in the target method

There are two types of proxies: Static proxy : Create a separate proxy for each class (Too troublesome) Dynamic proxy : Do not create a separate proxy for each class, instead automatically provide a proxy object

Proxy solves the problems of additional code interfering with core code and inconvenient unified maintenance

Static proxy

Calculator interface CalculatorPureImpl target class / landlord CalculatorStaticProxy proxy class / intermediary

assets/JavaWeb/image-20240120233144131.png



public class CalculatorStaticProxy implements Calculator {
    
    // Declare the proxied target object as a member variable
    private Calculator target;
    // Call the target method
    public CalculatorStaticProxy(Calculator target) {
        this.target = target;
    }
    
    @Override
    public int add(int i, int j) {
        // Additional functionality is implemented by the proxy method in the proxy class
        System.out.println("参数是:" + i + "," + j);
        // Implement core business logic through the target object
        int addResult = target.add(i, j);
        System.out.println("方法内部 result = " + result);
        return addResult;
    }

Dynamic proxy

Classification: JDK Dynamic proxy (interface) and cglib Dynamic proxy (inheritance)

JDK Native implementation method : when there is an interface, the target class implements the interface, JDK the created proxy class will also automatically implement the interface, and there is no inheritance relationship between the proxy class and the target class, so it cannot proxy classes that do not implement an interface cglib(spring in): when there is no interface, the target class does not need to implement interface,cglib the created proxy class will inherit from the target class to implement the proxy

JDK The dynamic proxy compared to the CGLIB dynamic proxy has higher performance when generating proxy objects, while the CGLIB dynamic proxy needs to perform the proxy by inheriting and overriding the target class, and needs to generate bytecode and class loading when generating proxy objects, which is relatively slow

This is because theJDKdynamic proxy uses theJavareflection mechanism provided by the standard library, while theCGLIBdynamic proxy needs to perform the proxy by inheriting and overriding the target class, and needs to generate bytecode and class loading when generating proxy objects, which is relatively slow.

Introduction

Summary: interface-implementing target classes accept the proxy component through the interface type, non-interface classes can directly use the class to accept the proxy component

JDK Native implementation method It can be obtained based on the interface namebean, it cannot be based on the class name assets/JavaWeb/image-20240121194639659.png

cglibIt can be obtained based on the class name

assets/JavaWeb/image-20240121194747469.png

JDK dynamic proxy factory

public class JdkProxy {  
  
        private Object target;  
        public JdkProxy(Object target) {  
            this.target = target;  
        }  
        public Object getProxy(){  
  
            /**  
             * newProxyInstance():Create a proxy instance  
             * It has three parameters:  
             * 1、classLoader:The class loader that loads the dynamically generated proxy class  
             * 2、interfaces:The of all interfaces implemented by the target object class array composed of objects  
             * 3、invocationHandler:The process of setting the proxy object to implement the target object method, that is, how to override in the proxy class  
             the abstract method in the write interface             */  
  
            ClassLoader classLoader = target.getClass().getClassLoader();  
            Class<?>[] interfaces = target.getClass().getInterfaces();  
            InvocationHandler invocationHandler = new InvocationHandler() {  
  
                @Override  
                public Object invoke(Object proxy, Method method, Object[] args)  
                        throws Throwable {  
                    /**  
                     * proxy:Proxy object  
                     * method:The method that the proxy object needs to implement, that is, the method that needs to be overridden among them  
                     * args:method The parameter of the corresponding method  
                     */  
                    Object result = null;  
                    try {  
                        result = method.invoke(target, args);  
                        System.out.println(method.getName()+",结果:"+ result);  
                    } catch (Exception e) {  
                        e.printStackTrace();  
                        System.out.println(method.getName()+",异常:"+e.getMessage());  
                    } finally {  
                        System.out.println(method.getName()+",方法执行完毕");  
                    }  
                    return result;  
                }  
            };  
            /**  
             * jdk Generate proxy object  
             *  Parameter one:Class loader  
             *  Parameter two:The interface of the target class  
             *  On the parameters:Specific proxy action to perform[Non core action,Call target method]  
             */            return Proxy.newProxyInstance(classLoader, interfaces,  
                    invocationHandler);  
        }  
}

JdkProxy factory=new JdkProxy(Target class);

Interfaces of the target class proxy1= (Interfaces of the target class)factory.getProxy(); //Generate proxy class
proxy1.add(1,1);//JdkProxy Will intercept,First execute invoke Method,Then execute add Method

proxy1 Is the proxy class,Can call methods normally

Dynamic proxy selection in AOP

When the target class using AOP hasInterfacesit will automatically select JDK the native implementation method to implement the proxy, when there are noInterfacesit will automatically select cglib to implement the proxy

Use

Usage method

Define an annotation class (@Component + @Aspect), and annotate it on a method (parameter ProceedingJoinPoint JoinPoint) annotation @Around("execution( )")


//This is an aspect class

@Slf4j
@Component
@Aspect
public class TimeAspect {

	//Pointcut expression : Control pointcut
	
    @Around("execution(* com.ithiema.service.*.*(..))")//:Specify which methods the common logic applies to:service All methods under the package will run the encapsulated method
    //Advice
    
    public Object recordTime(ProceedingJoinPoint JoinPoint) throws Throwable {//aspect Provides common ProceedingJoinPoint Class api
        long begin = System.currentTimeMillis();

        Object result = JoinPoint.proceed();

        long end = System.currentTimeMillis();

        log.info(JoinPoint.getSignature()+"方法执行耗时{}ms",end-begin);//JoinPoint.getSignature()Get method signature

        return result;
    }
}

///整个被称为切面

//The place where it takes effect is called a pointcut
  • Core concepts assets/JavaWeb/image-20230907110339042.png

Spring In aop The underlying basis is dynamic proxy (CGLIB proxy technology), at program runtime based on dynamic proxy technology, a proxy object is generated for the target object to enhance the functionality of the original method, during injection the target object is no longer injected, but the proxy object is injected, and the method in the proxy object is called

Advice types

Internally can fill in Expression or Annotationwhichever is filled in, the place where it is located becomes the Pointcut

JAVA microservices core / spring / Spring > 4 2 1 Related terms

  1. @Around: Around advice, the advice method annotated with this annotation can be executed before and after the target method according to the set position, code after the exception code will not execute
    1. Need toManuallyCall proceedingJoinPoint.proceed(); to execute the original method, and the return value must be specified as Objectto receive the method return value and return it
  2. @Before: Before advice, the advice method annotated with this annotation is executed before the target method
  3. @After: After advice, the advice method annotated with this annotation is executed after the target method,Regardless ofWhether there is an exception, it will still execute
  4. @AfterReturning: After returning notification, the notification method annotated with this annotation is executed after the target method, if there is an exceptionit will notexecute returning="result"
  5. @AfterThrowing: After throwing notification, the notification method annotated with this annotation executes after an exception occurs throwing = " "

assets/JavaWeb/image-20230926164032570.png

For @Around notification, to obtain join point information you can only use  ProceedingJoinPoint

For, to obtain join point information you can only use JoinPoint , it is ProceedingJoinPoint the parent class of

Notification order

assets/JavaWeb/image-20230908192924801.png The earlier it runs, for those after the original method runs, the closer to the front the later it runs

  1. In different aspect classes, by default they are sorted alphabetically by aspect class name: Notification methods before the target method: those with earlier alphabetical ranking execute first Notification methods after the target method: those with earlier alphabetical ranking execute later
  2. Use@Order(number)added to the aspect class to control the order
    Notification methods before the target method: those with smaller numbers execute first
    Notification methods after the target method: those with smaller numbers execute later assets/JavaWeb/image-20230919184230248.png

Pointcut expression

@execution

execution([访问修饰符] return value [包名.类名].方法名(方法形参) [throws 异常] ) [] can be omitted in

After omitting package name and class name, if there are methods with the same name, they will all run, so omitting is not recommended assets/JavaWeb/image-20230908195744480.png assets/JavaWeb/image-20230908195731342.png

  • Wildcard symbols * A single independent arbitrary symbol, can match any返回值、包名、类名、方法名、one of any type参数,can also match包、类、方法名of一部分 .. u Multiple consecutive arbitrary symbols, can matchpackages at any levelof, or 任意类型、任意个数的参数,cannot be used at the beginning of a package

assets/JavaWeb/image-20240121192501336.png assets/JavaWeb/image-20230908201025712.png

Match DeptService all methods under with method parameter of Integer type, return value of void type of methods

  • You can useand (&&)、or (||)、not (!) to combine relatively complex pointcut expressions.

All business method names should be named as standardized as possible to facilitate quick matching of pointcut expressions. For example: query methods all start with find, update methods all start with update.

Describe pointcut methods usuallybased on interface description, rather than directly describing the implementation class, to enhance extensibility.

Under the premise of meeting business needs, minimize the matching scope of the pointcut. For example: for package name matching, try not to use .., use * to match a single package

Reuse (extract) pointcut expressions @Pointcut

Add a pointcut expression annotation to a method @Pointcut, then reference that method in other notification types assets/JavaWeb/image-20230908183332860.png

Custom annotation class

@annotation(annotation class): Match methods that have the annotation on the method @within(annotation class): Match all methods that have the annotation on the class

Custom annotation, add it on the method where it needs to be used Simplify execution complex content in Also create a Aspect The class is just the pointcut expression; there is no need to set a scope, only to set the annotation class location.

@Retention (RetentionPolicy. RUNTIME): Indicates that this annotation retention policy is RUNTIME, that is, retained at runtime, so annotation information can be read through reflection. @Target(ElementType.TYPE): Indicates that this annotation can be applied to classes, interfaces, and enums (TYPE indicates class type) ^f3q55w

Usage method

  1. Create one named xxx of annotation annotation class, add annotation @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) assets/JavaWeb/image-20231031132350583.png
  2. Still define Aspect class, reference in the class @Around("@annotation(注解类位置)") @Around("@annotation(com.ithiema.anno.Log)")
  3. Add on the method where it is needed @xxx ready to use

while execution method directly is @Around("execution(location)

Declare a class, add two annotations@Detention@Target

Case

assets/JavaWeb/image-20230908202953092.png Set in Aspect@Around("@annotation(com.ithiema.Log)")

Finally, add the annotation on the method or class where it is needed, and the class it is in is called the pointcut method. assets/JavaWeb/image-20230908203214801.png

@Retention (RetentionPolicy. RUNTIME)  
@Target (ElementType. METHOD)  
public @interface Log {  
	String value () default "default value"; // define an element with a default value
	OperationType value ();
}

public enum OperationType {  
  UPDATE, INSERT  
}

use@AutoFill(OperationType.INSERT)

set the annotation attribute to an enum for convenience AOP

intercept
MethodSignature signature = (MethodSignature) joinPoint.getSignature ();//method signature object  
AutoFill autoFill = signature.getMethod (). getAnnotation (AutoFill. class);//get the annotation object on the method  
OperationType operationType = autoFill.value ();//get the database operation type

Join point or obtain notification related information

For @Around notification, obtaining join point information can only use  ProceedingJoinPoint

For the other four notifications, obtaining join point information can only use JoinPoint , it is ProceedingJoinPoint parent class of

assets/JavaWeb/image-20230908203953107.png

`joinPoint.getSignature().getDeclaringType().getAnnotation(MdcDot.class);`

Note: joinPoint is in aspectj not in aopalliance in

  • Obtain the return value of the target method
@AfterReturning(value = "execution(* com.atguigu.aop.annotation.CalculatorImpl.*(..))", returning = "result")
public void afterReturningMethod(JoinPoint joinPoint, Object result){
    String methodName = joinPoint.getSignature().getName();
    System.out.println("Logger-->返回通知,方法名:"+methodName+",结果:"+result);
}
  • Obtain the exception of the target method (when an exception occurs)
@AfterThrowing(value = "execution(* com.atguigu.aop.annotation.CalculatorImpl.*(..))", throwing = "ex")
public void afterThrowingMethod(JoinPoint joinPoint, Throwable ex){
    String methodName = joinPoint.getSignature().getName();
    System.out.println("Logger-->异常通知,方法名:"+methodName+",异常:"+ex);
}

Operation log

LogAspect

@Slf4j
@Component
@Aspect

public class LogAspect {
    @Autowired
    private OperateLogMapper operateLogMapper;
    @Autowired
    private HttpServletRequest request;
    @Around("@annotation(com.ithiema.anno.Log)")//specify annotation location
    public Object recordLog(ProceedingJoinPoint joinPoint) throws Throwable {

        //since variables cannot be defined arbitrarily
        //get the operator
        String jwt = request.getHeader("token");
        Claims claims = JwtUtils.parseJWT(jwt);
        Integer operateUser = (Integer) claims.get("id");
        //operation time
        LocalDateTime operateTime = LocalDateTime.now();
        //operation class name
        String className = joinPoint.getTarget().getClass().getName();
        //operation method name
        String methodName = joinPoint.getSignature().getName();
        //operation method parameters
        Object[] args = joinPoint.getArgs();
        String methodParams = Arrays.toString(args);
        long begin = System.currentTimeMillis();
        //call the original method
        Object result = joinPoint.proceed();
        //the return value of the original target method above is not json
        String returnValue = JSONObject.toJSONString(result);
        //record running time
        long end = System.currentTimeMillis();
        long costTime= end - begin;
        //record operation log
        OperateLog operateLog = new OperateLog(null,operateUser,operateTime,className,methodName,methodParams,returnValue,costTime);
        operateLogMapper.insert(operateLog);
        log.info("AOP记录操作日志: {}" , operateLog);
        return result;
    }
}

Log information includes: operator, operation time, full class name of the executed method, executed method name, method runtime parameters, return value, method execution duration.

Annotation class

@Retention (RetentionPolicy. RUNTIME): Indicates that this annotation retention policy is RUNTIME, that is, retained at runtime, so annotation information can be read through reflection. @Target(ElementType.TYPE): Indicates that this annotation can be applied to classes, interfaces, and enums (TYPE indicates class type)

  • Internal attribute format type name() default value;

other class Values of annotations on other classes can be obtained through reflection

java

The one you mentioned @AliasFor The annotation is part of the Spring Framework, and it does indeed use its own annotation within the annotation class. This situation is achieved through the special handling mechanism of the Spring framework, rather than a feature of the Java language itself. Java does not allow directly using its own annotation within an annotation class.

The value and attribute attributes of the @AliasFor annotation are aliases for each other, which means these two attributes can be used interchangeably, and setting the value of one attribute will automatically apply to the other attribute. For example:

@MyAnnotation(value = "test"), attribute will also be set to test

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface AliasFor {
    @AliasFor("attribute")
    String value() default "";

    @AliasFor("value")
    String attribute() default "";

    Class<? extends Annotation> annotation() default Annotation.class;
}

Code conventions

Business layer (Controller, Service)Method name: login etc.

The persistence layer (dao, mapper) is used to operate the database, so using login is not appropriate. It is suitable to use method names related to database operations, such as getByUsernameAndPassword

method into the corresponding mapper in it. For example, when deleting a department, employees also need to be deleted. The method for deleting a department goes into the department, and the method for deleting an employee goes into the employee. Do not put the code for deleting an employee into the department.