This article is mainly based onLogging
As 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 amethodologies
The pre-execution, post-execution, execution exception, and execution completion states of theintegrated operation
The 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:
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):
JoinPoint
under thegetArgs()
method, orProceedingJoinPoint
under 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
failure
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 transaction
If 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@Transactional
Transaction 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 amethodologies
The pre-execution, post-execution, execution exception, and execution completion states of theintegrated operation
The core advantage of AOP is to extract these cross-cutting functions from the core business logic, thereby enabling codedecoupled
cap (a poem)reuse
The lifting system'smaintainability
cap (a poem)scalability
。
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?