9 Ways Spring Gets Beans

foreword

With the popularity of SpringBoot, Spring is used more and more widely. In some scenarios, we cannot directly obtain a Bean through annotation or configuration. For example, in the implementation of some tool classes and design patterns, the beans managed by the Spring container need to be used. At this time, the corresponding beans need to be obtained directly.

This article summarizes the common ways to obtain beans for you, and provides some pros and cons analysis, so that you can have a better choice when you use it. At the same time, it will also properly popularize and expand some relevant knowledge for everyone.

Spring's IoC container

In Spring, Bean instantiation, location, configuration of objects in the application and the establishment of dependencies between objects are all carried out in the IoC container. Therefore, to obtain beans in Spring is essentially to obtain beans from the IoC container.

In Spring, BeanFactory is the actual representative of the IoC container, and this interface provides the most basic functions of the IoC container. At the same time, Spring also provides another type of container: ApplicationContext container.

The ApplicationContext container includes all the functions of the BeanFactory container (the sub-interface of the BeanFactory), and provides more application-oriented functions. It provides internationalization support and a framework event system, making it easier to create practical applications.

In general, we call BeanFactory an IoC container and ApplicationContext an application context. But sometimes for convenience, ApplicationContext is also called Spring container.

It is generally not recommended to use BeanFactory, but BeanFactory can still be used in lightweight applications, such as mobile devices or applet-based applications, where its data volume and speed are significant.

The difference between BeanFactory and ApplicationContext

BeanFactory is the infrastructure of the Spring framework, facing Spring itself. ApplicationContext is for developers who use the Spring framework. Almost all applications can use ApplicationContext directly instead of the underlying BeanFactory.

In addition, there is a major difference between the initialization of ApplicationContext and BeanFactory:

When the BeanFactory initializes the container, the Bean is not instantiated, and the target Bean is not instantiated until the first time a Bean is accessed. In this way, we cannot find some existing Spring configuration problems. If a certain property of the Bean is not injected, after the BeanFacotry is loaded, an exception will not be thrown until the getBean method is called for the first time.

The ApplicationContext instantiates all single-instance beans when the application context is initialized. Correspondingly, the initialization time of the ApplicationContext will be longer than that of the BeanFactory.

After understanding the above basic theoretical knowledge, we can try to get the Bean object from the IoC container.

Method 1: Obtain through BeanFactory

Bean is obtained through BeanFactory. Based on the era of xml configuration files, the BeanFactory can be obtained through the following methods, and then the corresponding Bean can be obtained through the BeanFactory.

BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
UserInfo userInfo = (UserInfo) beanFactory.getBean("userInfo");

Programmers with a certain programming age should have some impressions of this. This way of writing is estimated to only appear in ancient projects. In view of the fact that the xml form configuration file has been replaced by the annotation-based form, and XmlBeanFactory is also marked as obsolete. This method is not recommended.

In fact, there is another reason for not recommending. As mentioned above, try not to use BeanFactory, but use ApplicationContext.

Method 2: Obtain through BeanFactoryAware

In the above method, XmlBeanFactory has been discarded, but BeanFactory can be obtained in other ways, and then the specified Bean can be obtained from BeanFactory. The easiest way to obtain a BeanFactory instance is to implement the BeanFactoryAware interface.

BeanFactoryAware interface source code:

public interface BeanFactoryAware extends Aware {

	/**
	 * 初始化回调方法,Spring会自动将BeanFactory注入进去,接收之后即可使用BeanFactory
	 */
	void setBeanFactory(BeanFactory beanFactory) throws BeansException;
}

BeanFactoryAware belongs to org.springframework.beans.factory.Awarethe root marker interface and uses setter injection to get objects during application context startup. The Aware interface is a mixture of callback, listener and observer design patterns, which indicates that a bean is eligible to be notified by the Spring container through a callback.

A complete utility class is provided here:

@Component
public class BeanFactoryHelper implements BeanFactoryAware {

	private static BeanFactory beanFactory;

