1、准备阶段
application.properties;yml 可通过yaml<互转>properties
spring.datasource.url=jdbc:mysql://localhost:3306/study_annotate
spring.datasource.username=root
spring.datasource.password=123321
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.hibernate.ddl-auto=update
依赖(以 jpa 为例,简化代码方便举例):
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.28</version>
</dependency>
2、自定义注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.METHOD) // 因为路径在方法上所以作用目标为 METHOD
@Retention(RetentionPolicy.RUNTIME) // 运行时:通过反射在运行时读取注解信息
public @interface AccessLog {String value() default "";
}
3、先来定义一个实体类
import lombok.Data;import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import java.time.LocalDateTime;@Entity
@Data
public class AccessLogEntity {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;private String logMessage;private String ipAddress;private LocalDateTime timestamp;public AccessLogEntity() {this.timestamp = LocalDateTime.now();}public AccessLogEntity(String logMessage, String ipAddress) {this();this.logMessage = logMessage;this.ipAddress = ipAddress;}}
4、接着dao
import com.lfsun.demolfsunstudyannotate.entity.AccessLogEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;@Repository
public interface AccessLogRepository extends JpaRepository<AccessLogEntity, Long> {// 这里可以定义一些自定义的查询方法,根据需要进行扩展
}
5、然后service
import com.lfsun.demolfsunstudyannotate.dao.AccessLogRepository;
import com.lfsun.demolfsunstudyannotate.entity.AccessLogEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class AccessLogService {@Autowiredprivate AccessLogRepository accessLogRepository;public void saveLog(String logMessage, String ipAddress) {// 在这里实现将日志信息保存到MySQL数据库的逻辑AccessLogEntity logEntity = new AccessLogEntity(logMessage, ipAddress);accessLogRepository.save(logEntity);}
}
6、该切面了
import com.lfsun.demolfsunstudyannotate.annotate.AccessLog;
import com.lfsun.demolfsunstudyannotate.service.AccessLogService;
import com.lfsun.demolfsunstudyannotate.util.IpUtil;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import javax.servlet.http.HttpServletRequest;@Aspect
@Component
public class AccessLogAspect {@Autowiredprivate AccessLogService accessLogService;@Before("@annotation(accessLog)")public void logAccess(JoinPoint joinPoint, AccessLog accessLog) {String methodName = joinPoint.getSignature().toShortString();String logMessage = accessLog.value().isEmpty() ? methodName : accessLog.value();String ipAddress = IpUtil.getClientIpAddress();// 在这里将日志信息记录到MySQL数据库accessLogService.saveLog(logMessage, ipAddress);}}
7、controller
import com.lfsun.demolfsunstudyannotate.annotate.AccessLog;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/")
public class HelloController {@AccessLog("/hello")@GetMapping("/hello")public String hello() {return "hello lfsun!";}
}
如果观察仔细的话可以看到:点下就回到刚刚定义的切面了!
8、补个IpUtil ,获取ip的工具类
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.HttpServletRequest;
import java.util.Objects;@Slf4j
public class IpUtil {private static final String X_FORWARDED_FOR_HEADER = "X-Forwarded-For";private static final String PROXY_CLIENT_IP_HEADER = "Proxy-Client-IP";private static final String WL_PROXY_CLIENT_IP_HEADER = "WL-Proxy-Client-IP";/*** 获取客户端真实IP地址,考虑了代理服务器的情况。** @param request HttpServletRequest对象* @return 客户端真实IP地址*/public static String getClientIpAddress(HttpServletRequest request) {String xForwardedForHeader = request.getHeader(X_FORWARDED_FOR_HEADER);if (xForwardedForHeader != null && !xForwardedForHeader.isEmpty()) {// 如果有多个IP地址,取第一个return xForwardedForHeader.split(",")[0].trim();} else if (request.getHeader(PROXY_CLIENT_IP_HEADER) != null) {return request.getHeader(PROXY_CLIENT_IP_HEADER);} else if (request.getHeader(WL_PROXY_CLIENT_IP_HEADER) != null) {return request.getHeader(WL_PROXY_CLIENT_IP_HEADER);} else {// 如果以上都不存在,直接获取RemoteAddrString remoteAddr = request.getRemoteAddr();log.warn("使用 remoteAddr 无法确定客户端 IP 地址: {}", remoteAddr);return remoteAddr;}}/*** 获取客户端真实IP地址,使用Spring的RequestContextHolder。** @return 客户端真实IP地址*/public static String getClientIpAddress() {try {HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();return getClientIpAddress(request);} catch (NullPointerException e) {log.error("无法从 RequestContextHolder 获取 HttpServletRequest.", e);return "unknown";}}}