MVC容器初始化

MVC中的ApplicationContext初始化

这一部分,我们主要关注Spring IoC容器是如何在MVC中工作的,先看一张图来了解下Spring MVC的ApplicationContext的层次结构:

从这幅图片中我们可以看到,一个DispatcherServlet有一个他自己的WebApplicationContext,这个WebApplicationContext包含了诸如控制器,视图解析器等东西,并且继承了在根WebApplicationContext中定义的所有的Bean。而这个根WebApplicationContext包含了中间层的Service Bean,数据源相关Bean等Bean。

Root WebApplicationContext初始化

在Spring里面,一些Service对象,业务相关的对象,以及数据访问对象等,都是放在一个独立的业务上下文(Business Context)中的,因为这些Bean大多是在多个Servlet之间共享的,所以这些Bean都是被包含在Root WebApplicationContext中的,Root WebApplicationContext的初始化入口可以看下Spring Web项目中web.xml的配置,里面配置了一个ContextLoaderListener,从名字上看就是上下文载入器的监听器,而Root WebApplicationContext的初始化入口就在这里,从Listener的接口规范我们知道,listener的初始化方法是contextInitialized(ServletContextEvent event)方法,我们就来看下这个方法的实现:

public void contextInitialized(ServletContextEvent event) {
    this.contextLoader = createContextLoader();
    if (this.contextLoader == null) {
        this.contextLoader = this;
    }
	this.contextLoader.initWebApplicationContext(event.getServletContext());
}

这个方法首先创建了一个contextLoader,然后调用了它的initWebApplicationContext方法,看下这个方法的实现:

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
    // 检查是否已经存在一个Root WebApplicationContext了,如果存在了,则不创建,直接抛出一个异常
    if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
        throw new IllegalStateException(
                "Cannot initialize context because there is already a root application context present - " +
                "check whether you have multiple ContextLoader* definitions in your web.xml!");
    }

    Log logger = LogFactory.getLog(ContextLoader.class);
    servletContext.log("Initializing Spring root WebApplicationContext");
    if (logger.isInfoEnabled()) {
        logger.info("Root WebApplicationContext: initialization started");
    }
    long startTime = System.currentTimeMillis();

    try {
        // 获取Root WebApplicationContext的父Context
        ApplicationContext parent = loadParentContext(servletContext);

        // 创建WebApplicationContext
        this.context = createWebApplicationContext(servletContext, parent);
        // 将此Context作为Root WebApplicationContext加入到servletContext中去
        servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

        ClassLoader ccl = Thread.currentThread().getContextClassLoader();
        if (ccl == ContextLoader.class.getClassLoader()) {
            currentContext = this.context;
        }
        else if (ccl != null) {
            currentContextPerThread.put(ccl, this.context);
        }

        if (logger.isDebugEnabled()) {
            logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +
                    WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
        }
        if (logger.isInfoEnabled()) {
            long elapsedTime = System.currentTimeMillis() - startTime;
            logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
        }

        return this.context;
    }
    catch (RuntimeException ex) {
        logger.error("Context initialization failed", ex);
        servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
        throw ex;
    }
    catch (Error err) {
        logger.error("Context initialization failed", err);
        servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
        throw err;
    }
}

我们可以看到这个方法里面调用了一个createWebApplicationContext方法,看下这个方法的实现:

