SpringIoc容器之Aware | 京东云技术团队

news/2024/12/21 22:33:47/

1 前言

Aware是Spring提供的一个标记超接口,指示bean有资格通过回调样式的方法由Spring容器通知特定的框架对象,以获取到容器中特有对象的实例的方法之一。实际的方法签名由各个子接口确定,但通常只包含一个接受单个参数的void返回方法。

2 Spring中9个Aware内置实现

|--Aware|--BeanNameAware|--BeanClassLoaderAware|--BeanFactoryAware|--EnvironmentAware|--EmbeddedValueResolverAware|--ResourceLoaderAware|--ApplicationEventPublisherAware|--MessageSourceAware|--ApplicationContextAware

9个内置实现又分两类,前三个为直接调用,后6个通过ApplicationContextAwareProcessor后置处理器,间接回调

2.1 BeanNameAware

public interface BeanNameAware extends Aware {/***设置创建此bean的bean工厂中的bean的名称。*在普通bean属性填充之后但在*初始化之前回调,如{@link InitializingBean#afterPropertiesSet()}*或自定义初始化方法。* @param name工厂中bean的名称。*注意,此名称是工厂中使用的实际bean名称,这可能*与最初指定的名称不同:特别是对于内部bean* names,实际的bean名称可以通过添加*“#…”后缀。使用{@link BeanFactoryUtils#originalBeanName(String)}*方法提取原始bean名称(不带后缀),如果需要的话。* /void setBeanName(String name);}

实现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

public interface BeanClassLoaderAware extends Aware {/***提供bean {@link ClassLoader}类加载器的回调*一个bean实例在属性的填充之后但在初始化回调之前调用* {@link InitializingBean* {@link InitializingBean#afterPropertiesSet()}*方法或自定义初始化方法。* @param类加载器拥有的类加载器;可能是{@code null}在例如,必须使用默认的{@code ClassLoader}* 获取的{@code ClassLoader}* {@link org.springframework.util.ClassUtils#getDefaultClassLoader()}* /void setBeanClassLoader(ClassLoader classLoader);}

在bean属性填充之后初始化之前,提供类加制器的回调。让受管Bean本身知道它是由哪一类装载器负责装载的。

2.3 BeanFactoryAware

public interface BeanFactoryAware extends Aware {/*** 为bean实例提供所属工厂的回调。* 在普通bean属性填充之后调用但在初始化回调之前,如* {@link InitializingBean#afterPropertiesSet()}或自定义初始化方法。* @param beanFactory拥有beanFactory(非空)。bean可以立即调用工厂上的方法。* @在初始化错误时抛出BeansException* @参见BeanInitializationException* /void setBeanFactory(BeanFactory beanFactory) throws BeansException;}

在bean属性填充之后初始化之前,提bean工厂的回调。实现 BeanFactoηAware 接口的 bean 可以直接访问 Spring 容器,被容器创建以后,它会拥有一个指向 Spring 容器的引用,可以利用该bean根据传入参数动态获取被spring工厂加载的bean

2.4 EnvironmentAware

public interface EnvironmentAware extends Aware {/*** 设置该对象运行的{@code环境}。*/void setEnvironment(Environment environment);}

设置该对象运行的。所有注册到 Spring容器内的 bean,只要该bean 实现了 EnvironmentAware接口,并且进行重写了setEnvironment方法的情况下,那么在工程启动时就可以获取得 application.properties 的配置文件配置的属性值,这样就不用我们将魔法值写到代码里面了。

2.5 EmbeddedValueResolverAware

public interface EmbeddedValueResolverAware extends Aware {/*** 设置StringValueResolver用于解析嵌入的定义值。*/void setEmbeddedValueResolver(StringValueResolver resolver);}

在基于Spring获取properties文件属性值的时候,一般使用@Value的方式注入配置文件属性值,但是@Value必须要在Spring的Bean生命周期管理下才能使用,比如类被@Controller、@Service、@Component等注解标注。如有的抽象类中,基于Spring解析@Value的方式,使用EmbeddedValueResolverAware解析配置文件来实现。

2.6 ResourceLoaderAware

