org.springframework.beans.factory.Aware
Aware是一个具有标识作用的超级接口,具体实现是有子接口去决定的,但是子接口至少要有一个带一个参数的且返回时空的方法。实现该接口的bean是具有被spring 容器通知的能力的,而被通知的方式就是通过回调。也就是说:直接或间接实现了这个接口的类,都具有被spring容器通知的能力。
源码如下:
package org.springframework.beans.factory;
/**
* Marker superinterface indicating that a bean is eligible to be
* notified by the Spring container of a particular framework object
* through a callback-style method. Actual method signature is
* determined by individual subinterfaces, but should typically
* consist of just one void-returning method that accepts a single
* argument.
*
* <p>Note that merely implementing {@link Aware} provides no default
* functionality. Rather, processing must be done explicitly, for example
* in a {@link org.springframework.beans.factory.config.BeanPostProcessor BeanPostProcessor}.
* Refer to {@link org.springframework.context.support.ApplicationContextAwareProcessor}
* and {@link org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory}
* for examples of processing {@code *Aware} interface callbacks.
*
* @author Chris Beams
* @since 3.1
*/
public interface Aware {
}
常用的子接口如下:
org.springframework.context.ApplicationContextAware
org.springframework.beans.factory.BeanFactoryAware
org.springframework.beans.factory.BeanClassLoaderAware
org.springframework.beans.factory.BeanNameAware
org.springframework.context.EnvironmentAware
org.springframework.context.ResourceLoaderAware
org.springframework.context.annotation.ImportAware
ApplicationContextAware
实现该接口的类可以获取spring容器上线文信息 ApplicationContext ,主要是通过这个ApplicationContext applicationContext 去做一个bean初始化前的操作,
public interface ApplicationContextAware extends Aware {
/**
* Set the ApplicationContext that this object runs in.
* Normally this call will be used to initialize the object.
* <p>Invoked after population of normal bean properties but before an init callback such
* as {@link org.springframework.beans.factory.InitializingBean#afterPropertiesSet()}
* or a custom init-method. Invoked after {@link ResourceLoaderAware#setResourceLoader},
* {@link ApplicationEventPublisherAware#setApplicationEventPublisher} and
* {@link MessageSourceAware}, if applicable.
* @param applicationContext the ApplicationContext object to be used by this object
* @throws ApplicationContextException in case of context initialization errors
* @throws BeansException if thrown by application context methods
* @see org.springframework.beans.factory.BeanInitializationException
*/
void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}
调用顺序:
- 该接口的实现类setApplicationContext方法,会在创建bean及对其属性赋值以后,但是在调用bean的init方法和InitializingBean#afterPropertiesSet()方法之前;
- 该接口的实现类setApplicationContext方法,会在调用ResourceLoaderAware#setResourceLoader ApplicationEventPublisherAware#setApplicationEventPublisher之后调用。
Demo
@Service
public class ApplicationContextUtil implements ApplicationContextAware,ResourceLoaderAware{
private static ApplicationContext applicationContext = null;
private ResourceLoader resourceLoader;
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("3333333333333");
this.applicationContext = applicationContext;
}
public static <T> T getBean(String name , Class<T> T){
return applicationContext.getBean(name, T);
}
public static Map<String, Object> getBeansWithAnnotation(String name ){
//获取所有注解为Service的spring容器中的bean
return applicationContext.getBeansWithAnnotation(Service.class);
}
public static boolean containBean(String name){
return applicationContext.containsBean(name);
}
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
System.out.println("444444444444");
}
}
定义简单java bean
@Service
public class AwarePojoBean implements InitializingBean{
public AwarePojoBean() {
System.out.println("1111111");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("222222222");
}
}
配置类:
@Configuration
@ComponentScan("com.zhangfd.boot.spring.aware")
public class AwareConfig {
}
测试类:
@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = {AwareConfig.class})
public class AwareTest {
@Test
public void contextLoads() {
AwarePojoBean awarePojoBean = ApplicationContextUtil.getBean("awarePojoBean", AwarePojoBean.class);
System.out.println("==============="+awarePojoBean.getClass());
}
}
运行结果如下:
我们可以看到,ApplicationContextUtil类的setApplicationContext 方法调用顺序在bean的构造方法和InitializingBean#afterPropertiesSet()方法之前;在ResourceLoaderAware的setResourceLoader 方法之后。
BeanFactoryAware
我们通过上面的简图可以看出,这个Spring Ioc 中最顶层的父接口就是BeanFactory。实现这个BeanFactoryAware接口的子类可以获取spring容器的BeanFactory 对象,进而可以动态的去操作 要在spring容器中注入的bean。
通过上图,我们也可以看到ApplicationContext接口是BeanFactory的子接口,所以继承ApplicationContextAware的实现类拿到ApplicationContext 对象比实现BeanFactoryAware接口拿到BeanFactory 对象 可以获取更多的信息。
举个例子,比如我们写了一个接口,对应10个实现类,当然我们可以通过if else 去找对应的实现类,但是如果我们对这10个实现类的beanName定义的规则些,那么通过BeanFactory一行代码就可以拿到指定的对象。
调用顺序:
1、该接口的实现类setBeanFactory方法,会在创建bean及对其属性赋值以后,但是在调用bean的init方法和InitializingBean#afterPropertiesSet()方法之后;
2、该接口的实现类setApplicationContext方法,在调用ResourceLoaderAware#setResourceLoader ApplicationEventPublisherAware#setApplicationEventPublisher 之前调用。
注意: ApplicationContextAware的实现类和BeanFactoryAware 在以上两点的调用顺序上刚好相反的!!!
@Service
public class BeanFactoryUtils implements BeanFactoryAware,ResourceLoaderAware {
private static BeanFactory beanFactory;
private ResourceLoader resourceLoader;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("BeanFactoryUtils3333333333333");
this.beanFactory = beanFactory;
}
public static <T> T getBean(String name, Class<T> t) {
return beanFactory.getBean(name, t);
}
public static boolean containBean(String name) {
return beanFactory.containsBean(name);
}
public static BeanFactory getBeanFactory() {
return beanFactory;
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
System.out.println("BeanFactoryUtils444444444444");
}
}
定义简单javaBean
@Service
public class AwarePojoBean implements InitializingBean{
public AwarePojoBean() {
System.out.println("1111111");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("222222222");
}
}
定义配置类:
@Service
public class AwarePojoBean implements InitializingBean{
public AwarePojoBean() {
System.out.println("1111111");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("222222222");
}
}
测试类:
@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = {AwareConfig.class})
public class BeanFactoryTest {
@Test
public void contextLoads() {
AwarePojoBean awarePojoBean = BeanFactoryUtils.getBean("awarePojoBean", AwarePojoBean.class);
System.out.println("==============="+awarePojoBean.getClass());
}
}
运行测试类,结果如下:
通过运行结果,我们也可以看出调用顺序的确和ApplicationContextAware的实现类相反。
总结:
一个类继承ApplicationContextAware可获得spring容器中ApplicationContextAware;进而进行各种操作,和BeanFactoryAware相比,它可获取的信息更多,更全!
1.BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化,这样,我们就不能发现一些存在的Spring的配置问题。而ApplicationContext则相反,它是在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误。 相对于基本的BeanFactory,ApplicationContext 唯一的不足是占用内存空间。当应用程序配置Bean较多时,程序启动较慢。BeanFacotry延迟加载,如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常;而ApplicationContext则在初始化自身是检验,这样有利于检查所依赖属性是否注入;所以通常情况下我们选择使用 ApplicationContext。
应用上下文则会在上下文启动后预载入所有的单实例Bean。通过预载入单实例bean ,确保当你需要的时候,你就不用等待,因为它们已经创建好了。
2.BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册。(Applicationcontext比 beanFactory 加入了一些更好使用的功能。而且 beanFactory 的许多功能需要通过编程实现而 Applicationcontext 可以通过配置实现。比如后处理 bean , Applicationcontext 直接配置在配置文件即可而 beanFactory 这要在代码中显示的写出来才可以被容器识别。 )
3.beanFactory主要是面对与 spring 框架的基础设施,面对 spring 自己。而 Applicationcontex 主要面对与 spring 使用的开发者。基本都会使用 Applicationcontex 并非 beanFactory 。
BeanNameAware
这个接口的含义就是让实现类知道自己在spring容器中定义的beanName是啥,实际开发一般没啥用。一般在抽象类里使用虑䞢接口的比较多。
BeanClassLoaderAware
获取spring 容器的类加载器ClassLoader 对象,像我们在加载项目的静态文件时,一般使用的是Thread.currentThread().getContextClassLoader() 从而获取ClassLoader 对象,更快更方便些。
EnvironmentAware
实现这个接口的类能获取Environmet对象,进而可以各种系统变量信息,也可以设置 变量的优先级别等等。后面对Environment接口做一个专题讲解。
个别变量除外!!!!)。
先定义个实现类:
@Service
public class EnvironmentAwareUtils implements EnvironmentAware{
private static Environment environment;
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
public static Environment getEnvironment(){
return environment;
}
}
定义配置类:
@Configuration
@ComponentScan("com.zhangfd.boot.spring.aware")
public class AwareConfig {
}
application.properties 文件如下:
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/datatest
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
#设置服务器的端口
server.port=8080
spring.profiles.active=dev
#false设置不进行热部署
spring.devtools.restart.enabled = false
#表示这个文件的内容有变动就会热部署,前提是上面的enabled=true
spring.devtools.restart.trigger-file=myRestartTrigger.my
spring.devtools.remote.secret=true
#spring.main.banner-mode=off
application-dev.properties
spring.datasource.test=just for test
定义测试类
@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = {AwareConfig.class})
public class EnvironmentAwareTest {
@Value("${spring.datasource.test}")
private String testValue;
@Test
public void contextLoads() {
//通过注解获取和通过Environment获取同样的变量
System.out.println(testValue+"==============="+EnvironmentAwareUtils.getEnvironment().getProperty("spring.datasource.username"));
System.out.println("==============="+EnvironmentAwareUtils.getEnvironment().getProperty("JAVA_HOME"));
ConfigurableEnvironment configurableEnvironment = (ConfigurableEnvironment)EnvironmentAwareUtils.getEnvironment();
// System.out.println(configurableEnvironment.getSystemEnvironment());;
//模糊加载一类变量
RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(EnvironmentAwareUtils.getEnvironment(),"spring.datasource.");
System.out.println(resolver.getProperty("username")+"*******************");
System.out.println(resolver.getProperty("driver-class-name")+"*******************");
}
}
运行测试类 结果如下:
执行结果是 -1 而不是预期的8080.
ResourceLoaderAware
获取资源加载器ResourceLoader
package org.springframework.core.io;
import org.springframework.lang.Nullable;
import org.springframework.util.ResourceUtils;
/**
* Strategy interface for loading resources (e.. class path or file system
* resources). An {@link org.springframework.context.ApplicationContext}
* is required to provide this functionality, plus extended
* {@link org.springframework.core.io.support.ResourcePatternResolver} support.
*
* <p>{@link DefaultResourceLoader} is a standalone implementation that is
* usable outside an ApplicationContext, also used by {@link ResourceEditor}.
*
* <p>Bean properties of type Resource and Resource array can be populated
* from Strings when running in an ApplicationContext, using the particular
* context's resource loading strategy.
*
* @author Juergen Hoeller
* @since 10.03.2004
* @see Resource
* @see org.springframework.core.io.support.ResourcePatternResolver
* @see org.springframework.context.ApplicationContext
* @see org.springframework.context.ResourceLoaderAware
*/
public interface ResourceLoader {
/** Pseudo URL prefix for loading from the class path: "classpath:" */
String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;
/**
* Return a Resource handle for the specified resource location.
* <p>The handle should always be a reusable resource descriptor,
* allowing for multiple {@link Resource#getInputStream()} calls.
* <p><ul>
* <li>Must support fully qualified URLs, e.g. "file:C:/test.dat".
* <li>Must support classpath pseudo-URLs, e.g. "classpath:test.dat".
* <li>Should support relative file paths, e.g. "WEB-INF/test.dat".
* (This will be implementation-specific, typically provided by an
* ApplicationContext implementation.)
* </ul>
* <p>Note that a Resource handle does not imply an existing resource;
* you need to invoke {@link Resource#exists} to check for existence.
* @param location the resource location
* @return a corresponding Resource handle (never {@code null})
* @see #CLASSPATH_URL_PREFIX
* @see Resource#exists()
* @see Resource#getInputStream()
*/
Resource getResource(String location);
/**
* Expose the ClassLoader used by this ResourceLoader.
* <p>Clients which need to access the ClassLoader directly can do so
* in a uniform manner with the ResourceLoader, rather than relying
* on the thread context ClassLoader.
* @return the ClassLoader
* (only {@code null} if even the system ClassLoader isn't accessible)
* @see org.springframework.util.ClassUtils#getDefaultClassLoader()
* @see org.springframework.util.ClassUtils#forName(String, ClassLoader)
*/
@Nullable
ClassLoader getClassLoader();
}
说白了就是加载文件,那我们根据File 、 Input Stream、OutPutStream 然后根据文件路径是不是也能加载文件呢?答案是肯定的,spring内部也是用他们完成的,我们看到这里的 Resource getResource(String location);,
package org.springframework.core.io;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import org.springframework.lang.Nullable;
/**
* Interface for a resource descriptor that abstracts from the actual
* type of underlying resource, such as a file or class path resource.
*
* <p>An InputStream can be opened for every resource if it exists in
* physical form, but a URL or File handle can just be returned for
* certain resources. The actual behavior is implementation-specific.
*
* @author Juergen Hoeller
* @since 28.12.2003
* @see #getInputStream()
* @see #getURL()
* @see #getURI()
* @see #getFile()
* @see WritableResource
* @see ContextResource
* @see UrlResource
* @see ClassPathResource
* @see FileSystemResource
* @see PathResource
* @see ByteArrayResource
* @see InputStreamResource
*/
public interface Resource extends InputStreamSource {
/**
* Determine whether this resource actually exists in physical form.
* <p>This method performs a definitive existence check, whereas the
* existence of a {@code Resource} handle only guarantees a valid
* descriptor handle.
*/
boolean exists();
/**
* Indicate whether the contents of this resource can be read via
* {@link #getInputStream()}.
* <p>Will be {@code true} for typical resource descriptors;
* note that actual content reading may still fail when attempted.
* However, a value of {@code false} is a definitive indication
* that the resource content cannot be read.
* @see #getInputStream()
*/
default boolean isReadable() {
return true;
}
/**
* Indicate whether this resource represents a handle with an open stream.
* If {@code true}, the InputStream cannot be read multiple times,
* and must be read and closed to avoid resource leaks.
* <p>Will be {@code false} for typical resource descriptors.
*/
default boolean isOpen() {
return false;
}
/**
* Determine whether this resource represents a file in a file system.
* A value of {@code true} strongly suggests (but does not guarantee)
* that a {@link #getFile()} call will succeed.
* <p>This is conservatively {@code false} by default.
* @since 5.0
* @see #getFile()
*/
default boolean isFile() {
return false;
}
/**
* Return a URL handle for this resource.
* @throws IOException if the resource cannot be resolved as URL,
* i.e. if the resource is not available as descriptor
*/
URL getURL() throws IOException;
/**
* Return a URI handle for this resource.
* @throws IOException if the resource cannot be resolved as URI,
* i.e. if the resource is not available as descriptor
* @since 2.5
*/
URI getURI() throws IOException;
/**
* Return a File handle for this resource.
* @throws java.io.FileNotFoundException if the resource cannot be resolved as
* absolute file path, i.e. if the resource is not available in a file system
* @throws IOException in case of general resolution/reading failures
* @see #getInputStream()
*/
File getFile() throws IOException;
/**
* Return a {@link ReadableByteChannel}.
* <p>It is expected that each call creates a <i>fresh</i> channel.
* <p>The default implementation returns {@link Channels#newChannel(InputStream)}
* with the result of {@link #getInputStream()}.
* @return the byte channel for the underlying resource (must not be {@code null})
* @throws java.io.FileNotFoundException if the underlying resource doesn't exist
* @throws IOException if the content channel could not be opened
* @since 5.0
* @see #getInputStream()
*/
default ReadableByteChannel readableChannel() throws IOException {
return Channels.newChannel(getInputStream());
}
/**
* Determine the content length for this resource.
* @throws IOException if the resource cannot be resolved
* (in the file system or as some other known physical resource type)
*/
long contentLength() throws IOException;
/**
* Determine the last-modified timestamp for this resource.
* @throws IOException if the resource cannot be resolved
* (in the file system or as some other known physical resource type)
*/
long lastModified() throws IOException;
/**
* Create a resource relative to this resource.
* @param relativePath the relative path (relative to this resource)
* @return the resource handle for the relative resource
* @throws IOException if the relative resource cannot be determined
*/
Resource createRelative(String relativePath) throws IOException;
/**
* Determine a filename for this resource, i.e. typically the last
* part of the path: for example, "myfile.txt".
* <p>Returns {@code null} if this type of resource does not
* have a filename.
*/
@Nullable
String getFilename();
/**
* Return a description for this resource,
* to be used for error output when working with the resource.
* <p>Implementations are also encouraged to return this value
* from their {@code toString} method.
* @see Object#toString()
*/
String getDescription();
}
它就是和java的流联系起来的。Spring对此做了封装。
特别需要说明的是ApplicationContext 是继承了ResourceLoader的子接口 ResourcePatternResolver的,所以通过上面ApplicationContextAware获取的ApplicationContext 对象统统具有
想想下,我们通过xml配置文件把对象加载到spring容器中,首先就是加载指定路径的文件, 通过ClassPathXmlApplicationcontext 还是
AnnotationConfig Applicationcontext 也都有加载文件的功能就更不奇怪了。
ImportAware
package org.springframework.context.annotation;
import org.springframework.beans.factory.Aware;
import org.springframework.core.type.AnnotationMetadata;
/**
* Interface to be implemented by any @{@link Configuration} class that wishes
* to be injected with the {@link AnnotationMetadata} of the @{@code Configuration}
* class that imported it. Useful in conjunction with annotations that
* use @{@link Import} as a meta-annotation.
*
* @author Chris Beams
* @since 3.1
*/
public interface ImportAware extends Aware {
/**
* Set the annotation metadata of the importing @{@code Configuration} class.
*/
void setImportMetadata(AnnotationMetadata importMetadata);
}
通过源码我们可以看出这个主要是获取 AnnotationMetadata 对象,进而获取指定注解的一些信息。通过下面的接口ImportBeanDefinitionRegistrar也能获取AnnotationMetadata 对象 ,并且还能获取BeanDefinitionRegistry registry 。两者相比后者可以获取更多的信息,像Apollo框架就使用了 ImportBeanDefinitionRegistrar 接口
package org.springframework.context.annotation;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.core.type.AnnotationMetadata;
/**
* Interface to be implemented by types that register additional bean definitions when
* processing @{@link Configuration} classes. Useful when operating at the bean definition
* level (as opposed to {@code @Bean} method/instance level) is desired or necessary.
*
* <p>Along with {@code @Configuration} and {@link ImportSelector}, classes of this type
* may be provided to the @{@link Import} annotation (or may also be returned from an
* {@code ImportSelector}).
*
* <p>An {@link ImportBeanDefinitionRegistrar} may implement any of the following
* {@link org.springframework.beans.factory.Aware Aware} interfaces, and their respective
* methods will be called prior to {@link #registerBeanDefinitions}:
* <ul>
* <li>{@link org.springframework.context.EnvironmentAware EnvironmentAware}</li>
* <li>{@link org.springframework.beans.factory.BeanFactoryAware BeanFactoryAware}
* <li>{@link org.springframework.beans.factory.BeanClassLoaderAware BeanClassLoaderAware}
* <li>{@link org.springframework.context.ResourceLoaderAware ResourceLoaderAware}
* </ul>
*
* <p>See implementations and associated unit tests for usage examples.
*
* @author Chris Beams
* @since 3.1
* @see Import
* @see ImportSelector
* @see Configuration
*/
public interface ImportBeanDefinitionRegistrar {
/**
* Register bean definitions as necessary based on the given annotation metadata of
* the importing {@code @Configuration} class.
* <p>Note that {@link BeanDefinitionRegistryPostProcessor} types may <em>not</em> be
* registered here, due to lifecycle constraints related to {@code @Configuration}
* class processing.
* @param importingClassMetadata annotation metadata of the importing class
* @param registry current bean definition registry
*/
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
}