protected WebApplicationContext createWebApplicationContext(ServletContext sc, ApplicationContext parent) {
    // 确定上下文的Class
    Class<?> contextClass = determineContextClass(sc);
    if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
        throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
                "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
    }
    // 创建一个WebApplicationContext
    ConfigurableWebApplicationContext wac =
            (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

    // Assign the best possible id value.
    if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) {
        // Servlet <= 2.4: resort to name specified in web.xml, if any.
        String servletContextName = sc.getServletContextName();
        wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
                ObjectUtils.getDisplayString(servletContextName));
    }
    else {
        // Servlet 2.5's getContextPath available!
        try {
            String contextPath = (String) ServletContext.class.getMethod("getContextPath").invoke(sc);
            wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
                    ObjectUtils.getDisplayString(contextPath));
        }
        catch (Exception ex) {
            throw new IllegalStateException("Failed to invoke Servlet 2.5 getContextPath method", ex);
        }
    }

    wac.setParent(parent);
    wac.setServletContext(sc);
    wac.setConfigLocation(sc.getInitParameter(CONFIG_LOCATION_PARAM));
    customizeContext(sc, wac);
    // 又看到了我们熟悉的refresh()方法,这个方法就是初始化Root容器的地方
    wac.refresh();
    return wac;
}

DispatcherServlet对应的WebApplicationContext初始化

既然有Root WebApplicationContext,就会有相应的Sub WebApplication Context,一个在web.xml中定义的DispatcherServlet一般上都是对应一个WebApplicationContext,里面包含了一些诸如Spring Controller的一些东西,对着这个WebApplication的启动过程,可以看下DispatcherServlet,DispatcherServlet继承了FrameworkServlet,这个类实现了Servlet接口,从Servlet接口的定义知道,Servlet的初始化是调用init方法来初始化的,而FrameworkServlet的父类HttpServletBean在init方法里面调用了一个initServletBean方法,我们就来看下FrameworkServlet对initServletBean方法的实现:

protected final void initServletBean() throws ServletException {
    getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
    if (this.logger.isInfoEnabled()) {
        this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
    }
    long startTime = System.currentTimeMillis();

    try {
        // 初始化WebApplicationContext
        this.webApplicationContext = initWebApplicationContext();
        // 初始化FrameworkServlet,默认实现为空
        initFrameworkServlet();
    }
    catch (ServletException ex) {
        this.logger.error("Context initialization failed", ex);
        throw ex;
    }
    catch (RuntimeException ex) {
        this.logger.error("Context initialization failed", ex);
        throw ex;
    }

    if (this.logger.isInfoEnabled()) {
        long elapsedTime = System.currentTimeMillis() - startTime;
        this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
                elapsedTime + " ms");
    }
}

上述这段代码中的关键部分就是initWebApplicationContext方法,看下这个方法里面干了什么事情:

protected WebApplicationContext initWebApplicationContext() {
    // 根据attrName获取WebApplicationContext,防止重复初始化
    WebApplicationContext wac = findWebApplicationContext();
    if (wac == null) {
        // 获取Root WebApplicationContext
        WebApplicationContext parent =
                WebApplicationContextUtils.getWebApplicationContext(getServletContext());
        // 根据获取的到Root WebApplicationContext创建出一个Sub WebApplicationContext
        wac = createWebApplicationContext(parent);
    }

    if (!this.refreshEventReceived) {
        // Apparently not a ConfigurableApplicationContext with refresh support:
        // triggering initial onRefresh manually here.
        onRefresh(wac);
    }

    if (this.publishContext) {
        // Publish the context as a servlet context attribute.
        String attrName = getServletContextAttributeName();
        getServletContext().setAttribute(attrName, wac);
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
                    "' as ServletContext attribute with name [" + attrName + "]");
        }
    }

    return wac;
}

这段代码使用了Root WebApplicationContext创建了一个Sub WebApplicationContext,创建的过程调用了FrameworkServlet的createWebApplicationContext方法,这个方法和前面提到的createWebApplicationContext方法类似,但是稍有不同,来看下其实现:

protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
    Class<?> contextClass = getContextClass();
    if (this.logger.isDebugEnabled()) {
    	this.logger.debug("Servlet with name '" + getServletName() +
    			"' will try to create custom WebApplicationContext context of class '" +
    			contextClass.getName() + "'" + ", using parent context [" + parent + "]");
    }
    if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
    	throw new ApplicationContextException(
    			"Fatal initialization error in servlet with name '" + getServletName() +
    			"': custom WebApplicationContext class [" + contextClass.getName() +
    			"] is not of type ConfigurableWebApplicationContext");
    }
    ConfigurableWebApplicationContext wac =
    		(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

    // Assign the best possible id value.
    ServletContext sc = getServletContext();
    if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) {
    	// Servlet <= 2.4: resort to name specified in web.xml, if any.
    	String servletContextName = sc.getServletContextName();
    	if (servletContextName != null) {
    		wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + servletContextName +
    				"." + getServletName());
    	}
    	else {
    		wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + getServletName());
    	}
    }
    else {
    	// Servlet 2.5's getContextPath available!
    	wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + sc.getContextPath() +
    			"/" + getServletName());
    }

    wac.setParent(parent);
    wac.setServletContext(getServletContext());
    wac.setServletConfig(getServletConfig());
    wac.setNamespace(getNamespace());
    wac.setConfigLocation(getContextConfigLocation());
    // 注意这里注册了一个监听器,后面在完成refresh()以后分发事件就会触发这个监听器
    wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));

    postProcessWebApplicationContext(wac);
    // 调用refresh()方法
    wac.refresh();

    return wac;
}

这段代码中需要注意的一点是FrameworkContext向WebApplicationContext注册了一个监听器,会在WebApplicationContext完成refresh操作的时候调用DispatcherServlet的onRefresh方法,它又调用了initStrategies方法,看下initStrategies方法的具体实现:

protected void initStrategies(ApplicationContext context) {
    initMultipartResolver(context);
    initLocaleResolver(context);
    initThemeResolver(context);
    initHandlerMappings(context);
    initHandlerAdapters(context);
    initHandlerExceptionResolvers(context);
    initRequestToViewNameTranslator(context);
    initViewResolvers(context);
}

在这个方法里面Spring将所有的相关组件都初始化好.

感想

不论是WebX还是Spring MVC都是基于Servlet而做的web开发框架,其入口都是和普通的servlet应用是一样的,所以只需要抓住web.xml中定义的Listener,Servlet等就能够把握住学习的入口。

Spring MVC概览

Spring MVC概览


这是一张从Spring Reference上搞过来的图,展示了Spring MVC框架对于请求的处理过程,其中我们可以看到在Spring MVC中,使用了在web请求处理中非常流行的前端控制器模式,这个前端控制器负责将请求的处理代理给相应的Controller,并且负责将页面的渲染代理给相应的视图组件,可以说前端控制器在这里起到了一个请求的控制调度中心的作用。

Spring MVC中的主要组件

在Spring MVC中,有很多的组件分别负责处理不同的事情,我们可以先了解下这些组件到底是处理什么事情的,然后再去了解几个主要的组件是如何工作的:

  • Controller:就是MVC中的C部分
  • Handler Mapping:处理符合映射规则的pre-proccessors,post-processors和controllers
  • View Resolver:解析视图名称
  • Locale Resolver:查看本地信息,用于i18n,几个默认的实现包括AcceptHeaderLocaleResolver,CookieLocaleResolver,SessionLocaleResolver,用户可以通过配置LocaleChangeInterceptor来对Local信息做出修改。
  • Theme Resolver:解析应用中所使用的主题
  • Multipart File Resolver:处理上传的文件
  • Handler Exception Resolvers:处理异常

Spring AOP之横切逻辑的执行

AOP横切逻辑的执行

AOP横切逻辑的执行主要由一个接口来执行AopProxy,它有两个实现JdkDynamicAopProxy和Cglib2AopProxy,Spring会根据不同的情况选择不同的AopProxy来执行横切逻辑,先来看一下JdkDynamicAopProxy的一个情况:

JdkDynamicAopProxy

