Spring AOP 的一些核心概念
概念 | 含义 |
---|---|
Aspect | 切面 |
Join Point | 连接点,Spring AOP里总是代表一次方法执行 |
Advice | 通知,在连接点执行的动作 |
Pointcut | 切入点,说明如何匹配连接点 |
Introduction | 引入,为现有类型声明额外的方法和属性 |
Target object | 目标对象 |
AOP proxy | AOP 代理对象,可以是 JDK 动态代理,也可以是 CGLIB 代理 |
Weaving | 织入,连接切面与目标对象或类型创建代理的过程 |
常用注解
- @EnableAspectJAutoProxy
开启aspectj的支持,对打@aspectj相关注解的类做一个Proxy - @Aspect
用来声明当前这个类是一个切面,需要说明的是光加aspect还不能变成一个bean,要么使用Javaconfig将类声明成一个bean,要么增加一个@Component注解来将这个bean创建出来 - @Pointcut
用来指定Pointcut,具体在代码中体现 - @Before
用来指定advice是在方法执行前是执行的 - @After / @AfterReturning / @AfterThrowing
不管什么情况,在你结束了之后执行;在你成功返回之后执行;在你报错的时候执行 - @Around
把这个方法前后都可以处理,把代码封装到里面 - @Order
指定切面的一个执行顺序,order越小,优先级越高
如何打印 SQL
HikariCP
• P6SQL,https://github.com/p6spy/p6spy
使用p6sql库
Alibaba Druid
• 内置 SQL 输出
• https://github.com/alibaba/druid/wiki/Druid中使用log4j2进行日志输出
具体代码
@Aspect //声明这个类为切面
@Component //将这个类变成一个bean注入容器
@Slf4j
public class PerformanceAspect {
// @Around("execution(* geektime.spring.springbucks.repository..*(..))") 用来指定Pointcut 有两种方式 拦截repository下面的类
@Around("repositoryOps()")
public Object logPerformance(ProceedingJoinPoint pjp) throws Throwable {
long startTime = System.currentTimeMillis(); //在方法启动之前记录当前时间
String name = "-"; //方法名字
String result = "Y"; //方法执行是否成功
try {
name = pjp.getSignature().toShortString(); //取出方法名
return pjp.proceed();
} catch (Throwable t) {
result = "N";
throw t;
} finally {
long endTime = System.currentTimeMillis(); //打印当前时间
log.info("{};{};{}ms", name, result, endTime - startTime);
}
}
@Pointcut("execution(* geektime.spring.springbucks.repository..*(..))")
private void repositoryOps() {
}
}
@Slf4j
@EnableTransactionManagement
@SpringBootApplication
@EnableJpaRepositories
@EnableAspectJAutoProxy //开启aspectj支持
public class SpringBucksApplication implements ApplicationRunner {
@Autowired
private CoffeeRepository coffeeRepository;
@Autowired
private CoffeeService coffeeService;
@Autowired
private CoffeeOrderService orderService;
public static void main(String[] args) {
SpringApplication.run(SpringBucksApplication.class, args);
}
@Override
public void run(ApplicationArguments args) throws Exception {
log.info("All Coffee: {}", coffeeRepository.findAll());
Optional<Coffee> latte = coffeeService.findOneCoffee("Latte");
if (latte.isPresent()) {
CoffeeOrder order = orderService.createOrder("Li Lei", latte.get());
log.info("Update INIT to PAID: {}", orderService.updateState(order, OrderState.PAID));
log.info("Update PAID to INIT: {}", orderService.updateState(order, OrderState.INIT));
}
}
}
配置文件:
application.properties配置文件
spring.datasource.driver-class-name=com.p6spy.engine.spy.P6SpyDriver
spring.datasource.url=jdbc:p6spy:h2:mem:testdb
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.hibernate.ddl-auto=none
#spring.jpa.properties.hibernate.show_sql=true 因为使用了P6SQL,所以将jpa的输出SQL属性注释掉
#spring.jpa.properties.hibernate.format_sql=true
spy.properties配置文件
# 单行日志
logMessageFormat=com.p6spy.engine.spy.appender.SingleLineFormat
# 使用Slf4J记录sql
appender=com.p6spy.engine.spy.appender.Slf4JLogger
# 是否开启慢SQL记录
outagedetection=true
# 慢SQL记录标准,单位秒
outagedetectioninterval=2
model层、repository层和service层的代码不变,需要注意的是service层中的使用的Repository是被AOP增强的Repository。
程序执行之后,我们可以看到Repository层方法的调用,使用了多长时间。
另外,对于操作用户的信息的时候,要进行脱敏处理。