SpringIoc容器之Aware

打印 上一主题 下一主题

主题 503|帖子 503|积分 1509

1 前言

Aware是Spring提供的一个标记超接口,指示bean有资格通过回调样式的方法由Spring容器通知特定的框架对象,以获取到容器中特有对象的实例的方法之一。实际的方法签名由各个子接口确定,但通常只包含一个接受单个参数的void返回方法。
2 Spring中9个Aware内置实现
  1. |--Aware
  2.     |--BeanNameAware
  3.     |--BeanClassLoaderAware
  4.     |--BeanFactoryAware
  5.     |--EnvironmentAware
  6.     |--EmbeddedValueResolverAware
  7.     |--ResourceLoaderAware
  8.     |--ApplicationEventPublisherAware
  9.     |--MessageSourceAware
  10.     |--ApplicationContextAware
复制代码
9个内置实现又分两类,前三个为直接调用,后6个通过ApplicationContextAwareProcessor后置处理器,间接回调
2.1 BeanNameAware
  1. public interface BeanNameAware extends Aware {
  2.        /**
  3.         *设置创建此bean的bean工厂中的bean的名称。
  4.         *在普通bean属性填充之后但在
  5.         *初始化之前回调,如{@link InitializingBean#afterPropertiesSet()}
  6.         *或自定义初始化方法。
  7.         * @param name工厂中bean的名称。
  8.         *注意,此名称是工厂中使用的实际bean名称,这可能
  9.         *与最初指定的名称不同:特别是对于内部bean
  10.         * names,实际的bean名称可以通过添加
  11.         *“#…”后缀。使用{@link BeanFactoryUtils#originalBeanName(String)}
  12.         *方法提取原始bean名称(不带后缀),如果需要的话。
  13.         * /
  14.         void setBeanName(String name);
  15. }
复制代码
实现BeanNameAware接口需要实现setBeanName()方法,这个方法只是简单的返回我们当前的beanName,这个接口表面上的作用就是让实现这个接口的bean知道自己在spring容器里的名字,而且官方的意思是这个接口更多的使用在spring的框架代码中,实际开发环境应该不建议使用,因为spring认为bean的名字与bean的联系并不是很深,(的确,抛开spring API而言,我们如果获取了该bean的名字,其实意义不是很大,我们没有获取该bean的class,只有该bean的名字,我们也无从下手,相反,因为bean的名称在spring容器中可能是该bean的唯一标识,也就是说再beanDefinitionMap中,key值就是这个name,spring可以根据这个key值获取该bean的所有特性)所以spring说这个不是非必要的依赖。
2.2 BeanClassLoaderAware
  1. public interface BeanClassLoaderAware extends Aware {
  2.    /**
  3.     *提供bean {@link ClassLoader}类加载器的回调
  4.     *一个bean实例在属性的填充之后但在初始化回调之前调用
  5.     * {@link InitializingBean
  6.     * {@link InitializingBean#afterPropertiesSet()}
  7.     *方法或自定义初始化方法。
  8.     * @param类加载器拥有的类加载器;可能是{@code null}在例如,必须使用默认的{@code ClassLoader}
  9.     * 获取的{@code ClassLoader}
  10.     * {@link org.springframework.util.ClassUtils#getDefaultClassLoader()}
  11.     * /
  12.    void setBeanClassLoader(ClassLoader classLoader);
  13. }
复制代码
在bean属性填充之后初始化之前,提供类加制器的回调。让受管Bean本身知道它是由哪一类装载器负责装载的。
2.3 BeanFactoryAware
  1. public interface BeanFactoryAware extends Aware {
  2.    /**
  3.     * 为bean实例提供所属工厂的回调。
  4.     * 在普通bean属性填充之后调用但在初始化回调之前,如
  5.     * {@link InitializingBean#afterPropertiesSet()}或自定义初始化方法。
  6.     * @param beanFactory拥有beanFactory(非空)。bean可以立即调用工厂上的方法。
  7.     * @在初始化错误时抛出BeansException
  8.     * @参见BeanInitializationException
  9.     * /
  10.    void setBeanFactory(BeanFactory beanFactory) throws BeansException;
  11. }
复制代码
在bean属性填充之后初始化之前,提bean工厂的回调。实现 BeanFactoηAware 接口的 bean 可以直接访问 Spring 容器,被容器创建以后,它会拥有一个指向 Spring 容器的引用,可以利用该bean根据传入参数动态获取被spring工厂加载的bean
2.4 EnvironmentAware
  1. public interface EnvironmentAware extends Aware {
  2.    /**
  3.     * 设置该对象运行的{@code环境}。
  4.     */
  5.    void setEnvironment(Environment environment);
  6. }
