zoukankan      html  css  js  c++  java
  • [spring源码学习]五-BeanPostProcessor的使用

    一、接口描述

    spring提供了一个接口类-BeanPostProcessor,我们叫他:bean的加工器,应该是在bean的实例化过程中对bean做一些包装处理,里边提供两个方法

    public interface BeanPostProcessor
    {
    
        public abstract Object postProcessBeforeInitialization(Object obj, String s)
            throws BeansException;
    
        public abstract Object postProcessAfterInitialization(Object obj, String s)
            throws BeansException;
    }

       根据类的名称,我们可以猜测两个接口方法的定义分别为:

      1、在bean初始化之前执行

      2、在bean的初始化之后执行

      我们需要到找到spring源码中执行两个方法的代码进行验证,在AbstractAutowireCapableBeanFactory类的方法中,我们找到了执行方法:

    二、源码探查

    protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
            if (System.getSecurityManager() != null) {
                AccessController.doPrivileged(new PrivilegedAction<Object>() {
                    @Override
                    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;
        }

     applyBeanPostProcessorsBeforeInitialization和applyBeanPostProcessorsAfterInitialization的具体代码分别为:  

    public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
                throws BeansException {
    
            Object result = existingBean;
            for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
                result = beanProcessor.postProcessBeforeInitialization(result, beanName);
                if (result == null) {
                    return result;
                }
            }
            return result;
        }
    public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
                throws BeansException {
    
            Object result = existingBean;
            for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
                result = beanProcessor.postProcessAfterInitialization(result, beanName);
                if (result == null) {
                    return result;
                }
            }
            return result;
        }

      根据以上代码,我们得知,在invokeInitMethods的执行前后,spring会分别调用所有的BeanPostProcessor,执行其中的方法,那么invokeInitMethods的具体内容我们仍需要看下,发现此方法主要作用有两个:1、判断bean是否继承了InitializingBean,如果继承接口,执行afterPropertiesSet()方法,2、获得是否设置了init-method属性,如果设置了,就执行设置的方法

    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>() {
                            @Override
                            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);
                }
            }
        }

      根据以上描述,我们可以看到原有推断有一些问题,两个方法的执行主要是在bean完成初始化之后,准备执行默认方法时候对bean进行包装。

    三、应用场景

      几个典型的应用如:

      1、解析bean的注解,将注解中的字段转化为属性

      2、统一将属性在执行前,注入bean中,如数据库访问的sqlMap,如严重服务,这样不需要每个bean都配置属性

      3、打印日志,记录时间等。

    四、实践

      1、定义接口和实例

      

    package com.zjl.beanpostprocessor;
    
    public interface DemoService {
        public void sayHello();
    }
    public class DemoServiceImpl implements DemoService,NameInit {
        String name;
    
        @Override
        public void sayHello() {
            System.out.println("hello "+name);
        }
    
        @Override
        public void setName(String name) {
            this.name=name;
        }
    }

      2、定义bean的配置

            <bean id="demoService" class="com.zjl.beanpostprocessor.DemoServiceImpl">
            </bean>

      此处实例中需要name的值进行打印,但是我们bean中并没有提供此属性

      3、定义注入接口

    public interface NameInit {
        public void setName(String name);
    }

      4、定义一个BeanPostProcessor 实例,凡是继承了NameInit的接口,均实例化,注入name值。此处定义接口一方面是要使用接口中提供的setName方法,另一方面减轻系统压力,防止每个bean都进行注入。

    /**
     * 针对继承了接口的bean,注入name
     * @author lenovo
     * @time 2016年4月21日
     *
     */
    public class NameBeanPostProcessor implements BeanPostProcessor {
        String name;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            if(bean instanceof NameInit){
                ((NameInit)bean).setName(name);
            }
            return bean;
        }
    
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            return bean;
        }
    
    }

      5、定义bean,注入name的值

            <bean id="nameBeanPostProcessor" class="com.zjl.beanpostprocessor.NameBeanPostProcessor">
                <property name="name" value="zhangsan"></property>
            </bean>

      6、定义另一个BeanPostProcessor ,仅打印日志

    public class LogBeanPostProcessor implements BeanPostProcessor {
    
        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            System.out.println("正在处理"+beanName);
            return bean;
        }
    
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            System.out.println("已经处理完成"+beanName);
            return bean;
        }
    
    }

      7、定义bean

            <bean id="logBeanPostProcessor" class="com.zjl.beanpostprocessor.LogBeanPostProcessor">
            </bean>

      8、测试类

      

    public class BeanPostProcessorTest {
        public static void main(String[] args) {
            ApplicationContext context=new FileSystemXmlApplicationContext("beanpostprocessor.xml");
            DemoService demoService=(DemoService) context.getBean("demoService");
            demoService.sayHello();
        }
    }

      9、测试结果为

    正在处理demoService
    已经处理完成demoService
    hello zhangsan

      总结:根据执行结果,再次验证

      1、两个方法均在bean实例化期间已经完成,

      2、name属性是根据NameInit接口自动注入

      3、由于两个方法执行的时间特殊性,所以打印日志和记录时间意义不大,主要还是用于注入属性和完善配置

  • 相关阅读:
    Web开发技术大会演讲
    方法的长度
    避免复制与粘贴
    引入工厂方法支持扩展
    利用多态重构为带参方法
    代码质量对系统的影响
    《重构之美》开篇
    老鸟对菜鸟的一些建议
    jQuery 使用 AJAX解决跨域问题jQuery 使用 AJAX解决跨域问题
    uniapp renderjs通信
  • 原文地址:https://www.cnblogs.com/jyyzzjl/p/5417418.html
Copyright © 2011-2022 走看看