看下JdkDynamicAopProxy的Invoke方法:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    MethodInvocation invocation;
    Object oldProxy = null;
    boolean setProxyContext = false;

    TargetSource targetSource = this.advised.targetSource;
    Class targetClass = null;
    Object target = null;

    try {
        // equals hashcode等方法处理
        if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
            // The target does not implement the equals(Object) method itself.
            return equals(args[0]);
        }
        if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
            // The target does not implement the hashCode() method itself.
            return hashCode();
        }
        if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
                method.getDeclaringClass().isAssignableFrom(Advised.class)) {
            // Service invocations on ProxyConfig with the proxy config...
            return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
        }

        Object retVal;

        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 = targetSource.getTarget();
        if (target != null) {
            targetClass = target.getClass();
        }

        // Get the interception chain for this method.
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

        // 检查拦截器链是否为空,如果为空,就直接通过反射调用目标对象的方法,避免创建多余的MethodInvocation对象
        if (chain.isEmpty()) {
            // 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 = AopUtils.invokeJoinpointUsingReflection(target, method, args);
        }
        else {
            // 创建一个MethodInvocation
            invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
            // 通过拦截器链调用切面逻辑
            retVal = invocation.proceed();
        }

        // Massage return value if necessary.
        if (retVal != null && retVal == target && method.getReturnType().isInstance(proxy) &&
                !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
            // Special case: it returned "this" and the return type of the method
            // is type-compatible. Note that we can't help if the target sets
            // a reference to itself in another returned object.
            retVal = proxy;
        }
        return retVal;
    }
    finally {
        if (target != null && !targetSource.isStatic()) {
            // Must have come from TargetSource.
            targetSource.releaseTarget(target);
        }
        if (setProxyContext) {
            // Restore old proxy.
            AopContext.setCurrentProxy(oldProxy);
        }
    }
}

以上这个方法的重点是:invocation.proceed()方法,这个方法是通过递归的方式来调用拦截器链里面的每一个拦截器,我们看一下这个方法的具体实现:

public Object proceed() throws Throwable {
    // 从-1开始,然后先加
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        // 如果上一次调用的已经是最后一个拦截器,则调用切面逻辑
        return invokeJoinpoint();
    }

    Object interceptorOrInterceptionAdvice =
        this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
        // 动态方法匹配
        InterceptorAndDynamicMethodMatcher dm =
        (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
        if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
            return dm.interceptor.invoke(this);
        }
        else {
            // 动态方法匹配失败,然后直接调用拦截器链的下一个拦截器
            // Skip this interceptor and invoke the next in the chain.
            return proceed();
        }
    }
    else {
        // 非动态方法匹配,静态方法匹配,之前已经匹配过,这是一个拦截器,直接调用
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }
}

单看这一段代码还看不出递归影子,看下MethodInterceptor的各个实现类的invoke方法便可以看到,虽然MethodInterceptor的实现类对于invoke方法的具体实现有所不同,然是它们都调用了mi.proceed()这个方法,从而实现对拦截器链的递归调用。

Cglib2AopProxy

Cglib调用横切逻辑的方式和Jdk基本相似,代码上区别在于调用的方法不一样那个,Cglib2AopProxy调用的是其内部类DynamicAdvicedInterceptor中的intercept方法:

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 <code>null</code>. 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;
        // 检查是否只是有一个InvokerInterceptor:没有真正的Advice,直接反射调用目标方法
        if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
            // 跳过创建MethodInvocation的步骤:直接调用目标方法
            // 注意最后一个invoker必须使一个InvokerInterceptor,这样我们才能知道它并没有做任何事情,只是对目标做了一个反射操作
            retVal = methodProxy.invoke(target, args);
        }
        else {
            // 创建一个MethodInvocation并调用
            retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
        }
        retVal = massageReturnTypeIfNecessary(proxy, target, method, retVal);
        return retVal;
    }
    finally {
        if (target != null) {
            releaseTarget(target);
        }
        if (setProxyContext) {
            // Restore old proxy.
            AopContext.setCurrentProxy(oldProxy);
        }
    }
}

