共计 11876 个字符,预计需要花费 30 分钟才能阅读完成。
前言
在 上篇 实现了 判断一个类的形式是合乎配置的 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 实现局部曾经全副介绍结束。