浅谈Spring AOP的原理和应用

1.AOP简介

Aop(Aspect Oriented Programming)大家应该都知道这是面向切面编程思想,主要能够为我们在不影响原来的功能的前提,为软件横向拓展功能。简单来说,把对象中一些公用的行为抽取出来,减少代码冗余性,还可以将业务代码和系统功能代码分离开。以下,我对Spring AOP进行一系列探讨,(注意:Aop是一种思想,以下主要对Spring Aop技术的探讨的)

2.Aop使用的场景

1)权限验证

2)缓存

3)异常统一处理

4)日志系统

5)信息跟踪标记

6)信息校验

7)事务

以上只是简单自己的常见场景,AOP应用的方法千差万别,还有很多很多场景,具体还是看业务场景,主要判别的依据是业务代码,还是系统功能代码。我只是简单谈一下自己对Spring AOP。

3.AOP相关概念

方面(Aspect):一个关注点的模块化,这个关注点实现可能另外横切多个对象。事务管理是J2EE应用中一个很好的横切关注点例子。方面用Spring的 Advisor或拦截器实现。

连接点(Joinpoint): 程序执行过程中明确的点,如方法的调用或特定的异常被抛出。

通知(Advice): 在特定的连接点,AOP框架执行的动作。各种类型的通知包括“around”、“before”和“throws”通知。通知类型将在下面讨论。许多AOP框架包括Spring都是以拦截器做通知模型,维护一个“围绕”连接点的拦截器链。Spring中定义了四个advice: BeforeAdvice, AfterAdvice, ThrowAdvice和DynamicIntroductionAdvice

切入点(Pointcut): 指定一个通知将被引发的一系列连接点的集合。AOP框架必须允许开发者指定切入点:例如,使用正则表达式。 Spring定义了Pointcut接口,用来组合MethodMatcher和ClassFilter,可以通过名字很清楚的理解, MethodMatcher是用来检查目标类的方法是否可以被应用此通知,而ClassFilter是用来检查Pointcut是否应该应用到目标类上

引入(Introduction): 添加方法或字段到被通知的类。 Spring允许引入新的接口到任何被通知的对象。例如,你可以使用一个引入使任何对象实现 IsModified接口,来简化缓存。Spring中要使用Introduction, 可有通过DelegatingIntroductionInterceptor来实现通知,通过DefaultIntroductionAdvisor来配置Advice和代理类要实现的接口

目标对象(Target Object): 包含连接点的对象。也被称作被通知或被代理对象。POJO

AOP代理(AOP Proxy): AOP框架创建的对象,包含通知。 在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。

织入(Weaving): 组装方面来创建一个被通知对象。这可以在编译时完成(例如使用AspectJ编译器),也可以在运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入。

该部分来自:

作者:蛙课网
链接:https://www.zhihu.com/question/23641679/answer/706649497
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处,侵删。

4.AOP实现基本原理

Spring提供的实现方式来生成代理对象的方式于两种:JDK proxy和Cglib。具体实现时候使用哪一种,可以配置的。默认的策略是如果目标类是接口,则使用JDK动态代理技术,否则使用Cglib来生成代理。https://www.zhihu.com/question/23641679

简单总结两者的区别:

相同点:

  1. 两者都是会为目标对象生成一个代理对象类。

不同点:

  1. 实现原理不同,JDK proxy主要是利用放射机制来实现对的,而Cglib利用ASM开源包,对代理对象class文件加载进来,对其的字节码生成子类进行处理。
  2. 性能上,根据查询多番资料发现Cglib更优化,详细没有自测过,但是随着jdk版本,jdk proxy性能也渐渐优化了。
  3. 各自局限,jdk动态代理只能代理实现了接口的类,cglib不能对final修饰的类进行代理。

所以还是要看情景来选择适合的实现方式,一般情况个人偏向选择cglib。

详细查看,总结来自:

5.AOP简单实现和应用

Spring Aop实现可以通过xml文件来实现或者利用注解方式,个人更偏向注解方式,编写方式更简单。所以以下列举一下注解实现的方式。

1.maven的依赖
<dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>4.3.14.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>4.3.14.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.11</version>
        </dependency>
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.2.1.RELEASE</version>
        </dependency>
    </dependencies>
2.创建一个简单的方法类
@Service
public class HelloWorld {
    public String say(String content){
        System.out.println("执行业务代码");
        System.out.println("说话:"+content);
        return content;
    }
}
3.创建一个aop的实现类
@Aspect
@Component
public class LogAspect {
    //定义切面的执行规规则     
    //execution(返回类型 方法名称 传入参数)
    @Pointcut("execution(* com.rong.aop.aspect..*.*(..))")
    public void pointCut() {
    }

