关于springboot:15Spring中的AOP原则

1次阅读

共计 17330 个字符,预计需要花费 44 分钟才能阅读完成。

  1. AOP 概述

    AOP(Aspect Orient Programming)是一种设计思维,是软件设计畛域中的面向切面编程,它是面向对象编程 (OOP) 的一种补充和欠缺。它以通过预编译形式和运行期动静代理形式,实现在不批改源代码的状况下给程序动静对立增加额定性能的一种技术。如图 - 1 所示:

  2. AOP 利用场景剖析?

理论我的项目中通常会将零碎分为两大部分,一部分是外围业务,一部分是非核业务。在编程实现时咱们首先要实现的是外围业务的实现,非核心业务个别是通过特定形式切入到零碎中,这种特定形式个别就是借助 AOP 进行实现。
AOP 就是要基于 OCP(开闭准则),在不扭转原有系统核心业务代码的根底上动静增加一些扩大性能并能够 ” 管制 ” 对象的执行。例如 AOP 利用于我的项目中的日志解决,事务处理,权限解决,缓存解决等等。如图 - 2 所示:

  1. AOP 利用原理剖析:

Spring AOP 底层基于代理机制实现性能扩大:

  1. 如果指标对象 (被代理对象) 实现接口,则底层能够采纳 JDK 动静代理机制为指标对象创立代理对象(指标类和代理类会实现独特接口)。
  2. 如果指标对象 (被代理对象) 没有实现接口,则底层能够采纳 CGLIB 代理机制为指标对象创立代理对象(默认创立的代理类会继承指标对象类型)。

Spring AOP 原理剖析,如图 - 3 所示:

阐明:Spring boot2.x 中 AOP 当初默认应用的 CGLIB 代理, 如果须要应用 JDK 动静代理能够在配置文件 (applicatiion.properties) 中进行如下配置:
spring.aop.proxy-target-class=false

  1. AOP 相干术语剖析

  • 切面(aspect): 横切面对象, 个别为一个具体类对象(能够借助 @Aspect 申明)。
  • 告诉(Advice): 在切面的某个特定连接点上执行的动作(扩大性能),例如 around,before,after 等。
  • 连接点(joinpoint): 程序执行过程中某个特定的点,个别指被拦挡到的的办法。
  • 切入点 (pointcut): 对多个连接点(Joinpoint) 一种定义, 个别能够了解为多个连接点的汇合。

连接点与切入点定义如图 - 4 所示:

阐明:咱们能够简略的将机场的一个安检口了解为连接点,多个安检口为切入点,安全检查过程看成是告诉。总之,概念很艰涩难懂,多做例子,做完就会清晰。先能够按文言去了解。

  1. Spring AOP 疾速实际

    1. 业务形容

基于我的项目中的外围业务,增加简略的日志操作,借助 SLF4J 日志 API 输入指标办法的执行时长。(前提,不能批改指标办法代码 - 遵循 OCP 准则)
基于我的项目中的外围业务,增加简略的日志操作,借助 SLF4J 日志 API 输入指标办法的执行时长。(前提,不能批改指标办法代码 - 遵循 OCP 准则)

  1. 我的项目创立及配置

创立 maven 我的项目或在已有我的项目根底上增加 AOP 启动依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
阐明:基于此依赖 spring 能够整合 AspectJ 框架疾速实现 AOP 的根本实现。Aspect 是一个面向切面的框架,他定义了 AOP 的一些语法,有一个专门的字节码生成器来生成恪守 java 标准的 class 文件。

  1. 扩大业务剖析及实现

  2. 创立日志切面类对象

