1、背景:AspectJ作爲AOP一大應用已經廣爲人知了,具體的應用場景也很多,如:日誌處理、執行目標方法前做邏輯判斷、事物控制等等;其實質大都是抽取出各類、方法中重複的、與業務邏輯無關的代碼,形成一個切面(Aspect, 也就是一個類),在切面中定義切點(可以理解爲將代碼織入到那些 我從方法中提取出重複代碼的位置處,一個切點可能包含多個連接點)、連接點(也就是切點的一個具體)、通知等,這個一來就消除了代碼的冗餘。
那麼在這裏我要做的是AspectJ的一個應用,在邏輯代碼前後,做日誌記錄,並輸出到日誌文件。
2、AOP術語說明:
1)切面(Aspect):抽取重複代碼形成的一個類
2)切點(Pointcut):抽取重複代碼的位置,即將織入代碼的那些位置
3)連接點(Joinpoint): 切點的具體表現
如:execution(public * com.dw.controller..*(..) 是一個切面,意思com.dw.controller包下所有且返回任意類型的方法,都將織入這段代碼,但是從瀏覽器中請求的可能是某一個具體的方法,如:
joinPoint:execution(String com.dw.controller.HelloController.hello(String)),就是一個連接點
4)通知(Advice): 即織入點執行的具體代碼,有五種類型:
a:@Before,調用方法之前執行
b:@After,調用方法之後執行
c:@AfterThrowing,目標方法拋出異常後執行
d:@AfterReturing,對目標方法返回的結果做處理,但是不影響原返回結果
e:@Around,包裹目標方法執行
3、測試代碼:(springboot下進行,很多註解都由springboot默認配置)
1)切面:
@Component
@Aspect
public class AspectAdvice {
// private static final Logger logger = LoggerFactory.getLogger(AspectAdvice.class);
private static final Logger logger = LoggerFactory.getLogger("aspect-advice");
@Before("execution(public * com.dw.controller..*(..))")
public void before(JoinPoint joinPoint) {
// do
logger.info("[Before]..." + ", 即將執行" + ", joinPoint:" + joinPoint);
}
@Around("execution(public * com.dw.controller..*(..))")
public Object around(ProceedingJoinPoint pdj) throws Throwable {
// do
logger.info("[Around]...");
return pdj.proceed();
}
@After("execution(* com.dw.controller.HelloController.consumer())")
public void after() {
// do
logger.info("[After]..." + ", 執行完成");
}
@AfterReturning(pointcut = "execution(* com.dw.controller.HelloController.consumer())", returning = "tmp")
public void afterReturning(Object tmp) {
// do
logger.info("[AfterReturning]..." + ", and result is " + tmp);
}
@AfterThrowing(pointcut = "execution(* com.dw.controller.HelloController.throwing())", throwing = "tmp")
public void afterThrowing(Throwable tmp) {
// do
logger.warn("[AfterThrowing]..." + ", 處理目標方法拋出的異常, 異常信息: " + tmp);
}
}
2)Controller.java
@RestController
@RequestMapping("/hello")
public class HelloController {
@Autowired
private HelloService helloService;
@RequestMapping("/{name}")
public String hello(@PathVariable("name") String name) {
return helloService.hello(name);
}
@RequestMapping("/consumer")
public String consumer() {
System.out.println("this is a consumer function");
return "consumer test";
}
@RequestMapping("/throwing")
public String throwing() throws Exception{
System.out.println("this is a throwging funtion");
try {
double tmp = 1 / 0;
} catch (Exception e) {
throw new Exception(e.getMessage());
}
return "throwing test";
}
}
3)日誌配置:logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<property name="NORMAL_LOG_PATTERN"
value="%d{yyyy-MM-dd HH:mm:ss.SSS}	%p %20.20F--> %m%n"/>
<!-- appender 格式化輸出日誌, 控制檯輸出、文件輸出策略-->
<!-- 控制檯輸出 -->
<appender name="consoleLog" class="ch.qos.logback.core.ConsoleAppender">
<!--展示格式 layout-->
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d -1 %msg%n</pattern>
</layout>
</appender>
<!-- 文件輸出 -->
<appender name="fileAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
<File>/home/admin/demo1019/logs/demo.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 活動文件的名字會根據fileNamePattern的值,每隔一段時間改變一次 -->
<!-- 文件名:log/demo.2017-12-05.0.log -->
<fileNamePattern>/home/admin/demo1019/logs/demo.log.%d.%i.log</fileNamePattern>
<!-- 每產生一個日誌文件,該日誌文件的保存期限爲30天 -->
<maxHistory>30</maxHistory>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<!-- maxFileSize:這是活動文件的大小,默認值是10MB,測試時可改成1KB看效果 -->
<maxFileSize>50MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
<!-- pattern節點,用來設置日誌的輸入格式 -->
<pattern>${NORMAL_LOG_PATTERN}</pattern>
<!-- 記錄日誌的編碼:此處設置字符集 - -->
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- root必選節點, 指定最基本的輸出級別 -->
<root level="info">
<appender-ref ref="consoleLog"/>
</root>
<!-- 匹配到 LoggerFactory.getLogger("aspect-advice") -->
<logger name="aspect-advice">
<level value="info"/>
<appender-ref ref="fileAppender"/>
</logger>
</configuration>
4)springboot啓動類:
@SpringBootApplication
public class ApplicationStart {
public static void main(String[] args) {
SpringApplication.run(ApplicationStart.class, args);
}
}
這裏有一點需要注意:該類要和註解類所在包同級,否則一些掃描配置不起作用,
@SpringBootApplication包含三個註解, @ComponentScan, @Configuration, @EnableAutoConfiguration,所以簡單的程序不用再寫類來加載配置文件了。