ApplicationContextAware interface:
public interface ApplicationContextAware extends Aware { void setApplicationContext(ApplicationContext applicationContext) throws BeansException; }
First of all, the Aware interface knows that this is a springboot extension for users to use. The method setApplicationContext is provided here. The parameter is to pass in the spring container context object. We can receive this context object. We If you want to know the specific role of getting the spring container context ApplicationContext, this is the purpose of the extended interface. Get the context and do something based on the characteristics of the context.
Let’s look at the methods of the ApplicationContext object:
Let’s look at the methods of the AbstractApplicationContext implementation class:
public Object getBean(String name) throws BeansException {this.assertBeanFactoryActive();return this.getBeanFactory().getBean(name);} public <T> T getBean(String name, Class<T> requiredType) throws BeansException {this.assertBeanFactoryActive();return this.getBeanFactory().getBean(name, requiredType);} public Object getBean(String name, Object... args) throws BeansException {this.assertBeanFactoryActive();return this.getBeanFactory().getBean(name, args);} public <T> T getBean(Class<T> requiredType) throws BeansException {this.assertBeanFactoryActive();return this.getBeanFactory().getBean(requiredType);} public <T> T getBean(Class<T> requiredType, Object... args) throws BeansException {this.assertBeanFactoryActive();return this.getBeanFactory().getBean(requiredType, args);} public <T> ObjectProvider<T> getBeanProvider(Class<T> requiredType) {this.assertBeanFactoryActive();return this.getBeanFactory().getBeanProvider(requiredType);} public <T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType) {this.assertBeanFactoryActive();return this.getBeanFactory().getBeanProvider(requiredType);} public boolean containsBean(String name) {return this.getBeanFactory().containsBean(name);} public boolean isSingleton(String name) throws NoSuchBeanDefinitionException {this.assertBeanFactoryActive();return this.getBeanFactory().isSingleton(name);} public boolean isPrototype(String name) throws NoSuchBeanDefinitionException {this.assertBeanFactoryActive();return this.getBeanFactory().isPrototype(name);}
Here we can find getBean The () method is very familiar, because when I first learned spring, I didn’t use spring’s scaffolding to create a project. The way we obtain the bean is usually by classPathContextLoader scanning the bean’s xml file and parsing it to form an ApplicationCOntext object, and then calling its getBean method to obtain the instance bean.
From this we can find that our main application method is to use this getBean method. Then we can dynamically inject beans through many methods, so it is not difficult to think here that injected beans cannot be used in static methods. question.
Next, let’s reproduce this problem. Let’s take a look at the following code:
public class JsonGetter { @Resource private UuidGetter uuidGetter; public static string Test(){ return uuidGetter.getUuid(); } public static JsONobject set0bjectToJsonObject(object data){ return JsoNobject.parseObject(String.valueof(JsONObject.toJSON(data))); } public static JsONObject setStringTO3son0bject(String data) { return JsONObject.parseObject(data); }
We found that calling the injected bean in the static Test method directly reports an error. Here is an explanation: Thanks to the class According to the loading mechanism and order, static attributes and static code blocks are loaded first (static takes priority). There is no bean instance for you to load static methods here, so an error will naturally be reported.
How to solve? We can adopt the idea of calling the getBean method when Spring obtains the bean object, and statically store the context of the spring container when the container is loaded:
@Component @Lazy(value = false) public class SpringContextHolder implements ApplicationContextAware, DisposableBean { /** * 将上下文静态设置,在初始化组件时就进行静态上下文的覆盖(这个覆盖是将远spring容器的上下文对象引用加到我们预定设置) */ private static ApplicationContext applicationContext = null; public static ApplicationContext getApplicationContext() { assertContextInjected(); return applicationContext; } @SuppressWarnings("unchecked") public static <T> T getBean(String name) { assertContextInjected(); return (T) applicationContext.getBean(name); } public static <T> T getBean(Class<T> beanType) { assertContextInjected(); return applicationContext.getBean(beanType); } @Override public void setApplicationContext(@NotNull ApplicationContext applicationContext) throws BeansException { SpringContextHolder.applicationContext = applicationContext; } @Override public void destroy() { applicationContext = null; } private static void assertContextInjected() { Assert.notNull(applicationContext, "applicationContext属性未注入, 请在applicationContext.xml中定义SpringContextHolder."); } public static void pushEvent(ApplicationEvent event){ assertContextInjected(); applicationContext.publishEvent(event); } }
What we only need to pay attention to here is the definition, assignment and verification of the static member variable ApplicationContext. :
/** * 将上下文静态设置,在初始化组件时就进行静态上下文的覆盖(这个覆盖是将远spring容器的上下文对象引用加到我们预定设置) */ private static ApplicationContext applicationContext = null;
Rewrite the method of the extended interface to achieve static context coverage:
@Override public void setApplicationContext(@NotNull ApplicationContext applicationContext) throws BeansException { SpringContextHolder.applicationContext = applicationContext; }
Make the method of obtaining it public modification for easy sharing:
public static ApplicationContext getApplicationContext() { assertContextInjected(); return applicationContext; }
I still write it here I don’t understand, what is the role of defining a component like this and statically overriding the spring context object?
Don’t panic, let’s take a look at this method of this class:
public class AppContext { static transient ThreadLocal<Map<String, String>> contextMap = new ThreadLocal<>(); ......省略n行业务代码 public static void fillLoginContext() { DingAppInfo appInfo = SpringContextHolder.getBean(DingAppInfoService.class).findAppInfo(APP_CODE); setDingVerifyInfo(appInfo); CloudChatAppInfo cloudChatAppInfo = SpringContextHolder.getBean(CloudChatAppInfoService.class).findAppInfo(APP_CODE); setCloudChatInfo(cloudChatAppInfo); } public static void clear() { contextMap.remove(); //本地线程的remove方法极其重要,注意每次给它使用之后一定要调用remove清理,防止内存泄露。 } }
We found that the library check operation was performed in the above example code:
DingAppInfo appInfo = SpringContextHolder.getBean(DingAppInfoService.class).findAppInfo(APP_CODE);
The above is the detailed content of How to use SpringBoot ApplicationContextAware extended interface. For more information, please follow other related articles on the PHP Chinese website!