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,所以简单的程序不用再写类来加载配置文件了。

猜你喜欢

转载自blog.csdn.net/ldw201510803006/article/details/83242803
今日推荐