将此日志切面类作为外围业务加强(一个横切面对象)类,用于输入业务执行时长,其要害代码如下:
package com.cy.pj.common.aspect;
@Aspect
@Slf4j
@Component
public class SysLogAspect {
@Pointcut(“bean(sysUserServiceImpl)”)
public void logPointCut() {}
@Around(“logPointCut()”)
public Object around(ProceedingJoinPoint jp)
throws Throwable{
try {
log.info(“start:{}”+System.currentTimeMillis());
Object result=jp.proceed();// 最终会调用指标办法
log.info(“after:{}”+System.currentTimeMillis());
return result;
}catch(Throwable e) {
log.error(“after:{}”,e.getMessage());
throw e;
}
}
}
阐明:

  • @Aspect 注解用于标识或者形容 AOP 中的切面类型,基于切面类型构建的对象用于为指标对象进行性能扩大或控制目标对象的执行。
  • @Pointcut 注解用于形容切面中的办法,并定义切面中的切入点(基于特定表达式的形式进行形容),在本案例中切入点表达式用的是 bean 表达式,这个表达式以 bean 结尾,bean 括号中的内容为一个 spring 治理的某个 bean 对象的名字。
  • @Around 注解用于形容切面中办法,这样的办法会被认为是一个盘绕告诉(外围业务办法执行之前和之后要执行的一个动作),@Aournd 注解外部 value 属性的值为一个切入点表达式或者是切入点表达式的一个援用(这个援用为一个 @PointCut 注解形容的办法的办法名)。
  • ProceedingJoinPoint 类为一个连接点类型,此类型的对象用于封装要执行的指标办法相干的一些信息。只能用于 @Around 注解形容的办法参数。
  1. 业务切面测试实现

启动我的项目测试或者进行单元测试,其中 Spring Boot 我的项目中的单元测试代码如下:
@SpringBootTest
public class AopTests {
@Autowired
private SysUserService userService;
@Test
public void testSysUserService() {
PageObject<SysUserDeptVo> po=
userService.findPageObjects(“admin”,1);
System.out.println(“rowCount:”+po.getRowCount());
}
}
对于测试类中的 userService 对象而言, 它有可能指向 JDK 代理, 也有可能指向 CGLIB 代理, 具体是什么类型的代理对象, 要看 application.yml 配置文件中的配置.

  1. 利用总结剖析

在业务利用,AOP 相干对象剖析, 如图 - 5 所示:

  1. 扩大业务织入加强剖析

  2. 基于 JDK 代理形式实现

如果指标对象有实现接口, 则能够基于 JDK 为指标对象创立代理对象, 而后为指标对象进行性能扩大, 如图 - 6 所示:

  1. 基于 CGLIB 代理形式实现

如果指标对象没有实现接口 (当然实现了接口也是能够的),能够基于 CGLIB 代理形式为指标对象织入性能扩大,如图 - 7 所示:

阐明:指标对象实现了接口也能够基于 CGLIB 为指标对象创立代理对象。

  1. Spring AOP 编程加强

  2. 切面告诉利用加强

  3. 告诉类型

在基于 Spring AOP 编程的过程中,基于 AspectJ 框架规范,spring 中定义了五种类型的告诉(告诉形容的是一种扩大业务),它们别离是:

  • @Before。
  • @AfterReturning。
  • @AfterThrowing。
  • @After。
  • @Around. 重点把握(优先级最高)
    阐明:在切面类中应用什么告诉,由业务决定,并不是说,在切面中要把所有告诉都写上。
  1. 告诉执行程序

如果这些告诉全副写到一个切面对象中,其执行程序及过程,如图 - 8 所示:

阐明:理论我的项目中可能不会在切面中定义所有的告诉,具体定义哪些告诉要联合业务进行实现。

  1. 告诉实际过程剖析

