当前位置:首页 > 域名

Spring框架之Spring AOP Logging教程

在这个教程中,框架我们将一步一步的框架教大家使用Spring AOP实现一个记录service、controller、框架repository日志的框架Aspect。

Maven dependencies - pom.xml

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">

4.0.0

org.springframework.boot

spring-boot-starter-parent

2.7.0

com.whu

aop

0.0.1-SNAPSHOT

aop

Demo project for Spring Boot

11

org.springframework.boot

spring-boot-starter-actuator

org.springframework.boot

spring-boot-starter-data-jpa

org.springframework.boot

spring-boot-starter-web

org.springframework.boot

spring-boot-devtools

runtime

true

com.h2database

h2

runtime

org.springframework.boot

spring-boot-starter-test

test

org.springframework.boot

spring-boot-maven-plugin

</project>

Domain层

创建一个简单的框架Employee实体类:

package com.whu.aop.model;

import javax.persistence.Column;

import javax.persistence.Entity;

import javax.persistence.GeneratedValue;

import javax.persistence.GenerationType;

import javax.persistence.Id;

import javax.persistence.Table;

@Entity

@Table(name = "employees")

public class Employee {

private long id;

private String firstName;

private String lastName;

private String emailId;

public Employee() {

}

public Employee(long id, String firstName, String lastName, String emailId) {

super();

this.id = id;

this.firstName = firstName;

this.lastName = lastName;

this.emailId = emailId;

}

@Id

@GeneratedValue(strategy = GenerationType.AUTO)

public long getId() {

return id;

}

public void setId(long id) {

this.id = id;

}

@Column(name = "first_name", nullable = false)

public String getFirstName() {

return firstName;

}

public void setFirstName(String firstName) {

this.firstName = firstName;

}

@Column(name = "last_name", nullable = false)

public String getLastName() {

return lastName;

}

public void setLastName(String lastName) {

this.lastName = lastName;

}

@Column(name = "email_address", nullable = false)

public String getEmailId() {

return emailId;

}

public void setEmailId(String emailId) {

this.emailId = emailId;

}

@Override

public String toString() {

return "Employee [id=" + id + ", firstName=" + firstName + ", lastName=" + lastName + ", emailId=" + emailId +

"]";

}

}

Repository层

package com.whu.aop.repository;

import org.springframework.data.jpa.repository.JpaRepository;

import org.springframework.stereotype.Repository;

import com.whu.aop.model.Employee;

@Repository

public interface EmployeeRepository extends JpaRepository{

}

Service层

package com.whu.aop.service;

import java.util.HashMap;

import java.util.List;

import java.util.Map;

import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

import com.whu.aop.exception.ResourceNotFoundException;

import com.whu.aop.model.Employee;

import com.whu.aop.repository.EmployeeRepository;

@Service

public class EmployeeService {

@Autowired

private EmployeeRepository employeeRepository;

public ListgetAllEmployees() {

return employeeRepository.findAll();

}

public OptionalgetEmployeeById(Long employeeId)

throws ResourceNotFoundException {

return employeeRepository.findById(employeeId);

}

public Employee createEmployee(Employee employee) {

return employeeRepository.save(employee);

}

public Employee updateEmployee(Long employeeId,

Employee employeeDetails) throws ResourceNotFoundException {

Employee employee = employeeRepository.findById(employeeId)

.orElseThrow(()-> new ResourceNotFoundException("Employee not found for this id ::"+employeeId));

employee.setEmailId(employeeDetails.getEmailId());

employee.setLastName(employeeDetails.getLastName());

employee.setFirstName(employeeDetails.getFirstName());

final Employee updatedEmployee = employeeRepository.save(employee);

return updatedEmployee;

}

public MapdeleteEmployee(Long employeeId)

throws ResourceNotFoundException {

Employee employee = employeeRepository.findById(employeeId)

.orElseThrow(()-> new ResourceNotFoundException("Employee not found for this id :: "+employeeId));

employeeRepository.delete(employee);

Map response = new HashMap<>();

response.put("deleted", Boolean.TRUE);

return response;

}

}

Controller层

package com.whu.aop.controller;

import java.util.List;

import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.http.ResponseEntity;

import org.springframework.validation.annotation.Validated;

import org.springframework.web.bind.annotation.DeleteMapping;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.PathVariable;

import org.springframework.web.bind.annotation.PostMapping;

import org.springframework.web.bind.annotation.PutMapping;

import org.springframework.web.bind.annotation.RequestBody;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;

import com.whu.aop.exception.ResourceNotFoundException;

import com.whu.aop.model.Employee;

