浅析对Spring aware接口理解

目录
  • 1. aware接口的作用
  • 2. 常用aware接口及作用
  • 3. 使用样例:ApplicationContextAware 在Bean中获取上下文
  • 4. 自定义aware的方式
    • 4.1 定义继承Aware的接口
    • 4.2 注册实现BeanPostProcessor接口的Bean
    • 4.3 实现TimeAware接口,并测试
  • 5. 源码处理方式
    • 5.1 初始化阶段的源码逻辑
    • 5.2 实现前三个aware接口的处理
    • 5.3 剩余实现aware接口的Bean的处理

1. aware接口的作用

通过aware接口可以获取Spring容器相关信息,但这样会与Spring容器耦合。

2. 常用aware接口及作用

执行顺序从上到下。

类名 作用
BeanNameAware 获得容器中bean名称
BeanClassLoaderAware 获得类加载器
BeanFactoryAware 获得bean创建工厂
EnvironmentAware 获得环境变量
EmbeddedValueResolverAware 获取spring容器加载的properties文件属性值
ResourceLoaderAware 获得资源加载器
ApplicationEventPublisherAware 获得应用事件发布器
MessageSourceAware 获得文本信息
ApplicationContextAware 获得当前应用上下文

3. 使用样例:ApplicationContextAware 在Bean中获取上下文

/**
 * 获取spring注入对象方法
 */
@Component("springUtil")
public final class SpringUtil implements ApplicationContextAware {
    /**
     * 应用上下文
     */
    private static ApplicationContext applicationContext;
    /**
     * public方法可能被调用,导致线程不安全。这样写也是为了通过sonar检测
     * @param applicationContext 通过aware设置上下文
     */
    @Override
    public void setApplicationContext(@NonNull ApplicationContext applicationContext) {
        synchronized (SpringUtil.class) {
            if (null == SpringUtil.applicationContext) {
                SpringUtil.applicationContext = applicationContext;
            }
        }
    }

    /**
     * 获取注入对象
     *
     * @param name 对象名称
     * @return 指定注入对象
     */
    public static Object getBean(String name) {
        return getApplicationContext().getBean(name);
    }

    private static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    /**
     * 获取注入对象
     *
     * @param clazz 对象类型
     * @param <T>   泛型
     * @return 指定注入对象
     */
    public static <T> T getBean(Class<T> clazz) {
        return getApplicationContext().getBean(clazz);
    }

    /**
     * 获取注入对象
     *
     * @param name  对象名称
     * @param clazz 对象类型
     * @param <T>   泛型
     * @return 指定注入对象
     */
    public static <T> T getBean(String name, Class<T> clazz) {
        return getApplicationContext().getBean(name, clazz);
    }
}

4. 自定义aware的方式

先定义一个继承Aware的接口,然后注册一个实现BeanPostProcessor接口的Bean,在postProcessBeforeInitialization中处理Aware接口的逻辑。

举一个例子。获取调用自定义Aware接口方法的时间。

4.1 定义继承Aware的接口

public interface TimeAware extends Aware {
    void setTriggerTime(Date date);
}

4.2 注册实现BeanPostProcessor接口的Bean

@Component
public class AwarePostProcessor implements BeanPostProcessor {
    private final ConfigurableApplicationContext applicationContext;
    /**
     * 可写可不写,这个构造是为了获取applicationContext
     */
    public AwarePostProcessor(ConfigurableApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof Aware) {
            if (bean instanceof TimeAware) {
                // 实现自定义Aware接口的逻辑,设置调用的时间
                ((TimeAware)bean).setTriggerTime(new Date());
            }
        }
        return bean;
    }
}

4.3 实现TimeAware接口,并测试

@Import(AwarePostProcessor.class)
public class Test implements TimeAware {
    Date date;
    @Override
    public void setTriggerTime(Date date) {
        this.date = date;
    }

    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(Test.class);
        Test bean = context.getBean(Test.class);
        System.out.println(bean.date);
    }
}

结果:

5. 源码处理方式

  • Bean实例化->填充属性->初始化,在初始化阶段将实现aware接口的Bean的方法执行。

1.先执行实现了下面三个aware接口的方法

  • BeanNameAware
  • BeanClassLoaderAware
  • BeanFactoryAware

2.调用初始化方法

3.执行实现剩下aware接口的方法

