java村支书 · 2019年11月08日

慢慢进阶spring(三)之AOP

什么是AOP

传统的OOP开发中的代码逻辑是自上而下的,而这些过程会产生一些横切性问题,这些横切性的问题和我们的主业务逻辑关系不大,这些横切性问题不会影响到主逻辑实现的,但是会散落到代码的各个部分。AOP是处理一些横切性问题,AOP的编程思想就是把这些问题和主业务逻辑分开成为核心关注点以及与业务关系不大的横切关注点,达到与主业务逻辑解耦的目的。使代码的重用性和开发效率更高。

AOP在Spring框架中用于:

  • 提供声明式企业服务。最重要的此类服务是 声明式事务管理。
  • 让用户实现自定义方面,并用AOP补充其对OOP的使用。

spring AOP提供两种编程风格
基于注解:@AspectJ
基于xml文件:Schema-based AOP

理清AOP,spring AOP,AspectJ的关系:

  • Aop是一种概念。
  • springAop、AspectJ都是Aop的实现,Spring Aop有自己的语法,但是语法复杂,所以SpringAop借助了AspectJ的注解,但是底层实现还是spring自己的。

AOP概念和术语

  • Aspect:涉及多个类别的关注点的模块化。事务管理是企业Java应用程序中横切关注的一个很好的例子。在Spring AOP中,Aspect是通过使用常规类(基于模式的方法)或通过注释进行@Aspect注释的常规类 (@AspectJ样式)来实现的。
  • Join point:在程序执行过程中的一点,例如方法的执行或异常的处理。目标对象中的方法。
  • Advice:在特定的Join point处采取的操作。
  • Pointcut:与Advice匹配的谓词。Advice与Join point表达式关联,并在与该切入点匹配的任何连接点处运行。切点表示连接点的集合。
  • Target object:目标对象 也可叫 原始对象
  • Aop Proxy:代理对象 包含了原始对象的代码和增加后的代码的那个对象AOP代理:由AOP框架创建的一个对象,用于实现方面协定(建议方法执行等)。
  • Weaving:把代理逻辑加入到目标对象上的过程叫做织入

advice通知类型:

  • Before:连接点执行之前,但是无法阻止连接点的正常执行,除非该段执行抛出异常
  • After:连接点正常执行之后,执行过程中正常执行返回退出,非异常退出
  • After throwing:执行抛出异常的时候
  • After (finally):无论连接点是正常退出还是异常退出,都会执行
  • Around advice:围绕连接点执行,例如方法调用。这是最有用的切面方式。around通知可以在方法调用之前和之后执行自定义行为。它还负责选择是继续加入点还是通过返回自己的返回值或抛出异常来快速建议的方法执行。

不得不说的Proceedingjoinpoint 和JoinPoint

在使用advice在Join point处执行的方法,会经常使用到这两个参数Proceedingjoinpoint 和JoinPoint。比如以下两个代码:

@Around(value = "aspectPointCut()")
public Object around(ProceedingJoinPoint point){
    Object object = null;
    try {
        object = point.proceed();
        MethodSignature signature = (MethodSignature)point.getSignature();
        Method method = signature.getMethod();
        String value = method.getAnnotation(Log.class).value();//日志信息
        String methodName = method.getName();//方法名称
        String className = point.getTarget().getClass().getName();//类名
        String args = JSON.toJSONString(point.getArgs());//请求参数

        System.out.println("这个是环绕通知.....after。日志信息:"
                + value + ",访问方法:" + className+"."+methodName +"(),请求参数:" + args);
    }catch (Throwable throwable){
        throwable.printStackTrace();
    }
    return object;
}

@After(value = "aspectPointCut()")
public void after(JoinPoint point){
    MethodSignature signature = (MethodSignature)point.getSignature();

    Method method = signature.getMethod();
    String value = method.getAnnotation(Log.class).value();//日志信息
    String methodName = method.getName();//方法名称
    String className = point.getTarget().getClass().getName();//类名
    String args = JSON.toJSONString(point.getArgs());//请求参数
    System.out.println("这个是后置通知.....after。日志信息:"
            + value + ",访问方法:" + className+"."+methodName +"(),请求参数:" + args);
}

那么他们有什么作用以及却别呢??