代码实际剖析如下:
@Component
@Aspect
public class SysTimeAspect {
@Pointcut(“bean(sysUserServiceImpl)”)
public void doTime(){}
@Before(“doTime()”)
public void doBefore(JoinPoint jp){
System.out.println(“time doBefore()”);
}
@After(“doTime()”)
public void doAfter(){
System.out.println(“time doAfter()”);
}
/* 外围业务失常完结时执行 阐明:如果有 after,先执行 after, 再执行 returning*/
@AfterReturning(“doTime()”)
public void doAfterReturning(){
System.out.println(“time doAfterReturning”);
}
/* 外围业务出现异常时执行阐明:如果有 after,先执行 after, 再执行 Throwing/
@AfterThrowing(“doTime()”)
public void doAfterThrowing(){
System.out.println(“time doAfterThrowing”);
}
@Around(“doTime()”)
public Object doAround(ProceedingJoinPoint jp)
throws Throwable{
System.out.println(“doAround.before”);
try{
Object obj=jp.proceed();
System.out.println(“doAround.after”);
return obj;
}catch(Throwable e){
System.out.println(e.getMessage());
throw e;
}
}
}
阐明:对于 @AfterThrowing 告诉只有在出现异常时才会执行,所以当做一些异样监控时可在此办法中进行代码实现。
课堂练习: 定义一个异样监控切面, 对指标页面办法进行异样监控, 并以日志信息的模式输入异样
package com.cy.pj.common.aspect;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Aspect
@Component
public class SysExceptionAspect {
@AfterThrowing(pointcut=”bean(*ServiceImpl)”,throwing = “e”)
public void doHandleException(JoinPoint jp,Throwable e) {
MethodSignature ms=(MethodSignature)jp.getSignature();
log.error(“{}’exception msg is
{}”,ms.getName(),e.getMessage());
}
}
阐明:AfterThrowing 中 throwing 属性的值,须要与它形容的办法的异样参数名雷同。

  1. 切入点表达式加强

Spring 中通过切入点表达式定义具体切入点,其罕用 AOP 切入点表达式定义及阐明:
表 -1 Spring AOP 中切入点表达式阐明
批示符
作用
bean
用于匹配指定 bean 对象的所有办法
within
用于匹配指定包下所有类内的所有办法
execution
用于按指定语法规定匹配到具体方法
@annotation
用于匹配指定注解润饰的办法

  1. bean 表达式(重点)

bean 表达式个别利用于类级别,实现粗粒度的切入点定义,案例剖析:

  • bean(“userServiceImpl”)指定一个 userServiceImpl 类中所有办法。
  • bean(“*ServiceImpl”)指定所有后缀为 ServiceImpl 的类中所有办法。

阐明:bean 表达式外部的对象是由 spring 容器治理的一个 bean 对象, 表达式外部的名字应该是 spring 容器中某个 bean 的 name。

  1. within 表达式(理解)

within 表达式利用于类级别,实现粗粒度的切入点表达式定义,案例剖析:

  • within(“aop.service.UserServiceImpl”)指定以后包中这个类外部的所有办法。
  • within(“aop.service.*”) 指定当前目录下的所有类的所有办法。
  • within(“aop.service..*”) 指定当前目录以及子目录中类的所有办法。

within 表达式利用场景剖析:
1)对所有业务 bean 都要进行性能加强,然而 bean 名字又没有规定。
2)按业务模块 (不同包下的业务) 对 bean 对象进行业务性能加强。

  1. execution 表达式(理解)

execution 表达式利用于办法级别,实现细粒度的切入点表达式定义,案例剖析:
语法:execution(返回值类型 包名. 类名. 办法名(参数列表))。

  • execution(void aop.service.UserServiceImpl.addUser())匹配 addUser 办法。
  • execution(void aop.service.PersonServiceImpl.addUser(String)) 办法参数必须为 String 的 addUser 办法。
  • execution( aop.service...*(..)) 万能配置。
  1. @annotation 表达式(重点)

@annotaion 表达式利用于办法级别,实现细粒度的切入点表达式定义,案例剖析

  • @annotation(anno.RequiredLog) 匹配有此注解形容的办法。
  • @annotation(anno.RequiredCache) 匹配有此注解形容的办法。

其中:RequiredLog 为咱们本人定义的注解, 当咱们应用 @RequiredLog 注解润饰业务层办法时, 零碎底层会在执行此办法时进行日扩大操作。
课堂练习: 定义一 Cache 相干切面, 应用注解表达式定义切入点, 并应用此注解对须要应用 cache 的业务办法进行形容, 代码剖析如下:
第一步: 定义注解 RequiredCache
package com.cy.pj.common.annotation;
/**

  • 自定义注解, 一个非凡的类, 所有注解都默认继承 Annotation 接口

*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RequiredCache {
//…
}
第二步: 定义 SysCacheAspect 切面对象。
package com.cy.pj.common.aspect;
@Aspect
@Component
public class SysCacheAspect {
@Pointcut(“@annotation(com.cy.pj.common.annotation.RequiredCache)”)
public void doCache() {}
@Around(“doCache()”)
public Object around(ProceedingJoinPoint jp)
throws Throwable{
System.out.println(“Get data from cache”);
Object obj=jp.proceed();
System.out.println(“Put data to cache”);
return obj;
}
}
第三步: 应用 @RequiredCache 注解对特定业务指标对象中的查询方法进行形容。
@RequiredCache
@Override
public List<Map<String, Object>> findObjects() {
….
return list;
}

  1. 切面优先级设置实现

切面的优先级须要借助 @Order 注解进行形容,数字越小优先级越高,默认优先级比拟低。例如:
定义日志切面并指定优先级。
@Order(1)
@Aspect
@Component
public class SysLogAspect {

}
定义缓存切面并指定优先级:
@Order(2)
@Aspect
@Component
public class SysCacheAspect {

}
阐明:当多个切面作用于同一个指标对象办法时,这些切面会构建成一个切面链,相似过滤器链、拦截器链,其执行剖析如图 - 9 所示:

  1. 要害对象与术语总结

Spring 基于 AspectJ 框架实现 AOP 设计的要害对象概览,如图 -10 所示:

  1. 用户行为日志记录实现(实际)

本大节作为课后作业,以 AOP 形式记录我的项目中的用户行为信息,并将其存储到数据库。参考日志模块的文档。

  1. Spring AOP 事务处理

  2. Spring 中事务简介

  3. 事务定义

事务 (Transaction) 是一个业务, 是一个不可分割的逻辑工作单元,基于事务能够更好的保障业务的正确性。

  1. 事务个性

事务具备 ACID 个性,别离是:

  • 原子性(Atomicity):一个事务中的多个操作要么都胜利要么都失败。
  • 一致性(Consistency): 例如存钱操作, 存之前和存之后的总钱数应该是统一的。
  • 隔离性(Isolation):事务与事务应该是互相隔离的。
  • 持久性(Durability):事务一旦提交, 数据要长久保留。

阐明: 目前市场上在事务一致性方面, 通常会做肯定的优化, 比方说只有最终统一就能够了, 这样的事务咱们通常会称之为柔性事务(只有最终统一就能够了).

  1. Spring 中事务管理

  2. Spring 中事务形式概述

Spring 框架中提供了一种申明式事务的解决形式,此形式基于 AOP 代理, 能够将具体业务逻辑与事务处理进行解耦。也就是让咱们的业务代码逻辑不受净化或大量净化, 就能够实现事务管制。
在 SpringBoot 我的项目中, 其外部提供了事务的主动配置,当咱们在我的项目中增加了指定依赖 spring-boot-starter-jdbc 时,框架会主动为咱们的我的项目注入事务管理器对象,最罕用的为 DataSourceTransactionManager 对象。

  1. Spring 中事务管理实现

本大节重点解说理论我的项目中最罕用的注解形式的事务管理,以注解 @Transactional 配置形式为例,进行实际剖析。
基于 @Transactional 注解进行申明式事务管理的实现步骤分为两步:

  1. 启用申明式事务管理,在我的项目启动类上增加 @EnableTransactionManagement,新版本中也可不增加(例如新版 Spring Boot 我的项目)。
  2. 将 @Transactional 注解增加到适合的业务类或办法上,并设置适合的属性信息。

其代码示例如下:
@Transactional(timeout = 30,
readOnly = false,
isolation = Isolation.READ_COMMITTED,
rollbackFor = Throwable.class,
propagation = Propagation.REQUIRED)
@Service
public class implements SysUserService {
@Transactional(readOnly = true)
@Override
public PageObject<SysUserDeptVo> findPageObjects(
String username, Integer pageCurrent) {

}
}
其中,代码中的 @Transactional 注解用于形容类或办法,通知 spring 框架咱们要在此类的办法执行时进行事务管制,其具体阐明如下:。

  • 当 @Transactional 注解利用在类上时示意类中所有办法启动事务管理,并且个别用于事务共性的定义。
  • 当 @Transactional 形容办法时示意此办法要进行事务管理,如果类和办法上都有 @Transactional 注解,则办法上的事务个性优先级比拟高。

@Transactional 罕用属性利用阐明:

  • timeout:事务的超时工夫,默认值为 -1, 示意没有超时显示。如果配置了具体工夫, 则超过该工夫限度但事务还没有实现,则主动回滚事务。这个工夫的记录形式是在事务开启当前到 sql 语句执行之前。
  • read-only:指定事务是否为只读事务,默认值为 false;为了疏忽那些不须要事务的办法,比方读取数据,能够设置 read-only 为 true。对增加,批改,删除业务 read-only 的值应该为 false。
  • rollback-for:用于指定可能触发事务回滚的异样类型,如果有多个异样类型须要指定,各类型之间能够通过逗号分隔。
  • no-rollback- for: 抛出 no-rollback-for 指定的异样类型,不回滚事务。
  • isolation 事务的隔离级别,默认值采纳 DEFAULT。当多个事务并发执行时,可能会呈现脏读,不可反复读,幻读等景象时,但如果不心愿呈现这些景象可思考批改事务的隔离级别(但隔离级别越高并发就会越小,性能就会越差)

Spring 中事务管制过程剖析, 如图 -11 所示:

Spring 事务管理是基于接口代理(JDK)或动静字节码(CGLIB)技术,而后通过 AOP 施行事务加强的。当咱们执行增加了事务个性的指标形式时,零碎会通过指标对象的代理对象调用 DataSourceTransactionManager 对象,在事务开始的时,执行 doBegin 办法,事务完结时执行 doCommit 或 doRollback 办法。

  1. Spring 中事务流传个性

事务流传 (Propagation) 个性指 ” 不同业务 (service) 对象 ” 中的事务办法之间互相调用时, 事务的传播方式, 如图 -12 所示:

其中, 罕用事务传播方式如下:

  • @Transactional(propagation=Propagation.REQUIRED)。

如果没有事务创立新事务, 如果以后有事务参加以后事务, Spring 默认的事务流传行为是 PROPAGATION_REQUIRED,它适宜于绝大多数的状况。假如 ServiveX#methodX() 都工作在事务环境下(即都被 Spring 事务加强了),假如程序中存在如下的调用链:
Service1#method1()->Service2#method2()->Service3#method3(),那么这 3 个服务类的 3 个办法通过 Spring 的事务流传机制都工作在同一个事务中。如图 -13 所示:

代码示例如下:
@Transactional(propagation = Propagation.REQUIRED)
@Override
public List<Node> findZtreeMenuNodes() {
return sysMenuDao.findZtreeMenuNodes();
}
当有一个业务对象调用如上办法时,此办法始终工作在一个曾经存在的事务办法,或者是由调用者创立的一个事务办法中。

  • @Transactional(propagation=Propagation.REQUIRES_NEW)。

必须是新事务, 如果有以后事务, 挂起以后事务并且开启新事务,如图 -14 所示:

代码示例如下:
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Override
public void saveObject(SysLog entity) {
sysLogDao.insertObject(entity);
}
当有一个业务对象调用如上业务办法时,此办法会始终运行在一个新的事务中。

  1. Spring 中事务管理小结

Spring 申明式事务是 Spring 最外围,最罕用的性能。因为 Spring 通过 IOC 和 AOP 的性能十分通明地实现了申明式事务的性能,对于个别的开发者基本上毋庸理解 Spring 申明式事务的外部细节,仅须要懂得如何配置就能够了。但对于中高端开发者还须要理解其外部机制。

  1. Spring AOP 异步操作实现

  2. 异步场景剖析

在开发零碎的过程中,通常会思考到零碎的性能问题,晋升零碎性能的一个重要思维就是“串行”改“并行”。说起“并行”天然离不开“异步”,明天咱们就来聊聊如何应用 Spring 的 @Async 的异步注解。

  1. Spring 业务的异步实现

  2. 启动异步配置

在基于注解形式的配置中,借助 @EnableAsync 注解进行异步启动申明,Spring Boot 版的我的项目中,将 @EnableAsync 注解利用到启动类上,代码示例如下:
@EnableAsync //spring 容器启动时会创立线程池
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

  1. Spring 中 @Async 注解利用

在须要异步执行的业务办法上,应用 @Async 办法进行异步申明。
@Async
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Override
public void saveObject(SysLog entity) {
System.out.println(“SysLogServiceImpl.save:”+
Thread.currentThread().getName());
sysLogDao.insertObject(entity);
//try{Thread.sleep(5000);}catch(Exception e) {}
}
如果须要获取业务层异步办法的执行后果,可参考如下代码设计进行实现:
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Async
@Override
public Future<Integer> saveObject(SysLog entity) {
System.out.println(“SysLogServiceImpl.save:”+
Thread.currentThread().getName());
int rows=sysLogDao.insertObject(entity);
//try{Thread.sleep(5000);}catch(Exception e) {}
return new AsyncResult<Integer>(rows);
}
其中,AsyncResult 对象能够对异步办法的执行后果进行封装,如果外界须要异步办法后果时,能够通过 Future 对象的 get 办法获取后果。
当咱们须要本人对 spring 框架提供的连接池进行一些繁难配置,能够参考如下代码:
spring:
task:
execution:
pool:
queue-capacity: 128
core-size: 5
max-size: 128
keep-alive: 60000
thread-name-prefix: db-service-task-
对于 spring 框架中线程池配置参数的涵义,能够参考 ThreadPoolExecutor 对象中的解释。
阐明:对于 @Async 注解默认会基于 ThreadPoolTaskExecutor 对象获取工作线程,而后调用由 @Async 形容的办法,让办法运行于一个工作线程,以实现异步操作。然而如果零碎中的默认回绝解决策略, 工作执行过程的异样解决不能满足咱们本身业务需要的话, 我能够对异步线程池进行自定义.(SpringBoot 中默认的异步配置能够参考主动配置对象 TaskExecutionAutoConfiguration).

  1. Spring 自定义异步池的实现(拓展)

为了让 Spring 中的异步池更好的服务于咱们的业务, 同时也尽量避免 OOM,能够自定义线程池优化设计如下:要害代码如下:
package com.cy.pj.common.config
@Slf4j
@Setter
@Configuration
@ConfigurationProperties(“async-thread-pool”)
public class SpringAsyncConfig implements AsyncConfigurer{
/* 外围线程数 /
private int corePoolSize=20;
/* 最大线程数/
private int maximumPoolSize=1000;
/* 线程闲暇工夫/
private int keepAliveTime=30;
/* 阻塞队列容量/
private int queueCapacity=200;
/* 构建线程工厂/
private ThreadFactory threadFactory=new ThreadFactory() {
//CAS 算法
private AtomicInteger at=new AtomicInteger(1000);
@Override
public Thread newThread(Runnable r) {
return new Thread(r,
“db-async-thread-“+at.getAndIncrement());
}
};
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(corePoolSize);
executor.setMaxPoolSize(maximumPoolSize);
executor.setKeepAliveSeconds(keepAliveTime);
executor.setQueueCapacity(queueCapacity);
executor.setRejectedExecutionHandler((Runnable r,
ThreadPoolExecutor exe) -> {
log.warn(“ 当前任务线程池队列已满.”);
});
executor.initialize();
return executor;
}
@Override
public AsyncUncaughtExceptionHandler
getAsyncUncaughtExceptionHandler() {
return new AsyncUncaughtExceptionHandler() {
@Override
public void handleUncaughtException(Throwable ex ,
Method method , Object… params) {
log.error(“ 线程池执行工作产生未知异样.”, ex);
}
};
}}
其中:@ConfigurationProperties(“async-thread-pool”)的含意是读取 application.yml 配置文件中以 ”async-thread-pool” 名为前缀的配置信息, 并通过所形容类的 set 办法赋值给对应的属性, 在 application.yml 中连接器池的要害配置如下:
async-thread-pool:
corePoolSize: 20
maxPoolSize: 1000
keepAliveSeconds: 30
queueCapacity: 1000
后续在业务类中, 如果咱们应用 @Async 注解形容业务办法,默认会应用 ThreadPoolTaskExecutor 池对象中的线程执行异步工作。

  1. Spring AOP 中 Cache 操作实现(拓展)

  2. 缓存场景剖析

在业务办法中咱们可能调用数据层办法获取数据库中数据, 如果拜访数据的频率比拟高, 为了进步的查问效率, 升高数据库的拜访压力, 能够在业务层对数据进行缓存.

  1. Spring 中业务缓存利用实现

  2. 启动缓存配置

在我的项目 (SpringBoot 我的项目) 的启动类上增加 @EnableCaching 注解, 以启动缓存配置。要害代码如下:
package com.cy;
/**

  • 异步的主动配置失效).

    • @EnableCaching 注解示意启动缓存配置

*/
@EnableCaching
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

