乐趣区

关于java:如何实现一个简易版的-Spring-如何实现-AOP终结篇

前言

在 上篇 实现了 判断一个类的形式是合乎配置的 pointcut 表达式、依据一个 Bean 的名称和办法名,获取 Method 对象、实现了 BeforeAdvice、AfterReturningAdvice 以及 AfterThrowingAdvice 并依照指定秩序调用 等性能,这篇再来看看剩下的 代理对象如何生成 依据 XML 配置文件生成 BeanDefintion以及 如何将生成的代理对象放入到容器中 等性能,话不多说,上面进入主题。

代理对象生成

代理对象的生成策略和 Spring 框架统一,当被代理类实现了接口时采纳 JDK 动静代理的形式生成代理对象,被代理对象未实现接口时应用 CGLIB 来生成代理对象,为了简略起见这里不反对手动指定生成代理对象的策略,JDK 动静代理的实现这里不在介绍,感兴趣能够本人实现一下,这里次要探讨 CGLIB 的生成形式。

基于面向接口编程的思维,这里的生成代理对象须要定义一个对立的接口,不论是 CGLIB 生成形式还是 JDK 动静代理生成形式都要实现该接口。生成代理对象是依据一些配置去生成的,同样,这里生成代理的配置也能够抽取一个对立的接口,在实现类中定义拦截器(也就是 Advice)以及实现的接口等,CGLIB 的根本应用能够到官网自行查找。代理对象生成的整体的类图如下:

其中代理创立的工厂接口 AopProxyFactory 如下,提供了不指定 ClassLoader(应用默认的 ClassLoader)和指定 ClassLoader 两种形式创立代理对象,源码如下:

/**
 * @author mghio
 * @since 2021-06-13
 */
public interface AopProxyFactory {Object getProxy();

  Object getProxy(ClassLoader classLoader);

}

应用 CGLIB 创立代理的工厂接口实现类如下所示:

/**
 * @author mghio
 * @since 2021-06-13
 */
public class CglibProxyFactory implements AopProxyFactory {

  /*
   * Constants for CGLIB callback array indices
   */
  private static final int AOP_PROXY = 0;

  protected final Advised advised;

  public CglibProxyFactory(Advised config) {Assert.notNull(config, "AdvisedSupport must not be null");
    if (config.getAdvices().size() == 0) {throw new AopConfigException("No advisors and no TargetSource specified");
    }

    this.advised = config;
  }

  @Override
  public Object getProxy() {return getProxy(null);
  }

  @Override
  public Object getProxy(ClassLoader classLoader) {if (logger.isDebugEnabled()) {logger.debug("Creating CGLIB proxy: target class is" + this.advised.getTargetClass());
    }

    try {Class<?> rootClass = this.advised.getTargetClass();

      // Configure CGLIB Enhancer...
      Enhancer enhancer = new Enhancer();
      if (classLoader != null) {enhancer.setClassLoader(classLoader);
      }
      enhancer.setSuperclass(rootClass);
      enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);  // BySpringCGLIB
      enhancer.setInterceptDuringConstruction(false);

      Callback[] callbacks = getCallbacks(rootClass);
      Class<?>[] types = new Class<?>[callbacks.length];
      for (int i = 0; i < types.length; i++) {types[i] = callbacks[i].getClass();}
      enhancer.setCallbackFilter(new ProxyCallbackFilter(this.advised));
      enhancer.setCallbackTypes(types);
      enhancer.setCallbacks(callbacks);

      // Generate the proxy class and create a proxy instance.
      return enhancer.create();}
    catch (CodeGenerationException | IllegalArgumentException ex) {
      throw new AopConfigException("Could not generate CGLIB subclass of class [" +
          this.advised.getTargetClass() + "]:" +
          "Common causes of this problem include using a final class or a non-visible class",
          ex);
    } catch (Exception ex) {// TargetSource.getTarget() failed
      throw new AopConfigException("Unexpected AOP exception", ex);
    }
  }

  // omit other methods ...

}

整体来看还是比较简单的,次要是 CGLIB 第三方字节码生成库的根本用法,当然,前提是你曾经理解了 CGLIB 的根本应用。AOP 的相干配置接口 Advised 相对来说就比较简单了,次要是一些相干属性的增、删、改等操作,次要局部代码如下:

