Spring技术原理之Spring AOP

简介: Spring技术原理之Spring AOP

静态代理



    1. image.png image.png
    2.问题

     1.每个RealSubject对应编写一个Proxy类,比较麻烦。


     2.大量的Proxy类会导致类规模增大不易维护,影响编译


动态代理:



  1. image.png


  1. 两个问题:


     1.自动生成的Proxy类如何代理实体类?


     2.运行时如何生成Proxy类?


3.自动生成的Proxy类如何代理实体类?


     1.定义一个全局的公共代理类:Proxy


     2.Proxy类可以根据实体类RealSubject或者实体类的接口来生成一个功能增强的具体代理类(xxxProxy)。注:通过接口生成-JDK动态代理,通过继承生成-CgLib动态代理。


     3.还有一个问题,这个功能增强的类需要我们在每一个被代理的方法上添加自己的业务逻辑,怎么实现?


     4.聪明的你肯定可以想到:抽象出一个公共的业务处理接口类InvocationHandler,每个实体类实现这个接口类,定义自己的增强逻辑。


     5.调用时直接调用生成的xxxProxy对象就好了,因为他和原始的RealSubject具有同样的功能。


     6.JDK动态代理的核心类


public interface InvocationHandler {
    /**
    * 统一代理接口
    * @param proxy 动态代理生成的对象实例
    * @param method 被代理对象的方法,用于区分哪个对象
    * @param args 调用时传入的参数,用于调用时传入的参数
    * @return
    * @throws Throwable
    */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
public class Proxy implements java.io.Serializable {
    @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
    throws IllegalArgumentException{
    //...
    }
}


     7.例子


/*** @Description 实体类*/
public class ProxyTest implements ProxyTestInterface{
    @Overridepublic 
    Integer add(Integer a, Integer b) {return a+b;}
}
//下面分别通过静态代理和动态代理来实现代理功能
//静态代理
/**
* @Description 静态代理类
*/
public class StaticProxyTest implements ProxyTestInterface {
    public StaticProxyTest(ProxyTest proxyTest){
      this.proxyTest = proxyTest;
    }
    private ProxyTest proxyTest;
    @Override
    public Integer add(Integer a, Integer b) {
      return proxyTest.add(a,b);
    }
    public void main(String[] args){
        ProxyTest proxyTest = new ProxyTest();
        StaticProxyTest staticProxyTest = new StaticProxyTest(proxyTest);
        Integer ret = staticProxyTest.add(1,2);
        System.out.println(ret);
    }
}
//动态代理
/**
* @Description 动态代理测试类
*/
public class DynamicProxyTest {
    public static void main(String[] args){
        ProxyTest proxyTest = new ProxyTest();
        InvocationHandler handler = new MyInvocationHandler(proxyTest);
        ProxyTestInterface proxyTestInterface =
        (ProxyTestInterface) Proxy.newProxyInstance(proxyTest.getClass().getClassLoader(),ProxyTest.class.getInterfaces(),handler);
        Integer ret = proxyTestInterface.add(1,2);
        System.out.println(ret);
        //将动态代理生成的类输出到硬盘上,便于查看
      ProxyUtils.generateClassFile(proxyTest.getClass(),"test");
    }
    static class MyInvocationHandler implements InvocationHandler{
        public MyInvocationHandler(ProxyTest test){
          proxyTest = test;
        }
        ProxyTest proxyTest;
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println(method.getName());
            Object obj = method.invoke(proxyTest,args);
            return obj;
        }
    }
}


     8.动态代理源码分析


public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException {
    Objects.requireNonNull(h);
    final Class<?>[] intfs = interfaces.clone();
    //此处省略一些代码
    /*
    * 此处是关键,生成代理类,是jdk按照class文件的格式,生成二级制数据流,然后再调用native方法生成运行时的Class的
    */
    Class<?> cl = getProxyClass0(loader, intfs);
    //Invoke its constructor with the designated invocation handler.
    try {
        if (sm != null) {
            checkNewProxyPermission(Reflection.getCallerClass(), cl);
        }
        final Constructor<?> cons = cl.getConstructor(constructorParams);
        final InvocationHandler ih = h;
        if (!Modifier.isPublic(cl.getModifiers())) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    cons.setAccessible(true);
                    return null;
                }
            });
        }
        //通过反射,调用有参构造函数完成实例化,此处的参数h就是我们自定义的MyInvocationHandler的实例,那这个构造函数是从哪里生成的?目前猜想肯定是生成的代理的.
        return cons.newInstance(new Object[]{h});
    } catch (IllegalAccessException|InstantiationException e) {
        throw new InternalError(e.toString(), e);
    }
    //...
}


     9.生成代理类的class文件


