Spring切面編程-使用AspectJ日誌處理

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}&#9;%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,所以簡單的程序不用再寫類來加載配置文件了。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章