欢迎光临散文网 会员登陆 & 注册

Spring AOP的核心应用与原理解析

2019-09-19 15:37 作者:P8架构师诸葛  | 我要投稿


概述

AOP(Aspect Oriented Programming) 面向切面编程,实质上是对我们的对象进行增强,并且提供良好的管理机制。

对于对象增强,可以有以下几种方法

 

1.  装饰器模式,比如JDK中的I/O流

InputStream inputStream = new LineNumberInputStream(new BufferedInputStream(new FileInputStream("")));

2.  静态代理模式

3.  适配器模式

4.  动态代理,包括  Proxy 和 CGLIB 等方法进行动态代理

 

SpringAOP 是一套 AOP 的解决方案,他比传统的对象增强更容易管理和扩展。

在对象增强上,它的规则是:基于动态代理来实现。默认地,如果使用接口方法的,用 JDK 提供的动态代理实现,如果没有接口,使用 CGLIB 实现。SpringAOP 基于 IOC 容器,动态代理之后,会把原对象替换成动态代理的对象。

 

 

概念

Advisor 是 AOP 的一个概念,他是 保存AOP配置 的一个单位

 

·      Advice:方法拦截逻辑,可控制该方法执行前和方法执行后或者出现异常时候的执行的逻辑

·      PointCut: 在哪些地方的方法应用拦截

·      Advisor里面有且只有一个 Advice,可以对多个 PointCut 执行 Advice 方法。

 


 

注解配置 Spring AOP

 

SpringAOP 和 AspectJ 没多大关系,而仅仅是使用了 AspectJ 中的概念,包括使用的注解也是直接来自于 AspectJ 的包(有点迷)。

 

依赖

<dependency>

    <groupId>org.aspectj</groupId>

    <artifactId>aspectjweaver</artifactId>

    <version>1.8.11</version>

</dependency>

或者在 SpringBoot

<dependency>

   <groupId>org.springframework.boot</groupId>

   <artifactId>spring-boot-starter-aop</artifactId>

</dependency>

 

注解

注解开启,在Main 类上面标上这个注解

@EnableAspectJAutoProxy

 

在 配置文件Bean 上 打上 @Aspect

@Aspect

 

配置 PointCut

@Aspect

public class SystemArchitecture {

    @Pointcut("execution(* transfer(..))")// the pointcut expression

    private void anyOldTransfer() {}// the pointcut signature

}

 

一些配置规则

@Pointcut("execution(* transfer(..))")

// 方法签名

@Pointcut("within(com.javadoop.springaoplearning.service..*)")

// 包下所有类的所有方法

