关于aop:SpringBoot强化篇八-Spring-AOP

Spring AOP简介

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

AOP与OOP字面意思相近,但其实两者齐全是面向不同畛域的设计思维。理论我的项目中咱们通常将面向对象了解为一个动态过程(例如一个零碎有多少个模块,一个模块有哪些对象,对象有哪些属性),面向切面的运行期代理形式,了解为一个动静过程,能够在对象运行时动静织入一些扩大性能或管制对象执行。

AOP 利用场景剖析?

理论我的项目中通常会将零碎分为两大部分,一部分是外围业务,一部分是非核业务。在编程实现时咱们首先要实现的是外围业务的实现,非核心业务个别是通过特定形式切入到零碎中,这种特定形式个别就是借助AOP进行实现。

AOP就是要基于OCP(开闭准则),在不扭转原有系统核心业务代码的根底上动静增加一些扩大性能并能够”管制”对象的执行。例如AOP利用于我的项目中的日志解决,事务处理,权限解决,缓存解决等等。

Spring AOP 利用原理剖析

Spring AOP底层基于代理机制(动静形式)实现性能扩大:

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


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

#cglib aop proxy
#spring.aop.proxy-target-class=true
#jdk aop proxy
spring.aop.proxy-target-class=false

Spring 中AOP 相干术语剖析

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

Spring AOP疾速实际

我的项目创立及配置

第一步创立maven我的项目或在已有我的项目根底上增加AOP启动依赖:

<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

第二步定义业务层接口

package com.cy.pj.common.service;
public interface MailService {
    boolean sendMail(String msg);
}

第三步定义业务层实现类

@Service
public class MailServiceImpl implements MailService{
      @Override
     public boolean sendMail(String msg) {//ocp(开闭准则-->对扩大凋谢,对批改敞开)
         long t1=System.currentTimeMillis();
         System.out.println("send->"+msg);
         long t2=System.currentTimeMillis();
         System.out.println("send time:"+(t2-t1));
         return true; 
     }
}

咱们本人计算了执行工夫,然而违反了ocp准则,所以须要无侵入式扩大这个记录执行工夫的性能。咱们在这个类中增加外部类来实现两种形式的扩大。

package com.cy.pj.common.service;
import org.springframework.stereotype.Service;
@Service
public class MailServiceImpl implements MailService{
        @Override
         public boolean sendMail(String msg) {//ocp(开闭准则-->对扩大凋谢,对批改敞开)
            //long t1=System.currentTimeMillis();
             System.out.println("send->"+msg);
            //long t2=System.currentTimeMillis();
            //System.out.println("send time:"+(t2-t1));
             return true;
     }
}
//上面的两种设计理解?(基于原生形式实现性能扩大)
//本人入手写子类重写父类办法进行性能扩大
class TimeMailServiceImpl extends MailServiceImpl{//这种写法的原型就是CGLIB代理机制的形式(继承)
     @Override
     public boolean sendMail(String msg) {
         long t1=System.currentTimeMillis();
         boolean flag=super.sendMail(msg);
         long t2=System.currentTimeMillis();
         System.out.println("send time:"+(t2-t1));
         return flag;
     }
}
//本人写兄弟类对指标对象(兄弟类)进行性能扩大,这种形式又叫组合
class TimeMailServiceImpl2 implements MailService{//这种写法的原型就是JDK代理机制的形式(实现)
     private MailService mailService;
     public TimeMailServiceImpl2(MailService mailService){
            this.mailService=mailService;
     }
     @Override
     public boolean sendMail(String msg) {
         long t1=System.currentTimeMillis();
         boolean flag=mailService.sendMail(msg);
         long t2=System.currentTimeMillis();
         System.out.println("send time:"+(t2-t1));
         return flag;
     }
}

下来通过aop形式实现业务

package com.cy.pj.common.service.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect//通知spring我是一个切面(封装了扩大逻辑对象),这样的对象中要蕴含两局部内容(1.切入点,2.扩大逻辑-advice)
@Component//示意在spring中做一个注册
public class SysLogAspect {
     //定义切入点
     //bean表达式为spring中的一种粗粒度切入点表达式(不能准确到具体方法)
     //这里的mailServiceImpl名字为spring容器中一个bean对象的名字
     @Pointcut("bean(mailServiceImpl)")//多个bean的定义模式(bean(*ServiceImpl))
     public void doLogPointCut(){}//这个办法仅仅是承载切入点注解的一个载体,办法体内不须要写任何内容
     
