• spring揭秘 读书笔记 六 bean的一生


    我们知道,Spring容器具有对象的BeanDefinition来保存该对象实例化时须要的数据。
    对象通过container.getBean()方法是才会初始化该对象。
    BeanFactory
    我们知道BeanFactory默认是懒载入的,换句话说,当我们请求对象a的时候,a本身还并没有被实例化,同一时候假设a还依赖b,那么b也还没有被初始化。
    当我们显示的在代码里调用getBean("a")的时候,容器会先初始化b,再初始化a,然后把b注入到a中,当然,假设a或b实现了某些回调接口,就依据接口装备它,最后返还a。
    ApplicationContext
    与BeanFactory不同,ApplicationContext并非懒载入,当我们获得ApplicationContext的引用后,全部的bean就已经被实例化了。仅仅只是它会在启动阶段的活动完毕之后,紧接着调用注冊到该容器的全部bean定义的实例化方法getBean()。


    另外,我们得知道container.getBean()方法仅仅是有可能会触发对象的实例化,我们仅仅是说"有可能"是由于,假设对象已经实例化了,那么调用getBean的时候就会返回缓存的那个对象(当然prototype型的除外)。
    实例化过程如图:





    实例化Bean对象

    容器在内部实现的时候,採用“策略模式(Strategy Pattern)”来决定採用何种方式初始化bean实例。通常。能够通过反射或者CGLIB动态字节码生成来初始化对应的bean实例或者动态生成其子类。

     
    在这里,大家能够查阅一下策略模式的使用。


    org.springframework.beans.factory.support.InstantiationStrategy定义是实例化策略的抽象接口,其直接子类SimpleInstantiationStrategy实现了简单的对象实例化功能,能够通过反射来实例化对象实例,但不支持方法注入方式的对象实例化。CglibSubclassingInstantiationStrategy继承了SimpleInstantiationStrategy的以反射方式实例化对象的功能,而且通过CGLIB的动态字节码生成功能,该策略实现类能够动态生成某个类的子类,进而满足了方法注入所需的对象
    实例化需求。默认情况下,容器内部採用的是CglibSubclassingInstantiationStrategy。
    在这一步,问题不大。


    只是有个东西得说明一下,第一步实例化的结果并非原生的bean,spring为了在第二步设置属性方便就用BeanWrapper对其做了包装。


    BeanWrapper

    BeanWrapper是个接口,事实上现类是BeanWrapperImpl。


    Object provider = Class.forName("package.name.FXNewsProvider").newInstance(); 
    Object listener = Class.forName("package.name.DowJonesNewsListener").newInstance(); 
    Object persister = Class.forName("package.name.DowJonesNewsPersister").newInstance(); 
     
    BeanWrapper newsProvider = new BeanWrapperImpl(provider); 
    newsProvider.setPropertyValue("newsListener", listener); 
    newsProvider.setPropertyValue("newPersistener", persister); 
    
    
    assertTrue(newsProvider.getWrappedInstance() instanceof FXNewsProvider); 
    assertSame(provider, newsProvider.getWrappedInstance()); 
    assertSame(listener, newsProvider.getPropertyValue("newsListener")); 
    assertSame(persister, newsProvider.getPropertyValue("newPersistener")); 
    这是使用BeanWrapper设置属性的样例。
    假设直接用反射呢?



    		Object provider;
    		try {
    			provider = Class.forName("package.name.FXNewsProvider").newInstance();
    			Object listener = Class.forName("package.name.DowJonesNewsListener").newInstance(); 
    			Object persister = Class.forName("package.name.DowJonesNewsPersister").newInstance(); 
    
    
    			Class providerClazz = provider.getClass(); 
    			Field listenerField = providerClazz.getField("newsListener"); 
    			listenerField.set(provider, listener); 
    			Field persisterField = providerClazz.getField("newsListener"); 
    			persisterField.set(provider, persister); 
    		} catch (InstantiationException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		} catch (IllegalAccessException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		} catch (ClassNotFoundException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		} catch (NoSuchFieldException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		} catch (SecurityException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    呵呵,操蛋的try catch。


    使用BeanWrapper的优点就在于,我们能够统一对属性的操作。




    各种Aware接口

    还记得,我们在前几节讲的关于,从bean中获得BeanFactory的引用的样例吗?


    事实上这种接口还有几个:
    org.springframework.beans.factory.BeanNameAware。

    让bean知道自己在容器中究竟叫什么名字。
    org.springframework.beans.factory.BeanClassLoaderAware。让bean知道是那个classloader载入的自己。


    org.springframework.beans.factory.BeanFactoryAware。

    让bean知道自己究竟在哪个"世界"里
    上面的几个是针对beanFactory的。
    相应于ApplicationContext的Aware接口有
    org.springframework.context.ResourceLoaderAware 。 ApplicationContext 实 现 了Spring的ResourceLoader接口(后面会提及具体信息)。当容器检測到当前对象实例实现了ResourceLoaderAware接口之后,会将当前ApplicationContext自身设置到对象实例。这样当前对象实例就拥有了其所在ApplicationContext容器的一个引用。 


    org.springframework.context.ApplicationEventPublisherAware。ApplicationContext作为一个容器,同一时候还实现了ApplicationEventPublisher接口。这样,它就能够作为Appli- cationEventPublisher来使用。

    所以,当前ApplicationContext容器假设检測到当前实例化的对象实例声明了ApplicationEventPublisherAware接口。则会将自身注入当前对象。 


    org.springframework.context.MessageSourceAware。ApplicationContext通过MessageSource接口提供国际化的信息支持,即I18n(Internationalization)。它自身就实现了MessageSource接口,所以当检測到当前对象实例实现了MessageSourceAware接口,则会将自身注入当前对象实例。

     


    org.springframework.context.ApplicationContextAware。

    假设ApplicationContext容器检測到当前对象实现了ApplicationContextAware接口。则会将自身注入当前对象实例。

     




    BeanPostProcessor

    这是什么东西?这是applicationcontext检測Aware接口并设置相关依赖的东西。


    BeanPostProcessor的概念easy与BeanFactoryPostProcessor的概念混淆。但仅仅要记住BeanPostProcessor是存在于对象实例化阶段,而BeanFactoryPostProcessor则是存在于容器启动阶段,这两个概念就比較easy区分了。 
    BeanPostProcessor接口有两个方法,例如以下:

    public interface BeanPostProcessor {
    	Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
    	Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
    }
    两个方法
    postProcessBeforeInitialization相应与图4-10中的BeanPostProcessor前置处理。
    postProcessAfterInitialization相应与图4-10中的BeanPostProcessor后置处理。


    BeanPostProcessor这个两个方法的參数都包括了当前要处理的bean,也就是说,在这两个方法里,我们能够对bean做不论什么事情。


    通常比較常见的使用BeanPostProcessor的场景,是处理标记接口实现类,或者为当前对象提供代理实现。在图4-10的第三步中,ApplicationContext相应的那些Aware接口实际上就是通过BeanPostProcessor的方式进行处理的。当ApplicationContext中每个bean对象的实例化过程走到BeanPostProcessor前置处理这一步时,ApplicationContext容器会检測每个bean是否实现了之前注冊到容器的ApplicationContextAwareProcessor这个BeanPostProcessor的实现类。然后就会调用其postProcessBeforeInitialization()方法,检查并设置Aware相关依赖。
    postProcessBeforeInitialization方法调用了invokeAwareInterfaces方法。

    private void invokeAwareInterfaces(Object bean) {
    		if (bean instanceof Aware) {
    			if (bean instanceof EnvironmentAware) {
    				((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
    			}
    			if (bean instanceof EmbeddedValueResolverAware) {
    				((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(
    						new EmbeddedValueResolver(this.applicationContext.getBeanFactory()));
    			}
    			if (bean instanceof ResourceLoaderAware) {
    				((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
    			}
    			if (bean instanceof ApplicationEventPublisherAware) {
    				((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
    			}
    			if (bean instanceof MessageSourceAware) {
    				((MessageSourceAware) bean).setMessageSource(this.applicationContext);
    			}
    			if (bean instanceof ApplicationContextAware) {
    				((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
    			}
    		}
    	}

    注意ApplicationContextAwareProcessor这个类,实现了BeanPostProcessor,可是它并没有实现ApplicationContextAware这个接口。


    *******************************************

    下面为2016-04-20日更新

    假设你在spring的doc文档里找ApplicationContextAwareProcessor,是找不到的,由于它并非public的

    第二,看名字也知道这个类是作用于容器时applicationcontext的时候,假设我们单纯的使用beanfactory,是不会用到它的

    第三,ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)----->refresh()---->prepareBeanFactory(beanFactory);

    看到了吧,在这里手动注入的

    那么我们自己写的beanpostprocessor怎样增加到容器里呢?

    ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)----->refresh()-->registerBeanPostProcessors()



    第四 和beanfactory的差别

    applicationcontext是专门注入一个ApplicationContextAwareProcessor,来处理xxxaware接口的问题

    beanfactory并没有一个单独的类来处理它,当然也就不须要注入这个类了

    看AbstractAutowireCapableBeanFactory:




    以上为2016-04-20日更新

    *******************************************


    自己定义BeanPostProcessor

    为了演示BeanPostProcessor的强大功能,我们自己定义一个BeanPostProcessor
    如果系统中全部的IFXNewsListener实现类须要从某个位置取得对应的server连接password。并且系统中保存的password是加密的,那么在IFXNewsListener发送这个password给新闻server进行连接验证的时候,首先须要对系统中取得的password进行解密,然后才干发送。

    我们将採用BeanPostProcessor技术,对全部的IFXNewsListener的实现类进行统一的解密操作。

     
    究竟哪些类须要加密解密呢?

    我们用PasswordDecodable标记

    public interface PasswordDecodable { 
      String getEncodedPassword();   //从系统某处获得加密后的密码
      void setDecodedPassword(String password);  //将解密后的密码写入对象中
    } 
     
    public class DowJonesNewsListener implements IFXNewsListener,PasswordDecodable { 
      private String password;           //解密后的密码
      
      public String[] getAvailableNewsIds() { 
       // 省略 
      } 
     
      public FXNewsBean getNewsByPK(String newsId) { 
       // 省略 
      } 
     
      public void postProcessIfNecessary(String newsId) { 
       // 省略 
      } 
     
      public String getEncodedPassword() { 
       return this.password; 
      } 
     
      public void setDecodedPassword(String password) { 
       this.password = password; 
      } 
     
    } 
    然后实现我们自己的BeanPostProcessor。


    public class PasswordDecodePostProcessor implements BeanPostProcessor { 
     
      public Object postProcessAfterInitialization(Object object, String beanName) 
         throws BeansException { 
       return object; 
      } 
    
    
      public Object postProcessBeforeInitialization(Object object, String beanName) 
        throws BeansException { 
       if(object instanceof PasswordDecodable) { 
        String encodedPassword = ((PasswordDecodable)object).getEncodedPassword(); 
        String decodedPassword = decodePassword(encodedPassword); 
        ((PasswordDecodable)object).setDecodedPassword(decodedPassword); 
       }    
       return object; 
      } 
    
    
      private String decodePassword(String encodedPassword) { 
         if(encodedPassword.length()<5)    //解码是 去掉前5个字符就是真正的password..
            return "";
         encodedPassword=encodedPassword.subString(5);
         return encodedPassword; 
      } 
    }  
    下来就是注冊这个PasswordDecodePostProcessor了。
    对于BeanFactory类型的容器来说,我们须要通过手工编码的方式将对应的BeanPostProcessor注冊到容器。也就是调用ConfigurableBeanFactory的addBeanPostProcessor()方法,见例如以下代码:
    ConfigurableBeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource(...)); 
    beanFactory.addBeanPostProcessor(new PasswordDecodePostProcessor()); 
    ... 
    getBean(); 
    假设是applicationcontext那就直接在xml注明就OK
     <beans> 
      <bean id="passwordDecodePostProcessor" class="package.name.PasswordDecodePostProcessor"> 
       <!--假设须要,注入必要的依赖--> 
      </bean>   
      ... 
    </beans> 

    http://guoliangqi.iteye.com/blog/635826 里面包括了一个自己定义BeanPostProcessor实现动态代理的样例


    感谢glt


     

    參考资料

    http://guoliangqi.iteye.com/blog/635826
    http://langgufu.iteye.com/blog/1499966
    http://blog.csdn.net/wyabc1986/article/details/9415463
  • 相关阅读:
    在日志中记录Java异常信息的正确姿势
    基于Spring Boot架构的前后端完全分离项目API路径问题
    Spring生态简介
    WebSocket协议入门介绍
    Spring Boot程序正确停止的姿势
    python 中 __init__方法
    python中的if __name__ == 'main'
    python 类和实例
    内建模块 datetime使用
    内建模块collections的使用
  • 原文地址:https://www.cnblogs.com/claireyuancy/p/7048041.html
Copyright © 2020-2023  润新知