Spring笔记----Day02----DynamicAgentAndAOP

一、动态代理(Dynamic agent):

 1、特点:

字节码随用随创建,随用随加载。

 2、作用:

在不修改源码的基础上对方法增强。

 3、分类:

  <1>、基于接口的动态代理:

  (1)、涉及到的类:

Proxy

  (2)、提供者:

JDK官方

  (3)、创建方法:

使用Proxy类中的newProxyInstance方法。

  (4)、对代理对象的要求:

被代理对象至少实现一个接口,否则不可用。

  (5)、newProxyInstance方法的参数:

   ①、ClassLoader:
类加载器
用于加载代理对象的字节码,和被代理对象使用相同的类加载器。
固定写法
   ②、Class[]:
字节码数组
用于让代理对象和被代理对象拥有相同方法。
固定写法
   ③、InvocationHandler:
用于提供增强的代码
用于让我们代理,一般是一个该接口的实现类。通常是匿名内部类,但不是必须的。
作用:
    执行被代理对象的任何方法都会经过该方法。
函数:
    proxy:代理对象的引用。
    method:当前执行的方法。
    args:当前执行方法所需参数。
    返回值:和被代理对象拥有相同的返回值。

  <2>、基于子类的动态代理:

  (1)、涉及到的类:

Enhancer

  (2)、提供者:

第三方cglib库

  (3)、创建方法:

使用Enhancer中的create方法。

  (4)、对代理对象的要求:

被代理类不能是最终类。

  (5)、create方法的参数:

   ①、class:
字节码
指定被代理类的字节码,要代理谁,就用谁.class
   ②、callback:
用于提供增强的代码
一般使用该接口的一个子接口实现类:MethodInterceptor
函数:
    proxy:代理对象的引用。
    method:当前执行的方法。
    args:当前执行方法所需参数。
    methodProxy:当前执行方法的代理对象。
    返回值:和被代理对象拥有相同的返回值。

二、面向切面编程(AOP):

1、作用:

在程序运行期间,不修改源码,对已有方法进行增强。

2、优势:

 <1>、减少重复代码。
 <2>、提高开发效率。
 <3>、维护方便。

3、相关术语:

 <1>、Joinpoint(连接点):
指被拦截到的点。在spring中,这些点指的是方法。
因为spring只支持方法类型的连接点。也就是业务层的每一个方法。
 <2>、Pointcut(切入点):
指的是要对哪些Joinpoint进行拦截的定义。也就是业务层中被增强的方法。
 <3>、Advice(通知):
定义:
    指的是拦截到Joinpoint之后要做的事情。
分类:
    前置通知、后置通知、异常通知、最终通知、环绕通知
分类原理:
    以调用业务层方法(被try……catch包裹起来)为界限,在其上面就是前置通知;下面就是后置通知;
    在catch内就是异常通知;在finally内就是最终通知;整个invoke方法在执行时就是环绕通知。
 <4>、Introduction(引介):
在不修改类代码的前提下,Introduction可以在运行期为类动态的添加一些方法或Field。
 <5>、Target(目标对象):
代理的目标对象。
 <6>、Weaving(织入):
指把增强应用到目标对象来创建新的代理对象的过程。
spring采用的是动态代理织入,而AspectJ采用的是编译器织入和类加载期织入。
 <7>、Proxy(代理):
一个类被AOP织入增强后,就产生一个新的结果代理类。
 <8>、Aspect(切面):
切入点和通知(引介)的结合。

4、spring中AOP的配置:

<1>、基于XML的配置:

 (1)、把通知Bean交给spring管理
 (2)、使用aop:config标签表明开始AOP的配置
 (3)、使用aop:aspect标签表明配置切面
id属性:给切面提供一个唯一标识
ref属性:指定通知类bean的标签
 (4)、在aop:aspect标签内部使用对应标签来配置通知
前置通知:aop:before
    method属性:用于指定Logger类中哪个方法是前置通知。
    pointcut属性:用于指定切入点表达式,该表达式的含义指的是对业务层哪些方法增强。

后置通知:aop:after-returning
    method属性:用于指定Logger类中哪个方法是前置通知。
    pointcut属性:用于指定切入点表达式,该表达式的含义指的是对业务层哪些方法增强。

