AOP 概述
AOP(Aspect Orient Programming)是一种设计思维,是软件设计畛域中的面向切面编程,它是面向对象编程(OOP)的一种补充和欠缺。它以通过预编译形式和运行期动静代理形式,实现在不批改源代码的状况下给程序动静对立增加额定性能的一种技术。如图-1所示:
AOP 利用场景剖析?
理论我的项目中通常会将零碎分为两大部分,一部分是外围业务,一部分是非核业务。在编程实现时咱们首先要实现的是外围业务的实现,非核心业务个别是通过特定形式切入到零碎中,这种特定形式个别就是借助AOP进行实现。
AOP就是要基于OCP(开闭准则),在不扭转原有系统核心业务代码的根底上动静增加一些扩大性能并能够"管制"对象的执行。例如AOP利用于我的项目中的日志解决,事务处理,权限解决,缓存解决等等。如图-2所示:
AOP 利用原理剖析:
Spring AOP底层基于代理机制实现性能扩大:
- 如果指标对象(被代理对象)实现接口,则底层能够采纳JDK动静代理机制为指标对象创立代理对象(指标类和代理类会实现独特接口)。
- 如果指标对象(被代理对象)没有实现接口,则底层能够采纳CGLIB代理机制为指标对象创立代理对象(默认创立的代理类会继承指标对象类型)。
Spring AOP 原理剖析,如图-3所示:
阐明:Spring boot2.x 中AOP当初默认应用的CGLIB代理,如果须要应用JDK动静代理能够在配置文件(applicatiion.properties)中进行如下配置:
spring.aop.proxy-target-class=false
AOP 相干术语剖析
- 切面(aspect): 横切面对象,个别为一个具体类对象(能够借助@Aspect申明)。
- 告诉(Advice):在切面的某个特定连接点上执行的动作(扩大性能),例如around,before,after等。
- 连接点(joinpoint):程序执行过程中某个特定的点,个别指被拦挡到的的办法。
- 切入点(pointcut):对多个连接点(Joinpoint)一种定义,个别能够了解为多个连接点的汇合。
连接点与切入点定义如图-4所示:
阐明:咱们能够简略的将机场的一个安检口了解为连接点,多个安检口为切入点,安全检查过程看成是告诉。总之,概念很艰涩难懂,多做例子,做完就会清晰。先能够按文言去了解。
Spring AOP疾速实际
业务形容
基于我的项目中的外围业务,增加简略的日志操作,借助SLF4J日志API输入指标办法的执行时长。(前提,不能批改指标办法代码-遵循OCP准则)
基于我的项目中的外围业务,增加简略的日志操作,借助SLF4J日志API输入指标办法的执行时长。(前提,不能批改指标办法代码-遵循OCP准则)
我的项目创立及配置
创立maven我的项目或在已有我的项目根底上增加AOP启动依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
阐明:基于此依赖spring能够整合AspectJ框架疾速实现AOP的根本实现。Aspect是一个面向切面的框架,他定义了AOP的一些语法,有一个专门的字节码生成器来生成恪守java标准的class文件。
扩大业务剖析及实现
创立日志切面类对象
将此日志切面类作为外围业务加强(一个横切面对象)类,用于输入业务执行时长,其要害代码如下:
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注解形容的办法参数。
业务切面测试实现
启动我的项目测试或者进行单元测试,其中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配置文件中的配置.
利用总结剖析
在业务利用,AOP相干对象剖析,如图-5所示:
扩大业务织入加强剖析
基于JDK代理形式实现
如果指标对象有实现接口,则能够基于JDK为指标对象创立代理对象,而后为指标对象进行性能扩大,如图-6所示:
基于CGLIB代理形式实现
如果指标对象没有实现接口(当然实现了接口也是能够的),能够基于CGLIB代理形式为指标对象织入性能扩大,如图-7所示:
阐明:指标对象实现了接口也能够基于CGLIB为指标对象创立代理对象。
Spring AOP编程加强
切面告诉利用加强
告诉类型
在基于Spring AOP编程的过程中,基于AspectJ框架规范,spring中定义了五种类型的告诉(告诉形容的是一种扩大业务),它们别离是:
- @Before。
- @AfterReturning。
- @AfterThrowing。
- @After。
- @Around.重点把握(优先级最高)
阐明:在切面类中应用什么告诉,由业务决定,并不是说,在切面中要把所有告诉都写上。
告诉执行程序
如果这些告诉全副写到一个切面对象中,其执行程序及过程,如图-8所示:
阐明:理论我的项目中可能不会在切面中定义所有的告诉,具体定义哪些告诉要联合业务进行实现。
告诉实际过程剖析
代码实际剖析如下:
@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属性的值,须要与它形容的办法的异样参数名雷同。
切入点表达式加强
Spring中通过切入点表达式定义具体切入点,其罕用AOP切入点表达式定义及阐明:
表-1 Spring AOP 中切入点表达式阐明
批示符
作用
bean
用于匹配指定bean对象的所有办法
within
用于匹配指定包下所有类内的所有办法
execution
用于按指定语法规定匹配到具体方法
@annotation
用于匹配指定注解润饰的办法
bean表达式(重点)
bean表达式个别利用于类级别,实现粗粒度的切入点定义,案例剖析:
- bean("userServiceImpl")指定一个userServiceImpl类中所有办法。
- bean("*ServiceImpl")指定所有后缀为ServiceImpl的类中所有办法。
阐明:bean表达式外部的对象是由spring容器治理的一个bean对象,表达式外部的名字应该是spring容器中某个bean的name。
within表达式(理解)
within表达式利用于类级别,实现粗粒度的切入点表达式定义,案例剖析:
- within("aop.service.UserServiceImpl")指定以后包中这个类外部的所有办法。
- within("aop.service.*") 指定当前目录下的所有类的所有办法。
- within("aop.service..*") 指定当前目录以及子目录中类的所有办法。
within表达式利用场景剖析:
1)对所有业务bean都要进行性能加强,然而bean名字又没有规定。
2)按业务模块(不同包下的业务)对bean对象进行业务性能加强。
execution表达式(理解)
execution表达式利用于办法级别,实现细粒度的切入点表达式定义,案例剖析:
语法:execution(返回值类型 包名.类名.办法名(参数列表))。
- execution(void aop.service.UserServiceImpl.addUser())匹配addUser办法。
- execution(void aop.service.PersonServiceImpl.addUser(String)) 办法参数必须为String的addUser办法。
- execution( aop.service...*(..)) 万能配置。
@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;
}
切面优先级设置实现
切面的优先级须要借助@Order注解进行形容,数字越小优先级越高,默认优先级比拟低。例如:
定义日志切面并指定优先级。
@Order(1)
@Aspect
@Component
public class SysLogAspect {
…
}
定义缓存切面并指定优先级:
@Order(2)
@Aspect
@Component
public class SysCacheAspect {
…
}
阐明:当多个切面作用于同一个指标对象办法时,这些切面会构建成一个切面链,相似过滤器链、拦截器链,其执行剖析如图-9所示:
要害对象与术语总结
Spring 基于AspectJ框架实现AOP设计的要害对象概览,如图-10所示:
用户行为日志记录实现(实际)
本大节作为课后作业,以AOP形式记录我的项目中的用户行为信息,并将其存储到数据库。参考日志模块的文档。
Spring AOP事务处理
Spring 中事务简介
事务定义
事务(Transaction)是一个业务,是一个不可分割的逻辑工作单元,基于事务能够更好的保障业务的正确性。
事务个性
事务具备ACID个性,别离是:
- 原子性(Atomicity):一个事务中的多个操作要么都胜利要么都失败。
- 一致性(Consistency): 例如存钱操作,存之前和存之后的总钱数应该是统一的。
- 隔离性(Isolation):事务与事务应该是互相隔离的。
- 持久性(Durability):事务一旦提交,数据要长久保留。
阐明:目前市场上在事务一致性方面,通常会做肯定的优化,比方说只有最终统一就能够了,这样的事务咱们通常会称之为柔性事务(只有最终统一就能够了).
Spring 中事务管理
Spring 中事务形式概述
Spring框架中提供了一种申明式事务的解决形式,此形式基于AOP代理,能够将具体业务逻辑与事务处理进行解耦。也就是让咱们的业务代码逻辑不受净化或大量净化,就能够实现事务管制。
在SpringBoot我的项目中,其外部提供了事务的主动配置,当咱们在我的项目中增加了指定依赖spring-boot-starter-jdbc时,框架会主动为咱们的我的项目注入事务管理器对象,最罕用的为DataSourceTransactionManager对象。
Spring 中事务管理实现
本大节重点解说理论我的项目中最罕用的注解形式的事务管理,以注解@Transactional配置形式为例,进行实际剖析。
基于@Transactional 注解进行申明式事务管理的实现步骤分为两步:
- 启用申明式事务管理,在我的项目启动类上增加@EnableTransactionManagement,新版本中也可不增加(例如新版Spring Boot我的项目)。
- 将@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办法。
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);
}
当有一个业务对象调用如上业务办法时,此办法会始终运行在一个新的事务中。
Spring 中事务管理小结
Spring 申明式事务是 Spring 最外围,最罕用的性能。因为 Spring 通过 IOC 和 AOP的性能十分通明地实现了申明式事务的性能,对于个别的开发者基本上毋庸理解 Spring申明式事务的外部细节,仅须要懂得如何配置就能够了。但对于中高端开发者还须要理解其外部机制。
Spring AOP 异步操作实现
异步场景剖析
在开发零碎的过程中,通常会思考到零碎的性能问题,晋升零碎性能的一个重要思维就是“串行”改“并行”。说起“并行”天然离不开“异步”,明天咱们就来聊聊如何应用Spring的@Async的异步注解。
Spring 业务的异步实现
启动异步配置
在基于注解形式的配置中,借助@EnableAsync注解进行异步启动申明,Spring Boot版的我的项目中,将@EnableAsync注解利用到启动类上,代码示例如下:
@EnableAsync //spring容器启动时会创立线程池
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
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).
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池对象中的线程执行异步工作。
Spring AOP中Cache操作实现(拓展)
缓存场景剖析
在业务办法中咱们可能调用数据层办法获取数据库中数据,如果拜访数据的频率比拟高,为了进步的查问效率,升高数据库的拜访压力,能够在业务层对数据进行缓存.
Spring 中业务缓存利用实现
启动缓存配置
在我的项目(SpringBoot我的项目)的启动类上增加@EnableCaching注解,以启动缓存配置。要害代码如下:
package com.cy;
/**
异步的主动配置失效).
- @EnableCaching 注解示意启动缓存配置
*/
@EnableCaching
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
业务办法上利用缓存配置
在须要进行缓存的业务办法上通过@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所示:
Spring中自定义缓存的实现(拓展)
在Spring中默认cache底层实现是一个Map对象,假如此map对象不能满足咱们理论须要,在理论我的项目中咱们能够将数据存储到第三方缓存零碎中.
Spring AOP原生形式实现(拓展)
概述
Spring 整合AspectJ框架实现AOP只是Spring框架中AOP的一种实现形式,此形式绝对比较简单,实现不便。但此形式底层还是要转换为Spring原生AOP的实现,Spring AOP原生形式实现的外围有三大部分形成,别离是:
- JDK代理。
- CGLIB代理。
- org.aopalliance包下的拦挡体系。
案例架构剖析
本大节以Spring中一种原生AOP架构的根本实现为例进行原理剖析和阐明,其繁难架构如图-16所示:
其中DefaultAdvisorAutoProxyCreator这个类性能更为弱小,这个类的微妙之处是他实现BeanPostProcessor接口,当ApplicationContext读取所有的Bean配置信息后,这个类将扫描上下文,寻找所有的Advisor对象(一个Advisor由切入点和告诉组成),将这些Advisor利用到所有合乎切入点的Bean中。
案例业务实现
业务形容
创立SpringBoot我的项目,并基于Spring原生AOP的实现为特定业务对象增加繁难日志实现。
外围业务接口定义及实现
定义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;
}
}
日志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对象,基于此对象办法能够对指标办法进行拦挡。
日志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能够间接继承此类进行资源整合。
日志业务单元测试实现
基于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进行了性能的扩大和实现。
总结
重难点剖析
- AOP 是什么,解决了什么问题,实现原理,利用场景。
- AOP 编程根本步骤及实现过程(以基于AspectJ框架实现为例)。
- AOP 编程中的外围对象及利用关系。
- AOP 思维在Spring中的实现原理剖析。
- AOP 编程中基于注解形式的配置实现。(@Aspect,@PointCut,@Around,...)
- AOP 编程中基于注解形式的事务管制。(@Transactional)
- AOP 编程中异步操作的实现?(@EnableAsync,@Async)
- AOP 编程中的缓存利用?(@EnableCaching,@Cacheable,@CacheEvict)
FAQ剖析
- 什么是OCP准则(开闭准则)?
- 什么是DIP准则 (依赖倒置)?
- 什么是繁多职责准则(SRP)?
- Spring 中AOP的有哪些配置形式?(XML,注解)
- Spring 中AOP 的告诉有哪些根本类型?(5种)
- Spring 中AOP是如何为Bean对象创立代理对象的?(JDK,CGLIB)
- Spring 中AOP切面的执行程序如何指定?(@Order)
- Spring 单体架构我的项目中事务的管制要通过Connection对象实现,?
- Spring 如何保障一个线程一个Connection对象?借助ThreadLocal实现.?
- 多个事务并发执行时可能会呈现什么问题?(脏读,不可反复读,幻影读)
- 如何了解数据库中的的乐观锁和乐观锁?
- 你理解事务的隔离级别吗?晓得具体的利用场景吗?
- ……
- ….
Bug剖析
- 切入点利用谬误,如图-17所示:
问题剖析:查看切入点的引入是否丢掉了"()".