@Pointcut("execution( .*(..))

@annotation(com.javadoop.annotation.Subscribe)")

// 指定注解的所有方法

@Pointcut("bean(*Service)")

// 指定 bean 名的所有方法

// 通常 "." 代表一个包名,".." 代表包及其子包,方法参数任意匹配使用两个点 ".."

 

 

一些实践中的配置

@Aspect

public class SystemArchitecture {

 

    // web 层

    @Pointcut("within(com.javadoop.web..*)")

    public void inWebLayer() {}

 

    // service 层

    @Pointcut("within(com.javadoop.service..*)")

    public void inServiceLayer() {}

 

    // dao 层

    @Pointcut("within(com.javadoop.dao..*)")

    public void inDataAccessLayer() {}

 

    // service 实现,注意这里指的是方法实现,其实通常也可以使用 bean(*ServiceImpl)

    @Pointcut("execution(* com.javadoop..service.*.*(..))")

    public void businessService() {}

 

    // dao 实现

    @Pointcut("execution(* com.javadoop.dao.*.*(..))")

    public void dataAccessOperation() {}

}

 

配置 Advice

@Aspect

public class AdviceExample {

 

    // 这里会用到我们前面说的 SystemArchitecture

    // 下面方法就是写拦截 "dao层实现"

    @Before("com.javadoop.aop.SystemArchitecture.dataAccessOperation()")

    public void doAccessCheck() {

        // ... 实现代码

    }

 

    @Before("com.javadoop.springaoplearning.aop_spring_2_aspectj.SystemArchitecture.businessService()")

    public void logArgs(JoinPoint joinPoint) {

        System.out.println("方法执行前,打印入参:" + Arrays.toString(joinPoint.getArgs()));

    }

 

    @AfterReturning("com.javadoop.aop.SystemArchitecture.dataAccessOperation()")

    public void doAccessCheck() {

        // ...

    }

 

    @AfterReturning(

        pointcut="com.javadoop.aop.SystemArchitecture.dataAccessOperation()",

        returning="retVal")

    public void doAccessCheck(Object retVal) {

        // 这样,进来这个方法的处理时候,retVal 就是相应方法的返回值,是不是非常方便

        //  ... 实现代码

    }

 

    // 异常返回

    @AfterThrowing("com.javadoop.aop.SystemArchitecture.dataAccessOperation()")

    public void doRecoveryActions() {

        // ... 实现代码

    }

 

    @AfterThrowing(

        pointcut="com.javadoop.aop.SystemArchitecture.dataAccessOperation()",

        throwing="ex")

    public void doRecoveryActions(DataAccessException ex) {

        // ... 实现代码

    }

 

    // 注意理解它和 @AfterReturning 之间的区别,这里会拦截正常返回和异常的情况

    @After("com.javadoop.aop.SystemArchitecture.dataAccessOperation()")

    public void doReleaseLock() {

        // 通常就像 finally 块一样使用,用来释放资源。

        // 无论正常返回还是异常退出,都会被拦截到

    }

 

    // 感觉这个很有用吧,既能做 @Before 的事情,也可以做 @AfterReturning 的事情

    @Around("com.javadoop.aop.SystemArchitecture.businessService()")

    public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {

        // start stopwatch

        Object retVal = pjp.proceed();

        // stop stopwatch

        return retVal;

    }

 

}

 

advisor会由Spring给我们生成。

 

原理解析

 

在 Spring中,Bean 初始化结束后,会对每一个 bean 调用一次实现 BeanPostProcessor 接口的 Bean postProcessAfterInitialization()  方法。

 

当我们通过注解配置 AOP (XML差不多),Spring 会 给我们注册一个 AnnotationAwareAspectJAutoProxyCreator 的 Bean,这个 Bean 就实现了 BeanPostProcessor 接口 的 postProcessAfterInitialization() 方法。

 

这个方法传入 原始Bean,返回处理过的 Bean ,我们在这里就可以对 这个 Bean 偷梁换柱,换成 Proxy 对象。实现我们代理的目的。

 

在 ProxyCreater 中实现了这个方法:

 

@Override

public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

   if (bean != null) {

      Object cacheKey = getCacheKey(bean.getClass(), beanName);

      if (!this.earlyProxyReferences.contains(cacheKey)) {

         return wrapIfNecessary(bean, beanName, cacheKey);

      }

   }

   return bean;

}

 

这个方法干了这些事情:

1.  把这个 bean 匹配的AOP增强配置打包成 advisor

 

2.  保存这个Bean的接口方法

3.  根据接口方法和配置决定使用 JDK Proxy 代理生成器(JdkDynamicAopProxy) 还是 CGLIB 代理生成器(ObjenesisCglibAopProxy) 来生成代理对象,一般情况下:

a.  如果被代理的目标类实现了一个或多个自定义的接口,那么就会使用 JDK 动态代理

b.  如果没有实现任何接口,会使用 CGLIB 实现代理。

4.  生成代理对象后,把这个代理对象替换掉原来的 Bean

 

简单讲一下创建 JDK Proxy 代理,CGLIB类似,但是操纵比较复杂。

Proxy 使用方法见 Java 动态代理机制 (一) JDK Proxy 详解

JdkDynamicAopProxy -> getProxy

public Object getProxy(ClassLoader classLoader) {

   if (logger.isDebugEnabled()) {

      logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());

   }

  

   Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);

   findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);

   // 获取这个Bean的所有接口,调用 JDK 的 Proxy 生成 Proxy

   // 第三个是  InvocationHandler,代理类实现了  InvocationHandler 接口,有 invoke 方法

   return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);

}

 

当我们调用 被 Proxy 代理的类的时候,都会调用到 JdkDynamicAopProxy 的 invoke 方法,invoke 方法 在我们原方法的周围做一些增强(从advisor中获取我们写好的增强函数)。剩下的就交给 JDK 来处理了。

 


Spring AOP的核心应用与原理解析的评论 (共 条)

分享到微博请遵守国家法律