Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What is the function of Spring's FactoryBean?

2025-02-24 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >

Share

Shulou(Shulou.com)06/01 Report--

This article mainly explains "what is the function of FactoryBean of Spring". The content of the explanation is simple and clear, and it is easy to learn and understand. Please follow the editor's train of thought to study and learn "what is the role of FactoryBean of Spring"?

1. A Demo

Let's start with a simple Demo.

Create a new Maven project, introduce Spring dependencies, and then create a HelloService, as follows:

Public class HelloService {

Public String hello () {

Return "hello javaboy"

}

}

Then create a factory class for HelloService:

Public class HelloServiceFactoryBean implements FactoryBean {

@ Override

Public HelloService getObject () throws Exception {

Return new HelloService ()

}

@ Override

Public Class getObjectType () {

Return HelloService.class

}

@ Override

Public boolean isSingleton () {

Return true

}

}

Our new HelloServiceFactoryBean class implements the FactoryBean interface and specifies the HelloService generics.

Next, we configure the HelloServiceFactoryBean instance in the beans.xml file:

Load the configuration file:

@ Test

Void contextLoads () {

ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext ("beans.xml")

Object helloService = ctx.getBean ("helloService")

System.out.println (helloService.getClass () .toString ())

}

Load the XML configuration file, get the Bean instance, and print out the type of the Bean instance.

According to our previous understanding, the Bean obtained here should be an instance of HelloServiceFactoryBean, but the actual print result is as follows:

Class org.javaboy.springdemo03.HelloService

It's HelloService!

What's it all about? We have to start with the FactoryBean interface.

2.FactoryBean interface

FactoryBean plays an important role in the Spring framework, and the Spring framework itself provides a variety of different implementation classes for this purpose:

In theory, we can configure a Bean and configure it directly in the XML file. But sometimes there are so many properties of a class that it is very inconvenient to configure in XML. At this time, the advantage of Java code configuration is reflected. Using FactoryBean, you can configure the relevant properties of Bean through Java code.

Starting with Spring3.0, FactoryBean supports generics, which is what you see as FactoryBean. There are three methods in the FactoryBean interface:

Public interface FactoryBean {

@ Nullable

T getObject () throws Exception

@ Nullable

Class getObjectType ()

Default boolean isSingleton () {

Return true

}

}

GetObject: this method returns the instance created by FactoryBean. If the class we provide in the XML configuration file is a FactoryBean, then when we call the getBean method to get the instance, we finally get the return value of the getObject method. GetObjectType: returns the type of the object. Whether the object returned by the isSingleton:getObject method is a singleton.

So when we call the getBean method to get the Bean instance, we actually get the return value of the getObject method, so is there no way to get the HelloServiceFactoryBean? Of course there is!

@ Test

Void contextLoads () {

ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext ("beans.xml")

Object helloService = ctx.getBean (& helloService)

System.out.println (helloService.getClass () .toString ())

}

The print result is as follows:

Class org.javaboy.springdemo03.HelloServiceFactoryBean

As you can see, you only need to put an & symbol in front of the name of Bean, and what you get is the HelloServiceFactoryBean instance.

3. Source code analysis

According to the Spring source code of the sixth bullet! Brother Song will talk to you about the introduction of DefaultListableBeanFactory, the ancestor of the container. There is also a preInstantiateSingletons method in DefaultListableBeanFactory that can register Bean in advance. This method is declared in the ConfigurableListableBeanFactory interface. The DefaultListableBeanFactory class implements the ConfigurableListableBeanFactory interface and the methods in the interface:

@ Override

