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);
  }
}


目录
相关文章
|
2月前
|
Java
Spring5入门到实战------9、AOP基本概念、底层原理、JDK动态代理实现
这篇文章是Spring5框架的实战教程,深入讲解了AOP的基本概念、如何利用动态代理实现AOP,特别是通过JDK动态代理机制在不修改源代码的情况下为业务逻辑添加新功能,降低代码耦合度,并通过具体代码示例演示了JDK动态代理的实现过程。
Spring5入门到实战------9、AOP基本概念、底层原理、JDK动态代理实现
|
3天前
|
存储 缓存 Java
Spring高手之路23——AOP触发机制与代理逻辑的执行
本篇文章深入解析了Spring AOP代理的触发机制和执行流程,从源码角度详细讲解了Bean如何被AOP代理,包括代理对象的创建、配置与执行逻辑,帮助读者全面掌握Spring AOP的核心技术。
11 3
Spring高手之路23——AOP触发机制与代理逻辑的执行
|
1月前
|
设计模式 Java 测试技术
spring复习04,静态代理动态代理,AOP
这篇文章讲解了Java代理模式的相关知识,包括静态代理和动态代理(JDK动态代理和CGLIB),以及AOP(面向切面编程)的概念和在Spring框架中的应用。文章还提供了详细的示例代码,演示了如何使用Spring AOP进行方法增强和代理对象的创建。
spring复习04,静态代理动态代理,AOP
|
3月前
|
Java Spring
在Spring Boot中使用AOP实现日志切面
在Spring Boot中使用AOP实现日志切面
|
13天前
|
Java 编译器 Spring
Spring AOP 和 AspectJ 的区别
Spring AOP和AspectJ AOP都是面向切面编程(AOP)的实现,但它们在实现方式、灵活性、依赖性、性能和使用场景等方面存在显著区别。‌
28 2
|
21天前
|
Java Spring 容器
Spring IOC、AOP与事务管理底层原理及源码解析
【10月更文挑战第1天】Spring框架以其强大的控制反转(IOC)和面向切面编程(AOP)功能,成为Java企业级开发中的首选框架。本文将深入探讨Spring IOC和AOP的底层原理,并通过源码解析来揭示其实现机制。同时,我们还将探讨Spring事务管理的核心原理,并给出相应的源码示例。
74 9
|
13天前
|
XML Java 数据格式
Spring的IOC和AOP
Spring的IOC和AOP
32 0
|
1月前
|
Java 数据库连接 数据库
Spring基础3——AOP,事务管理
AOP简介、入门案例、工作流程、切入点表达式、环绕通知、通知获取参数或返回值或异常、事务管理
Spring基础3——AOP,事务管理
|
2月前
|
XML Java 数据格式
Spring5入门到实战------11、使用XML方式实现AOP切面编程。具体代码+讲解
这篇文章是Spring5框架的AOP切面编程教程,通过XML配置方式,详细讲解了如何创建被增强类和增强类,如何在Spring配置文件中定义切入点和切面,以及如何将增强逻辑应用到具体方法上。文章通过具体的代码示例和测试结果,展示了使用XML配置实现AOP的过程,并强调了虽然注解开发更为便捷,但掌握XML配置也是非常重要的。
Spring5入门到实战------11、使用XML方式实现AOP切面编程。具体代码+讲解
|
2月前
|
缓存 Java 开发者
Spring高手之路22——AOP切面类的封装与解析
本篇文章深入解析了Spring AOP的工作机制,包括Advisor和TargetSource的构建与作用。通过详尽的源码分析和实际案例,帮助开发者全面理解AOP的核心技术,提升在实际项目中的应用能力。
37 0
Spring高手之路22——AOP切面类的封装与解析