异常通知:aop:after-throwing
    method属性:用于指定Logger类中哪个方法是前置通知。
    pointcut属性:用于指定切入点表达式,该表达式的含义指的是对业务层哪些方法增强。

最终通知:aop:after
    method属性:用于指定Logger类中哪个方法是前置通知。
    pointcut属性:用于指定切入点表达式,该表达式的含义指的是对业务层哪些方法增强。

切入点表达式:
关键字:execution(表达式)
    表达式:
        访问修饰符 返回值 包名.包名...包名.类名.方法名(参数列表)
    eg:
        标准写法:
            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()
        类名可以采用通配符*实现通配:
            * *..*.saveAccount()
        方法名可以采用通配符*实现通配:
            * *..*.*()
        参数列表:
            可以直接写数据类型:
                基本类型直接写名称           int
                引用类型写包名.类名的方式     java.long.String
            可以使用通配符*表示任意类型,但是必须有参数
            可以使用..表示有无参数均可,有参数可以是任意类型
            通配符写法:
                * *..*.*(..)
            实际开发中,切入点表达式的通常写法:
                切入到业务层实现类下的所有方法。

    
        
        

            
            

            
            

            
            

            
            
        
    

    

        
        

        
        

            
            

            
            

            
            

            
            
        
    
 (5)、配置环绕通知:
环绕通知:aop:before
    它是spring框架提供的一种可以在代码中手动控制增强方法何时执行的方式。
    问题:当配置了环绕通知之后,切入点方法没有执行,而通知方法执行了。
    分析:对比动态代理中的环绕通知代码,发现动态代理的环绕通知中有明确的
         切入点方法调用,而此处的代码中没有。
    解决:Spring框架提供了一个接口:ProceedingJoinPoint。该接口中有一个
         方法proceed(),此方法就相当于明确调用切入点方法。该接口可以作为
         环绕通知的方法参数,在程序执行时,Spring框架会为我们提供该接口的实现类供我们使用。
 

        
        

        
        
        
        
        
        
        
    
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
public class Logger {
    
    /**
     * 环绕通知
     */
    public Object aroundPringLog(ProceedingJoinPoint pjp){
        Object rtValue = null;
        try {
            //得到方法执行所需的参数
            Object[] args = pjp.getArgs();

            System.out.println("Logger类中的aroundPringLog方法开始记录日志!!!前置通知");

            //明确调用业务层方法(切入点方法)
            rtValue = pjp.proceed(args);

            System.out.println("Logger类中的aroundPringLog方法开始记录日志!!!后置通知");

            return rtValue;
        }catch (Throwable t){
            System.out.println("Logger类中的aroundPringLog方法开始记录日志!!!异常通知");
            throw new RuntimeException(t);
        }finally {
            System.out.println("Logger类中的aroundPringLog方法开始记录日志!!!最终通知");
        }
    }
}
 (5)、五种通知的分类依据:
Spring笔记----Day02----DynamicAgentAndAOP_第1张图片

<1>、基于注解的配置:

(1)、相关约束:

    
    

    
    

(2)、相关注解:

①、@Aspect:
将""替换成
实现类上的注解@Aspect,表示当前类是一个切面。
②、@Pointcut:
将""
替换成方法上的注解
@Pointcut("execution(* *..*.*(..))")
private void pt1(){}
③、@Before("pt1()"):
前置通知,注解在前置通知的实现方法上,替换
""
注解的参数指的是切入点表达式注解的方法的方法名。
④、@AfterReturning("pt1()"):
后置通知,注解在后置通知的实现方法上,替换
""
注解的参数指的是切入点表达式注解的方法的方法名。
⑤、@AfterThrowing("pt1()"):
异常通知,注解在异常通知的实现方法上,替换
""
注解的参数指的是切入点表达式注解的方法的方法名。
⑥、@After("pt1()"):
最终通知,注解在最终通知的实现方法上,替换
""
注解的参数指的是切入点表达式注解的方法的方法名。
⑦、@Around("pt1()"):
环绕通知,注解在环绕通知的实现方法上,替换
""
注解的参数指的是切入点表达式注解的方法的方法名。
⑧、特别注意:
Spring基于注解的通知,有顺序调用的问题,采用注解时,
后置通知/异常通知会在最终通知之后执行;而采用环绕通知时,就不会有顺序问题。

你可能感兴趣的:(Spring笔记----Day02----DynamicAgentAndAOP)