import com.whu.aop.service.EmployeeService;

@RestController

@RequestMapping("/api/v1")

public class EmployeeController {

@Autowired

private EmployeeService employeeService;

@GetMapping("/employees")

public ListgetAllEmployees() {

return employeeService.getAllEmployees();

}

@GetMapping("/employees/{ id}")

public ResponseEntitygetEmployeeById(@PathVariable(value = "id") Long employeeId)

throws ResourceNotFoundException {

Employee employee = employeeService.getEmployeeById(employeeId)

.orElseThrow(() ->new ResourceNotFoundException("Employee not found for this id :: "+employeeId));

return ResponseEntity.ok().body(employee);

}

@PostMapping("/employees")

public Employee createEmployee(@Validated @RequestBody Employee employee) {

return employeeService.createEmployee(employee);

}

@PutMapping("/employees/{ id}")

public ResponseEntity < Employee > updateEmployee(@PathVariable(value = "id") Long employeeId,

@Validated @RequestBody Employee employeeDetails) throws ResourceNotFoundException {

Employee updatedEmployee = employeeService.updateEmployee(employeeId, employeeDetails);

return ResponseEntity.ok(updatedEmployee);

}

@DeleteMapping("/employees/{ id}")

public MapdeleteEmployee(@PathVariable(value = "id") Long employeeId)

throws ResourceNotFoundException {

return employeeService.deleteEmployee(employeeId);

}

}

至此,一个简单的框架web应该已经建好,可以通过http://localhost:8080//api/v1/employees 访问第一个接口,框架请求获取所有employees。框架

创建Logging Aspect

现在,框架让我们创建一个Aspect来记录service和repository组件的框架执行情况。我们将创建4个方法,框架以下是源码下载框架详细内容:

springBeanPointcut()--匹配所有repository、service和Web REST端点的框架pointcut。applicationPackagePointcut()--用于匹配应用程序主包中的框架所有Spring Bean的pointcut。logAfterThrowing()--记录抛出异常的框架方法的advice。logAround()--记录方法进入和退出时的advice。package com.whu.aop.aspect;

import java.util.Arrays;

import org.aspectj.lang.JoinPoint;

import org.aspectj.lang.ProceedingJoinPoint;

import org.aspectj.lang.annotation.AfterThrowing;

import org.aspectj.lang.annotation.Around;

import org.aspectj.lang.annotation.Aspect;

import org.aspectj.lang.annotation.Pointcut;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.stereotype.Component;

@Aspect

@Component

public class LoggingAspect {

private final Logger log = LoggerFactory.getLogger(this.getClass());

@Pointcut("within(@org.springframework.stereotype.Repository *)" +

" || within(@org.springframework.stereotype.Service *)" +

" || within(@org.springframework.web.bind.annotation.RestController *)")

public void springBeanPointcut() {

// Method is empty as this is just a Pointcut, the implementations are in the advices.

}

/

**

* Pointcut that matches all Spring beans in the applications main packages.

*/

@Pointcut(value = "within(com.whu.aop..*)" +

" || within(com.whu.aop.service..*)" +

" || within(com.whu.aop.controller..*)")

public void applicationPackagePointcut() {

}

/

**

* Advice that logs method throwing exceptions.

* @param joinpoint

* @param e

*/

@AfterThrowing(pointcut = "applicationPackagePointcut() && springBeanPointcut()", throwing = "e")

public void logAfterThrowing(JoinPoint joinpoint, Throwable e) {

log.error("Exception in { }.{ }() with cause = { }",

joinpoint.getSignature().getDeclaringTypeName(),

joinpoint.getSignature().getName(),

e.getCause() != null ? e.getCause() : "NULL");

}

@Around("applicationPackagePointcut() && springBeanPointcut()")

public Object logAround(ProceedingJoinPoint joinpoint) throws Throwable {

if(log.isDebugEnabled()) {

log.debug("Enter: { }.{ }() with arguments[s] = { }",

joinpoint.getSignature().getDeclaringType(),

joinpoint.getSignature().getName(),

Arrays.toString(joinpoint.getArgs()));

}

try {

Object result = joinpoint.proceed();

if(log.isDebugEnabled()) {

log.debug("Exit: { }.{ }() with result = { }",

joinpoint.getSignature().getDeclaringType(),

joinpoint.getSignature().getName(),

result);

}

return result;

}catch (IllegalArgumentException e) {

log.error("Illegal argument: { } in { }.{ }()",

Arrays.toString(joinpoint.getArgs()),

joinpoint.getSignature().getDeclaringType(),

joinpoint.getSignature().getName());

throw e;

}

}

}