public class ProxyUtils {
    /**
     * 将根据类信息动态生成的二进制字节码保存到硬盘中,默认的是clazz目录下
     * @param clazz 需要生成动态代理类的类
     * @param proxyName 生成的代理类的名称
     */
    public static void generateClassFile(Class clazz,String proxyName)
    {
        //根据类信息和提供的代理类名称,生成字节码
        byte[] classFile = ProxyGenerator.generateProxyClass(proxyName, clazz.getInterfaces());
        String paths = clazz.getResource(".").getPath();
        System.out.println(paths);
        FileOutputStream out = null;
        try {
            //保留到硬盘中
            out = new FileOutputStream(paths+proxyName+".class");
            out.write(classFile);
            out.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}


     10.运行时生成的代理类


public final class jdkProxyTest extends Proxy implements ProxyTestInterface {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;
  public jdkProxyTest(InvocationHandler var1) throws  {
        super(var1);
    }
     public final Integer add(Integer var1, Integer var2) throws  {
        try {
            return (Integer)super.h.invoke(this, m3, new Object[]{var1, var2});
        } catch (RuntimeException | Error var4) {
            throw var4;
        } catch (Throwable var5) {
            throw new UndeclaredThrowableException(var5);
        }
    }
    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("com.javhl.course.dynamicproxy.ProxyTestInterface").getMethod("add", Class.forName("java.lang.Integer"), Class.forName("java.lang.Integer"));
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}


     11.JDK动态代理为什么要求被代理类必须实现接口: Java只支持单继承,可以实现多接口.


4.CGLIB动态代理:


/**
 * @Description cglib代理测试类
 */
public class CGLibProxyTest{
    public static void main(String[] args){
        // 代理类class文件存入本地磁盘方便我们反编译查看源码
              System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\code");
        MethodInterceptor methodInterceptor = new MyMethodInterceptor();
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(ProxyTest.class);
        enhancer.setCallback(methodInterceptor);
        ProxyTest proxyTest = (ProxyTest) enhancer.create();
        Integer ret = proxyTest.add(1,2);
        System.out.println(ret);
    }
    /**
     * 业务增强类,实现耗时时间统计的功能
     */
    static class MyMethodInterceptor implements MethodInterceptor{
    @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            long curTime = System.nanoTime();
            //调用父类的方法
            Object ret = methodProxy.invokeSuper(o,objects);
            System.out.println(String.format("耗时:%s 纳秒",System.nanoTime()-curTime));
            return ret;
        }
    }
}


     1.CGLIB动态代理类源码


public interface Factory {
    Object newInstance(Callback var1);
    Object newInstance(Callback[] var1);
    Object newInstance(Class[] var1, Object[] var2, Callback[] var3);
    Callback getCallback(int var1);
    void setCallback(int var1, Callback var2);
    void setCallbacks(Callback[] var1);
    Callback[] getCallbacks();
}
/**
 * Cglib运行时生成的动态代理源码
 */
public class ProxyTest$$EnhancerByCGLIB$$17e654b8 extends ProxyTest
  implements Factory{
     public final Integer add(Integer paramInteger1, Integer paramInteger2){
        MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
        if (tmp4_1 == null)
        {
          tmp4_1;
          CGLIB$BIND_CALLBACKS(this);
        }
        MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;
        //如果自己设置了MethodInterceptor那么就使用MethodInterceptor的intercepor方法,否则就使用父类的方法
        if (tmp17_14 != null)
          return (Integer)tmp17_14.intercept(this, CGLIB$add$0$Method, new Object[] { paramInteger1, paramInteger2 }, CGLIB$add$0$Proxy);
        //调用父类ProxyTest的方法,其实此时跟没应用代理是一样的
        return super.add(paramInteger1, paramInteger2);
    }
}


5.JDK动态代理 VS CGLIB动态代理:


     1.JDK动态代理基于接口,动态代理类和被代理类实现同样的接口。


     2.CGLIB动态代理基于继承,动态代理类继承被代理类,是被代理类的子类。


     3.两者都需要一个中间类来实现具体的代理逻辑,JDK动态代理的中间类必须实现InvocationHandler接口;CGLIB动态代理的中间类必须实现MethodInterceptor接口。


     4.在InvocationHandler中JDK动态代理通过反射调用被代理类的接口,必须持有被代理类的实例引用;在MethodInterceptor中CGLIB动态代理通过继承显示调用被代理类的接口


Spring AOP