     /*依照Aspect标准定义一个@Around盘绕告诉*/
     //@Around("bean(mailServiceImpl)")//间接在advice注解外部定义切入点表达式
     //对于@Around注解形容的办法器标准要求
     //1)返回值类型为Object(用于封装指标办法的执行后果)
     //2)参数类型为ProceedingJoinPoint(用于封装执行的指标办法信息)
     //3)抛出的异样Throwable(用于封装执行指标办法时抛出的异样)
     //4)在@Around注解形容的办法外部,能够手动调用指标办法
     @Around("doLogPointCut()")//也能够在advice注解外部通过办法援用引入切入点表达式
     public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable{
         long t1=System.currentTimeMillis();
         Object result = joinPoint.proceed();//示意调用指标办法
         long t2=System.currentTimeMillis();
         System.out.println("send time:"+(t2-t1));
         return result;
     }
}

编写测试类来测试实现

package com.cy.pj.common.service;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class MailServiceTests {
     @Autowired
     private MailService mailService;
     @Test//面向切面的测试类
     void testSendMail03(){
            mailService.sendMail("hello mailService");
     }
     @Test//本人入手写的子类测试
     void testSendMail01(){
    //new TimeMailServiceImpl().sendMail("hello aop");
     }
     @Test//本人入手写的兄弟类测试
     void testSendMail02(){
    //new TimeMailServiceImpl2(new MailServiceImpl()).sendMail("hello CGB2007");
     }
}

整个aop面向切面编程的过程图示

debug追踪cglib代理的注入对象

debug追踪jdk代理的注入对象

利用总结剖析

代理过程图示剖析

基于JDK代理形式实现

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

阐明:如果指标对象类型没有实现接口,则不容许应用JDK代理。

基于CGLIB代理形式实现

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

阐明:指标对象实现了接口也能够基于CGLIB为指标对象创立代理对象。然而指标对象类型如果应用了final润饰,则不能够应用CGBLIB。

切面告诉利用加强

告诉类型

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

  • @Before。(指标办法执行之前执行)
  • @AfterReturning。(指标办法胜利完结时执行)
  • @AfterThrowing。(指标办法异样完结时执行)
  • @After。(指标办法完结时执行)
  • @Around.(重点把握,指标办法执行前后都能够做业务拓展)(优先级最高)
    阐明:在切面类中应用什么告诉,由业务决定,并不是说,在切面中要把所有告诉都写上。
package com.cy.pj.common.aspect;
@Component
@Aspect
public class SysTimeAspect {
    @Pointcut("bean(sysUserServiceImpl)")
    public void doTime(){}
 
    @Before("doTime()")
    public void doBefore(){
        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;
         }
        
    }
}

切入点表达式加强

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) 匹配有此注解形容的办法。

切面优先级设置实现

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

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

阐明:当多个切面作用于同一个指标对象办法时,这些切面会构建成一个切面链,相似过滤器链、拦截器链

要害对象与术语总结

Spring 基于AspectJ框架实现AOP设计的要害对象概览

Spring AOP事务处理

Spring 中事务简介

事务定义

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

事务个性(ACID)

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

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

Spring 中事务管理

Spring 中事务形式概述

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

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中事务管制过程剖析

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

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

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

@Transactional(propagation=Propagation.REQUIRED)。

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

    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public List<Node> findZtreeMenuNodes() {
        return sysMenuDao.findZtreeMenuNodes();
    }

当有一个业务对象调用如上办法时,此办法始终工作在一个曾经存在的事务办法,或者是由调用者创立的一个事务办法中。

@Transactional(propagation=Propagation.REQUIRES_NEW)。

必须是新事务, 如果有以后事务, 挂起以后事务并且开启新事务,

 @Transactional(propagation = Propagation.REQUIRES_NEW)
 @Override
 public void saveObject(SysLog entity) {
    sysLogDao.insertObject(entity);
 }

当有一个业务对象调用如上业务办法时,此办法会始终运行在一个新的事务中。

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默认为办法理论参数的组合。
第二步:在相干模块更新时,革除指定缓存数据
allEntries示意革除所有。

 @CacheEvict(value="menuCache",allEntries=true)
 @Override
 public int saveObject(SysDept entity) {...}

spring中的缓存利用原理

进行一个小案例

第一步定义业务接口

package com.cy.pj.module.service;
import java.util.List;
public interface ModuleService {
    List<String> findPermissions();
}

第二步定义业务实现类

package com.cy.pj.module.service;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class ModuleServiceImpl implements ModuleService{
    @Override
     public List<String> findPermissions() {
         System.out.println("select permissions from database");
         List<String> list=new ArrayList<>();
         list.add("sys:log:delete");
         list.add("sys:log:select");//假如这些数据来自数据库
         return list;
     }
}

第三步定义切面

package com.cy.pj.commom.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Component
@Aspect
public class CacheAspect {//零碎底层会将这个切面中的内容转换为Advisor对象

