《从零开始写JavaWeb框架》的AOP实现和SpringAOP实现的比较

前言

写本文本文的原因是,刚才看完《从零开始写JavaWeb框架》的AOP实现,于是想比较一下Spring的AOP实现有什么不同。本文没有想比较好坏的意思,只想看看实现方式有什么不同。

正文

1,AOP入口

先说一下《从零开始写JavaWeb框架》(后面简称《框架》)书上介绍的程序的入口方法。这个入口方法是一个静态块,静态块是在类被初始化(initialize的时候被执行的,想了解的朋友可以看看这个文章)。

static {
    try {
        // 创建切面类和目标类们的键值对Map
        Map<Class<?>, Set<Class<?>>> proxyMap = createProxyMap();
        // 目标类和它的切面类们的集合的Map
        Map<Class<?>, List<Proxy>> targetMap = createTargetMap(proxyMap);
        // 创建目标类的代理对象,并替换BeanHelper中的类和实例键值对
        for (Map.Entry<Class<?>, List<Proxy>> targetEntry : targetMap.entrySet()) {
            Class<?> targetClass = targetEntry.getKey();
            List<Proxy> proxies = targetEntry.getValue();
            Object proxy = ProxyManager.createProxy(targetClass, proxies);
            // 把生成的代理对象设置到BeanSet中,替换原来Bean
            BeanHelper.setBean(targetClass, proxy);
        }

    } catch (Exception e) {
        LOGGER.error("AOP initialization error", e);
        throw new RuntimeException(e);
    }
}


上面的块里面的逻辑可以分成下面的大概几块:

  • 目标类和切面的查找
  • 代理的生成
  • 代理对象的设置

下面我们就分开进行一下说明。

2,目标类和切面的查找

目标类和切面的查找 查找这一部分中,主要是下面的两行代码:

// 创建切面类和目标类们的键值对Map。
// Key是切面类(这里只定义了一个切面类ControllerAspect)
// Value是目标类的集合(所有带有Controller注解的类)
Map<Class<?>, Set<Class<?>>> proxyMap = createProxyMap();
// 目标类和它的切面类们的集合的Map(利用上面的结果)
// Key是目标类(带有@Controller注解的类)
// Value是和这目标类相关的切面类的集合(这里只定义一个切面类,ControllerAspect)
Map<Class<?>, List<Proxy>> targetMap = createTargetMap(proxyMap);

我们一行一行分析。先分析一下createProxyMap 方法。

2.1 createProxyMap方法

我们先看一下源码,再来分析:

private static Map<Class<?>, Set<Class<?>>> createProxyMap() {
    // 声明返回对象
    Map<Class<?>, Set<Class<?>>> rtnMap = new HashMap<Class<?>, Set<Class<?>>>();
    // 取得所有定义的切面类
    Set<Class<?>> proxyClasses = ClassHelper.getClassSetBySuper(AspectProxy.class);
    // 根据代理类的注解里的value值(也就是Aspect(contorller.class)中的controller.class),
    // 取得被代理的集合
    for (Class<?> proxyClass : proxyClasses) {
        // 判断注解是不是Aspect
        if (!proxyClass.isAnnotationPresent(Aspect.class))
            continue;

        // 取得带有注解value值(值是一个注解)的业务类集合
        Aspect annotation = proxyClass.getAnnotation(Aspect.class);
        Set<Class<?>> targetClasses = createTargetClassSet(annotation);
        rtnMap.put(proxyClass, targetClasses);

    }
    return rtnMap;
}

这个方法中,核心的代码有两个地方:

  • Set<Class<?>> proxyClasses = ClassHelper.getClassSetBySuper(AspectProxy.class)
  • Set<Class<?>> targetClasses = createTargetClassSet(annotation)

第一个地方的作用是,查找所有切面类;第二个地方的作用是根据切面类注解中的值(这个值也是一个注解:Controller.class),找到切面类所对应的目标对象。下面来看看这两个地方的源码。

2.1.1 getClassSetBySuper

《框架》源码:
看下面的源码,你就会知道,这个方法的实现方法是:

循环所有Bean,看哪个类是“注解基类的子类” 并且 “这个类不是基类本身”(使用了isAssignableFrom方法)

public static Set<Class<?>> getClassSetBySuper(Class<?> superClass) {
    // 声明返回值
    Set<Class<?>> result = new HashSet<>();
    // 循环判断哪个类是这个参数类的子类或子接口
    for (Class<?> clazz : CLASS_SET) {
        if (superClass.isAssignableFrom(clazz) && !superClass.equals(clazz)) {
            result.add(clazz);
        }
    }
    return result;
}