从这段代码中可以看出,Cglib2AopProxy是创建了一个CglibMethodInvocation并调用其proceed()方法,而CglibMethodInvocation是继承了RefectiveMethodInvocation,所以最后还是调用了ReflectiveMethodInvocation的proceed()方法。但是CglibMethodInvocation重写了ReflectiveMethodInvocation的invokeJoinpoint()方法,按照作者写的注释似乎能带来微小的性能提升:

/**
 * Gives a marginal performance improvement versus using reflection to
 * invoke the target when invoking public methods.
 */
protected Object invokeJoinpoint() throws Throwable {
    if (this.protectedMethod) {
        return super.invokeJoinpoint();
    }
    else {
        return this.methodProxy.invoke(this.target, this.arguments);
    }
}

Spring AOP之代理对象的生成

AOP代理对象的生成

在声明式的AOP中,我们定义了一个ProxyFactoryBean来生成代理对象,在Spring配置文件中的配置如下:

<bean id="beanA" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="target" ref="beanAImpl" />
    <property name="interceptorNames" value="testAdvisor" />
    <property name="proxyInterfaces" value="com.alibaba.khotyn.test.BeanInterface">
</property>

而ProxyFactoryBean之所以叫做ProxyFactoryBean是因为它继承了FactoryBean,所以要知道关于AOP的代理对象如何生成,只需要关注ProxyFactoryBean的getObeject()方法即可,我们先来看一下它的实现:

public Object getObject() throws BeansException {
    // 初始化通知器链
    initializeAdvisorChain();
    if (isSingleton()) {
        // 获取单例对象
        return getSingletonInstance();
    }
    else {
        if (this.targetName == null) {
            logger.warn("Using non-singleton proxies with singleton targets is often undesirable." +
                            "Enable prototype proxies by setting the 'targetName' property.");
        }
        // 获取Prototype对象
        return newPrototypeInstance();
    }
}

这里面两个关键的方法是getSingletonInstance()和newPrototypeInstance(),看下这两个方法的实现:

private synchronized Object getSingletonInstance() {
    if (this.singletonInstance == null) {
        this.targetSource = freshTargetSource();
        if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) {
            // Rely on AOP infrastructure to tell us what interfaces to proxy.
            setInterfaces(ClassUtils.getAllInterfacesForClass(this.targetSource.getTargetClass()));
        }
        // Eagerly initialize the shared singleton instance.
        super.setFrozen(this.freezeProxy);
        // 创建一个AopProxy,然后通过它获取到代理对象
        this.singletonInstance = getProxy(createAopProxy());
        // We must listen to superclass advice change events to recache the singleton
        // instance if necessary.
        addListener(this);
    }
    return this.singletonInstance;
}

从这段代码中看出,Spring是首先创建一个AopProxy对象(JdkDynamicAopProxy或者是Cglib2AopProxy),然后通过它来创建出真正的代理对象(JdkDynamicAopProxy,Cglib2AopProxy分别根据自己的方法来创建对象),而newPrototypeInstance()方法的实现和getSingletonInstance()的实现类似,这里不做赘述。

Spring AOP概览

AOP概述

介绍AOP的基本概念,首先对AOP做一个大致的了解

AOP原理以及关键概念


下面结合上面的AOP原理图来看一下AOP的一些关键概念

  • JoinPoint:代表程序运行时的一个具体的织入点
  • Pointcut:一组JoinPoint的集合,通常由正则表达式来定义
  • Advice:切面的逻辑
  • Advisor:一个Advisor包含一个Advise和一个PointCut

Spring AOP实现的基本类图

了解了这些基本的概念以后,来看下Spring使如何实现这些概念的,看下面的类图