复制代码
设置该对象运行的。所有注册到 Spring容器内的 bean,只要该bean 实现了 EnvironmentAware接口,并且进行重写了setEnvironment方法的情况下,那么在工程启动时就可以获取得 application.properties 的配置文件配置的属性值,这样就不用我们将魔法值写到代码里面了。
2.5 EmbeddedValueResolverAware
  1. public interface EmbeddedValueResolverAware extends Aware {
  2.    /**
  3.     * 设置StringValueResolver用于解析嵌入的定义值。
  4.     */
  5.    void setEmbeddedValueResolver(StringValueResolver resolver);
  6. }
复制代码
在基于Spring获取properties文件属性值的时候,一般使用@Value的方式注入配置文件属性值,但是@Value必须要在Spring的Bean生命周期管理下才能使用,比如类被@Controller、@Service、@Component等注解标注。如有的抽象类中,基于Spring解析@Value的方式,使用EmbeddedValueResolverAware解析配置文件来实现。
2.6 ResourceLoaderAware
  1. public interface ResourceLoaderAware extends Aware {
  2.    /**
  3.     *设置该对象运行的ResourceLoader。这可能是一个ResourcePatternResolver,它可以被检查
  4.     *通过{@code instanceof ResourcePatternResolver}。另请参阅
  5.     * {@code ResourcePatternUtils。getResourcePatternResolver}方法。
  6.     * <p>在填充普通bean属性之后但在init回调之前调用
  7.     *像InitializingBean的{@code afterPropertiesSet}或自定义初始化方法。
  8.     *在ApplicationContextAware的{@code setApplicationContext}之前调用。
  9.     * @param resourceLoader该对象使用的resourceLoader对象
  10.     * @ @ springframework.core. io.support.resourcepatternresolver
  11.     * @ @ resourcepatternutils #获取resourcepatternresolver
  12.     * /
  13.    void setResourceLoader(ResourceLoader resourceLoader);
  14. }
复制代码
ResourceLoaderAware 是特殊的标记接口,它希望拥有一个 ResourceLoader 引用的对象。当实现了 ResourceLoaderAware接口的类部署到application context(比如受Spring管理的bean)中时,它会被application context识别为 ResourceLoaderAware。 接着application context会调用setResourceLoader(ResourceLoader)方法,并把自身作为参数传入该方法(记住,所有Spring里的application context都实现了ResourceLoader接口)。
既然 ApplicationContext 就是ResourceLoader,那么该bean就可以实现 ApplicationContextAware接口并直接使用所提供的application context来载入资源,但是通常更适合使用特定的满足所有需要的 ResourceLoader 实现。 这样一来,代码只需要依赖于可以看作辅助接口的资源载入接口,而不用依赖于整个Spring ApplicationContext 接口。
2.7 ApplicationEventPublisherAware
  1. public interface ApplicationEventPublisherAware extends Aware {
  2.    /**
  3.     *设置该对象运行的ApplicationEventPublisher。
  4.     * <p>在普通bean属性填充之后但在init之前调用像InitializingBean的afterPropertiesSet或自定义初始化方法。
  5.     *在ApplicationContextAware的setApplicationContext之前调用。
  6.     *该对象使用的事件发布者
  7.     * /
  8.    void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher);
  9. }
复制代码
ApplicationEventPublisherAware 是由 Spring 提供的用于为 Service 注入 ApplicationEventPublisher 事件发布器的接口,使用这个接口,我们自己的 Service 就拥有了发布事件的能力。
2.8 MessageSourceAware
  1. public interface MessageSourceAware extends Aware {
  2.    /**
  3.     *设置该对象运行的MessageSource。
  4.     * <p>在普通bean属性填充之后但在init之前调用像InitializingBean的afterPropertiesSet或自定义初始化方法。
  5.     *在ApplicationContextAware的setApplicationContext之前调用。
  6.     * @param messageSource消息源
  7.     * /
  8.    void setMessageSource(MessageSource messageSource);
  9. }
复制代码
获得message source这样可以获得文本信息,使用场景如为了国际化。
2.9 ApplicationContextAware
  1. public interface ApplicationContextAware extends Aware {
  2.    /**
  3.     *设置该对象运行的ApplicationContext。通常这个调用将用于初始化对象。
  4.     * <p>在普通bean属性填充之后但在init回调之前调用
  5.     *作为{@link org.springframework.beans.factory.InitializingBean#afterPropertiesSet()}
  6.     *或自定义初始化方法。在{@link ResourceLoaderAware#setResourceLoader}之后调用,
  7.     * {@link ApplicationEventPublisherAware#setApplicationEventPublisher}和
  8.     * {@link MessageSourceAware},如果适用。
  9.     * @param applicationContext该对象将使用的applicationContext对象
  10.     * @在上下文初始化错误时抛出ApplicationContextException如果由应用程序上下文方法抛出,则抛出BeansException
  11.     * @see org.springframework.beans.factory.BeanInitializationException
  12.     * /
  13.    void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
  14. }
复制代码
ApplicationContextAware的作用是可以方便获取Spring容器ApplicationContext,从而可以获取容器内的Bean。ApplicationContextAware接口只有一个方法,如果实现了这个方法,那么Spring创建这个实现类的时候就会自动执行这个方法,把ApplicationContext注入到这个类中,也就是说,spring 在启动的时候就需要实例化这个 class(如果是懒加载就是你需要用到的时候实例化),在实例化这个 class 的时候,发现它包含这个 ApplicationContextAware 接口的话,sping 就会调用这个对象的 setApplicationContext 方法,把 applicationContext Set 进去了。
3 Spring中调用时机

Aware接口由Spring在AbstractAutowireCapableBeanFactory.initializeBean(beanName, bean,mbd)方法中通过调用invokeAwareMethods(beanName, bean)方法和applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName)触发Aware方法的调用

3.1 invokeAwareMethods
  1. private void invokeAwareMethods(final String beanName, final Object bean) {
  2.    if (bean instanceof Aware) {
  3.       if (bean instanceof BeanNameAware) {
  4.          ((BeanNameAware) bean).setBeanName(beanName);
  5.       }
  6.       if (bean instanceof BeanClassLoaderAware) {
  7.          ((BeanClassLoaderAware) bean).setBeanClassLoader(getBeanClassLoader());
  8.       }
  9.       if (bean instanceof BeanFactoryAware) {
  10.          ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
  11.       }
  12.    }
  13. }
复制代码
判断并直接回调
3.2 applyBeanPostProcessorsBeforeInitialization
  1. public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
  2.       throws BeansException {
  3.    Object result = existingBean;
  4.    for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
  5.       result = beanProcessor.postProcessBeforeInitialization(result, beanName);
  6.       if (result == null) {
  7.          return result;
  8.       }
  9.    }
  10.    return result;
  11. }
复制代码
通过ApplicationContextAwareProcessor.postProcessBeforeInitialization(Object bean, String beanName)间接调用,并在方法invokeAwareInterfaces中进行回调。
  1. public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
  2.    AccessControlContext acc = null;
  3.    if (System.getSecurityManager() != null &&
  4.          (bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
  5.                bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
  6.                bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) {
  7.       acc = this.applicationContext.getBeanFactory().getAccessControlContext();
  8.    }
  9.    if (acc != null) {
  10.       AccessController.doPrivileged(new PrivilegedAction<Object>() {
  11.          @Override
  12.          public Object run() {
  13.             invokeAwareInterfaces(bean);
  14.             return null;
  15.          }
  16.       }, acc);
  17.    }
  18.    else {
  19.       invokeAwareInterfaces(bean);
  20.    }
  21.    return bean;
  22. }
  23. private void invokeAwareInterfaces(Object bean) {
  24.    if (bean instanceof Aware) {
  25.       if (bean instanceof EnvironmentAware) {
  26.          ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
  27.       }
  28.       if (bean instanceof EmbeddedValueResolverAware) {
  29.          ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(
  30.                new EmbeddedValueResolver(this.applicationContext.getBeanFactory()));
  31.       }
  32.       if (bean instanceof ResourceLoaderAware) {
  33.          ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
  34.       }
  35.       if (bean instanceof ApplicationEventPublisherAware) {
  36.          ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
  37.       }
  38.       if (bean instanceof MessageSourceAware) {
  39.          ((MessageSourceAware) bean).setMessageSource(this.applicationContext);
  40.       }
  41.       if (bean instanceof ApplicationContextAware) {
  42.          ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
  43.       }
  44.    }
  45. }
复制代码
4 总结

通过上面的分析,可以知道Spring生命周期中的初始化方法里,在真正执行初始化方法之前,分别通过invokeAwareMethods方法和后置处理器ApplicationContextAwareProcessor来触发Aware的调用,那么,Spring为什么要使用两种方式而不使用其中之一呢?
通过本章我们了解了9中内置接口的作用,以及它们能够获取到的不同上下文信息。
作者:京东零售 曾登均
来源:京东云开发者社区

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

渣渣兔

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表