文档章节

spring源码阅读笔记(一)

纳兰清风
 纳兰清风
发布于 2015/12/20 16:13
字数 1321
阅读 222
收藏 19
点赞 0
评论 0
spring源码阅读笔记(一)


    最近工作不忙,抽空阅读了下《spring源码深度剖析》,特此做一下记录。


    先说下BeanFactoryPostProcessor接口和BeanPostProcessor接口,这两个接口都是spring 初始化bean时对外暴露的扩展点。两个接口名称看起来很相似,但作用及使用场景却不同。


1.BeanFactoryPostProcessor
public interface BeanFactoryPostProcessor {
        void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
    }

    实现该接口,可以在spring的bean创建之前,修改bean的定义属性。也就是说,Spring允许BeanFactoryPostProcessor在容器实例化任何其它bean之前读取配置元数据,并可以根据需要进行修改,例如可以把bean的scope从singleton改为prototype,也可以把property的值给修改掉。可以同时配置多个BeanFactoryPostProcessor,实现类可以通过实现PriorityOrdered接口来控制各个BeanFactoryPostProcessor的执行次序。

    
    注意:BeanFactoryPostProcessor是在spring容器加载了bean的定义文件之后,在bean实例化之前执行的。接口方法的入参是ConfigurrableListableBeanFactory,使用该参数,可以获取到相关bean的定义信息。
    
    spring中内置了一些BeanFactoryPostProcessor实现类,常用的有: (org.springframework.beans.factory.config包下)
    1.PropertyPlaceholderConfigurer  替换xml中的占位符为*.properties文件中的值
    2.PropertyOverrideConfigurer  比PropertyPlaceholderConfigurer功能高级些,支持缺省值
    3.CustomEditorConfigurer:用来注册自定义的属性编辑器

    例如自定义一个BeanFactoryPostProcessor:

public MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) 
                throw BeansException {
            BeanDefinition bd = beanFactory.getBeanDefinition("myJavaBean");
            MutablePropertyValues mpv = bd.getPropertyValues();
            mpv.addPropertyValue("propertyName", "newValue");
        }
    }
    xml中配置一下:
<bean class="com.test.MyBeanFactoryPostProcessor" />
2.BeanPostProcessor

public interface BeanPostProcessor {
        Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
        Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
    }

    BeanPostProcessor可以在spring容器实例化bean之后,在执行bean的初始化方法前后,添加一些自己的处
理逻辑。这里的初始化方法包括bean实现InitializingBean接口的afterPropertiesSet方法和在配置文件中指定
的init-method方法。

    其具体的执行过程为:
   

    spring中有一些内置的BeanPostProcessor实现类,例如

    1.org.springframework.context.annotation.CommonAnnotationBeanPostProcessor: 支持@Resource注解的注入
    2.org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor: 支持@Required注解的注入
    3.org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor: 支持@Autowired注解的注入
    4.org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor:支持@PersistenceUnit和@PersistenceContext注解的注入
    5.org.springframework.context.support.ApplicationContextAwareProcessor: 用来为bean注入ApplicationContext等容器对象
    
    这些注解类会在配置文件中通过配置<context:component-scan base-package="*.*" />后自动进行注册。

    还有aop中的功能也是通过实现BeanPostProcessor接口去做的代理,有兴趣的同学可以去看下
AspectJAwareAdvisorAutoProxyCreator类,其父类AbstractAutoProxyCreator中实现了postProcessAfterInitialization
接口完成了对方法的代理。代码如下:

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;
    }

    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;
    }

    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);
    }

    [proxyFactory]
    public Object getProxy(ClassLoader classLoader) {
        return createAopProxy().getProxy(classLoader);
    }

    [JdkDynamicAopProxy]
    public Object getProxy(ClassLoader classLoader) {
        if (logger.isDebugEnabled()) {
            logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
        }
        Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
        findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
        return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
    }
    如果你有一些其他独特的需求也可以仿照上面的方式自己写一个BeanPostProcessor注册到容器中。

3.InitializingBean和init-method

    这两个方法的调用点是AbstractAutowireCapableBeanFactory类的invokeInitMethods方法:

   我们看下代码:

protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd)
            throws Throwable {

        boolean isInitializingBean = (bean instanceof InitializingBean);
        if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
            if (logger.isDebugEnabled()) {
                logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
            }
            if (System.getSecurityManager() != null) {
                try {
                    AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
                        public Object run() throws Exception {
                            ((InitializingBean) bean).afterPropertiesSet();
                            return null;
                        }
                    }, getAccessControlContext());
                }
                catch (PrivilegedActionException pae) {
                    throw pae.getException();
                }
            }
            else {
                ((InitializingBean) bean).afterPropertiesSet();
            }
        }

        if (mbd != null) {
            String initMethodName = mbd.getInitMethodName();
            if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
                    !mbd.isExternallyManagedInitMethod(initMethodName)) {
                invokeCustomInitMethod(beanName, bean, mbd);
            }
        }
    }
    但是invokeInitMethods方法又是在哪里调用的呢?是同一个类中的initializeBean方法调用的,代码如下:
protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
        if (System.getSecurityManager() != null) {
            AccessController.doPrivileged(new PrivilegedAction<Object>() {
                public Object run() {
                    invokeAwareMethods(beanName, bean);
                    return null;
                }
            }, getAccessControlContext());
        }
        else {
            invokeAwareMethods(beanName, bean);
        }

        Object wrappedBean = bean;
        if (mbd == null || !mbd.isSynthetic()) {
            wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
        }

        try {
            invokeInitMethods(beanName, wrappedBean, mbd);
        }
        catch (Throwable ex) {
            throw new BeanCreationException(
                    (mbd != null ? mbd.getResourceDescription() : null),
                    beanName, "Invocation of init method failed", ex);
        }

        if (mbd == null || !mbd.isSynthetic()) {
            wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
        }
        return wrappedBean;
    }
    我们可以看到这里首先调用了bean的Aware接口的一些相关方法,然后是调用BeanPostProcessor接口的前置处理,
然后调用上面的bean初始化方法,然后是BeanPostProcessor的后置处理,这正好符合了上图中描述的调用顺序。


    其中Aware有很多子接口,像BeanNameAware,BeanClassLoaderAware,BeanFactoryAware等等,用户可以编写实现
了这些接口的bean,这样在spring回调aware相关接口的时候获取到一些容器的相关信息。


    下面简单说下使用InitializingBean和init-method方法初始化bean的区别:
    1.实现InitializingBean接口是直接调用afterPropertiesSet方法,比通过反射调用init-method指定的方法效率相对来说要高点。
但是init-method方式消除了对spring的依赖
    2:如果调用afterPropertiesSet方法时出错,则不调用init-method指定的方法。

© 著作权归作者所有

共有 人打赏支持
纳兰清风
粉丝 30
博文 32
码字总数 32526
作品 0
朝阳
程序员
springmvc+mybatis学习笔记(汇总)

springmvc+mybatis学习笔记(汇总) 标签 : springmvc mybaits [TOC] 笔记分为两大部分:mybatis和springmvc mybatis springmvc 笔记内容主要是mybatis和springmvc的一些基本概念和使用方法,...

brianway ⋅ 2016/03/30 ⋅ 2

Spring源码解析系列之IOC容器(一)

前言 实际上我所有的博客都是原来对原来印象笔记里笔记内容的加工,关于Spring源码自己已经解析了很多遍,但是时间长总是忘记,写一篇博客权当加强记忆,也算再次学习下大师们的设计思想,思...

后厂村老司机 ⋅ 06/02 ⋅ 0

spring boot 2.0 源码分析(一)

在学习spring boot 2.0源码之前,我们先利用spring initializr快速地创建一个基本的简单的示例: 1.先从创建示例中的main函数开始读起: 2.在这里我们可以看到,spring boot是通过SpringApp...

java高级架构牛人 ⋅ 06/05 ⋅ 0

Spring IOC 容器源码分析系列文章导读

1. 简介 Spring 是一个轻量级的企业级应用开发框架,于 2004 年由 Rod Johnson 发布了 1.0 版本。经过十几年的迭代,现在的 Spring 框架已经非常成熟了。Spring 包含了众多模块,包括但不限于...

coolblog ⋅ 05/30 ⋅ 0

学习Java和Spring Boot Cloud ,不妨看看这个

专注于编程、互联网动态。最终将总结的技术、心得、经验(数据结构与算法、源码分析等)分享给大家,这里不只限于技术!还有职场心得、生活感悟、以及面经。 1 java版web项目 java版web项目,...

b644rofp20z37485o35m ⋅ 05/04 ⋅ 0

spring源码解析系列之事件驱动模型@EventListener

说到前面 本次主要介绍监听器的实例化过程源码解析。 正文 我们跟踪@EventListener源码可以发现,背后的实现是这个类EventListenerMethodProcessor,spring应用上下文启动的时候会加载这个类...