  1. AOP相关概念


     1.**Aspect:**切面,一系列Pointcut和Advice的组合。可以想象成多个切点组成了一个切面。


     2.**Joinpoint :**连接点,主要是指类的方法,一个方法就是一个连接点。


     3.**Advice:**当切点与joinpoint匹配时,对joinpoint所采取的行动。具体来说这个Advice就是一个方法,当joinpoint匹配时,就执行这个方法。在Spring AOP中可以用@Before,@After,@Around,@AfterReturn,@AfterThrow注解来定义一个Advice。


     4.**Pointcut:**与 joinpoint 匹配的正则表达式,用于判断连接点是否被命中的规则。主要通过包路径和注解等方式来定义配置规则。


     5.Weaving:将Advice植入到Joinpoint中,具体的方法是代理模式,在Spring中采用的是动态代理,具体有JDK动态代理和CGLib动态代理。


2.工作原理


image.png


3.面向切面编程机制


image.png


4.例子


/**
 * @Description AOP测试类,统计接口运行时长
 */
@Component
@Aspect
public class MethodCallTimeAspect {
    @Pointcut("execution(* com.javhl..*.*(..))")
    private void myPointCut(){
    }
    @Around("myPointCut()")
    public Object advice (ProceedingJoinPoint joinPoint) throws Throwable{
        //方法调用开始时间
        long startTime = System.nanoTime();
        Object result = null;
        try {
            result = joinPoint.proceed();
        }finally {
            long timeUsed = System.nanoTime() - startTime;
            StringBuilder sb = new StringBuilder();
            sb.append("class name = [ ").append(joinPoint.getTarget().getClass().getName()).append(" ] ");
            sb.append("methd name = [ ").append(joinPoint.getSignature().getName()).append(" ]")
                    .append(" 耗时: ").append(timeUsed).append(" 纳秒");
            System.out.println(sb.toString());
        }
        return result;
    }
}


5.Spring框架在创建bean的过程中,当bean完成了初始化后,会用AnnotationAwareAspectJAutoProxyCreator的后置处理器判断bean是否需要代理,如果需要代理(表明配置的Pointcut命中了bean)那么会根据切面中配置的advice为bean创建一个动态代理对象,并将这个对象放到Spring容器中,以后就用这个代理对象来代理具体的bean完成相关业务逻辑。


6.AbstractAutoProxyCreator源码分析


public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
    implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware{
    //BeanPostProcessor的前置处理,这里不做任何处理原样返回
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
      return bean;
    }
    /**
     * 在bean实例化完成之后,对bean进行包装,生成代理对象。
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
      if (bean != null) {
        //获取bean的缓存名称
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        //判断之前创建的代理引用对象中是否包含当前bean,若有:说明bean已经被包装过了直接返回;否则:调用wrapIfNecessary方法对bean进行包装
        //earlyProxyReferences主要是用来解决Spring中的bean循环依赖时(A依赖B,B依赖A),为需要代理的bean创建代理对象的
        if (!this.earlyProxyReferences.contains(cacheKey)) {
          return wrapIfNecessary(bean, beanName, cacheKey);
        }
      }
      return bean;
    }
    /*
    Spring容器启动创建循环依赖的bean的时候,会调用该方法给存在循环依赖的Bean一个创建AOP代理的机会,如果bean满足AOP的条件则创建,否则不创建。
    */
    @Override
    public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        //如果引用不存在则添加
        if (!this.earlyProxyReferences.contains(cacheKey)) {
          this.earlyProxyReferences.add(cacheKey);
        }
        //调用包装方法创建代理对象
        return wrapIfNecessary(bean, beanName, cacheKey);
      }
    /**
     * 判断bean是否需要被包装,如果需要返回代理后的对象,否则返回原对象
     */
    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
      return bean;
    }
    if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
      return bean;
    }
    if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
      this.advisedBeans.put(cacheKey, Boolean.FALSE);
      return bean;
    }
    // 判断该bean是否有匹配的切点,如果有则根据pointcut和advise生成advisors
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    if (specificInterceptors != DO_NOT_PROXY) {
      this.advisedBeans.put(cacheKey, Boolean.TRUE);
      //真正的创建代理的方法,下文会重点分析该方法
      Object proxy = createProxy(
          bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
      this.proxyTypes.put(cacheKey, proxy.getClass());
      return proxy;
    }
    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    return bean;
   }
    protected Object createProxy(
      Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
     //...
    //此句代码是关键,获取具体的代理对象,下面我们继续分析该代码
    //DefaultAopProxyFactory ==>proxyFactory
    return proxyFactory.getProxy(getProxyClassLoader());
  }
 }