  1. 业务办法上利用缓存配置

在须要进行缓存的业务办法上通过 @Cacheable 注解对办法进行相干形容. 示意办法的
返回值要存储到 Cache 中, 如果在更新操作时须要将 cache 中的数据移除, 能够在更新办法上应用 @CacheEvict 注解对办法进行形容。例如:
第一步: 在相干模块查问相干业务办法中, 应用缓存,要害代码如下:
@Cacheable(value = “menuCache”)
@Transactional(readOnly = true)
public List<Map<String,Object>> findObjects() {
….
}
其中,value 属性的值示意要应用的缓存对象, 名字本人指定,其中底层为一个 map 对象,当向 cache 中增加数据时,key 默认为办法理论参数的组合。
第二步:在相干模块更新时, 革除指定缓存数据, 要害代码如下:
@CacheEvict(value=”menuCache”,allEntries=true)
@Override
public int saveObject(SysDept entity) {…}
其中,allEntries 示意革除所有。
阐明:spring 中的缓存利用原理,如图 -15 所示:

  1. Spring 中自定义缓存的实现(拓展)

在 Spring 中默认 cache 底层实现是一个 Map 对象, 假如此 map 对象不能满足咱们理论须要, 在理论我的项目中咱们能够将数据存储到第三方缓存零碎中.