下面对几个重要的类做简要说明:

  • Advice:Advice接口,使一个标签接口,没有定义任何成员和方法
  • BeforeAdvice,AfterAdvice,ThrowsAdvice:这三个接口都只是标签接口,没有定义任何成员方法
  • MethodBeforeAdvice:接口,定义了方法调用前调用的Advice
  • AfterReturningAdvice:接口,定义了方法调用返回后的Advice
  • Pointcut:Spring Pointcut的核心抽象,一个Pointcut包含了一个ClassFilter和一个MethodMatcher
  • MethodMatcher:Pointcut的一部分,检查目标方法是否和advice是相匹配的
  • Advisor:Advisor接口,持有一个Advice
  • PointcutAdvisor:在Advisor接口的基础上持有了一个Pointcut

从整个类图我们也可以看出来Advice,Pointcut以及Advisor之间的关系

Spring IoC之容器扩展点

容器扩展点

Spring中容器的扩展点一共有三个地方可以扩展,分别对应于三个接口:BeanPostProcessor,BeanFactoryPostProcessor和FactoryBean

BeanPostProcessor

BeanPostProcessor:这个接口可以使Bean在初始化前面或者后面对Bean实例作出修改,下面来看一个例子,作用是仅仅打出Bean的名字以及信息,首先需要在实现BeanPostProcessor接口:

public class TracingBeanPostProcessor implements BeanPostProcessor {

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

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("Bean:" + beanName + " ---- " + bean.toString());
        return bean;
    }
}

然后将这个BeanPostProcessor定义到Bean配置信息里面去:

<bean class="com.alibaba.khotyn.test.TracingBeanPostProcessor"/>

这样在容器初始化一个Bean之后,容器就会调用TracingBeanPostProcessor将Bean的信息打印到控制台上。注意这里面配置的BeanPostProcessor甚至没有BeanName。

BeanFactoryPostProcessor

BeanFactoryPostProcessor和前面的BeanPostProcessor在名字上看上去很像,在功能上也是类似的,区别的地方在于BeanFactoryPostProcessor可以对Bean定义信息的元数据进行修改(BeanDefinition),而后者是在Bean初始化的时候可以对Bean实例进行修改,可以说BeanFactoryPostProcessor修改地更为彻底一些。在Spring中,已经有一些类实现了BeanFactoryPostPorcessor来提供一些功能,比如PropertyPlaceholderConfigurer,可以对用Property文件中的属性替换XML文件中的属性,下面是一个简单的例子:

<bean id="beanA" class="com.alibaba.khotyn.test.BeanA">
    <property name="name" value="${name}"></property>
    <property name="address" value="${address}"></property>
</bean>
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations">
         <list>
            <value>configure.properties</value>
         </list>
    </property>
</bean>

这样,Properties中的属性就可以去替换XML中相应的属性了

FactoryBean

这个接口乍看可能不怎么像容器的扩展点。但是考虑到最终的Bean使FactoryBean生产出来,所以Bean的初始化完全掌控在FactoryBean当中,这个接口的确是容器的一个扩展点。

Spring IoC之Bean生命周期管理

Bean的生命周期管理

Spring的Bean容器只管理非单例Bean的生命周期,单例Bean的生命周期不在管理范围内。用一张很老的图来展示下Bean的生命周期管理
Bean生命周期
关于这张图里面我们可以主要关注3个类:

  • BeanPostProcessor:这个接口定义了两个方法,分别是:postProcessBeforeInitialization和postProcessAfterInitialization,分别在初始化Bean之前和之后被调用,通过这两个方法,接口的实现者可以修改Bean的定义信息。如果接口的实现者需要定制多个BeanPostProcessor,那么它也需要实现Ordered接口。和这个类的非常相似的一个类是BeanFactoryPostProcessor,这两个类的一个区别使后者操纵的使Bean配置信息的元数据,这就意味着Spring允许在容器初始化Bean之前去修改Bean配置信息的元数据。这个接口的一个实现就是PropertyPlaceholderConfigurer和PropertyOverrideConfigurer,具体的例子可以参考Spring Reference
  • InitializingBean:这个接口定义了一个afterPropertiesSet()方法,这个方法在Bean的属性被全部设置后发出。
  • DesposableBean:这个接口定义了一个destroy()方法,这个方法在可以在Bean销毁时调用,比如可以做释放资源用。

