Location>code7788 >text

Ultra-practical SpringAOP practice of logging

Popularity:588 ℃/2024-11-17 22:09:48

This article is mainly based onLoggingAs an entry point to explain Spring AOP in the actual project development how to better make the project business code more concise, more efficient development.

Logging is just one of the AOP application scenarios, when you master this one scenario, for other application scenarios can also be easily dealt with.

AOP common application scenarios are: logging, exception handling, performance monitoring, transaction management, security control, custom validation, cache processing and so on. At the end of the article will be briefly listed.

After reading this article (logging scenarios), you can practice how to implement other scenarios. Application scenarios of all kinds, all kinds of changes do not change, to grasp the essence of the most important.

The Nature of Usage ScenariosYes: in amethodologiesThe pre-execution, post-execution, execution exception, and execution completion states of theintegrated operationThe core advantage of AOP is to extract these cross-cutting functions from the core business logic, thus realizing decoupling and reuse of code, and improving the maintainability and scalability of the system.

Case 1: Simple logging

The main purpose of this case is to understand how the entire AOP code is written.

Introducing dependencies

<dependency>
    <groupId></groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

Customize an annotation class

import ;
import ;
import ;
import ;

// Logging Notes:act on methods
@Target()
@Retention()
public @interface RecordLog {
    String value() default "";
}

Write a faceted class Aspect

AOP facets have a variety of notification methods: @Before, @AfterReturning, @AfterThrowing, @After, @Around. because @Around contains the first four cases, this article case are only use @Around, other can be self-understanding.

import ;
import ;
import ;
import ;

@Aspect
@Component
public class RecordLogAspect {

    // Specify the custom annotation as the entry point
    @Around("@annotation()")
    public void around(ProceedingJoinPoint proceedingJoinPoint){
        try {
            ("Logging - Before Execution");
            ();
            ("Logging - After Execution");
        } catch (Throwable e) {
// (); ("Logging - after execution"); } catch (Throwable e) {
            ("Logging - Execution Exception"); }
        }
        ("Logging - execution complete"); }

    }
}

Write a Demo method

import ;
import ;

@Component
public class RecordLogDemo {

    @RecordLog
    public void simpleRecordLog(){
        ("Execute the current method:"+().getStackTrace()[1].getMethodName());
        // Test anomalies
// int a = 1/0;
    }
}

Perform unit testing

import ;
import ;
import ;
import ;

@SpringBootTest
class SpringDemoAOPApplicationTests {

    @Autowired
    private RecordLogDemo recordLogDemo;

    @Test
    void contextLoads() {
        ("Test...");
        ();
    }

}

Test results:

image

This is the simplest logging, and the main purpose is to understand what the whole code looks like.

Case II: transaction log records

This case completes the implementation of dynamic logging by the cutter class based on the parameters passed in from outside.

Cutting to get external informationSome of the methods:

  • Gets the parameters of the target method (connection point):JoinPointunder thegetArgs()method, orProceedingJoinPointunder thegetArgs()methodologies

  • Getting parameters in custom annotations: Custom annotations can define multiple parameters, required parameters, default parameters, etc.

  • The business process is conveyed by throwing exceptions, and the cutover logs the exception information by catching the exception

You can also check to see if it's the parameter you want based on the name of the method parameter.String[] paramNames = ((MethodSignature) ()).getParameterNames();

Sections get internal information: e.g. get the time before and after execution.

The adjusted code is as follows

Added enum class

public enum TransType {
    // Transaction type
    TRANSFER.
    // Querying transaction type
    QUERYING.
}

Custom Annotation Classes

Note more mandatory transaction types and optional transaction description

import ;

import ;
import ;
import ;
import ;

// Logging annotations: acting on methods
@Target()
@Retention()
public @interface RecordLog {
    // Mandatory transaction type
    TransType transType() ; // Mandatory transaction type.
    // Optional transaction description
    String description() default ""; }
}

tangent class (math.)

Added the three types of code described at the beginning for obtaining external information

import ;
import . *; ; import ; import .
import ;
import ;
import ;*; import ;*; import ;*; import ;*; import
import ;

import ;

@Aspect
@Component
public class RecordLogAspect {