/**
 * @author mghio
 * @since 2021-06-13
 */
public interface Advised {Class<?> getTargetClass();

  boolean isInterfaceProxied(Class<?> intf);

  List<Advice> getAdvices();

  void addAdvice(Advice advice);

  List<Advice> getAdvices(Method method);

  void addInterface(Class<?> clazz);

  // omit other methods ...

}

实现类也比较简单,代码如下:

/**
 * @author mghio
 * @since 2021-06-13
 */
public class AdvisedSupport implements Advised {

  private boolean proxyTargetClass = false;
  private Object targetObject = null;
  private final List<Advice> advices = new ArrayList<>();
  private final List<Class<?>> interfaces = new ArrayList<>();

  public AdvisedSupport() {}

  @Override
  public Class<?> getTargetClass() {return this.targetObject.getClass();
  }

  @Override
  public boolean isInterfaceProxied(Class<?> intf) {return interfaces.contains(intf);
  }

  @Override
  public List<Advice> getAdvices() {return this.advices;}

  @Override
  public void addAdvice(Advice advice) {this.advices.add(advice);
  }

  @Override
  public List<Advice> getAdvices(Method method) {List<Advice> result = new ArrayList<>();
    for (Advice advice : this.getAdvices()) {Pointcut pc = advice.getPointcut();
      if (pc.getMethodMatcher().matches(method)) {result.add(advice);
      }
    }
    return result;
  }

  @Override
  public void addInterface(Class<?> clazz) {this.interfaces.add(clazz);
  }

  // omit other methods ...

}

到这里,代理对象应用 CGLIB 生成的形式就曾经实现了,外围代码其实比较简单,次要是须要多思考思考代码前期的扩展性。

创立 BeanDefinition

咱们先来看看个别 AOP 在 XML 配置文件中是如何定义的,一个蕴含 BeforeAdvice、AfterReturningAdvice 以及 AfterThrowingAdvice 的 XML 配置文件如下:

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.e3.org/2001/XMLSchema-instance"
  xmlns:aop="http://www.springframework.org/schema/aop"
  xmlns:context="http://www.springframework.org/schema/context"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/aop
           http://www.springframework.org/schema/aop/spring-aop.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/beans/spring-context.xsd">

  <context:scann-package base-package="cn.mghio.service.version5,cn.mghio.dao.version5" />

  <bean id="tx" class="cn.mghio.tx.TransactionManager"/>

  <aop:config>
    <aop:aspect ref="tx">
      <aop:pointcut id="placeOrder" expression="execution(* cn.mghio.service.version5.*.placeOrder(..))"/>
      <aop:before pointcut-ref="placeOrder" method="start"/>
      <aop:after-returning pointcut-ref="placeOrder" method="commit"/>
      <aop:after-throwing pointcut-ref="placeOrder" method="rollback"/>
    </aop:aspect>
  </aop:config>
</beans>

有了之前解析 XML 的 Bean 定义的教训后,很显然这里咱们须要一个数据结构去示意这个 AOP 配置,如果你浏览过 上篇 的话,类 AspectJExpressionPointcut 示意的是 <aop:pointcut id=”placeOrder” expression=”execution( cn.mghio.service.version5..placeOrder(..))”/>,另外几个 Advice 配置别离对应 AspectJBeforeAdvice、AspectJAfterReturningAdvice 以及 AspectJAfterThrowingAdvice 等几个类。
这里只有解析 XML 配置文件,而后应用对应的 Advice 的结构器创立对应的对象即可,解析 XML 应用的是 dom4j,次要局部代码如下所示:

/**
 * @author mghio
 * @since 2021-06-13
 */
public class ConfigBeanDefinitionParser {

  private static final String ASPECT = "aspect";
  private static final String EXPRESSION = "expression";
  private static final String ID = "id";
  private static final String REF = "ref";
  private static final String BEFORE = "before";
  private static final String AFTER = "after";
  private static final String AFTER_RETURNING_ELEMENT = "after-returning";
  private static final String AFTER_THROWING_ELEMENT = "after-throwing";
  private static final String AROUND = "around";
  private static final String POINTCUT = "pointcut";
  private static final String POINTCUT_REF = "pointcut-ref";
  private static final String ASPECT_NAME_PROPERTY = "aspectName";