Spring IoC之获取Bean

非单例Bean的获取

Spring Bean获取的主体代码在AbstractBeanFactory的doGetBean方法中,看下面的方法:

protected <T> T doGetBean(
          final String name, final Class&lt;T&gt; requiredType, final Object[] args, boolean typeCheckOnly)
          throws BeansException {
     // 转换BeanName,如果name参数传过来是aliasName的话,在这里会根据aliasName取出真正的BeanName
     final String beanName = transformedBeanName(name);
     Object bean;

     // 首先检查单例缓存里面有没有这个Bean
     Object sharedInstance = getSingleton(beanName);
     if (sharedInstance != null && args == null) {
          if (logger.isDebugEnabled()) {
               if (isSingletonCurrentlyInCreation(beanName)) {
                    logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
                         "' that is not fully initialized yet - a consequence of a circular reference");
               }
               else {
                    logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
               }
          }
         // 将取出的object(可能是一个FactoryBean)转换成Bean的类型
         bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
     }

     else {
          // 检查是否已经在创建中,如果正在创建中,就确定存在循环引用
          if (isPrototypeCurrentlyInCreation(beanName)) {
               throw new BeanCurrentlyInCreationException(beanName);
          }

          // 检查ParentBeanFactory是否存在Bean定义信息
          BeanFactory parentBeanFactory = getParentBeanFactory();
          if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
               // 如果ParentBeanFacotory不为空而且此BeanFactory不包含Bean定义信息
               String nameToLookup = originalBeanName(name);
               if (args != null) {
                    // 如果参数不为空,带上参数将获取Bean委托给ParentBeanFactory
                    return (T) parentBeanFactory.getBean(nameToLookup, args);
               }
               else {
                    // 如果参数为空,则将获取Bean委托给ParentBeanFactory的标准getBean方法
                    return parentBeanFactory.getBean(nameToLookup, requiredType);
               }
          }

          // typeCheckOnly,实例用来做类型检测的,而不是真正地拿来用的
          if (!typeCheckOnly) {
               markBeanAsCreated(beanName);
          }

          // 如果Bean的定义信息在子Bean定义信息中,则合并到当前的Bean定义信息中
          final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
          checkMergedBeanDefinition(mbd, beanName, args);

          // 处理depends on的情况,确保当前Bean依赖的Bean先创建
          String[] dependsOn = mbd.getDependsOn();
          if (dependsOn != null) {
               for (String dependsOnBean : dependsOn) {
                    getBean(dependsOnBean);
                    registerDependentBean(dependsOnBean, beanName);
               }
          }

          // 开始创建Bean实例
          if (mbd.isSingleton()) {
               sharedInstance = getSingleton(beanName, new ObjectFactory() {
                    public Object getObject() throws BeansException {
                         try {
                              return createBean(beanName, mbd, args);
                         }
                         catch (BeansException ex) {
                              // Explicitly remove instance from singleton cache: It might have been put there
                              // eagerly by the creation process, to allow for circular reference resolution.
                              // Also remove any beans that received a temporary reference to the bean.
                              destroySingleton(beanName);
                              throw ex;
                         }
                    }
               });
               bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
          }

          elseif (mbd.isPrototype()) {
               // Bean的Scope是Prototype,创建一个新实例
               Object prototypeInstance = null;
               try {
                    // 在Bean创建之前,默认行为是:将当前Bean标记为正在创建中,用于前面检测循环引用
                    beforePrototypeCreation(beanName);
                    prototypeInstance = createBean(beanName, mbd, args);
               }
               finally {
                    // 在Bean创建之后,默认行为是:将当前Bean标记为不在创建中,用于前面检测循环引用
                    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() {
                         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);
               }
          }
     }

     // 检测Bean实例和需要的类型是否匹配
     if (requiredType != null && bean != null && !requiredType.isAssignableFrom(bean.getClass())) {
          throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
     }
     return (T) bean;
}