Spring源码:
doGetBeanNamesForType方法是相对应《框架》上面的实现。

  • 这个方法里的for 循环,也是对所有Bean进行循环(这里循环的是名字,不是Class)。
  • 判断哪个Bean是这个参数类的子类也是使用isAssignableFrom 方法来实现的。
private String[] doGetBeanNamesForType(Class<?> type, boolean includeNonSingletons, boolean allowEagerInit) {
List<String> result = new ArrayList<String>();

// Check all bean definitions.
for (String beanName : this.beanDefinitionNames) {
    // Only consider bean as eligible if the bean name
    // is not defined as alias for some other bean.
    if (!isAlias(beanName)) {
        try {
            RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
            // Only check bean definition if it is complete.
            if (!mbd.isAbstract() && (allowEagerInit ||
                    ((mbd.hasBeanClass() || !mbd.isLazyInit() || isAllowEagerClassLoading())) &&
                            !requiresEagerInitForType(mbd.getFactoryBeanName()))) {
                // In case of FactoryBean, match object created by FactoryBean.
                boolean isFactoryBean = isFactoryBean(beanName, mbd);
                boolean matchFound = (allowEagerInit || !isFactoryBean || containsSingleton(beanName)) &&
                        (includeNonSingletons || isSingleton(beanName)) && isTypeMatch(beanName, type);
                if (!matchFound && isFactoryBean) {
                    // In case of FactoryBean, try to match FactoryBean instance itself next.
                    beanName = FACTORY_BEAN_PREFIX + beanName;
                    matchFound = (includeNonSingletons || mbd.isSingleton()) && isTypeMatch(beanName, type);
                }
                if (matchFound) {
                    result.add(beanName);
                }
            }
        }
        catch (CannotLoadBeanClassException ex) {
        ......省略
        }
    }
}
// 这个方法是上面方法中isTypeMatch一层层调用,最后调用的方法
public static boolean isAssignable(Class<?> lhsType, Class<?> rhsType) {
    Assert.notNull(lhsType, "Left-hand side type must not be null");
    Assert.notNull(rhsType, "Right-hand side type must not be null");
    if (lhsType.isAssignableFrom(rhsType)) {
        return true;
    }
    if (lhsType.isPrimitive()) {
        Class<?> resolvedPrimitive = primitiveWrapperTypeMap.get(rhsType);
        if (resolvedPrimitive != null && lhsType.equals(resolvedPrimitive)) {
            return true;
        }
    }
    else {
        Class<?> resolvedWrapper = primitiveTypeToWrapperMap.get(rhsType);
        if (resolvedWrapper != null && lhsType.isAssignableFrom(resolvedWrapper)) {
            return true;
        }
    }
    return false;
}

结论:

查找切面的实现方法基本一致。


2.1.2 createTargetClassSet

这个方法的作用是根据注解(Controller),找出所有使用了注解的类。《框架》和Spring的实现方式有所不同,Spring是通过目标类,找到所有适合的切面。如果要比较的话,和下面的createTargetMap方法可以比较一下。
结论:

因为实现方式的不同,这里无法比较


2.2 createTargetMap方法

《框架》源码:
这个方法的作用是,利用getClassSetBySuper方法取得“切面”和“目标对象们”的Map,转换成“目标对象”和“切面们”的Map。一次把所有的“目标对象和他们的切面们”都准备好。

private static Map<Class<?>, List<Proxy>> createTargetMap(
        Map<Class<?>, Set<Class<?>>> proxyMap)
        throws IllegalAccessException, InstantiationException {
    Map<Class<?>, List<Proxy>> rtnClasses = new HashMap<>();

    // 循环所有被代理的类,组成一个被代理类和它的代理们的键值对
    for (Map.Entry<Class<?>, Set<Class<?>>> classSetEntry : proxyMap.entrySet()) {
        // 取得代理类
        Class<?> proxyClass = classSetEntry.getKey();
        // 循环代理类的子类
        for (Class<?> targetClass : classSetEntry.getValue()) {
            Proxy proxy = (Proxy)proxyClass.newInstance();

            if (rtnClasses.containsKey(targetClass)) {
                rtnClasses.get(targetClass).add(proxy);
            } else {
                ArrayList<Proxy> proxies = new ArrayList<>();
                proxies.add(proxy);
                rtnClasses.put(targetClass, proxies);
            }
        }
    }

    return rtnClasses;
}

Spring源码:
而Spring的实现是,每个Bean要生成时,去查找这个要生成的Bean能够适配的切面。如果找到了,就生成代理;如果没有就把原来的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;
    }

    // Create proxy if we have advice.
    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;
}