	/**
	 * 重写 BeanFactoryAware 接口的方法
	 * @param beanFactory :参数赋值给本地属性之后即可使用 BeanFactory
	 * @throws BeansException BeansException
	 */
	@Override
	public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
		BeanFactoryHelper.beanFactory = beanFactory;
	}
	/**
	 * 根据名称获取容器中的对象实例
	 * @param beanName :注入的实例必须已经存在容器中,否则抛异常:NoSuchBeanDefinitionException
	 * @return Object
	 */
	public static Object getBean(String beanName) {
		return beanFactory.getBean(beanName);
	}
	/**
	 * 根据 class 获取容器中的对象实例
	 * @param requiredType :被注入的必须已经存在容器中,否则抛异常:NoSuchBeanDefinitionException
	 * @param <T> Class
	 * @return 对象
	 */
	public static <T> T getBean(Class<T> requiredType) {
		return beanFactory.getBean(requiredType);
	}
	/**
	 * 判断 spring 容器中是否包含指定名称的对象
	 * @param beanName bean名称
	 * @return 是否存在
	 */
	public static boolean containsBean(String beanName) {
		return beanFactory.containsBean(beanName);
	}
	//其它需求皆可参考 BeanFactory 接口和它的实现类
}

In the above tool class, the BeanFactory is obtained based on the characteristics of BeanFactoryAware, and then the specified Bean is obtained through the BeanFactory.

This solution meets the basic needs of obtaining Bean, but at the same time has the disadvantage of using BeanFactory. According to the characteristics of BeanFactory introduced above, it can be used as appropriate.

The above provides two ways to obtain beans based on the BeanFactory container, and the following uses the ApplicationContext to obtain the beans in the container. The difference is the difference in the way to obtain the ApplicationContext.

Method 3: Start to get the ApplicationContext

Get the ApplicationContext object first when the project starts, and then store it in one place for subsequent use.

Two scenarios are provided here:

  • The form of bean configuration based on xml is suitable for older projects and is rarely used;
  • Get the ApplicationContext object based on SpringBoot startup;

XML-based implementation:

// 其中applicationContext.xml 为配置容器的xml,不过现在一般很少使用了
ApplicationContext ac = new FileSystemXmlApplicationContext("applicationContext.xml");

This is equivalent to directly initializing the container and obtaining a reference to the container. This method is applicable to the stand-alone application program using the Spring framework, which requires the program to manually initialize Spring through the configuration file. At present, most Spring projects no longer use xml configuration, and it is rarely used.

Implementation based on SpringBoot startup:

@SpringBootApplication
public class ExampleApplication {

    public static void main(String[] args) {
        // 启动时,保存上下文,并保存为静态
        ConfigurableApplicationContext ac = SpringApplication.run(ExampleApplication.class, args);
        SpringContextUtil.setAc(ac);
    }
}

The corresponding SpringContextUtil class is as follows:

public class SpringContextUtil1 {

    private static ApplicationContext ac;

    public static <T>  T getBean(String beanName, Class<T> clazz) {
        T bean = ac.getBean(beanName, clazz);
        return bean;
    }

    public static void setAc(ApplicationContext applicationContext){
        ac = applicationContext;
    }
}

Both methods are to directly obtain the reference of ApplicationContext when starting the Spring project, and then store it in the tool class. When in use, obtain the ApplicationContext container from the tool class, and then obtain the Bean object from it.

Method 4: By inheriting ApplicationObjectSupport

This method still obtains the ApplicationContext container first, and then obtains the Bean object from it, but it is implemented based on inheriting the ApplicationObjectSupport class.

The specific implementation code:

@Component
public class SpringContextUtil extends ApplicationObjectSupport {
	public <T> T getBean(Class<T> clazz) {
		ApplicationContext ac = getApplicationContext();
		if(ac == null){
			return null;
		}
		return ac.getBean(clazz);
	}
}

Note that the SpringContextUtil class here needs to be instantiated.

Method 5: By inheriting WebApplicationObjectSupport

WebApplicationObjectSupport is an implementation class of ApplicationObjectSupport that provides Web-related support. The implementation principle is the same as ApplicationObjectSupport.

The specific implementation code is as follows:

@Component
public class SpringContextUtil extends WebApplicationObjectSupport {
	public <T> T getBean(Class<T> clazz) {
		ApplicationContext ac = getApplicationContext();
		if(ac == null){
			return null;
		}
		return ac.getBean(clazz);
	}
}

Compared with the implementation based on ApplicationObjectSupport, there is no other difference except that the inherited objects are different, and they are all obtained based on the getApplicationContext method.

Way 6: Through WebApplicationContextUtils

Spring provides the tool class WebApplicationContextUtils, through which the WebApplicationContext object can be obtained.

