前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >学习AOP之透过Spring的Ioc理解Advisor

学习AOP之透过Spring的Ioc理解Advisor

作者头像
用户1105954
发布2018-01-12 17:44:03
8030
发布2018-01-12 17:44:03
举报
文章被收录于专栏:mini188mini188

花了几天时间来学习Spring,突然明白一个问题,就是看书不能让人理解Spring,一方面要结合使用场景,另一方面要阅读源代码,这种方式理解起来事半功倍。那看书有什么用呢?主要还是扩展视野,毕竟书是别人总结出来的东西,看一遍可以发现自己的理解偏差,而且还可以看到一些平时不太关注的内容,当然看也可以是一种学习技术的方式。

最开始只是想了解一下AOP,没想到就陷的这么深,为了搞清楚spring是如何完成切面功能这两天还是把Ioc部分的内容也给读了读。还是看懂了大概,只不过这复杂的内部结构确实不易理解与阅读,我在想Spring确实是个好的开源软件,但代码可能真的少了点亲近感。一个BeanFactory和FactroyBean就可以写上好几页纸来说明,毕竟这些名字没有多少Ioc的影子。

一、Spring Ioc的简单理解

对于Ioc的功能就不再多说,这里主要是理解一下Ioc的关键代码,至于BeanFactory、ApplicationContent、Resource之类就不说了,直接从getBean开始吧。

从最基本的容器开始:

代码语言:javascript
复制
public interface ISay {
    void say();
    void noaop();
}

public class SayImpl implements ISay{

    public void say() {
        System.out.print("我是5207.");
    }

    public void noaop() {
        System.out.println("别aop我");
    }

}


public class Client {

    @SuppressWarnings("resource")
    public static void main(String[] args) {
        XmlBeanFactory benBeanFactory = new XmlBeanFactory(new ClassPathResource("aop/demo/spring.xml"));
        ISay say1 = (ISay) benBeanFactory.getBean("sayImpl");
        say1.say();
        say1.noaop();
    }
}

这里面有一个sayImpl的java代码,下面是spring.xml文件:

代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       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/context
       http://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="sayImpl" class="aop.demo.SayImpl"></bean>
</beans>

运行Client的会得到下面的结果:

我是5207.别aop我

这里的XmlBeanFactory只是用来解析Xml文件而创建的类,它本身只是传递了一个Resource而已,最终的容器功能是在AbstractBeanFactory中完成,那么我们直接就看AbstractBeanFactory中getBean的实现,而getBean又调用了doGetBean方法,这才是本尊。

下面是doGetBean的主要代码,代码太多,有些不是很重要的就删除了。

代码语言:javascript
复制
protected <T> T doGetBean(
        final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
        throws BeansException {

    final String beanName = transformedBeanName(name);
    Object bean;

    //先从缓存里查找单例的对象
    Object sharedInstance = getSingleton(beanName);
    if (sharedInstance != null && args == null) {
        //如果对象存在则判断是否要通过FactoryBean来审批
        bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }
    else {
        //判断是否需要从父ParentBeanFactory获得对象
        BeanFactory parentBeanFactory = getParentBeanFactory();
        if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
            // Not found -> check parent.
            String nameToLookup = originalBeanName(name);
            if (args != null) {
                // Delegation to parent with explicit args.
                return (T) parentBeanFactory.getBean(nameToLookup, args);
            }
            else {
                // No args -> delegate to standard getBean method.
                return parentBeanFactory.getBean(nameToLookup, requiredType);
            }
        }

        //先把当前bean依赖的bean给加载起来,通过getBean递归来完成
        String[] dependsOn = mbd.getDependsOn();
        if (dependsOn != null) {
            for (String dependsOnBean : dependsOn) {
                getBean(dependsOnBean);
                registerDependentBean(dependsOnBean, beanName);
            }
        }

        if (mbd.isSingleton()) {
            //创建单实例的对象
            sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
                public Object getObject() throws BeansException {
                    try {
                        //这里是真正的实例化一个对象的地方
                        return createBean(beanName, mbd, args);
                    }
                    catch (BeansException ex) {
                        destroySingleton(beanName);
                        throw ex;
                    }
                }
            });
            //看看是不是FactoryBean,如果是的话要使用FactoryBean.getObject来返回对象
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
        }
        else if (mbd.isPrototype()) {
            // 如果是prototype,创建新对象
            Object prototypeInstance = null;
            try {
                beforePrototypeCreation(beanName);
                prototypeInstance = createBean(beanName, mbd, args);
            }
            finally {
                afterPrototypeCreation(beanName);
            }
            bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
        }

        else {
            String scopeName = mbd.getScope();
            final Scope scope = this.scopes.get(scopeName);
            if (scope == null) {
                throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");
            }
            try {
                Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
                    public Object getObject() throws BeansException {
                        beforePrototypeCreation(beanName);
                        try {
                            return createBean(beanName, mbd, args);
                        }
                        finally {
                            afterPrototypeCreation(beanName);
                        }
                    }
                });
                bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
            }
            catch (IllegalStateException ex) {
                throw new BeanCreationException(beanName,
                        "Scope '" + scopeName + "' is not active for the current thread; " +
                        "consider defining a scoped proxy for this bean if you intend to refer to it from a singleton",
                        ex);
            }
        }
    }

    return (T) bean;
}