5.1 初始化阶段的源码逻辑

AbstractAutowireCapableBeanFactory#initializeBean

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
		if (System.getSecurityManager() != null) {
			AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
				invokeAwareMethods(beanName, bean);
				return null;
			}, getAccessControlContext());
		}
		else {
			/**
			 * 调用Bean实现的Aware接口的方法,主要包括下面三个接口
			 * BeanNameAware ----> setBeanName()
			 * BeanClassLoaderAware ----> setBeanClassLoader()
			 * BeanFactoryAware  ----> setBeanFactory()
			 */
			invokeAwareMethods(beanName, bean);
		}

		Object wrappedBean = bean;
		if (mbd == null || !mbd.isSynthetic()) {
			/** 调用Bean对象的postProcessBeforeInitialization方法,此处会执行标注@PostConstruct注解的方法 */
			// 此处会调用ApplicationContextAwareProcessor执行其他的aware方法.
			wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
		}

		try {
			/**
			 * 执行Bean的初始化方法:
			 *
			 * 1.先判断Bean是否实现了InitializingBean接口,如果实现了InitializingBean接口,则调用Bean对象的afterPropertiesSet方法;
			 * 2.然后判断Bean是否有指定init-method方法,如果指定了init-method方法,则调用bean对象的init-method指定的方法.
			 */
			invokeInitMethods(beanName, wrappedBean, mbd);
		}
		catch (Throwable ex) {
			throw new BeanCreationException(
					(mbd != null ? mbd.getResourceDescription() : null),
					beanName, "Invocation of init method failed", ex);
		}
		if (mbd == null || !mbd.isSynthetic()) {
			/**
			 * 调用Bean对象的postProcessAfterInitialization方法
			 *
			 * 如果需要创建代理,在该步骤中执行postProcessAfterInitialization方法的时候会去创建代理
			 * 调用AbstractAutoProxyCreator类的postProcessAfterInitialization方法,然后调用wrapIfNecessary方法去创建代理.
			 *
			 *
			 * 另外还有一些Aware接口,也会在该步骤中执行,例如:ApplicationContextAwareProcessor后置处理器,对应的setApplicationContext方法会被执行.
			 */
			wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
		}

		return wrappedBean;
	}

5.2 实现前三个aware接口的处理

调用initializeBean =>invokeAwareMethods方法将前三个aware方法调用
AbstractAutowireCapableBeanFactory#invokeAwareMethods

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

5.3 剩余实现aware接口的Bean的处理

调用initializeBean =>applyBeanPostProcessorsBeforeInitialization=>BeanPostProcessor.postProcessBeforeInitialization
进入ApplicationContextAwareProcessor#postProcessBeforeInitialization=>invokeAwareInterfaces