天河2018 ⋅ 04/27 ⋅ 0

Spring IOC 容器源码分析 - 获取单例 bean

1. 简介 为了写 Spring IOC 容器源码分析系列的文章,我特地写了一篇 Spring IOC 容器的导读文章。在导读一文中,我介绍了 Spring 的一些特性以及阅读 Spring 源码的一些建议。在做完必要的准...

coolblog ⋅ 06/01 ⋅ 0

让Spring Security 来保护你的Spring Boot项目吧

参考资料: 书籍:Spring实战(第4版) 第9章和第14章 Spring Security 参考手册 初识 Spring Security 程序猿DD的Spring Security学习笔记 Spring Security 简介 Spring Security是一个能够为...

潇潇漓燃 ⋅ 05/19 ⋅ 0

谈谈加速源码理解的几种方法

总结一一下本人在看Spring,SpringMVC,Tomcat,Mybatis,Druid,Log4j2等源码过程中的一些基本核心理念。知晓它们能加速你对源码的理解。 1. 拦截性接口和事件通知 在重要的过程上设置拦截接...

夫礼者 ⋅ 2017/12/10 ⋅ 0

Spring AOP 源码分析 - 筛选合适的通知器

1.简介 从本篇文章开始,我将会对 Spring AOP 部分的源码进行分析。本文是 Spring AOP 源码分析系列文章的第二篇,本文主要分析 Spring AOP 是如何为目标 bean 筛选出合适的通知器(Advisor...

java高级架构牛人 ⋅ 06/21 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

OSChina 周日乱弹 —— 这么好的姑娘都不要了啊

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @TigaPile :分享曾惜的单曲《讲真的》 《讲真的》- 曾惜 手机党少年们想听歌,请使劲儿戳(这里) @首席搬砖工程师 :怎样约女孩子出来吃饭,...

小小编辑 ⋅ 29分钟前 ⋅ 1

Jenkins实践3 之脚本

#!/bin/sh# export PROJ_PATH=项目路径# export TOMCAT_PATH=tomcat路径killTomcat(){pid=`ps -ef | grep tomcat | grep java|awk '{print $2}'`echo "tom...

晨猫 ⋅ 今天 ⋅ 0

Spring Bean的生命周期

前言 Spring Bean 的生命周期在整个 Spring 中占有很重要的位置,掌握这些可以加深对 Spring 的理解。 首先看下生命周期图: 再谈生命周期之前有一点需要先明确: Spring 只帮我们管理单例模...

素雷 ⋅ 今天 ⋅ 0

zblog2.3版本的asp系统是否可以超越卢松松博客的流量[图]

最近访问zblog官网,发现zlbog-asp2.3版本已经进入测试阶段了,虽然正式版还没有发布,想必也不久了。那么作为aps纵横江湖十多年的今天,blog2.2版本应该已经成熟了,为什么还要发布这个2.3...

原创小博客 ⋅ 今天 ⋅ 0

聊聊spring cloud的HystrixCircuitBreakerConfiguration

序 本文主要研究一下spring cloud的HystrixCircuitBreakerConfiguration HystrixCircuitBreakerConfiguration spring-cloud-netflix-core-2.0.0.RELEASE-sources.jar!/org/springframework/......

go4it ⋅ 今天 ⋅ 0

二分查找

二分查找,也称折半查找、二分搜索,是一种在有序数组中查找某一特定元素的搜索算法。搜素过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜素过程结束;如果某一特定元素大于...

人觉非常君 ⋅ 今天 ⋅ 0

VS中使用X64汇编

需要注意的是,在X86项目中,可以使用__asm{}来嵌入汇编代码,但是在X64项目中,再也不能使用__asm{}来编写嵌入式汇编程序了,必须使用专门的.asm汇编文件来编写相应的汇编代码,然后在其它地...

simpower ⋅ 今天 ⋅ 0

ThreadPoolExecutor

ThreadPoolExecutor public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, ......

4rnold ⋅ 昨天 ⋅ 0

Java正无穷大、负无穷大以及NaN

问题来源:用Java代码写了一个计算公式,包含除法和对数和取反,在页面上出现了-infinity,不知道这是什么问题,网上找答案才明白意思是负的无穷大。 思考:为什么会出现这种情况呢?这是哪里...

young_chen ⋅ 昨天 ⋅ 0

前台对中文编码,后台解码

前台:encodeURI(sbzt) 后台:String param = URLDecoder.decode(sbzt,"UTF-8");

west_coast ⋅ 昨天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部