  public void parse(Element element, BeanDefinitionRegistry registry) {List<Element> childElements = element.elements();
    for (Element el : childElements) {String localName = el.getName();
      if (ASPECT.equals(localName)) {parseAspect(el, registry);
      }
    }
  }

  private void parseAspect(Element aspectElement, BeanDefinitionRegistry registry) {String aspectName = aspectElement.attributeValue(REF);

    List<BeanDefinition> beanDefinitions = new ArrayList<>();
    List<RuntimeBeanReference> beanReferences = new ArrayList<>();

    // parse advice
    List<Element> elements = aspectElement.elements();
    boolean adviceFoundAlready = false;
    for (Element element : elements) {if (isAdviceNode(element)) {if (!adviceFoundAlready) {
          adviceFoundAlready = true;
          if (!StringUtils.hasText(aspectName)) {return;}
          beanReferences.add(new RuntimeBeanReference(aspectName));
        }
        GenericBeanDefinition advisorDefinition = parseAdvice(aspectName, element, registry,
            beanDefinitions, beanReferences);
        beanDefinitions.add(advisorDefinition);
      }
    }

    // parse pointcut
    List<Element> pointcuts = aspectElement.elements(POINTCUT);
    for (Element pointcut : pointcuts) {parsePointcut(pointcut, registry);
    }
  }

  private void parsePointcut(Element pointcutElement, BeanDefinitionRegistry registry) {String id = pointcutElement.attributeValue(ID);
    String expression = pointcutElement.attributeValue(EXPRESSION);

    GenericBeanDefinition pointcutDefinition = createPointcutDefinition(expression);
    if (StringUtils.hasText(id)) {registry.registerBeanDefinition(id, pointcutDefinition);
    } else {BeanDefinitionReaderUtils.registerWithGeneratedName(pointcutDefinition, registry);
    }
  }

  private GenericBeanDefinition parseAdvice(String aspectName, Element adviceElement,
      BeanDefinitionRegistry registry, List<BeanDefinition> beanDefinitions,
      List<RuntimeBeanReference> beanReferences) {GenericBeanDefinition methodDefinition = new GenericBeanDefinition(MethodLocatingFactory.class);
    methodDefinition.getPropertyValues().add(new PropertyValue("targetBeanName", aspectName));
    methodDefinition.getPropertyValues().add(new PropertyValue("methodName",
        adviceElement.attributeValue("method")));
    methodDefinition.setSynthetic(true);

    // create instance definition factory
    GenericBeanDefinition aspectFactoryDef = new GenericBeanDefinition(AopInstanceFactory.class);
    aspectFactoryDef.getPropertyValues().add(new PropertyValue("aspectBeanName", aspectName));
    aspectFactoryDef.setSynthetic(true);

    // register the pointcut
    GenericBeanDefinition adviceDef = createAdviceDefinition(adviceElement, aspectName,
        methodDefinition, aspectFactoryDef, beanDefinitions, beanReferences);
    adviceDef.setSynthetic(true);

    // register the final advisor
    BeanDefinitionReaderUtils.registerWithGeneratedName(adviceDef, registry);

    return adviceDef;
  }

  // omit other methods ...

}

创立 BeanDefinition 曾经实现了,当初可依据 XML 配置文件解析出对应的 BeanDefintion 了,上面只须要在适合的机会将这些 BeanDefinition 放到容器中就实现了全副流程了。

如何放到容器中

该如何把解析进去的 BeanDefintion 放到容器当中去呢?咱们晓得在 Spring 框架当中提供了很多的“钩子函数”,能够从这里动手,Bean 的生命周期如下:

抉择在 Bean 实例化实现之后 BeanPostProcessor 的 postProcessAfterInitialization() 办法创立代理对象,AOP 应用的是 AspectJ,将创立代理对象的类命名为 AspectJAutoProxyCreator,实现 BeanPostProcessor 接口,解决代理对象的创立,AspectJAutoProxyCreator 类的外围源码如下:

/**
 * @author mghio
 * @since 2021-06-13
 */
public class AspectJAutoProxyCreator implements BeanPostProcessor {

  private ConfigurableBeanFactory beanFactory;

  @Override
  public Object beforeInitialization(Object bean, String beanName) throws BeansException {return bean;}

  @Override
  public Object afterInitialization(Object bean, String beanName) throws BeansException {
    // 如果这个 bean 自身就是 Advice 及其子类,则不生成动静代理
    if (isInfrastructureClass(bean.getClass())) {return bean;}

    List<Advice> advices = getCandidateAdvices(bean);
    if (advices.isEmpty()) {return bean;}

    return createProxy(advices, bean);
  }

  protected Object createProxy(List<Advice> advices, Object bean) {Advised config = new AdvisedSupport();
    for (Advice advice : advices) {config.addAdvice(advice);
    }

    Set<Class> targetInterfaces = ClassUtils.getAllInterfacesForClassAsSet(bean.getClass());
    for (Class targetInterface : targetInterfaces) {config.addInterface(targetInterface);
    }
    config.setTargetObject(bean);

    AopProxyFactory proxyFactory = null;
    if (config.getProxiedInterfaces().length == 0) {
      // CGLIB 代理
      proxyFactory = new CglibProxyFactory(config);
    } else {// TODO(mghio): JDK dynamic proxy ...

    }

    return proxyFactory.getProxy();}

  public void setBeanFactory(ConfigurableBeanFactory beanFactory) {this.beanFactory = beanFactory;}

  private List<Advice> getCandidateAdvices(Object bean) {List<Object> advices = this.beanFactory.getBeansByType(Advice.class);
    List<Advice> result = new ArrayList<>();
    for (Object advice : advices) {Pointcut pointcut = ((Advice) advice).getPointcut();
      if (canApply(pointcut, bean.getClass())) {result.add((Advice) advice);
      }
    }
    return result;
  }

  private boolean canApply(Pointcut pointcut, Class<?> targetClass) {MethodMatcher methodMatcher = pointcut.getMethodMatcher();
    Set<Class> classes = new LinkedHashSet<>(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
    classes.add(targetClass);
    for (Class<?> clazz : classes) {Method[] methods = clazz.getDeclaredMethods();
      for (Method m : methods) {if (methodMatcher.matches(m)) {return true;}
      }
    }
    return false;
  }

  private boolean isInfrastructureClass(Class<?> beanClass) {return Advice.class.isAssignableFrom(beanClass);
  }
}

最初别忘了,这里的 BeanPostProcessor 接口是咱们新加的,须要到之前定义的 DefaultFactoryBean 中加上对 BeanPostProcessor 的解决逻辑,次要批改如下:

public class DefaultBeanFactory extends AbstractBeanFactory implements BeanDefinitionRegistry {

    @Override
    public Object createBean(BeanDefinition bd) throws BeanCreationException {
        // 1. instantiate bean
        Object bean = instantiateBean(bd);
        // 2. populate bean
        populateBean(bd, bean);
        // 3. initialize bean
        bean = initializeBean(bd, bean);
        return bean;
    }

    protected Object initializeBean(BeanDefinition bd, Object bean) {
        
        ...

        // 非合成类型则创立代理
        if (!bd.isSynthetic()) {return applyBeanPostProcessorAfterInitialization(bean, bd.getId());
        }
        return bean;
    }

    private Object applyBeanPostProcessorAfterInitialization(Object existingBean, String beanName) {
        Object result = existingBean;
        for (BeanPostProcessor postProcessor : getBeanPostProcessors()) {result = postProcessor.afterInitialization(result, beanName);
            if (result == null) {return null;}
        }
        return result;
    }

    // omit other field and methods ...

}

最初运行当时测试用例,失常通过合乎预期。

总结

本文次要介绍了 AOP 代理对象生成、解析 XML 配置文件并创立对应的 BeanDefinition 以及最初注入到容器中,只是介绍了大体实现思路,具体代码实现已上传 mghio-spring,感兴趣的敌人能够参考,到这里,AOP 实现局部曾经全副介绍结束。

退出移动版