本文共 9079 字,大约阅读时间需要 30 分钟。
说明:在切面类中使用什么通知,由业务决定,并不是说,在切面中要把所有通知都写上。
假如这些通知全部写到一个切面对象中,其执行顺序及过程,如图
@Component@Aspectpublic 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通知只有在出现异常时才会执行,所以当做一些异常监控时可在此方法中进行代码实现。
Spring中通过切入点表达式定义具体切入点,其常用AOP切入点表达式定义及说明:
bean表达式一般应用于类级别,实现粗粒度的切入点定义,案例分析:
说明:bean表达式内部的对象是由spring容器管理的一个bean对象,表达式内部的名字应该是spring容器中某个bean的name。
within表达式应用于类级别,实现粗粒度的切入点表达式定义,案例分析:
within表达式应用场景分析:
1)对所有业务bean都要进行功能增强,但是bean名字又没有规则。 2)按业务模块(不同包下的业务)对bean对象进行业务功能增强。execution表达式应用于方法级别,实现细粒度的切入点表达式定义,案例分析:
语法:execution(返回值类型 包名.类名.方法名(参数列表))。
@annotaion表达式应用于方法级别,实现细粒度的切入点表达式定义,案例分析
其中:RequiredLog为我们自己定义的注解,当我们使用@RequiredLog注解修饰业务层方法时,系统底层会在执行此方法时进行日扩展操作。
切面的优先级需要借助@Order注解进行描述,数字越小优先级越高,默认优先级比较低。例如:
事务定义
事务(Transaction)是一个业务,是一个不可分割的逻辑工作单元,基于事务可以更好的保证业务的正确性。 事务特性 事务具备ACID特性,分别是:说明:目前市场上在事务一致性方面,通常会做一定的优化,比方说只要最终一致就可以了,这样的事务我们通常会称之为柔性事务(只要最终一致就可以了).
Spring框架中提供了一种声明式事务的处理方式,此方式基于AOP代理,可以将具体业务逻辑与事务处理进行解耦。也就是让我们的业务代码逻辑不受污染或少量污染,就可以实现事务控制。
在SpringBoot项目中,其内部提供了事务的自动配置,当我们在项目中添加了指定依赖spring-boot-starter-jdbc时,框架会自动为我们的项目注入事务管理器对象,最常用的为DataSourceTransactionManager对象。
实际项目中最常用的注解方式的事务管理,以注解**@Transactional**配置方式为例,进行实践分析。
基于@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 PageObjectfindPageObjects(String username, Integer pageCurrent) { … }}
其中,代码中的@Transactional注解用于描述类或方法,告诉spring框架我们要在此类的方法执行时进行事务控制,其具体说明如下:。
@Transactional注解,则方法上的事务特性优先级比较高。
事务传播(Propagation)特性指"不同业务(service)对象"中的事务方法之间相互调用时,事务的传播方式,如图-所示
如果没有事务创建新事务, 如果当前有事务参与当前事务, Spring 默认的事务传播行为是PROPAGATION_REQUIRED,它适合于绝大多数的情况。假设 ServiveX#methodX() 都工作在事务环境下(即都被 Spring 事务增强了),假设程序中存在如下的调用链:
Service1#method1()->Service2#method2()->Service3#method3(),那么这 3 个服务类的 3 个方法通过 Spring 的事务传播机制都工作在同一个事务中。如图-所示:
Spring 声明式事务是 Spring 最核心,最常用的功能。由于 Spring 通过 IOC 和 AOP的功能非常透明地实现了声明式事务的功能,对于一般的开发者基本上无须了解 Spring声明式事务的内部细节,仅需要懂得如何配置就可以了。但对于中高端开发者还需要了解其内部机制。
在开发系统的过程中,通常会考虑到系统的性能问题,提升系统性能的一个重要思想就是“串行”改“并行”。说起“并行”自然离不开“异步”,今天我们就来聊聊如何使用Spring的**@Async**的异步注解。
@EnableAsync //spring容器启动时会创建线程 @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); }}
2.Spring中@Async注解应用
在需要异步执行的业务方法上,使用**@Async**方法进行异步声明。当我们需要自己对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中的异步池更好的服务于我们的业务,同时也尽量避免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池对象中的线程执行异步任务。
在业务方法中我们可能调用数据层方法获取数据库中数据,假如访问数据的频率比较高,为了提高的查询效率,降低数据库的访问压力,可以在业务层对数据进行缓存.
在项目(SpringBoot项目)的启动类上添加@EnableCaching注解,以启动缓存配置。关键代码如下:
业务方法上应用缓存配置
在需要进行缓存的业务方法上通过@Cacheable注解对方法进行相关描述.表示方法的返回值要存储到Cache中,假如在更新操作时需要将cache中的数据移除,可以在更新方法上使用@CacheEvict注解对方法进行描述。例如:第一步:在相关模块查询相关业务方法中,使用缓存,关键代码如下:
第二步:在相关模块更新时,清除指定缓存数据,关键代码如下:
spring中的缓存应用原理
在Spring中默认cache底层实现是一个Map对象,假如此map对象不能满足我们实际需要,在实际项目中我们可以将数据存储到第三方缓存系统中.
转载地址:http://ljmo.baihongyu.com/