直观理解AOP

AOP

1. 概念:AOP(Aspect Oriented Programming),译为面向切面编程,其本质是:在运行时,动态地将增强方法添加到待被增强类的指定方法上

2. 原因:面向对象编程要求将不同功能代码封装到不同的类和方法中,以降低代码复杂程度,增加代码可重用性。但是在使用过程中,这种方式又增加了代码间的耦合程度,不利于协同开发。举例说明,日志打印功能被封装到一个打印类Printer的打印方法print()中,现在多个类中要使用日志打印功能,方式一在各个类中分别自己写日子打印,这样降低了代码重用性;方式二把打印类Printer的打印方法print()集成到各个类中,这无疑增加了类间的耦合。那有没有即可重用又不耦合的方式呢?那就是AOP技术。

这里讲解Spring常用的动态AOP,也即动态代理的方式,其他AOP可以参考(https://www.cnblogs.com/xiaoxiao7/p/6057724.html)。接下来是我自己的理解,有不对的地方请指正。

3. 相关概念

直观理解AOP_第1张图片

  • 切面:之前学JAVA是面向对象的,对象之间的相互关联操作,这里的切面是指从对象的切面看,我们的目标是类的方法,需要被增强的是方法,提供增强功能的也是方法,所以切面编程可以说是,类间方法的关联编程,它不同于方法间的引用(这样会有耦合),是一种多个方法拼接式的功能增强

  • 切面类和待被增强类:待被增强类是我们的核心类,是把切面类的一些方法“拼接”到待被增强类的待被增强方法前后,得到一个和待被增强类有相同接口的代理类——已被增强类,如下图。已被增强类的使用方法和待被增强类相同,因为其实现了相同的接口。

  • 连接点和切入点:待被增强类的所有方法都可以被称为连接点,如果一个连接点方法被增强,就被称为切入点。

  • 织入:拼接的过程就是织入,织入的结果就是已被增强类,或者称为代理类(动态代理的说法)。

直观理解AOP_第2张图片

  • Advice(通知/增强):直译通知不如翻译为增强,也就是切面类的可用来拼接的方法,如上图的methodA1和methodA3。根据Advice的位置和执行方式不同,可以被分为四类,如下图所示(这里的拼接方式和上面的不一样了,需要注意):
    • 前置通知
    • 后置通知
    • 异常通知
    • 最终通知
    • 环绕通知:手动构建以上四个通知

直观理解AOP_第3张图片

​ 通过,这个try-catch-finally模型可以简便记住这四种Advice的功能的不同。

3. Spring AOP方法

  • xml配置方式:
	
    <bean id="Service" class="com.AccountServiceImpl">bean>
    <bean id="Logger" class="com.utils.Logger">bean>

    
    <aop:config>
        
        <aop:aspect id="logAdvice" ref="Logger">
            
            <aop:before method="print" pointcut="execution(* com.*.*(..))">aop:before>
        aop:aspect>
    aop:config>

  • 注解的方式:

    • @Aspect//类前,表示当前类是一个切面类

    • @Pointcut(“execution(* com.itheima.service.impl..(…))”)
      private void pt1(){}//filed,声明执行表达式,指定被增强的方法

    • @Before(“pt1()”)//方法前,前置通知

    • @AfterReturning(“pt1()”)//方法前,后置通知

    • @AfterThrowing(“pt1()”)//方法前,异常通知

    • @After(“pt1()”)//最终通知

    • @Around(“pt1()”)//环绕通知

另外:
1.环绕通知需要在切面类中手动配置以下代码:

 public Object aroundPringLog(ProceedingJoinPoint pjp){
      Object rtValue = null;
    try{
          Object[] args = pjp.getArgs();//得到方法执行所需的参数
        //这里手动插入前置通知
          rtValue = pjp.proceed(args);//明确调用业务层方法(切入点方法)
        //这里手动插入后置通知
          return rtValue;
      }catch (Throwable t){
          //这里手动插入异常通知
          throw new RuntimeException(t);
      }fi nally {
         //这里手动插入最终通知
      }
  }
}

2.切入点表达式的写法:
关键字:execution(表达式)
表达式:
访问修饰符 返回值 包名.包名.包名…类名.方法名(参数列表)
标准的表达式写法:
public void com.itheima.service.impl.AccountServiceImpl.saveAccount()
访问修饰符可以省略
void com.itheima.service.impl.AccountServiceImpl.saveAccount()
返回值可以使用通配符,表示任意返回值

                * com.itheima.service.impl.AccountServiceImpl.saveAccount()
                            包名可以使用通配符,表示任意包。但是有几级包,就需要写几个*.
                                    * *.*.*.*.AccountServiceImpl.saveAccount())
                                            包名可以使用..表示当前包及其子包
                                                        * *..AccountServiceImpl.saveAccount()
                                                            类名和方法名都可以使用*来实现通配
                                                                            * *..*.*()
                                                                            参数列表:
                                                                            ​                    可以直接写数据类型:
                    基本类型直接写名称           int
                    引用类型写包名.类名的方式   java.lang.String
                                                                            ​                    可以使用通配符表示任意类型,但是必须有参数
                                                                            ​                    可以使用..表示有无参数均可,有参数可以是任意类型
                                                            ​                全通配写法:
                                                                            
                                                                                                * *..*.*(..)

你可能感兴趣的:(JAVA)