  1. Spring AOP 原生形式实现(拓展)

  2. 概述

Spring 整合 AspectJ 框架实现 AOP 只是 Spring 框架中 AOP 的一种实现形式,此形式绝对比较简单,实现不便。但此形式底层还是要转换为 Spring 原生 AOP 的实现,Spring AOP 原生形式实现的外围有三大部分形成,别离是:

  • JDK 代理。
  • CGLIB 代理。
  • org.aopalliance 包下的拦挡体系。
  1. 案例架构剖析

本大节以 Spring 中一种原生 AOP 架构的根本实现为例进行原理剖析和阐明,其繁难架构如图 -16 所示:

其中 DefaultAdvisorAutoProxyCreator 这个类性能更为弱小,这个类的微妙之处是他实现 BeanPostProcessor 接口, 当 ApplicationContext 读取所有的 Bean 配置信息后,这个类将扫描上下文,寻找所有的 Advisor 对象(一个 Advisor 由切入点和告诉组成),将这些 Advisor 利用到所有合乎切入点的 Bean 中。

  1. 案例业务实现

  2. 业务形容

创立 SpringBoot 我的项目,并基于 Spring 原生 AOP 的实现为特定业务对象增加繁难日志实现。

  1. 外围业务接口定义及实现

定义 RequiredLog 注解,用于形容指标业务对象
package com.cy.spring.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RequiredLog {
}
定义搜寻业务接口,用于定义搜寻业务标准
package com.cy.spring.aop;
public interface SearchService {
Object search(String key);
}
定义搜寻业务接口实现,并应用 requiredLog 注解形容
package com.cy.spring.aop;
import org.springframework.stereotype.Service;
import com.cy.spring.annotation.RequiredLog;
@Service
public class DefaultSearchService implements SearchService {
@RequiredLog
@Override
public Object search(String key) {
System.out.println(“search by “+key);
return null;
}
}