    @Before("pointCut()")
    public void logStart(JoinPoint joinPoint) {
        System.out.println("------------------------------------------------");
        System.out.println("方法切面开始");
        //将JoinPoint相关参数输出
        printlnJoinPoint(joinPoint);
        System.out.println("------------------------------------------------");
    }
  
    //方法结束之后执行
    @After("pointCut()")
    public void logEnd() {
        System.out.println("------------------------------------------------");
        System.out.println("方法结束通知");
        System.out.println("------------------------------------------------");
    }
    
    //抛出异常之后执行
    @AfterThrowing("pointCut()")
    public void logException() {
        System.out.println("------------------------------------------------");
        System.out.println("异常通知");
        System.out.println("------------------------------------------------");
    }
    
    //方法返回之前执行,要比 @After和@AfterThrowing之后,其实 @AfterReturning实现是放在     //finally里面的
    @AfterReturning(returning="obj",pointcut = "pointCut()")
    public void logReturn(JoinPoint joinPoint,Object obj) {
        System.out.println("------------------------------------------------");
        System.out.println("返回通知:"+obj);
        System.out.println("------------------------------------------------");
    }

  

    private void printlnJoinPoint(JoinPoint joinPoint){
        System.out.println("目标方法名为:" + joinPoint.getSignature().getName());
        System.out.println("目标方法所属类的简单类名:" +        joinPoint.getSignature().getDeclaringType().getSimpleName());
        System.out.println("目标方法所属类的类名:" + joinPoint.getSignature().getDeclaringTypeName());
        System.out.println("目标方法声明类型:" + Modifier.toString(joinPoint.getSignature().getModifiers()));
        //获取传入目标方法的参数
        Object[] args = joinPoint.getArgs();
        for (int i = 0; i < args.length; i++) {
            System.out.println("第" + (i+1) + "个参数为:" + args[i]);
        }
        System.out.println("被代理的对象:" + joinPoint.getTarget());
        System.out.println("代理对象自己:" + joinPoint.getThis());
    }
}
@Pointcut的execution的匹配规则

基本公式:execution(返回类型 方法名称 传入参数)

常用的方式:

任意公共方法的执行:

execution(public * *(…))

任何一个以“set”开始的方法的执行:

execution(* set*(…))

HelloWorld接口的任意方法的执行:

execution(* com.rong.aop.aspect.HelloWorld.*(…))

定义在aspect包里的任意方法的执行:

execution(* com.rong.aop.aspect..(…))

定义在aspect包和所有子包里的任意类的任意方法的执行:

execution(* com.rong.aop.aspect….(…))

详细参考此资料:

4.添加一个配置类
package com.rong.aop.aspect;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

//开启扫描
@ComponentScan
//开启切面编程,开启cglib
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class MyConfigAspect {
}
5.添加一个启动类
public class AspectTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(MyConfigAspect.class);
        HelloWorld helloWorld = annotationConfigApplicationContext.getBean(HelloWorld.class);
        helloWorld.say("rong is coming");
    }
}
6.运行结果

android looper原理 androidaop原理_spring

6.简单介绍@EnableAspectJAutoProxy

@EnableAspectJAutoProxy

该注解有两个方法:

proxyTargetClass默认为false,则是默认使用jdk动态代理,如果配置为ture,则使用cglib。

exposeProxy默认为false,则默认不暴露自己在aopContext,如果为ture,则可以将自己暴露出来,可访问。
详细可以查看:

(个人理解是自己调用自己方法,不会执行代理类之后的方法的。)

android looper原理 androidaop原理_System_02

而这个注解的核心起作用是AspectJAutoProxyRegistrar

android looper原理 androidaop原理_spring_03

大概意思是注册一个目标类的代理类,然后去代替ioc容器中的现在的目标类,但是执行流程还是很复杂的。该类重写了注册BeanDefinition的方式。

android looper原理 androidaop原理_AOP_04


android looper原理 androidaop原理_AOP_05

参考来自,详细查看,很优秀的文章:

7.总结

本文只是简单介绍了Spring aop和对Spring aop简单的实现,而没有对实现的细节描述,主要是对基本的功能和理解,详细实现可以根据需求再一一查询。所以aop基本功能实现是可以的,但是觉得很遗憾是对spring的源码不了解,还需要很多的探究,因此只对EnableAspectJAutoProxy注解的浅层的了解,深入还需努力。发现错误,望各位多多指出。