我们经常使用自定义注解和AOP实现操作日志、权限、统计执行时间等功能,本文记录使用SpringBoot2.x实现自定义注解
本文在一个ssm项目的基础上进行的
SpringBoot2.x搭建SSM项目:
https://blog.csdn.net/qidasheng2012/article/details/84233549
https://github.com/qidasheng2012/springboot2.x_ssm/tree/V1.0.0
【添加依赖】
springBoot2.x 项目,只需要添加下面依赖
org.springframework.boot
spring-boot-starter-aop
而spring项目,则需要添加下面依赖
org.aspectj
aspectjrt
1.6.10
org.aspectj
aspectjweaver
1.7.2
【pom.xml】
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.2.0.RELEASE
com.springboot
springboot2.x_ssm
1.0.0
Spring Boot2.x 搭建 SSM 项目
UTF-8
UTF-8
1.8
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-thymeleaf
org.springframework.boot
spring-boot-starter-aop
org.springframework.boot
spring-boot-starter-test
test
org.mybatis.spring.boot
mybatis-spring-boot-starter
2.1.0
mysql
mysql-connector-java
runtime
org.springframework.boot
spring-boot-devtools
runtime
true
org.springframework.boot
spring-boot-configuration-processor
true
org.projectlombok
lombok
true
org.springframework.boot
spring-boot-maven-plugin
【LogAnnotation 】
自定义注解类
package com.springboot.ssm.annotation;
import java.lang.annotation.*;
/**
* 操作日志自定义注解
*/
@Target({ElementType.METHOD}) // 作用在方法上
@Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到
@Documented // 说明该注解将被包含在javadoc中
public @interface LogAnnotation {
/**
* 记录操作描述
*/
String description() default "";
/**
* 增删改的数据的类型
*/
Class> clazz();
}
@Target
@Target 说明了Annotation所修饰的对象范围
取值(ElementType)有:
@Retention
@Retention定义了该Annotation被保留的时间长短:某些Annotation仅出现在源代码中,而被编译器丢弃;而另一些却被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略,而另一些在class被装载时将被读取(请注意并不影响class的执行,因为Annotation与class在使用上是被分离的)。使用这个meta-Annotation可以对 Annotation的“生命周期”限制。
取值(RetentionPoicy)有:
@Documented
@Documented用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。Documented是一个标记注解,没有成员。
【LogAspect 】
切面类
package com.springboot.ssm.aspect;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* 操作日志切面
*/
@Slf4j
@Aspect
@Component
public class LogAspect {
// 切入点签名
@Pointcut("@annotation(com.springboot.ssm.annotation.LogAnnotation)")
private void cut() {
}
// 前置通知
@Before("cut()")
public void BeforeCall() {
log.info("====前置通知start");
log.info("====前置通知end");
}
// 环绕通知
@Around(value = "cut()")
public Object AroundCall(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("====环绕通知start");
// 注解所切的方法所在类的全类名
String typeName = joinPoint.getTarget().getClass().getName();
log.info("目标对象:[{}]", typeName);
// 注解所切的方法名
String methodName = joinPoint.getSignature().getName();
log.info("所切方法名:[{}]", methodName);
StringBuilder sb = new StringBuilder();
// 获取参数
Object[] arguments = joinPoint.getArgs();
for (Object argument : arguments) {
sb.append(argument.toString());
}
log.info("所切方法入参:[{}]", sb.toString());
// 统计方法执行时间
long start = System.currentTimeMillis();
//执行目标方法,并获得对应方法的返回值
Object result = joinPoint.proceed();
log.info("返回结果:[{}]", result);
long end = System.currentTimeMillis();
log.info("====执行方法共用时:[{}]", (end - start));
log.info("====环绕通知之结束");
return result;
}
// 后置通知
@After("cut()")
public void AfterCall() {
log.info("====后置通知start");
log.info("====后置通知end");
}
// 最终通知
@AfterReturning("cut()")
public void AfterReturningCall() {
log.info("====最终通知start");
log.info("====最终通知end");
}
// 异常通知
@AfterThrowing(value = "cut()", throwing = "ex")
public void afterThrowing(Throwable ex) {
throw new RuntimeException(ex);
}
}
@Aspect : 作用是把当前类标识为一个切面供容器读取
@component : 把普通pojo实例化到spring容器中,相当于配置文件中的
@Pointcut : 定义一个切点
@Before : 标识一个前置增强方法,相当于BeforeAdvice的功能
@Around : 环绕增强方法
@After :后置增强方法
【UserController 】
在controler层使用注解
package com.springboot.ssm.controller;
import com.springboot.ssm.annotation.LogAnnotation;
import com.springboot.ssm.domain.User;
import com.springboot.ssm.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
@Controller
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/toUserListPage")
public String toUserListPage() {
return "user/userList";
}
@LogAnnotation(description = "获取所有用户信息", clazz = User.class)
@RequestMapping("/getAll")
@ResponseBody
public List getAll() {
return userService.getAll();
}
}
访问: http://localhost:8080/user/getAll?id=1&name=tom
日志:
2019-07-22 13:48:32.183 INFO 7112 --- [nio-8080-exec-1] com.springboot.ssm.aspect.LogAspect : ====环绕通知start
2019-07-22 13:48:32.184 INFO 7112 --- [nio-8080-exec-1] com.springboot.ssm.aspect.LogAspect : 目标对象:[com.springboot.ssm.controller.UserController]
2019-07-22 13:48:32.188 INFO 7112 --- [nio-8080-exec-1] com.springboot.ssm.aspect.LogAspect : 所切方法名:[getAll]
2019-07-22 13:48:32.188 INFO 7112 --- [nio-8080-exec-1] com.springboot.ssm.aspect.LogAspect : 所切方法入参:[User(id=1, name=tom, age=null)]
2019-07-22 13:48:32.188 INFO 7112 --- [nio-8080-exec-1] com.springboot.ssm.aspect.LogAspect : ====前置通知start
2019-07-22 13:48:32.188 INFO 7112 --- [nio-8080-exec-1] com.springboot.ssm.aspect.LogAspect : ====前置通知end
2019-07-22 13:48:32.219 INFO 7112 --- [nio-8080-exec-1] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2019-07-22 13:48:32.221 WARN 7112 --- [nio-8080-exec-1] com.zaxxer.hikari.util.DriverDataSource : Registered driver with driverClassName=com.mysql.jdbc.Driver was not found, trying direct instantiation.
2019-07-22 13:48:32.491 INFO 7112 --- [nio-8080-exec-1] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2019-07-22 13:48:32.545 INFO 7112 --- [nio-8080-exec-1] com.springboot.ssm.aspect.LogAspect : 返回结果:[[User(id=1, name=张三, age=23), User(id=2, name=李四, age=24)]]
2019-07-22 13:48:32.545 INFO 7112 --- [nio-8080-exec-1] com.springboot.ssm.aspect.LogAspect : ====执行方法共用时:[357]
2019-07-22 13:48:32.545 INFO 7112 --- [nio-8080-exec-1] com.springboot.ssm.aspect.LogAspect : ====环绕通知之结束
2019-07-22 13:48:32.545 INFO 7112 --- [nio-8080-exec-1] com.springboot.ssm.aspect.LogAspect : ====后置通知start
2019-07-22 13:48:32.545 INFO 7112 --- [nio-8080-exec-1] com.springboot.ssm.aspect.LogAspect : ====后置通知end
2019-07-22 13:48:32.545 INFO 7112 --- [nio-8080-exec-1] com.springboot.ssm.aspect.LogAspect : ====最终通知start
2019-07-22 13:48:32.545 INFO 7112 --- [nio-8080-exec-1] com.springboot.ssm.aspect.LogAspect : ====最终通知end