  1. 日志 Advice 对象定义

定义 LogAdvice 对象,基于此对象为指标业务对象做日志加强。
package com.cy.spring.advisor;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class LogAdvice implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation)
throws Throwable {
System.out.println(“start:”+System.currentTimeMillis());
Object result=invocation.proceed();
System.out.println(“after:”+System.currentTimeMillis());
return result;
}
}
其中,MethodInterceptor 对象继承 Advice 对象,基于此对象办法能够对指标办法进行拦挡。

  1. 日志 Advisor 对象定义及实现

创立日志 Advisor 对象,在对象外部定义要切入扩大性能的点以及要利用的告诉 (Advice) 对象。
package com.cy.spring.advisor;
import java.lang.reflect.Method;
import org.springframework.stereotype.Component;
import com.cy.spring.annotation.RequiredLog;
@Component
public class LogAdvisor extends StaticMethodMatcherPointcutAdvisor {
private static final long serialVersionUID = 7022316764822635205L;
public LogMethodMatcher() {
// 在特定切入点上要执行的告诉
setAdvice(new LogAdvice());
}
//Pointcut
// 办法返回值为 true 时, 则能够为指标办法对象创立代理对象
@Override
public boolean matches(Method method,Class<?> targetClass) {
try {
Method targetMethod=
targetClass.getMethod(method.getName(),
method.getParameterTypes());
return targetMethod.isAnnotationPresent(RequiredLog.class);
}catch(Exception e) {
return false;
}
}
}
其中,StaticMethodMatcherPointcutAdvisor 类为 Spring 框架中定义的一种 Advisor,咱们本人写的 Advisor 能够间接继承此类进行资源整合。

  1. 日志业务单元测试实现