private void invokeAwareInterfaces(Object bean) {
		if (bean instanceof EnvironmentAware) {
			((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
		}
		if (bean instanceof EmbeddedValueResolverAware) {
			((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
		}
		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);
		}
	}

因此可以自定义aware接口,并且注册一个实现BeanPostProcessor的postProcessBeforeInitialization方法的Bean,处理调用aware方法时的处理逻辑。

(0)

相关推荐

  • Spring中@Import的各种用法以及ImportAware接口详解

    @Import 注解 @Import注解提供了和XML中<import/>元素等价的功能,实现导入的一个或多个配置类.@Import即可以在类上使用,也可以作为元注解使用. @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Import { /** * {@link Configuration}, {@link ImportSelector}, {@link I

  • 详解spring中的Aware接口功能

    目录 一,ApplicationContextAware 二.ApplicationEventPublisherAware 在spring中有很多以XXXAware命名的接口,很多人也不清楚这些接口都是做什么用的,这篇文章将描述常用的一些接口. 一,ApplicationContextAware 获取spring容器,用来访问容器中定义的其他bean.实现接口方法public void setApplicationContext(ApplicationContext applicationCon

  • Spring中的aware接口详情

    Spring中有很多继承于aware中的接口,这些接口到底是做什么用到的. aware,翻译过来是知道的,已感知的,意识到的,所以这些接口从字面意思应该是能感知到所有Aware前面的含义. 先举个BeanNameAware的例子,实现BeanNameAware接口,可以让该Bean感知到自身的BeanName(对应Spring容器的BeanId属性)属性,举个例子: BeanNameAware接口的定义: public interface BeanNameAware extends Aware

  • Spring Aware标记接口使用案例解析

    Aware接口是一个标记接口 XXXAare在Spring中表示对XXX可以感知,通俗点解释就是:如果在某个类里面想要使用Spring的一些东西,就可以通过实现XXXAware接口告诉Spring,Spring看到后就会送过来,而接受的方式是通过实现接口唯一的方法setXXX.比如ApplicationContextAware ApplicationContextAware使用 编写SpringAware实现ApplicationContext接口 package com.rookie.bigd

  • Spring实现Aware接口自定义获取bean的两种方式

    在使用spring编程时,常常会遇到想根据bean的名称来获取相应的bean对象,这时候,就可以通过实现BeanFactoryAware来满足需求,代码很简单: @Servicepublic class BeanFactoryHelper implements BeanFactoryAware { private static BeanFactory beanFactory; @Override public void setBeanFactory(BeanFactory beanFactory

  • Spring的Aware接口你知道多少

    若 Spring 检测到 bean 实现了 Aware 接口,则会为其注入相应的依赖.所以通过让bean 实现 Aware 接口,则能在 bean 中获得相应的 Spring 容器资源. Spring 中提供的 Aware 接口有: BeanNameAware:注入当前 bean 对应 beanName BeanClassLoaderAware:注入加载当前 bean 的 ClassLoader BeanFactoryAware:注入 当前BeanFactory容器 的引用 BeanNameAw

  • 浅析对Spring aware接口理解

    目录 1. aware接口的作用 2. 常用aware接口及作用 3. 使用样例:ApplicationContextAware 在Bean中获取上下文 4. 自定义aware的方式 4.1 定义继承Aware的接口 4.2 注册实现BeanPostProcessor接口的Bean 4.3 实现TimeAware接口,并测试 5. 源码处理方式 5.1 初始化阶段的源码逻辑 5.2 实现前三个aware接口的处理 5.3 剩余实现aware接口的Bean的处理 1. aware接口的作用 通过a

  • Spring Aware接口示例代码详解

    若 Spring 检测到 bean 实现了 Aware 接口,则会为其注入相应的依赖.所以通过让bean 实现 Aware 接口,则能在 bean 中获得相应的 Spring 容器资源. Spring 中提供的 Aware 接口有: BeanNameAware:注入当前 bean 对应 beanName BeanClassLoaderAware:注入加载当前 bean 的 ClassLoader BeanFactoryAware:注入 当前BeanFactory容器 的引用 BeanNameAw

  • Spring Aware源码设计示例解析

    目录 1. Aware介绍 2. Aware类别 2.1 BeanClassLoaderAware 2.2 BeanFactoryAware 2.3 BeanNameAware 2.4 ApplicationContextAware 3. Aware的使用 4. Aware的作用 5. Aware的调用 1. Aware介绍 前一篇讲到了BeanPostProcessor的相关知识,我们知道BeanPostProcessor是对整个容器中的Bean做前置和后置增强处理.这样的实现方式限制了开发者

  • 基于Mock测试Spring MVC接口过程解析

    1. 前言 在Java开发中接触的开发者大多数不太注重对接口的测试,结果在联调对接中出现各种问题.也有的使用Postman等工具进行测试,虽然在使用上没有什么问题,如果接口增加了权限测试起来就比较恶心了.所以建议在单元测试中测试接口,保证在交付前先自测接口的健壮性.今天就来分享一下胖哥在开发中是如何对Spring MVC接口进行测试的. 在开始前请务必确认添加了Spring Boot Test相关的组件,在最新的版本中应该包含以下依赖: <dependency> <groupId>

  • Spring Boot接口限流的常用算法及特点

    前言 在一个高并发系统中对流量的把控是非常重要的,当巨大的流量直接请求到我们的服务器上没多久就可能造成接口不可用,不处理的话甚至会造成整个应用不可用. 那么何为限流呢?顾名思义,限流就是限制流量,就像你宽带包了1个G的流量,用完了就没了.通过限流,我们可以很好地控制系统的qps,从而达到保护系统的目的.本篇文章将会介绍一下常用的限流算法以及他们各自的特点. 算法介绍 计数器法 计数器法是限流算法里最简单也是最容易实现的一种算法.比如我们规定,对于A接口来说,我们1分钟的访问次数不能超过100个.

随机推荐