public interface ResourceLoaderAware extends Aware {/***设置该对象运行的ResourceLoader。这可能是一个ResourcePatternResolver,它可以被检查*通过{@code instanceof ResourcePatternResolver}。另请参阅* {@code ResourcePatternUtils。getResourcePatternResolver}方法。* <p>在填充普通bean属性之后但在init回调之前调用*像InitializingBean的{@code afterPropertiesSet}或自定义初始化方法。*在ApplicationContextAware的{@code setApplicationContext}之前调用。* @param resourceLoader该对象使用的resourceLoader对象* @ @ springframework.core. io.support.resourcepatternresolver* @ @ resourcepatternutils #获取resourcepatternresolver* /void setResourceLoader(ResourceLoader resourceLoader);}

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

public interface ApplicationEventPublisherAware extends Aware {/***设置该对象运行的ApplicationEventPublisher。* <p>在普通bean属性填充之后但在init之前调用像InitializingBean的afterPropertiesSet或自定义初始化方法。*在ApplicationContextAware的setApplicationContext之前调用。*该对象使用的事件发布者* /void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher);}

ApplicationEventPublisherAware 是由 Spring 提供的用于为 Service 注入 ApplicationEventPublisher 事件发布器的接口,使用这个接口,我们自己的 Service 就拥有了发布事件的能力。

2.8 MessageSourceAware

public interface MessageSourceAware extends Aware {/***设置该对象运行的MessageSource。* <p>在普通bean属性填充之后但在init之前调用像InitializingBean的afterPropertiesSet或自定义初始化方法。*在ApplicationContextAware的setApplicationContext之前调用。* @param messageSource消息源* /void setMessageSource(MessageSource messageSource);}

获得message source这样可以获得文本信息,使用场景如为了国际化。

2.9 ApplicationContextAware

public interface ApplicationContextAware extends Aware {/***设置该对象运行的ApplicationContext。通常这个调用将用于初始化对象。* <p>在普通bean属性填充之后但在init回调之前调用*作为{@link org.springframework.beans.factory.InitializingBean#afterPropertiesSet()}*或自定义初始化方法。在{@link ResourceLoaderAware#setResourceLoader}之后调用,* {@link ApplicationEventPublisherAware#setApplicationEventPublisher}和* {@link MessageSourceAware},如果适用。* @param applicationContext该对象将使用的applicationContext对象* @在上下文初始化错误时抛出ApplicationContextException如果由应用程序上下文方法抛出,则抛出BeansException* @see org.springframework.beans.factory.BeanInitializationException* /void setApplicationContext(ApplicationContext applicationContext) throws BeansException;}

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方法的调用

image.png

3.1 invokeAwareMethods

private void invokeAwareMethods(final String beanName, final Object bean) {if (bean instanceof Aware) {if (bean instanceof BeanNameAware) {((BeanNameAware) bean).setBeanName(beanName);}if (bean instanceof BeanClassLoaderAware) {((BeanClassLoaderAware) bean).setBeanClassLoader(getBeanClassLoader());}if (bean instanceof BeanFactoryAware) {((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);}}
}

判断并直接回调

3.2 applyBeanPostProcessorsBeforeInitialization

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

通过ApplicationContextAwareProcessor.postProcessBeforeInitialization(Object bean, String beanName)间接调用,并在方法invokeAwareInterfaces中进行回调。

public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {AccessControlContext acc = null;if (System.getSecurityManager() != null &&(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) {acc = this.applicationContext.getBeanFactory().getAccessControlContext();}if (acc != null) {AccessController.doPrivileged(new PrivilegedAction<Object>() {@Overridepublic Object run() {invokeAwareInterfaces(bean);return null;}}, acc);}else {invokeAwareInterfaces(bean);}return bean;
}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);}}
}

4 总结

通过上面的分析,可以知道Spring生命周期中的初始化方法里,在真正执行初始化方法之前,分别通过invokeAwareMethods方法和后置处理器ApplicationContextAwareProcessor来触发Aware的调用,那么,Spring为什么要使用两种方式而不使用其中之一呢?

通过本章我们了解了9中内置接口的作用,以及它们能够获取到的不同上下文信息。

作者:京东零售 曾登均

来源:京东云开发者社区


http://www.ppmy.cn/news/759187.html

相关文章

BST比哈希的优势

对于search insert delete操作&#xff0c; Hash Table的时间复杂度是O(1)。 对于BST&#xff08;self-balancing Binary Search Tree&#xff0c; 比如 红黑树&#xff0c;AVL树等&#xff09;时间复杂度是O(LgN)。 看起来Hash Table在所有操作中都要优于BST的。那BST有什么…

BST、AVL、红黑树

关于树的名词 节点、根节点、父节点、子节点、叶子节点、节点权、层、子树、树的高度、森林 二叉树 满二叉树 所有叶子节点都在最后一层&#xff0c;并且节点总数为2^n - 1&#xff0c;n为层数 完全二叉树 叶子节点都在最后一层或倒数第二层&#xff0c;且最后一层只有叶子…

BST

Closest Binary Search Tree Value 所谓 “最近的点”&#xff0c;可能是 parent &#xff0c;可能是 child&#xff0c;可能在左边&#xff0c;也可能在右边。 所以一要存好 prev; 二要两边都探&#xff0c;不能沿着一边硬走。 为什么写成两个函数&#xff1f;因为这里是pr…

二叉搜索树BST

二叉搜索树&#xff08;英语&#xff1a;Binary Search Tree&#xff09;&#xff0c;也称二叉查找树、有序二叉树&#xff08;英语&#xff1a;ordered binary tree&#xff09;&#xff0c;排序二叉树&#xff08;英语&#xff1a;sorted binary tree&#xff09;&#xff0c…

BST+AVL+SB

BST 性质 左子树<根节点、右子树>根节点 用途 解决排名相关的检索需求 基本操作 插入操作 一直插入到叶子节点 删除操作 1、删除叶子节点&#xff1a;直接删除&#xff0c;并将其父节点的孩子节点置空 2、删除度为1的节点&#xff1a;删除后&#xff0c;将孩子…

bst java_Java经典算法:最大的BST子树

给定一棵二叉树&#xff0c;找到最大的子树&#xff0c;即二叉搜索树(BST)&#xff0c;其中最大表示其中的节点数最多的子树。 Java解决方案 class Wrapper{ int size; int lower, upper; boolean isBST; public Wrapper(){ lower Integer.MAX_VALUE; upper Integer.MIN_VALU…

二叉检索树(BST)

使用无序表和有序表组织的数据&#xff0c;不是查找时间复杂度偏高&#xff0c;就是插入时间复杂度偏高&#xff0c;而接下来将要介绍的二叉检索树&#xff08;BST&#xff09;则能很好的解决以上问题。二叉检索树又称二叉查找树、二叉排序树。 BST性质 BST是满足下面所给出条…

玩转数据结构(十三)构建BST

1、二分搜索树简介 二分搜索树又称为二叉搜索树、排序二叉树等&#xff0c;是指一棵空树或者具有以下性质的二叉树&#xff1a; 若任意一个结点的左子树不为空&#xff0c;则左子树所有结点的值均小于它的根结点的值若任意一个结点的右子树不为空&#xff0c;则右子树所有结点…