基于 Spring boot 我的项目进行单元测试:
package com.cy;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import com.cy.spring.aop.SearchService;
@SpringBootTest
public class CgbSbootAop01ApplicationTests {
@Autowired
private SearchService searchService;
@Test
public void testSearch() {
//System.out.println(searchService);
searchService.search(“tedu”);
}
}
阐明:在 spring 框架中,很多性能都是原生 AOP 进行了性能的扩大和实现。

  1. 总结

  2. 重难点剖析

  • AOP 是什么,解决了什么问题,实现原理,利用场景。
  • AOP 编程根本步骤及实现过程(以基于 AspectJ 框架实现为例)。
  • AOP 编程中的外围对象及利用关系。
  • AOP 思维在 Spring 中的实现原理剖析。
  • AOP 编程中基于注解形式的配置实现。(@Aspect,@PointCut,@Around,…)
  • AOP 编程中基于注解形式的事务管制。(@Transactional)
  • AOP 编程中异步操作的实现?(@EnableAsync,@Async)
  • AOP 编程中的缓存利用?(@EnableCaching,@Cacheable,@CacheEvict)
  1. FAQ 剖析

  • 什么是 OCP 准则(开闭准则)?
  • 什么是 DIP 准则 (依赖倒置)?
  • 什么是繁多职责准则(SRP)?
  • Spring 中 AOP 的有哪些配置形式?(XML, 注解)
  • Spring 中 AOP 的告诉有哪些根本类型?(5 种)
  • Spring 中 AOP 是如何为 Bean 对象创立代理对象的?(JDK,CGLIB)
  • Spring 中 AOP 切面的执行程序如何指定?(@Order)
  • Spring 单体架构我的项目中事务的管制要通过 Connection 对象实现,?
  • Spring 如何保障一个线程一个 Connection 对象? 借助 ThreadLocal 实现.?
  • 多个事务并发执行时可能会呈现什么问题?(脏读,不可反复读,幻影读)
  • 如何了解数据库中的的乐观锁和乐观锁?
  • 你理解事务的隔离级别吗?晓得具体的利用场景吗?
  • ……
  • ….
  1. Bug 剖析

  • 切入点利用谬误, 如图 -17 所示:


问题剖析: 查看切入点的引入是否丢掉了 ”()”.

正文完
 0