application.properties

logging.level.org.springframework.web=INFO

logging.level.org.hibernate=ERROR

logging.level.com.whu=DEBUG

Exception Handling

我们可以用@ResponseStatus注解来指定特定异常的响应状态,以及异常的定义。

package com.whu.aop.exception;

import org.springframework.http.HttpStatus;

import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(value = HttpStatus.NOT_FOUND)

public class ResourceNotFoundException extends Exception {

private static final long serialVersionUID = 1L;

public ResourceNotFoundException(String message){

super(message);

}

}

自定义Error Response结构

Spring Boot提供的云服务器默认错误响应,通常包含所有需要的detail。但是,你可以创建一个独立于框架的响应结构。在这种情况下,你可以定义一个特定的错误响应结构。让我们来定义一个简单的错误响应Bean。

package com.whu.aop.exception;

import java.util.Date;

public class ErrorDetails {

private Date timestamp;

private String message;

private String details;

public ErrorDetails(Date timestamp, String message, String details) {

super();

this.timestamp = timestamp;

this.message = message;

this.details = details;

}

public Date getTimestamp() {

return timestamp;

}

public String getMessage() {

return message;

}

public String getDetails() {

return details;

}

}package com.whu.aop.exception;

import java.util.Date;

import org.springframework.http.HttpStatus;

import org.springframework.http.ResponseEntity;

import org.springframework.web.bind.annotation.ControllerAdvice;

import org.springframework.web.bind.annotation.ExceptionHandler;

import org.springframework.web.context.request.WebRequest;

@ControllerAdvice

public class GlobalExceptionHandler {

@ExceptionHandler(ResourceNotFoundException.class)

public ResponseEntity resourceNotFoundException(ResourceNotFoundException ex, WebRequest request) {

ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(), request.getDescription(false));

return new ResponseEntity<>(errorDetails, HttpStatus.NOT_FOUND);

}

@ExceptionHandler(Exception.class)

public ResponseEntity globleExcpetionHandler(Exception ex, WebRequest request) {

ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(), request.getDescription(false));

return new ResponseEntity<>(errorDetails, HttpStatus.INTERNAL_SERVER_ERROR);

}

}

日志输出

访问http://localhost:8080/api/v1/employees/。

2022-06-04 20:35:22.702 DEBUG 34484 --- [nio-8080-exec-4] com.whu.aop.aspect.LoggingAspect : Enter: class com.whu.aop.controller.EmployeeController.getAllEmployees() with arguments[s] = []

2022-06-04 20:35:22.702 DEBUG 34484 --- [nio-8080-exec-4] com.whu.aop.aspect.LoggingAspect : Enter: class com.whu.aop.service.EmployeeService.getAllEmployees() with arguments[s] = []

2022-06-04 20:35:22.704 DEBUG 34484 --- [nio-8080-exec-4] com.whu.aop.aspect.LoggingAspect : Exit: class com.whu.aop.service.EmployeeService.getAllEmployees() with result = []

2022-06-04 20:35:22.704 DEBUG 34484 --- [nio-8080-exec-4] com.whu.aop.aspect.LoggingAspect : Exit: class com.whu.aop.controller.EmployeeController.getAllEmployees() with result = []

访问http://localhost:8080/api/v1/employees/1。

2022-06-04 20:36:19.902 DEBUG 34484 --- [nio-8080-exec-6] com.whu.aop.aspect.LoggingAspect : Enter: class com.whu.aop.controller.EmployeeController.getEmployeeById() with arguments[s] = [1]

2022-06-04 20:36:19.903 DEBUG 34484 --- [nio-8080-exec-6] com.whu.aop.aspect.LoggingAspect : Enter: class com.whu.aop.service.EmployeeService.getEmployeeById() with arguments[s] = [1]

2022-06-04 20:36:19.907 DEBUG 34484 --- [nio-8080-exec-6] com.whu.aop.aspect.LoggingAspect : Exit: class com.whu.aop.service.EmployeeService.getEmployeeById() with result = Optional.empty

2022-06-04 20:36:19.910 ERROR 34484 --- [nio-8080-exec-6] com.whu.aop.aspect.LoggingAspect : Exception in com.whu.aop.controller.EmployeeController.getEmployeeById() with cause = NULL

2022-06-04 20:36:19.918 WARN 34484 --- [nio-8080-exec-6] .m.m.a.ExceptionHandlerExceptionResolver : Resolved [com.whu.aop.exception.ResourceNotFoundException: Employee not found for this id :: 1]

分享到:

滇ICP备2023006006号-16