    // Specify the custom annotation as the entry point
    @Around("@annotation()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint){
        // 1. Get information about the parameters of the target method (the join point)
        Object[] args = ();
        // Get type-specific arguments: case-specific
        for (Object arg : args) {
            if (arg instanceof String) {
                ("Logging - before execution: method request parameter information logging: " + arg);
            }
        }
        // 2. Getting parameters in custom annotations
        MethodSignature methodSignature = (MethodSignature)();
        Method method = ();
        RecordLog annotation = ();
        // Transaction type
        TransType transType = (); // Transaction description information.
        // Transaction description information
        String description = (); // Transaction description information.
        try {
            ("Logging - before execution: annotation parameter information logged: "+transType+"|"+description+"|");
            Object proceed = ();
            ("Logging - after execution: "+());
            // As long as there are no exceptions, the execution was successful
            ("Logging - execution success: 200"); // As long as there are no exceptions, then the execution was successful.
            return proceed; } catch (Throwable e)
        } catch (Throwable e) {
// (); } catch (Throwable e) {
            // 3. Catch the exception to log the exception message
            String errorMessage = (); ("Logging
            ("Logging - Execution Exception: "+errorMessage);
            throw new Exception("Logging - Execution Exception: ").initCause(e);
        } finally {
            ("Logging - execution complete"); }
        }; }

    }
}

New business-related categories have been added


public class TransInfoBean {
    private String transStatusCode;
    private String transResultInfo;
    private String account;
    private BigDecimal transAmt;

    public String getTransStatusCode() {
        return transStatusCode;
    }

    public void setTransStatusCode(String transStatusCode) {
         = transStatusCode;
    }

    public String getTransResultInfo() {
        return transResultInfo;
    }

    public void setTransResultInfo(String transResultInfo) {
         = transResultInfo;
    }

    public String getAccount() {
        return account;
    }

    public void setAccount(String account) {
         = account;
    }

    public BigDecimal getTransAmt() {
        return transAmt;
    }

    public void setTransAmt(BigDecimal transAmt) {
         = transAmt;
    }
}

Business Demo Class

Added external parametersAnnotations, Method Parameters, and Exceptions, exceptions are thrown whenever a transaction fails.

import ;
import ;
import ;

import ;

@Component
public class RecordLogDemo {

    @RecordLog(transType = ,description = "Check how much money is left in your account")
    public BigDecimal queryRecordLog(String account) throws Exception {
        ("--Execute the current method:"+().getStackTrace()[1].getMethodName());

        try{
            // Perform query operations:This is just a simulation.
            TransInfoBean transInfoBean = (account);
            BigDecimal accountAmt = ();
            ("--The inquired account balance is:"+accountAmt);
            return accountAmt;
        }catch (Exception e){
            throw new Exception("Checking account balance anomalies:"+());
        }
    }

    /**
     * Call Query Transaction
     * @param account
     * @return TransInfoBean
     */
    private TransInfoBean queryAccountAmt(String account) throws Exception {
        TransInfoBean transInfoBean = new TransInfoBean();
        (account);
        try{
            // Call Query Transaction
// int n = 1/0;
            (new BigDecimal("1.25"));
            //Successful transaction:The status returned by the demo trading interface
            ("200");
            ("successes");
        }catch (Exception e){
            //Successful transaction:The status returned by the demo trading interface
            ("500");
            ("fail (e.g. experiments)");
            throw new Exception(()+"|"+());
        }
        return transInfoBean;
    }
}

unit test

@SpringBootTest
class SpringDemoAOPApplicationTests {

    @Autowired
    private RecordLogDemo recordLogDemo;

    @Test
    void contextLoads() throws Exception {
        ("Test...");
        ("123567890");
    }

}

Test results

Successful situations

image

failure

image

summarize

After the use of AOP, the transaction processing logs do not need to be intertwined with the business code to play a decoupled role in improving the readability and maintainability of the code.

Secondly, there is the issue of code extensibility, such as the later development of thetransfer transactionIf you want to write business code, you just need to write the logging annotations to complete the logging related code.@RecordLog(transType = ,description = "Transfer Transaction"). If a project dozens of transaction interfaces need to be written, then this log record is written dozens of times less, greatly improving development efficiency.

This case just explains logging, if the output log messages are saved to an object and theSave to database, that would log the processing of all transactions. Just replace the logging processing with the operation you need to do.

Brief description of common scenarios

transaction management

Spring AOP provides@Transactional annotations to simplify transaction management, the underlying implementation is through AOP. With declarative transaction management, transactions can be automatically committed or rolled back based on the execution of the method.

For example, to turn on transactions before a method executesPost-implementation submission of transactionsoccurrenceRolling Back Transactions on Exception

@Transactional
public void transferMoney(String fromAccount, String toAccount, double amount) {
    (fromAccount, amount);
    (toAccount, amount);
}

It is possible to customize the transaction management cutout and it is also compatible with both the@TransactionalTransaction management annotation. Enable the transaction before executing the target method, roll back the transaction when the execution is abnormal, and do not need to deal with it when the execution is normal.

@Aspect
@Component
public class TransactionAspect {

    @Before("execution(* .*.*(..)) && @annotation()")
    public void beforeTransaction(JoinPoint joinPoint) {
        ("Starting transaction for method: " + ().getName());
    }