其实上面的代码中可以找出一段代码中完成核心调用的是createBean和getObjectForBeanInstance这两个方法。getObjectForBeanInstance的主要逻辑是

  • 检查对象是否为FactoryBean类型,如果不是直接返回对象实例
  • 如果是FactoryBean类型,那么就要通过FactoryBean的getObject来返回对象实例

看到这里就明白了FactoryBean的用处了吧,这个也是Spring Aop与Ioc进行交互的一个重要逻辑。

再来看看createBean

会发现createBean在AbstractBeanFactory中只是一个虚方法,说明是在派生类中实现的,前面是从XmlBeanFactory开始的,那么顺着它的继承关系看看,最终是在AbstractAutowireCapableBeanFactory中找到了createBean的实现:

代码语言:javascript
复制
@Override
protected Object createBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
        throws BeanCreationException {
....省略.
    Object beanInstance = doCreateBean(beanName, mbd, args);
....省略.
    return beanInstance;
}

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
    BeanWrapper instanceWrapper = null;
    if (mbd.isSingleton()) {
        instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
    }
    if (instanceWrapper == null) {
        instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
    final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
    Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);

....省略.
    // Initialize the bean instance.
    Object exposedObject = bean;
    try {
        populateBean(beanName, mbd, instanceWrapper);
        if (exposedObject != null) {
            exposedObject = initializeBean(beanName, exposedObject, mbd);
        }
    }
    catch (Throwable ex) {
....省略.
    }
....省略.
    return exposedObject;
}

代码太多了,找关键部分吧。createBean的过程主要是

  • 生成bean的wrapper

首先是创建好bean,然后将新创建的bean实例并放在BeanWrapper中返回。

  • populateBean的调用

1、先是执行InstantiationAwareBeanPostProcessor的postProcessAfterInstantiation,这里会有实例创建后的一个回调,如果是false会退出整个过程。

2、完成对象的注入autowireByName/autowireByType,这里就看到很眼熟的东西了吧“autowire”。

3、调用InstantiationAwareBeanPostProcessor的postProcessPropertyValues

4、最后是applyPropertyValues方法,把属性值都写入

  • initializeBean的调用

1、先是invokeAwareMethods,完成BeanNameAware、BeanClassLoaderAware、BeanFactoryAware的注入

2、然后是BeanPostProcessor的postProcessBeforeInitialization调用

3、完成InitializingBean的调用

4、然后是BeanPostProcessor的postProcessAfterInitialization调用,这里就是后面Aop中Advistor接管对象的地方啦

可以看到initializeBean方法最后返回的是wrappedBean有两种可能,一种是经过BeanPostProcessor生成的对象,如果当前bean没有注册BeanPostProcessor的话就直接返回bean自己。aop中就运用这个方法来完成代码的切面编程。