    //假如这个map就是咱们的一个小cache,咱们从数据库取出的数据能够存储到此cache中
    private Map<String,Object> cache=new ConcurrentHashMap<>();

    @Pointcut("bean(moduleServiceImpl)")
    public void doCache(){}

    @Around("doCache()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
        //1.从cache中取数据
        Object obj = cache.get("userPer");//假如key为userPer
        if(obj!=null) return obj;
        //2.cache中没有则查数据
        obj= joinPoint.proceed();
        //3.将数据存储到cache
        cache.put("userPer", obj);
        return obj;
    }
}

第四步编写测试类测试

package com.cy.pj.module.service;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

@SpringBootTest
public class ModuleServiceTests {

    @Autowired
    private ModuleService moduleService;

    @Test
    void testFindPermissions(){
        List<String> permissions = moduleService.findPermissions();
        permissions = moduleService.findPermissions();
        permissions = moduleService.findPermissions();
    }
}

Spring AOP原生形式实现

Spring 整合AspectJ框架实现AOP只是Spring框架中AOP的一种实现形式,此形式绝对比较简单,实现不便。但此形式底层还是要转换为Spring原生AOP的实现,Spring AOP原生形式实现的外围有两大部分形成,别离是:
▪ 代理(JDK,CGLIB)。
▪ org.aopalliance包下的拦挡体系。

以Spring中一种原生AOP架构的根本实现为例进行原理剖析和阐明,其繁难架构

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

外围业务接口定义及实现

定义邮件业务接口,用于定义搜寻业务标准

package com.cy.pj.common.service;
public interface MailService {
    boolean sendMsg(String message);
}

定义邮件业务接口实现

package com.cy.pj.common.service;

import org.springframework.stereotype.Service;

@Service
public class MailServiceImpl implements MailService{

    @Override
    public boolean sendMsg(String message) {
        System.out.println("send->"+message);
        return true;
    }

}

定义LogAdvice对象,基于此对象为指标业务对象做日志加强。

其中,MethodInterceptor对象继承Advice对象,基于此对象办法能够对指标办法进行拦挡。

package com.cy.pj.common.advisor;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

/**封装了扩大业务逻辑的对象,这样的对象在原生的aop中须要在advisor中注册*/
public class LogAdvice implements MethodInterceptor {//Advice

    /**此办法能够在指标业务办法执行之前和之后增加扩大逻辑*/
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        System.out.println("start:"+System.nanoTime());
        Object result = methodInvocation.proceed();//执行指标办法
        System.out.println("end:"+System.nanoTime());
        return result;
    }
}

日志Advisor对象定义及实现

其中,StaticMethodMatcherPointcutAdvisor类为Spring框架中定义的一种Advisor,咱们本人写的Advisor能够间接继承此类进行资源整合。

package com.cy.pj.common.advisor;

import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * 此Advisor中定义了一种标准
 * 1)定义了哪些办法为切入点办法
 * 2)定义了在切入点办法执行时要植入的告诉(扩大逻辑)
 */
@Component
public class LogAdvisor extends StaticMethodMatcherPointcutAdvisor {
    //定告诉 advice
    public LogAdvisor(){
        setAdvice(new LogAdvice());
    }

    //定切点 pointcut
    /**
     * matches办法中能够获取咱们要执行的指标办法,并且咱们能够在此判断这个指标办法是否为咱们的一个切入点办法
     * 1)返回值为true示意指标办法为切入点办法(在此办法执行时能够植入扩大逻辑)
     * 2)返回值为false示意指标办法为非切入点办法
     */
    @Override
    public boolean matches(Method method, Class<?> aClass) {
        try {
            Method targetMethod =
                    aClass.getMethod(method.getName(), method.getParameterTypes());
            return targetMethod.getName().equals("sendMsg");
        }catch (Exception e){
            return false;
        }
    }
}

BeanPostProcessor类型对象初始化

在我的项目启动类中,增加DefaultAdvisorAutoProxyCreator对象初始化办法,基于此对象在容器启动时扫描所有Advisor对象,而后基于切入点形容的指标办法为指标对象创立代理对象

package com.cy;

import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class Application {

    @Bean //@Bean注解形容办法时,这个办法的返回值交给spring治理
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
        return new DefaultAdvisorAutoProxyCreator();
    }
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

基于Spring boot我的项目进行单元测试

package com.cy.pj.common.service;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class MailServiceTests {
    @Autowired
    private MailService mailService;

    @Test
    void testSendMsg(){
        System.out.println(mailService.sendMsg("hello spring aop"));
    }
}

原生实现AOP的过程剖析图

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理