The specific implementation code is as follows:

public class SpringContextUtil2 {
	public static <T> T getBean(ServletContext request, String name, Class<T> clazz){
		WebApplicationContext webApplicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(request);
		// 或者
		WebApplicationContext webApplicationContext1 = WebApplicationContextUtils.getWebApplicationContext(request);
//        webApplicationContext1.getBean(name, clazz)
		T bean = webApplicationContext.getBean(name, clazz);
		return bean;
	}
}

This method is very common in Web projects built by SpringMVC and is suitable for the B/S structure of Web projects.

Method 7: Through ApplicationContextAware

By implementing the ApplicationContextAware interface, the ApplicationContext is injected into the Spring container when it starts to obtain the ApplicationContext object. This method is also a common way to obtain Beans and is recommended.

The specific implementation code is as follows:

@Component
public class SpringContextUtil3 implements ApplicationContextAware {

	private static ApplicationContext ac;

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		ac = applicationContext;
	}

	public static <T> T getBean(Class<T> clazz) {
		T bean = ac.getBean(clazz);
		return bean;
	}

}

This method is consistent with the idea of ​​obtaining BeanFactory through BeanFactoryAware.

Way 8: Through ContextLoader

Using the getCurrentWebApplicationContext method provided by ContextLoader is also a commonly used method to obtain WebApplicationContext.

The specific implementation code is as follows:

WebApplicationContext wac = ContextLoader.getCurrentWebApplicationContext();
wac.getBean(beanID);

This method is common in Web projects implemented by SpringMVC. This method is a method that does not depend on Servlet and does not require injection. However, it should be noted that when the server starts and the Spring container is initialized, the Spring container cannot be obtained through this method.

Method 9: Through BeanFactoryPostProcessor

Spring tool class, which is convenient for obtaining beans in non-Spring management environments.

@Component
public final class SpringUtils implements BeanFactoryPostProcessor{
    
    /** Spring应用上下文环境 */
    private static ConfigurableListableBeanFactory beanFactory;

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException{
        SpringUtilsS.beanFactory = beanFactory;
    }

    /**
     * 获取对象
     *
     * @param name
     * @return Object 一个以所给名字注册的bean的实例
     * @throws BeansException
     *
     */
    @SuppressWarnings("unchecked")
    public static <T> T getBean(String name) throws BeansException{
        return (T) beanFactory.getBean(name);
    }

    /**
     * 获取类型为requiredType的对象
     *
     * @param clz
     * @return
     * @throws BeansException
     *
     */
    public static <T> T getBean(Class<T> clz) throws BeansException{
        T result = (T) beanFactory.getBean(clz);
        return result;
    }

    /**
     * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
     *
     * @param name
     * @return boolean
     */
    public static boolean containsBean(String name){
        return beanFactory.containsBean(name);
    }

    /**
     * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
     *
     * @param name
     * @return boolean
     * @throws NoSuchBeanDefinitionException
     *
     */
    public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException{
        return beanFactory.isSingleton(name);
    }

    /**
     * @param name
     * @return Class 注册对象的类型
     * @throws NoSuchBeanDefinitionException
     *
     */
    public static Class<?> getType(String name) throws NoSuchBeanDefinitionException{
        return beanFactory.getType(name);
    }

    /**
     * 如果给定的bean名字在bean定义中有别名,则返回这些别名
     *
     * @param name
     * @return
     * @throws NoSuchBeanDefinitionException
     *
     */
    public static String[] getAliases(String name) throws NoSuchBeanDefinitionException{
        return beanFactory.getAliases(name);
    }

    /**
     * 获取aop代理对象
     * 
     * @param invoker
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> T getAopProxy(T invoker){
        return (T) AopContext.currentProxy();
    }
}

Among them, the ConfigurableListableBeanFactory interface also belongs to the sub-interface of BeanFactory.

summary

In this article, 9 methods of obtaining beans from Spring containers are introduced. Although each method has different implementations, in essence, it is nothing more than obtaining beans through BeanFactory or ApplicationContext, but the methods of obtaining BeanFactory or ApplicationContext containers are different. That's all.

So, do you realize that to learn a technology or an implementation method, as long as you grasp its fundamentals, no matter how the form changes, it will remain the same. And here "Zong" is the IoC container.

Guess you like

Origin blog.csdn.net/wo541075754/article/details/128669276