小结 写到这Ioc创建一个对象的过程就说完了,过程中发现了很多Ioc容器对一个对象生成过程的技巧与奥秘,也理解了为什么可以在spring中灵活的完成对对象创建的各种“干预”。

二、Advisor与Ioc的联系

好了,接下来开始进入spring aop部分,前面的例子做一些补充加入aop的功能,spring.xml如下:

代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       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/context
       http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 声明被代理的目标对象 -->
    <bean id="sayImpl" class="aop.demo.SayImpl"></bean>
    <!-- 声明用于增强的拦截器对象 -->
    <bean id="sayImplAroundAdvice" class="aop.demo.SayImplAroundAdvice"></bean>

    <!-- 配置一个切面 -->
    <bean id="sayAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <property name="advice" ref="sayImplAroundAdvice"/>            <!-- 增强 -->
        <property name="pattern" value="aop.demo.SayImpl.s.*"/> <!-- 切点(正则表达式) -->
    </bean>   
   
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
        <property name="optimize" value="true"/>
    </bean>
</beans>

这里有一个环绕增强及一个切面,最后使用DefaultAdvisorAutoProxyCreator来完成自动代理。前面在学习Ioc时使用的是XmlBeanFactory,是一个基本的基于xml的ioc容器,那么我们再使用ClassPathXmlApplicationContext吧,看看会发生什么变化:

代码语言:javascript
复制
public class Client {

    @SuppressWarnings("resource")
    public static void main(String[] args) {
        XmlBeanFactory benBeanFactory = new XmlBeanFactory(new ClassPathResource("aop/demo/spring.xml"));
        ISay say1 = (ISay) benBeanFactory.getBean("sayImpl");
        say1.say();
        say1.noaop();

        ApplicationContext context = new ClassPathXmlApplicationContext("aop/demo/spring.xml");
        ISay say = (ISay)context.getBean("sayImpl");
        say.say();
        say.noaop();
    }
}

执行结果:

我是5207.别aop我 大家好:我是5207.希望大家多多点赞. 别aop我

可以发现使用ClassPathXmlApplicationContext容器时aop的功能才有用,有点奇怪啊,都是读取的配置为啥结果不同呢?这里主要是BeanFactory和ApplicationContext的主要区别之一。查看XmlBeanFactory的加载过程发现其只是调用了一个XmlBeanDefinitionReader读取了spring.xml并解析为BeanDefinition,然后具体的bean都要到getBean时才会去实例化,而且bean的依赖也要自己去处理。

那applicationContext呢?从ClassPathXmlApplicationContext的代码发现就复杂一些,在构造函数中有一个调用比较特别:

代码语言:javascript
复制
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
        throws BeansException {

    super(parent);
    setConfigLocations(configLocations);
    if (refresh) {
        refresh();
    }
}

看到这里发现多了一个refresh()的过程,进去一看一堆的处理。其实主要过程就是初始化Ioc容器,完成容器的配置,加载好并注册好各种回调。比如前面提到的BeanPostProcessor就是在这个refresh里就完成了注册。

正因为refresh这个过程我们用起spring来才会觉得简单好用。

先从DefaultAdvisorAutoProxyCreator说起

有了上面的基础再来看DefaultAdvisorAutoProxyCreator就会简单的多。我们可以看一下DefaultAdvisorAutoProxyCreator的类继承关系,其中有一个AbstractAutoProxyCreator需要特别关注。这个AbstractAutoProxyCreator实现了一堆的接口:

代码语言:javascript
复制
public abstract class AbstractAutoProxyCreator extends ProxyConfig
        implements SmartInstantiationAwareBeanPostProcessor, BeanClassLoaderAware, BeanFactoryAware,
        Ordered, AopInfrastructureBean {

其中SmartInstantiationAwareBeanPostProcessor继承关系中最底层的是BeanPostProcessor,前面说明Ioc的时候提到过,BeanPostProcessor可以在getBean时返回BeanPostProcessor加工过的对象。好了,奥秘就在这里啦,看一下AbstractAutoProxyCreator中如何实现的吧:

代码语言:javascript
复制
public Object postProcessBeforeInitialization(Object bean, String beanName) {
    return bean;
}

/**
 * Create a proxy with the configured interceptors if the bean is
 * identified as one to proxy by the subclass.
 * @see #getAdvicesAndAdvisorsForBean
 */
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    if (bean != null) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        if (!this.earlyProxyReferences.containsKey(cacheKey)) {
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    return bean;
}

其中Before方法啥也没做,都在After中完成的。在After中有一个wrapIfNecessary方法,这个方法的作用是如果需要进行代理则生成代理并返回代理对象实例。

代码语言:javascript
复制
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    if (beanName != null && this.targetSourcedBeans.containsKey(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;
}

方法中使用了一个缓存advisedBeans存取bean是否需要advise,如果是需要的话就会存TRUE,否则就是FALSE,这样下次再来取时不用再判断直接返回结果就行。当然最重要的是如果是需要为bean生成代理时则会创建代理啦。至于如何判断是否需要代理是通过一个getAdvicesAndAdvisorsForBean方法来完成的,如果getAdvicesAndAdvisorsForBean返回的是DO_NOT_PROXY则表示不需要代理。为了扩展getAdvicesAndAdvisorsForBean是抽象方法。从DefaultAdvisorAutoProxyCreator的父类AbstractAdvisorAutoProxyCreator中找到了getAdvicesAndAdvisorsForBean的实现:

代码语言:javascript
复制
@Override
protected Object[] getAdvicesAndAdvisorsForBean(Class beanClass, String beanName, TargetSource targetSource) {
    List advisors = findEligibleAdvisors(beanClass, beanName);
    if (advisors.isEmpty()) {
        return DO_NOT_PROXY;
    }
    return advisors.toArray();
}

这里面会查找所有的Advisors并返回,当然这个过程中因为Advisor是包含了Advice的,所以Aop的基本要素就到齐了。然后回到wrapIfNecessary中,接着就会调用createProxy来生成代理对象啦。

代码语言:javascript
复制
protected Object createProxy(
        Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {

    ProxyFactory proxyFactory = new ProxyFactory();
    // Copy our properties (proxyTargetClass etc) inherited from ProxyConfig.
    proxyFactory.copyFrom(this);

    if (!shouldProxyTargetClass(beanClass, beanName)) {
        // Must allow for introductions; can't just set interfaces to
        // the target's interfaces only.
        Class<?>[] targetInterfaces = ClassUtils.getAllInterfacesForClass(beanClass, this.proxyClassLoader);
        for (Class<?> targetInterface : targetInterfaces) {
            proxyFactory.addInterface(targetInterface);
        }
    }

    Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
    for (Advisor advisor : advisors) {
        proxyFactory.addAdvisor(advisor);
    }

    proxyFactory.setTargetSource(targetSource);
    customizeProxyFactory(proxyFactory);

    proxyFactory.setFrozen(this.freezeProxy);
    if (advisorsPreFiltered()) {
        proxyFactory.setPreFiltered(true);
    }

    return proxyFactory.getProxy(this.proxyClassLoader);
}

createProxy过程主要是通过ProxyFactory来生成代理对象,在之前的《学习AOP之深入一点Spring Aop》里已经讲过。最后将生成的代理对象放入缓存并返回给Ioc容器。

从些之后,getBean时获得的已经不是beanName的实际对象,而是代理对象。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2016-11-15 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、Spring Ioc的简单理解
  • 二、Advisor与Ioc的联系
    • 先从DefaultAdvisorAutoProxyCreator说起
    相关产品与服务
    云顾问
    云顾问(Tencent Cloud Smart Advisor)是一款提供可视化云架构IDE和多个ITOM领域垂直应用的云上治理平台,以“一个平台,多个应用”为产品理念,依托腾讯云海量运维专家经验,助您打造卓越架构,实现便捷、灵活的一站式云上治理。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档