前言
你是不是還在類裏面直接拿到日誌對象調用方法來輸出日誌?接下來教你如何簡單使用AOP+自定義註解優雅的實現一個日誌記錄功能
提示:以下是本篇文章正文內容,案例僅供參考
一、技術介紹
1.AOP是什麼?
在軟件業,AOP爲Aspect Oriented Programming的縮寫,意爲:面向切面編程,通過預編譯方式和運行期間動態代理實現程序功能的統一維護的一種技術。AOP是OOP的延續,是軟件開發中的一個熱點,也是Spring框架中的一個重要內容,是函數式編程的一種衍生範型。利用AOP可以對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度降低,提高程序的可重用性,同時提高了開發的效率。 --摘自百度百科
二、開始使用
1.代碼目錄結構
2.開始編寫代碼
POM.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
</dependencies>
咱們這裏採用SpringBoot 寫一個按需注入的方式開啓註解,編寫一個按需注入的註解
package com.hyh.log.annotation;
import com.hyh.log.config.HyhLogAutoConfiguration;
import org.springframework.context.annotation.Import;
import java.lang.annotation.*;
/**
* 開啓日誌註解
*
* @Author: heyuhua
* @Date: 2021/1/28 15:32
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import({HyhLogAutoConfiguration.class})
public @interface EnableHyhLog {
}
自動配置類
package com.hyh.log.config;
import com.hyh.log.aspect.LogAspect;
import com.hyh.log.service.LogService;
import com.hyh.log.service.impl.LogServiceImpl;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Log AutoConfiguration
*
* @Author: heyuhua
* @Date: 2021/1/28 16:02
*/
@ConditionalOnWebApplication
@Configuration(proxyBeanMethods = false)
public class HyhLogAutoConfiguration {
@Bean
public LogAspect logAspect() {
return new LogAspect();
}
@Bean
public LogService logService() {
return new LogServiceImpl();
}
}
自定義日誌註解
package com.hyh.log.annotation;
import java.lang.annotation.*;
/**
* 日誌註解
*
* @Author: heyuhua
* @Date: 2021/1/28 16:02
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface HyhLog {
/**
* 描述
*
* @return
*/
String value() default "";
}
複製代碼
日誌切面代碼編寫
package com.hyh.log.aspect;
import com.hyh.log.annotation.HyhLog;
import com.hyh.log.service.LogService;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import javax.annotation.Resource;
/**
* Log Aspect
*
* @Author: heyuhua
* @Date: 2021/1/28 16:02
*/
@Aspect
public class LogAspect {
/**
* 日誌服務
*/
@Resource
private LogService logService;
/**
* 環繞操作
*
* @param point
* @param hyhLog
* @return
* @throws Throwable
*/
@Around("@annotation(hyhLog)")
public Object around(ProceedingJoinPoint point, HyhLog hyhLog) throws Throwable {
String className = point.getTarget().getClass().getName();
String methodName = point.getSignature().getName();
logService.info("【類】:{},【方法】:{}", className, methodName);
Object obj = point.proceed();
// do something
return obj;
}
/**
* 前置操作
*
* @param point
* @param hyhLog
* @return
* @throws Throwable
*/
@Before("@annotation(hyhLog)")
public void before(JoinPoint point, HyhLog hyhLog) throws Throwable {
logService.info("執行前置操作..." + hyhLog.value());
// do something
}
/**
* 後置操作
*
* @param point
* @param hyhLog
* @return
* @throws Throwable
*/
@After("@annotation(hyhLog)")
public void after(JoinPoint point, HyhLog hyhLog) throws Throwable {
logService.info("執行後置操作..." + hyhLog.value());
// do something
}
}
複製代碼
日誌服務接口
package com.hyh.log.service;
/**
* Log 接口
*
* @Author: heyuhua
* @Date: 2021/1/28 15:37
*/
public interface LogService {
/**
* info
*
* @param msg
*/
void info(String msg);
/**
* info
*
* @param msg
* @param o
*/
void info(String msg, Object o);
/**
* info
*
* @param msg
* @param throwable
*/
void info(String msg, Throwable throwable);
/**
* info
*
* @param msg
* @param o
*/
void info(String msg, Object... o);
/**
* info
*
* @param msg
* @param o
* @param throwable
*/
void info(String msg, Object o, Throwable throwable);
/**
* error
*
* @param msg
*/
void error(String msg);
/**
* error
*
* @param msg
* @param o
*/
void error(String msg, Object o);
/**
* error
*
* @param msg
* @param throwable
*/
void error(String msg, Throwable throwable);
/**
* error
*
* @param msg
* @param o
* @param throwable
*/
void error(String msg, Object o, Throwable throwable);
}
複製代碼
日誌服務接口實現
package com.hyh.log.service.impl;
import com.hyh.log.service.LogService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
/**
* 日誌服務接口實現
*
* @Author: heyuhua
* @Date: 2021/1/28 17:04
*/
@Service
public class LogServiceImpl implements LogService {
/**
* 日誌
*/
private static final Logger LOGGER = LoggerFactory.getLogger(LogServiceImpl.class);
@Override
public void info(String msg) {
LOGGER.info(msg);
}
@Override
public void info(String msg, Object o) {
LOGGER.info(msg, o);
}
@Override
public void info(String msg, Throwable throwable) {
LOGGER.info(msg, throwable);
}
@Override
public void info(String msg, Object... o) {
LOGGER.info(msg, o);
}
@Override
public void info(String msg, Object o, Throwable throwable) {
LOGGER.info(msg, o, throwable);
}
@Override
public void error(String msg) {
LOGGER.error(msg);
}
@Override
public void error(String msg, Object o) {
LOGGER.error(msg, o);
}
@Override
public void error(String msg, Throwable throwable) {
LOGGER.error(msg, throwable);
}
@Override
public void error(String msg, Object o, Throwable throwable) {
LOGGER.error(msg, o, throwable);
}
}
複製代碼
三、單元測試
OK,不廢話,直接上單元測試,先在SpringBoot項目中開啓使用我們的註解
看紅框框選的我們寫的一個按需導入的註解,然後Controller層寫一個接口,使用HyhLog註解
@HyhLog(value = "hello world test")
@RequestMapping(value = "hello", method = RequestMethod.GET)
public String hello() {
return "hello";
}
訪問hello接口來看下執行結果
總結
AOP基於動態代理可以實現很多功能,例如權限控制、日誌記錄、事務提交與回滾等一系列操作,通過無侵入式的方式實現業務解耦、功能強化,讓我們能用最優雅的代碼實現最複雜的功能。