Proceedingjoinpoint 继承了JoinPoint,proceed()这个是aop代理链执行的方法。并扩充实现了proceed()方法,用于继续执行连接点。JoinPoint仅能获取相关参数,无法执行连接点。

JoinPoin相关api

1.java.lang.Object[] getArgs():获取连接点方法运行时的入参列表;

2.Signature getSignature() :获取连接点的方法签名对象;

3.java.lang.Object getTarget() :获取连接点所在的目标对象;

4.java.lang.Object getThis() :获取代理对象本身;

proceed()有重载,有个带参数的方法,可以修改目标方法的的参数

springAop支持AspectJ(使用步骤)

1、启用@AspectJ支持

要使用Java @Configuration启用@AspectJ支持,请添加@EnableAspectJAutoProxy注释

@Configuration
@EnableAspectJAutoProxy
public class AppConfig {

}

2、声明一个Aspect

申明一个@Aspect注释类,并且定义成一个bean交给Spring管理。

@Component
@Aspect
public class LogAspect {
}

3、在Aspect中申明一个pointCut

切入点表达式由@Pointcut注释表示。切入点声明由两部分组成:一个签名包含名称和任何参数,以及一个切入点表达式,该表达式确定我们对哪个方法执行。

/**
 * 申明Aspect,并且交给spring容器管理
 */
@Component
@Aspect
public class LogAspect {
    /**
     * 申明切入点,匹配标注Log注解的所有方法调用
     */
    @Pointcut(value = "@annotation(com.email.annotation.Log)")
    public void pintCut() {
        System.out.println("point cut");
    }
} 

4、在Aspect中申明一个Advice通知

advice通知与pointcut切入点表达式相关联,并在切入点匹配的方法执行@Before之前、@After之后或前后运行。

  /**
 * 申明Aspect,并且交给spring容器管理
 */
    @Aspect
@Component
public class LogAspect {
    
            /**
     * 申明切入点,匹配标注Log注解的所有方法调用
     */
    @Pointcut(value = "@annotation(com.email.annotation.Log)")
    private void aspectPointCut(){

    }

    @Before(value = "aspectPointCut()")
    public void before(JoinPoint point){
        MethodSignature signature = (MethodSignature)point.getSignature();
        Method method = signature.getMethod();
        String value = method.getAnnotation(Log.class).value();//日志信息
        String methodName = method.getName();//方法名称
        String className = point.getTarget().getClass().getName();//类名
        String args = JSON.toJSONString(point.getArgs());//请求参数
        System.out.println("这个是前置通知.....before。日志信息:"
                + value + ",访问方法:" + className+"."+methodName +"(),请求参数:" + args);
    }
    
    @Around(value = "aspectPointCut()")
    public Object around(ProceedingJoinPoint point){
        Object object = null;
        try {
            object = point.proceed();
            MethodSignature signature = (MethodSignature)point.getSignature();
            Method method = signature.getMethod();
            String value = method.getAnnotation(Log.class).value();//日志信息
            String methodName = method.getName();//方法名称
            String className = point.getTarget().getClass().getName();//类名
            String args = JSON.toJSONString(point.getArgs());//请求参数

            System.out.println("这个是环绕通知.....after。日志信息:"
                    + value + ",访问方法:" + className+"."+methodName +"(),请求参数:" + args);
        }catch (Throwable throwable){
            throwable.printStackTrace();
        }
        return object;
    }
}

spring Aop原理

spring Aop使用动态代理机制来实现。使用JDK动态代理或CGLIB创建给定目标对象的代理。JDK动态代理内置在JDK中,而CGLIB是常见的开源类定义库。

  • 如果要代理的目标对象实现了接口,则使用JDK动态代理。代理了由目标类型实现的所有接口。
  • 如果目标对象未实现任何接口,则将创建CGLIB代理。
  • 如果要强制使用CGLIB代理,可以使用在配置类上加注解@EnableAspectJAutoProxy(proxyTargetClass = true)。但是,应该考虑以下问题:如果使用CGLIB,那么对象不能有final修饰,因为CGLIB基于继承生成子类实现。

本人水平有限,难免有错误或遗漏之处,望大家指正和谅解,提出宝贵意见,愿与之交流。

推荐阅读
关注数
0
文章数
16
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息