Public void preInstantiateSingletons () throws BeansException {

If (logger.isTraceEnabled ()) {

Logger.trace ("Pre-instantiating singletons in" + this)

}

/ / Iterate over a copy to allow for init methods which in turn register new bean definitions.

/ / While this may not be part of the regular factory bootstrap, it does otherwise work fine.

List beanNames = new ArrayList (this.beanDefinitionNames)

/ / Trigger initialization of all non-lazy singleton beans...

For (String beanName: beanNames) {

RootBeanDefinition bd = getMergedLocalBeanDefinition (beanName)

If (! bd.isAbstract () & & bd.isSingleton () & &! bd.isLazyInit ()) {

If (isFactoryBean (beanName)) {

Object bean = getBean (FACTORY_BEAN_PREFIX + beanName)

If (bean instanceof FactoryBean) {

Final FactoryBean factory = (FactoryBean) bean

Boolean isEagerInit

If (System.getSecurityManager ()! = null & & factory instanceof SmartFactoryBean) {

IsEagerInit = AccessController.doPrivileged ((PrivilegedAction))

((SmartFactoryBean) factory):: isEagerInit

GetAccessControlContext ()

}

Else {

IsEagerInit = (factory instanceof SmartFactoryBean & &

((SmartFactoryBean) factory) .isEagerInit ()

}

If (isEagerInit) {

GetBean (beanName)

}

}

}

Else {

GetBean (beanName)

}

}

}

/ / Trigger post-initialization callback for all applicable beans...

For (String beanName: beanNames) {

Object singletonInstance = getSingleton (beanName)

If (singletonInstance instanceof SmartInitializingSingleton) {

Final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance

If (System.getSecurityManager ()! = null) {

AccessController.doPrivileged ((PrivilegedAction) ()-> {

SmartSingleton.afterSingletonsInstantiated ()

Return null

}, getAccessControlContext ()

}

Else {

SmartSingleton.afterSingletonsInstantiated ()

}

}

}

}

The overall logic of the preInstantiateSingletons method is relatively simple, which is to traverse the beanNames and instantiate the qualified Bean, and note that the so-called pre-initialization here is actually calling getBean before we call the getBean method.

Here are a few key points.

The first is the call to the isFactoryBean method, which obtains the Bean instance according to beanName to determine whether it is a FactoryBean or not. If there is no Bean instance (which has not been created yet), then determine whether it is a FactoryBean according to the BeanDefinition.

If it is FactoryBean, the FACTORY_BEAN_PREFIX prefix is automatically added to the getBean. This constant is actually &, so the obtained instance is actually the instance of FactoryBean. After obtaining the FactoryBean instance, determine whether it is necessary to call the getObject method to initialize the Bean during the container startup phase, and to initialize it if the isEagerInit is true.

According to our previous definition, the isEagerInit attribute obtained here is false, that is, the Bean is not loaded in advance, but only when the developer calls the getBean method manually. If you want to be able to load here in advance, you need to redefine HelloServiceFactoryBean and make it implement the SmartFactoryBean interface, as follows:

Public class HelloServiceFactoryBean2 implements SmartFactoryBean {

@ Override

Public HelloService getObject () throws Exception {

Return new HelloService ()

}

@ Override

Public Class getObjectType () {

Return HelloService.class

}

@ Override

Public boolean isSingleton () {

Return true

}

@ Override

Public boolean isEagerInit () {

Return true

}

}

After implementing the SmartFactoryBean interface, override the isEagerInit method and return true, other methods remain the same, reconfigure the beans.xml file, and then start the container. At this point, when the container starts, the getBean method will be called in advance to complete the loading of the Bean.

Next let's look at the getBean method.

The getBean method first calls AbstractBeanFactory#doGetBean, and in this method, the AbstractBeanFactory#getObjectForBeanInstance method is called:

Protected Object getObjectForBeanInstance (

Object beanInstance, String name, String beanName, @ Nullable RootBeanDefinition mbd) {

/ / Don't let calling code try to dereference the factory if the bean isn't a factory.

If (BeanFactoryUtils.isFactoryDereference (name)) {

If (beanInstance instanceof NullBean) {

Return beanInstance

}

If (! (beanInstance instanceof FactoryBean)) {

Throw new BeanIsNotAFactoryException (beanName, beanInstance.getClass ())

}

If (mbd! = null) {

Mbd.isFactoryBean = true

}

Return beanInstance

}

/ / Now we have the bean instance, which may bea normal bean or a FactoryBean.

/ / If it's a FactoryBean, we use it to create a bean instance, unless the

/ / caller actually wants a reference to the factory.

If (! (beanInstance instanceof FactoryBean)) {

Return beanInstance

}

Object object = null

If (mbd! = null) {

Mbd.isFactoryBean = true

}

Else {

Object = getCachedObjectForFactoryBean (beanName)

}

If (object = = null) {

/ / Return bean instance from factory.

FactoryBean factory = (FactoryBean) beanInstance

/ / Caches object obtained from FactoryBean if it is a singleton.

If (mbd = = null & & containsBeanDefinition (beanName)) {

Mbd = getMergedLocalBeanDefinition (beanName)

}

Boolean synthetic = (mbd! = null & & mbd.isSynthetic ())

Object = getObjectFromFactoryBean (factory, beanName,! synthetic)

}

Return object

}

This source code is very interesting:

The BeanFactoryUtils.isFactoryDereference method is used to determine whether name starts with &. If it starts with &, it indicates that you want to get a FactoryBean, then if beanInstance happens to be a FactoryBean, it will be returned directly. And set the isFactoryBean property in mbd to true.

If name does not start with &, it means that the user wants to obtain the Bean constructed by FactoryBean, so if beanInstance is not a FactoryBean instance, it will be returned directly.

If the current beanInstance is a FactoryBean and all the user wants to get is a normal Bean, then you will go into the following code.

First call the getCachedObjectForFactoryBean method to get the Bean from the cache. If this is the first time to get the Bean, there is no data in the cache, and the getObject method will not be called once before the Bean can be saved in the cache.

Why is it possible? If the Bean is singleton, it will be saved in the cache, and if the Bean is not singleton, it will not be saved in the cache, but will create a new one each time it is loaded.

If you fail to load the Bean from the cache, the getObjectFromFactoryBean method is eventually called to load the Bean.

Protected Object getObjectFromFactoryBean (FactoryBean factory, String beanName, boolean shouldPostProcess) {

If (factory.isSingleton () & & containsSingleton (beanName)) {

Synchronized (getSingletonMutex ()) {

Object object = this.factoryBeanObjectCache.get (beanName)

If (object = = null) {

Object = doGetObjectFromFactoryBean (factory, beanName)

/ / Only post-process and store if not put there already during getObject () call above

/ / (e.g. Because of circular reference processing triggered by custom getBean calls)

Object alreadyThere = this.factoryBeanObjectCache.get (beanName)

If (alreadyThere! = null) {

Object = alreadyThere

}

Else {

If (shouldPostProcess) {

If (isSingletonCurrentlyInCreation (beanName)) {

/ / Temporarily return non-post-processed object, not storing it yet..

Return object

}

BeforeSingletonCreation (beanName)

Try {

Object = postProcessObjectFromFactoryBean (object, beanName)

}

Catch (Throwable ex) {

Throw new BeanCreationException (beanName

"Post-processing of FactoryBean's singleton object failed", ex)

}

Finally {

AfterSingletonCreation (beanName)

}

}

If (containsSingleton (beanName)) {

This.factoryBeanObjectCache.put (beanName, object)

}

}

}

Return object

}

}

Else {

Object object = doGetObjectFromFactoryBean (factory, beanName)

If (shouldPostProcess) {

Try {

Object = postProcessObjectFromFactoryBean (object, beanName)

}

Catch (Throwable ex) {

Throw new BeanCreationException (beanName, "Post-processing of FactoryBean's object failed", ex)

}

}

Return object

}

}

In the getObjectFromFactoryBean method, first of all, through the isSingleton and containsSingleton methods to determine whether the return value of the getObject method is a single case, single case goes one way, non-single case takes another way.

If it is a singleton:

First go to the cache and get it again, see if you can get it. If it is not in the cache, call the doGetObjectFromFactoryBean method to get it, which is the real get method. After it is obtained, the post-processing of the Bean is performed, and after the processing is completed, if the Bean is a singleton, it will be cached.

If it is not a single case:

It is not a simple case. Call the doGetObjectFromFactoryBean method directly to get the Bean instance, and then do post-processing without caching.

Next let's take a look at the doGetObjectFromFactoryBean method:

Private Object doGetObjectFromFactoryBean (FactoryBean factory, String beanName) throws BeanCreationException {

Object object

Try {

If (System.getSecurityManager ()! = null) {

AccessControlContext acc = getAccessControlContext ()

Try {

Object = AccessController.doPrivileged ((PrivilegedExceptionAction) factory::getObject, acc)

}

Catch (PrivilegedActionException pae) {

Throw pae.getException ()

}

}

Else {

Object = factory.getObject ()

}

}

Catch (FactoryBeanNotInitializedException ex) {

Throw new BeanCurrentlyInCreationException (beanName, ex.toString ())

}

Catch (Throwable ex) {

Throw new BeanCreationException (beanName, "FactoryBean threw exception on object creation", ex)

}

/ / Do not accept a null value for a FactoryBean that's not fully

/ / initialized yet: Many FactoryBeans just return null then.

If (object = = null) {

If (isSingletonCurrentlyInCreation (beanName)) {

Throw new BeanCurrentlyInCreationException (

BeanName, "FactoryBean which is currently in creation returned null from getObject")

}

Object = new NullBean ()

}

Return object

}

After making some judgments, we finally get the instance we want through the factory.getObject (); method.

This is the entire FactoryBean loading process.

4. Application

FactoryBean is widely used in the Spring framework, and even if we don't write the above code, we still use a lot of FactoryBean.

Next, Brother Song gives two random examples.

4.1 SqlSessionFactoryBean

This is probably the FactoryBean that everyone has the most contact with. When MyBatis integrates Spring, we must configure the following line:

Classpath*:org/javaboy/meeting/mapper/*.xml

This is configuring FactoryBean. When we use MyBatis alone, we need a SqlSessionFactory. After integration, there is no SqlSessionFactory. We have reason to believe that SqlSessionFactoryBean's getObject method provides SqlSessionFactory. Let's take a look at its source code:

@ Override

Public SqlSessionFactory getObject () throws Exception {

If (this.sqlSessionFactory = = null) {

AfterPropertiesSet ()

}

Return this.sqlSessionFactory

}

That's true!

4.2 Jackson2ObjectMapperFactoryBean

This is also a FactoryBean that people use relatively much.

If you use Jackson in your project and want to do some configuration at the global level, you might use this class in general:

The objectMapper property of the MappingJackson2HttpMessageConverter class actually requires an ObjectMapper object, but we provide a Jackson2ObjectMapperFactoryBean here because the getObject method of Jackson2ObjectMapperFactoryBean is the ObjectMapper we need:

@ Override

@ Nullable

Public ObjectMapper getObject () {

Return this.objectMapper

}

4.3 FormattingConversionServiceFactoryBean

FormattingConversionServiceFactoryBean has also appeared in Song GE's SpringMVC tutorials (official account Jiangnan a little rain background reply springmvc to get tutorials).

If the format of the parameter passed by the front end is in the form of key-value, then the parameter of the date type requires the server to provide a date type converter, as follows:

@ Component

Public class DateConverter implements Converter {

SimpleDateFormat sdf = new SimpleDateFormat ("yyyy-MM-dd")

Public Date convert (String source) {

Try {

Return sdf.parse (source)

} catch (ParseException e) {

E.printStackTrace ()

}

Return null

}

}

Then configure this in the XML file:

The getObject method in FormattingConversionServiceFactoryBean ends up returning FormattingConversionService.

There are many similar examples, such as EhCacheManagerFactoryBean, YamlPropertiesFactoryBean and so on.

Thank you for your reading, the above is the content of "what is the role of Spring FactoryBean?" after the study of this article, I believe you have a deeper understanding of the role of Spring FactoryBean, and the specific use needs to be verified in practice. Here is, the editor will push for you more related knowledge points of the article, welcome to follow!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Internet Technology

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report