从这里可以看出,Spring是首先尝试从单例缓存中获取Bean,如果找不到,然后在根据不同的Bean Scope调用不同的方法去创建Bean。

Spring IoC之容器初始化

容器初始化

Spring容器的初始化主要分为三个步骤,分别使Bean信息的定位,解析与注册。以XML文件形式的Bean信息为例,Spring首先找到XML文件的位置,然后读取此文件,并且将文件解析成相应的BeanDefinition,最后将获取到的BeanDefinition注册到BeanFactory中去。

Bean信息定位,解析与注册

Spring容器的初始化的主要工作是定位Bean配置文件,读取并解析Bean配置文件,最后生成相应的BeanDefinition,先看一张简单的序列图对Spring容器的初始化做大概的了解:
Spring IoC容器初始化时序图
从这副顺序图中我们可以看出,Spring容器的初始化过程主要由四个类来完成,我们可以参考下类的注释以了解这几个类的职责:

  • XmlBeanDefinitionReader:用于XML Bean定义信息的BeanDefinitionReader,将事实上的XML文件读取代理给BeanDefinitionDocumentReader接口的一个实现。
  • BeanDefinitionDocumentReader:用于解析Spring Bean定义信息的服务提供接口(SPI),在实际解析DOM文档的时候,由XmlBeanDefinitionReader来调用。
  • BeanDefinitionParserDelegate:用于解析XML Bean定义信息的有状态代理类。负责一个Bean的定义信息解析策划能够BeanDefinition对象。
  • BeanDefinitionRegistry:负责将得到的Bean定义信息注册到BeanFactory中。

Spring IoC 概览

主要的设计思想

关于Spring设计的主要设计思想,其实Rod Johnson在他的那本《Expert One on One – J2EE Development without EJB》中有详细的解释。这里做下概括,Spring出现的主要原因是为了提供一个轻量级的容器,这个容器除了提供容器的基本服务以外,还应该具备以下的特点:

  • 容器应该能够管理在其中的应用程序代码,但是不应该将容器侵入到应用程序的代码之中。也就是说,应用程序的代码应该是独立于容器的
  • 容器应该能够快速启动
  • 在容器中部署一个对象应该不需要任何特殊的部署步骤
  • 容器应该依赖于最少的API以能够在不同的环境下运行
  • ……..

而IoC正是为了解决以上的第一个特点的,在IoC中,组件不需要去查找它所依赖的对象,而是由容器来负责将组件所依赖的对象通过JavaBean Properties(Setter方法)或者构造函数来注入给组件。

主要类及其职责

Spring IoC Class Diagram
这个类图相对来说比较简单些,主要包括了继承BeanFactory接口的各个接口及其实现,主要目的是为了摸清BeanFactory这一条线里面各个类之间的关系。整个类的继承体系相对比较简单,其中有几个类/接口的作用需要重点把握:

  • BeanFactory:这个接口的作用是定义了访问Bean容器所需的基本方法,是整个Spring Bean容器的底层接口
  • AbstractBeanFactory:这个类的作用从名字上就可以看出,使BeanFactory的一个抽象实现,提供了诸如getBean等方法的默认实现
  • SingletonBeanRegistry:这个接口的主要作用对单例Bean进行管理。
  • 其他的一些类/接口基本上是对BeanFactory接口进行了扩展,主要的功能可以看类/接口的名字或者注释来了解

感想

从Spring的类图中可以看出,Spring的类的命名的非常好,基本上让人一下就看出了这个类的职责所在,并且这个职责基本上是单一的,不会有一个类去做多件事情。Spring对BeanFactory的扩展接口也是只加了一种特性,然后通过实现多个接口以实现多个特性,这样的设计使接口的实现方可以方便的根据需求挑选接口来实现,而不用担心实现过多的功能。