7.DefaultAopProxyFactory源码分析


public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
  @Override
  public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
      Class<?> targetClass = config.getTargetClass();
      //判断目标对象是否为空,为空则抛出异常
      if (targetClass == null) {
        //...
      }
      //判断目标对象是否是接口或者JDK动态代理动态生成的对象,
      //若是则构造一个JDK动态代理对象返回,否则构造一个Cglib动态代理对象返回
      if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
        return new JdkDynamicAopProxy(config);
      }
      return new ObjenesisCglibAopProxy(config);
    }
    else {
      return new JdkDynamicAopProxy(config);
    }
  }
}


8.JdkDynamicAopProxy 源码分析:


//这个类实现了InvocationHandler接口
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
  @Override
  public Object getProxy() {
    return getProxy(ClassUtils.getDefaultClassLoader());
  }
  @Override
  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);
    //生成具体的代理对象
    return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
  }
}


目录
打赏
0
0
0
0
4
分享
相关文章
Spring Boot中的AOP实现
Spring AOP(面向切面编程)允许开发者在不修改原有业务逻辑的情况下增强功能,基于代理模式拦截和增强方法调用。Spring Boot通过集成Spring AOP和AspectJ简化了AOP的使用,只需添加依赖并定义切面类。关键概念包括切面、通知和切点。切面类使用`@Aspect`和`@Component`注解标注,通知定义切面行为,切点定义应用位置。Spring Boot自动检测并创建代理对象,支持JDK动态代理和CGLIB代理。通过源码分析可深入了解其实现细节,优化应用功能。
101 6
|
20天前
|
Spring AOP—通知类型 和 切入点表达式 万字详解(通俗易懂)
Spring 第五节 AOP——切入点表达式 万字详解!
88 25
|
20天前
|
Spring AOP—深入动态代理 万字详解(通俗易懂)
Spring 第四节 AOP——动态代理 万字详解!
76 24
Spring Boot 3 集成Spring AOP实现系统日志记录
本文介绍了如何在Spring Boot 3中集成Spring AOP实现系统日志记录功能。通过定义`SysLog`注解和配置相应的AOP切面,可以在方法执行前后自动记录日志信息,包括操作的开始时间、结束时间、请求参数、返回结果、异常信息等,并将这些信息保存到数据库中。此外,还使用了`ThreadLocal`变量来存储每个线程独立的日志数据,确保线程安全。文中还展示了项目实战中的部分代码片段,以及基于Spring Boot 3 + Vue 3构建的快速开发框架的简介与内置功能列表。此框架结合了当前主流技术栈,提供了用户管理、权限控制、接口文档自动生成等多项实用特性。
87 8
Spring Aop该如何使用
本文介绍了AOP(面向切面编程)的基本概念和术语,并通过具体业务场景演示了如何在Spring框架中使用Spring AOP。文章详细解释了切面、连接点、通知、切点等关键术语,并提供了完整的示例代码,帮助读者轻松理解和应用Spring AOP。
104 2
Spring Aop该如何使用
什么是AOP?如何与Spring Boot一起使用?
什么是AOP?如何与Spring Boot一起使用?
113 5
深入解析:Spring AOP的底层实现机制
在现代软件开发中,Spring框架的AOP(面向切面编程)功能因其能够有效分离横切关注点(如日志记录、事务管理等)而备受青睐。本文将深入探讨Spring AOP的底层原理,揭示其如何通过动态代理技术实现方法的增强。
107 8
Spring AOP 底层原理技术分享
Spring AOP(面向切面编程)是Spring框架中一个强大的功能,它允许开发者在不修改业务逻辑代码的情况下,增加额外的功能,如日志记录、事务管理等。本文将深入探讨Spring AOP的底层原理,包括其核心概念、实现方式以及如何与Spring框架协同工作。
|
3月前
|
深入调查研究Spring AOP
【11月更文挑战第15天】
63 5
Spring AOP深度解析:探秘动态代理与增强逻辑
Spring框架中的AOP(Aspect-Oriented Programming,面向切面编程)功能为开发者提供了一种强大的工具,用以将横切关注点(如日志、事务管理等)与业务逻辑分离。本文将深入探讨Spring AOP的底层原理,包括动态代理机制和增强逻辑的实现。
75 4

热门文章

最新文章

AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等