    @AfterThrowing(pointcut = "execution(* .*.*(..)) && @annotation()", throwing = "exception")
    public void transactionFailure(Exception exception) {
        ("Transaction failed: " + ());
    }
}

Performance Monitoring

AOP can be used to monitor the execution performance of methods (e.g., execution time, frequency, etc.) and is particularly suitable for system performance analysis and optimization.

Example:

  • Calculate the method execution time and keep a log.
  • Monitor the frequency of method calls.
@Aspect
@Component
public class PerformanceMonitorAspect {

    @Around("execution(* .*.*(..))")
    public Object monitorPerformance(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = ();
        
        // Implementation of the target methodology
        Object result = ();

        long elapsedTime = () - start;
        ("Method " + ().getName() + " executed in " + elapsedTime + " ms");

        return result;
    }
}

safety control

AOP is suitable for implementing method-level security controls. For example, you can set theBefore the method callCheck the user's permissions and decide whether to allow access.

Example:

  • Checks whether the user has the privileges for a certain operation.
  • Using Annotations@Secured or custom annotations for security validation based on roles or permissions.
@Aspect
@Component
public class SecurityAspect {
    
    @Before("@annotation()")
    public void checkSecurity(Secured secured) {
        // Get the current user's privileges
        String currentUserRole = getCurrentUserRole();
        if (!(()).contains(currentUserRole)) {
            throw new SecurityException("Insufficient permissions");
        }
    }
}

Cache Management

AOP can be used for methodsCaching of resultsAOP can be used to improve performance by performing the computation on the first call. For some time-consuming methods, you can use AOP to improve performance by performing the computation on the first call and getting the result directly from the cache on subsequent calls.

Example: Using AOP to implement method result caching to avoid double counting.

@Aspect
@Component
public class CachingAspect {

    private Map<String, Object> cache = new HashMap<>();

    @Around("execution(* .*.get*(..))")
    public Object cacheResult(ProceedingJoinPoint joinPoint) throws Throwable {
        String key = ().toShortString();
        if ((key)) {
            return (key); // Fetch from cache
        }

        // Execute the target method and cache the results
        Object result = ();
        (key, result);
        return result;
    }
}

Exception handling

AOP can unify the handling of exceptions in methods, such as logging, sending alerts, or performing other processing. This can be accomplished by@AfterThrowing maybe@Around annotation to implement exception catching and handling.

Example: Uniformly catching exceptions and logging or sending notifications.

@Aspect
@Component
public class ExceptionHandlingAspect {

    @AfterThrowing(pointcut = "execution(* .*.*(..))", throwing = "exception")
    public void handleException(Exception exception) {
        ("Exception caught: " + ());
        // Send an e-mail or log entry
    }
}

Custom Verification

AOP can be used for method parameter validation, especially on input data validation. Parameter validation is performed before the method call to avoid invalid data being passed in.

Example: Check if the method parameters are empty or conform to specific rules, such as password format checking.

@Aspect
@Component
public class ValidationAspect {
    // regular expression (math.)
    private static final String PASSWORD_PATTERN =
            "^(?=.*[A-Z])(?=.*[a-z])(?=.*\\d)(?=.*[!@#$%^&*(),.?:{}|<>_]).{8,16}$";

    @Before("execution(* (..))")
    public void validateUserInput(JoinPoint joinPoint) {
        Object[] args = ();
        for(Object arg : args){
            // Type checking
            if(arg instanceof UserService){
                UserService userService = (UserService) arg;
                // The value of the object property is then checked:Whether or not it is empty、Whether it does not meet formatting requirements, etc.,
                // For example, password verification
                if(!validatePassword(())){
                    // Just throw an exception if it doesn't match
                    throw new IllegalArgumentException("Invalid user input");
                }
            }
        }
    }

    /**
     * Password verification:8~16because of,It's got to have major and minor letters.+digital (electronics etc)+special character
     * @param password String
     * @return boolean
     */
    public static boolean validatePassword(String password) {
        if(password == null)
            return false;
        return (PASSWORD_PATTERN, password);
    }
}

summarize

There are all kinds of application scenarios, and it is most important to grasp the essence of them.

The Nature of Usage ScenariosYes: in amethodologiesThe pre-execution, post-execution, execution exception, and execution completion states of theintegrated operationThe core advantage of AOP is to extract these cross-cutting functions from the core business logic, thereby enabling codedecoupledcap (a poem)reuseThe lifting system'smaintainabilitycap (a poem)scalability

image

Soft Exam Intermediate - Software Designer unreserved preparation sharing

The Monocle Pattern and its Ideology

Heavy news for the second half of 2023 softball exams

Passing the soft exam but not receiving a physical certificate?

Computer Algorithm Design and Analysis (5th Edition)

Java Full Stack Learning Route, Learning Resources and Interview Questions in One Place

Soft exam certificates = title certificates?

What are design patterns?