zoukankan      html  css  js  c++  java
  • Spring之-InitializingBean接口的应用

    一、InitializingBean接口说明

      InitializingBean接口为bean提供了属性初始化后的处理方法,它只包括afterPropertiesSet方法,凡是继承该接口的类,在bean的属性初始化后都会执行该方法。如下源码,从方法名afterPropertiesSet也可以清楚的理解该方法是在属性设置后才调用的。

    package org.springframework.beans.factory;
    
    /**
     * Interface to be implemented by beans that need to react once all their
     * properties have been set by a BeanFactory: for example, to perform custom
     * initialization, or merely to check that all mandatory properties have been set.
     *
     * <p>An alternative to implementing InitializingBean is specifying a custom
     * init-method, for example in an XML bean definition.
     * For a list of all bean lifecycle methods, see the BeanFactory javadocs.
     *
     * @author Rod Johnson
     * @see BeanNameAware
     * @see BeanFactoryAware
     * @see BeanFactory
     * @see org.springframework.beans.factory.support.RootBeanDefinition#getInitMethodName
     * @see org.springframework.context.ApplicationContextAware
     */
    public interface InitializingBean {
    
        /**
         * Invoked by a BeanFactory after it has set all bean properties supplied
         * (and satisfied BeanFactoryAware and ApplicationContextAware).
         * <p>This method allows the bean instance to perform initialization only
         * possible when all bean properties have been set and to throw an
         * exception in the event of misconfiguration.
         * @throws Exception in the event of misconfiguration (such
         * as failure to set an essential property) or if initialization fails.
         */
        void afterPropertiesSet() throws Exception;
    
    }

    二、源码分析接口应用

    通过查看spring的加载bean的源码类(AbstractAutowireCapableBeanFactory)可以看到:

    protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd)
                throws Throwable {
    //判断该bean是否实现了实现了InitializingBean接口,如果实现了InitializingBean接口,则调用bean的afterPropertiesSet方法
            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 {
                                //调用afterPropertiesSet
                                ((InitializingBean) bean).afterPropertiesSet();
                                return null;
                            }
                        }, getAccessControlContext());
                    }
                    catch (PrivilegedActionException pae) {
                        throw pae.getException();
                    }
                }
                else {
                    //调用afterPropertiesSet
                    ((InitializingBean) bean).afterPropertiesSet();
                }
            }
    
            if (mbd != null) {            //判断是否指定了init-method方法,如果指定了init-method方法,则再调用制定的init-method
                String initMethodName = mbd.getInitMethodName();
                if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
                        !mbd.isExternallyManagedInitMethod(initMethodName)) {
                    //反射调用init-method方法
                    invokeCustomInitMethod(beanName, bean, mbd);
                }
            }
        }

    分析代码可以了解:

    1. spring为bean提供了两种初始化bean的方式,实现InitializingBean接口,实现afterPropertiesSet方法,或者在配置文件中同过init-method指定,两种方式可以同时使用

    2. 实现InitializingBean接口是直接调用afterPropertiesSet方法,比通过反射调用init-method指定的方法效率相对来说要高点。但是init-method方式消除了对spring的依赖

    3. 如果调用afterPropertiesSet方法时出错,则不调用init-method指定的方法。

    三、接口应用

      InitializingBean接口在spring框架中本身就很多应用,这就不多说了。我们在实际应用中如何使用该接口呢?

    应用一:

    1、使用InitializingBean接口处理一个配置文件:

    import java.io.File;
    import java.io.FileInputStream;
    import java.util.Properties;
    
    import org.springframework.beans.factory.InitializingBean;
    
    public class ConfigBean implements InitializingBean{
        
        //微信公众号配置文件
        private String configFile;
        
        private String appid;
        
        private String appsecret;
        
        public String getConfigFile() {
            return configFile;
        }
    
        public void setConfigFile(String configFile) {
            this.configFile = configFile;
        }
        
        public void afterPropertiesSet() throws Exception {
            if(configFile!=null){
                File cf = new File(configFile);
                if(cf.exists()){
                    Properties pro = new Properties();
                    pro.load(new FileInputStream(cf));
                    appid = pro.getProperty("wechat.appid");
                    appsecret = pro.getProperty("wechat.appsecret");
                }
            }
            System.out.println(appid);
            System.out.println(appsecret);
        }
    }

    2、配置
    spring配置文件:

        <bean id="configBean" class="com.ConfigBean">
            <property name="configFile" value="d:/wechat.properties"></property>
        </bean>

    wechat.properties配置文件

        wechat.appid=wxappid
        wechat.appsecret=wxappsecret

    3、测试

     public static void main(String[] args) throws Exception {
         String config = Test.class.getPackage().getName().replace('.', '/') + "/bean.xml";
         ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(config);
         context.start();
     }

    应用二:

      在我们将一个Bean交给Spring管理的时候,有时候我们的Bean中有某个属性需要注入,但是又不能通过一般的方式注入,什么意思呢?举个栗子:首先我们有个Service,在该Service中有一个属性

    ,但是该属性不支持Spring注入,只能通过Build或者new的方式创建(比如StringBuffer之类的),但是我们想在Spring配置Bean的时候一起将该属性注入进来,这时候该怎么办呢?这时候可以通

    过实现InitializingBean接口来解决!

    @Service
    public class DemoService implements InitializingBean{
    
        private StringBuffer stringBuffer;
    
    
        @Override
        public void afterPropertiesSet() throws Exception {
            stringBuffer = new StringBuffer();
        }
    }

      上面的列子实现了InitializingBean接口并实现其afterPropertiesSet方法,通过这种方式就可以实现一些比较特殊的注入,当然也可以在afterPropertiesSet方法中添加一些其他逻辑来控制创建的对象。当然除了InitializingBean接口,还有一个类似的接口:DisposableBean ,该接口的作用是在对象销毁时调用。

    原理:
      首先说说spring的IOC容器初始化过程,首先Spring会定位BeanDefinition资源文件,然后会一个一个的去加载所有BeanDefinition,这里的BeanDefinition就是指的Bean的资源文件,即:在XML中配置的Bean和通过注解装配的Bean,在加载完所有BeanDefinition之后,会将这些BeanDefinition注册到一个HashMap中。到此spring的IOC初始化完成,那么依赖注入发生在哪里呢?在用户第一次向IOC容器索要Bean时才开始依赖注入过程(也可以通过配置lazy-init属性让容器初始化的时候就对Bean预实例化)那究竟afterPropertiesSet()方法的调用是在哪个时间点呢?通过查看该方法上的注释:

    Invoked by a BeanFactory after it has set all bean properties supplied  (and satisfied BeanFactoryAware and ApplicationContextAware).
      可以看到在Bean所有的属性都被注入之后会去调用这个afterPropertiesSet()方法,其实在依赖注入完成的时候,spring会去检查这个类是否实现了InitializingBean接口,如果实现了InitializingBean接口,就会去调用这个类的afterPropertiesSet()方法。所以afterPropertiesSet()方法的执行时间点就很清楚了,发生在所有的properties被注入后。

    转载自:
    1. 链接:https://www.jianshu.com/p/cbfed4b6541f
    2. https://segmentfault.com/a/1190000012461362
  • 相关阅读:
    String
    Xposed源码编译踩坑实录
    Hello 博客园
    HDU 1430 关系映射 + 打表 .
    HDU 1430 关系映射 + 打表 .
    hdu1043 经典的八数码问题 逆向bfs打表 + 逆序数
    hdu1043 经典的八数码问题 逆向bfs打表 + 逆序数
    hdu 1044 BFS(压缩图)+DFS
    hdu 1044 BFS(压缩图)+DFS
    hdu3338 最大流
  • 原文地址:https://www.cnblogs.com/damoblog/p/14224535.html
Copyright © 2011-2022 走看看