结论:

  • 《框架》:一次把所有的“目标对象和他们的切面们”都准备好。
  • Spring: 每个Bean要生成时,去查找这个要生成的Bean能够适配的切面。


3,代理的生成

《框架》源码:
在createProxy方法中,其实做了两件事:
1. 生成代理对象(Enhancer.create())
2. 定义代理对象的回调方法(new MethodInterceptor())

public static Object createProxy(final Class<?> targetClass, final List<Proxy> proxyList) {
    // 生成代理对象
    return Enhancer.create(targetClass, 
        // 定义回调方法
        new MethodInterceptor() {
        @Override
        public Object intercept(
                Object targetObject,
                Method method,
                Object[] methodParams,
                MethodProxy methodProxy) throws Throwable {
            // 回调方法中,生成一个ProxyChain对象,并调用doProxyChain方法,开始代理链的调用
            return new ProxyChain(targetClass, targetObject, method, methodParams, methodProxy, proxyList).doProxyChain();
        }
    });
}

Spring源码:
在Spring的代码中,把《框架》中做的事,分成了2个方法:

  • getProxy:生成代理对象
  • intercept:定义代理对象的回调方法

其实作用都是一样的。然后也调用了一个方法,叫做proceed,作用和上面的doProxyChain也是一样的。proxy链的调用也是一样的,代码就不贴了。

CglibAopProxy.java

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

    try {
        Class<?> rootClass = this.advised.getTargetClass();
        Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");

        Class<?> proxySuperClass = rootClass;
        if (ClassUtils.isCglibProxyClass(rootClass)) {
            proxySuperClass = rootClass.getSuperclass();
            Class<?>[] additionalInterfaces = rootClass.getInterfaces();
            for (Class<?> additionalInterface : additionalInterfaces) {
                this.advised.addInterface(additionalInterface);
            }
        }

        // Validate the class, writing log messages as necessary.
        validateClassIfNecessary(proxySuperClass, classLoader);

        // Configure CGLIB Enhancer...
        Enhancer enhancer = createEnhancer();
        if (classLoader != null) {
            enhancer.setClassLoader(classLoader);
            if (classLoader instanceof SmartClassLoader &&
                    ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
                enhancer.setUseCache(false);
            }
        }
        enhancer.setSuperclass(proxySuperClass);
        enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
        enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
        enhancer.setStrategy(new UndeclaredThrowableStrategy(UndeclaredThrowableException.class));

        Callback[] callbacks = getCallbacks(rootClass);
        Class<?>[] types = new Class<?>[callbacks.length];
        for (int x = 0; x < types.length; x++) {
            types[x] = callbacks[x].getClass();
        }
        // fixedInterceptorMap only populated at this point, after getCallbacks call above
        enhancer.setCallbackFilter(new ProxyCallbackFilter(
                this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
        enhancer.setCallbackTypes(types);

        // Generate the proxy class and create a proxy instance.
        return createProxyClassAndInstance(enhancer, callbacks);
    }
    catch (CodeGenerationException 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 (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);
    }
}
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
    Object oldProxy = null;
    boolean setProxyContext = false;
    Class<?> targetClass = null;
    Object target = null;
    try {
        if (this.advised.exposeProxy) {
            // Make invocation available if necessary.
            oldProxy = AopContext.setCurrentProxy(proxy);
            setProxyContext = true;
        }
        // May be null. Get as late as possible to minimize the time we
        // "own" the target, in case it comes from a pool...
        target = getTarget();
        if (target != null) {
            targetClass = target.getClass();
        }
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
        Object retVal;
        // Check whether we only have one InvokerInterceptor: that is,
        // no real advice, but just reflective invocation of the target.
        if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
            // We can skip creating a MethodInvocation: just invoke the target directly.
            // Note that the final invoker must be an InvokerInterceptor, so we know
            // it does nothing but a reflective operation on the target, and no hot
            // swapping or fancy proxying.
            retVal = methodProxy.invoke(target, args);
        }
        else {
            // We need to create a method invocation...
            retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
        }
        retVal = processReturnType(proxy, target, method, retVal);
        return retVal;
    }
    finally {
        if (target != null) {
            releaseTarget(target);
        }
        if (setProxyContext) {
            // Restore old proxy.
            AopContext.setCurrentProxy(oldProxy);
        }
    }
}


结论:

创建代理的机制基本一致(细节的地方略去)


4,总结

上面的结论来看,除了在目标类和切面的对应关系取得时方式不太一样外,结构框架基本都是一样的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值