Spring deep learning (1)

Spring IOC

Dependency Injection

  1. constructor injection
  2. setter injection
  3. interface injection

constructor injection

public FXNewsProvider(IFXNewsListener newsListner,IFXNewsPersister newsPersister) {
    
     
 	this.newsListener = newsListner; 
	this.newPersistener = newsPersister; 
}

The IoC Service Provider will check the construction method of the injected object, obtain the list of dependent objects it needs, and then inject the corresponding object into it. It is impossible for the same object to be constructed twice. Therefore, the construction of the injected object and its entire life cycle should be managed by the IoC Service Provider.

setter method injection

For JavaBean objects, the corresponding properties are usually accessed through the setXXX() and getXXX() methods. These setXXX() methods are collectively called setter methods, and getXXX() is of course called a getter method. Through the setter method, the corresponding object property can be changed, and through the getter method, the state of the corresponding property can be obtained. Therefore, as long as the current object adds a setter method to the attribute corresponding to its dependent object, the corresponding dependent object can be set to the injected object through the setter method.

public class FXNewsProvider{
    
     
     private IFXNewsListener newsListener; 
     private IFXNewsPersister newPersistener; 

     public IFXNewsListener getNewsListener() {
    
     
    	 return newsListener; 
     } 
     public void setNewsListener(IFXNewsListener newsListener) {
    
     
     	this.newsListener = newsListener; 
     } 
     public IFXNewsPersister getNewPersistener() {
    
     
    	return newPersistener; 
     } 
     public void setNewPersistener(IFXNewsPersister newPersistener) {
    
     
    	this.newPersistener = newPersistener; 
     } 
}

In this way, the outside world can inject dependent objects into the FXNewsProvider object by calling the setNewsListener and setNewPersistener methods.

Although the setter method injection is not like the construction method injection, which allows the object to be used after the construction is completed, it is relatively looser and can be injected after the object construction is completed.

interface injection

​ Compared with the first two injection methods, interface injection is not so simple and clear. If the injected object wants the IoC Service Provider to inject dependent objects into it, it must implement an interface. This interface provides a method for injecting dependent objects into it. The IoC Service Provider finally uses these interfaces to understand what dependent objects should be injected into the injected object. The figure below demonstrates how to use interface injection to inject dependent objects for FXNewsProvider.

​ In order for the IoC Service Provider to inject the IFXNewsListener it depends on, FXNewsProvider first needs to implement the IFXNewsListenerCallable interface. This interface will declare an injectNewsListner method (the method name is optional), and the parameter of this method is the type of the dependent object. In this way, the InjectionServiceContainer object, that is, the corresponding IoC Service Provider, can inject the dependent object into the injected object FXNewsProvider through this interface method.

Comparison of three injection methods

  • Interface injection. In terms of the use of injection methods, interface injection is a method that is not advocated now, and is basically in a "retired state". Because it forces the injected object to implement unnecessary interfaces, it is intrusive. This is not required for constructor injection and setter method injection.
  • Constructor injection. The advantage of this injection method is that after the object is constructed, it is in a ready state and can be used immediately. The disadvantage is that when there are many dependent objects, the parameter list of the construction method will be relatively long. When constructing objects through reflection, it will be more difficult to deal with parameters of the same type, and it will be more troublesome to maintain and use. And in Java, constructors cannot be inherited and default values ​​cannot be set. For non-essential dependency processing, multiple construction methods may need to be introduced, and changes in the number of parameters may cause inconvenience in maintenance.
  • Setter method injection. Because methods can be named, setter method injection is more descriptive than constructor method injection. In addition, setter methods can be inherited, allowing default values ​​to be set, and have good IDE support. The disadvantage, of course, is that the object cannot enter the ready state immediately after construction.

Benefits of IoCs

​ Switching from the method of actively obtaining dependencies to the IoC method is not just a change in one direction. There are actually more mysteries behind the simple transformation. To say what benefits the IoC model can bring us, there may be many listed in various materials or books. For example, it will not be very intrusive to business objects. After using IoC, objects have better testability, reusability and scalability, and so on. However, talking about it in general may not really give you a deep understanding of the many benefits brought by the IoC pattern, so let us start with specific examples to find out.

IOC Provider

Although business objects can declare the corresponding dependencies through IOC, a service is still needed to bind these interdependent objects together, and IoC Service Provider does this work.

​Ioc Service Provider is an abstract concept that can refer to any implementation that binds business objects in an IoC scenario together. It can be a piece of code, or a group of related classes, or even a more general IoC framework or IoC container implementation. Spring's Ioc container is an Ioc Service Provider that provides dependency injection services.

IoC Service Provider Responsibilities

The responsibilities of the IoC Service Provider are relatively simple. There are two main responsibilities: the construction and management of business objects and the dependency binding between business objects.

  • **Business object construction management:**In IoC scenarios, business objects don’t need to care about how to build and obtain the objects they depend on, but this part of the work always needs someone to do it. Therefore, the IoC Service Provider needs to separate the object construction logic from the client object to prevent this part of logic from polluting the implementation of the business object.
  • **Dependency binding between business objects: **For Ioc Service Provider, this responsibility is the most difficult and most important. If there is a problem with this part of the responsibility, the object that needs to be injected will not find the required dependent object. Ioc Service Provider injects and binds the objects that these objects depend on by combining all the business objects that have been built and managed before, and the identifiable dependencies between each business object, so as to ensure that each business object can be ready when it is used state.

How IoC Service Provider manages dependencies between objects

​ The injected object can notify the IoC Service Provider to inject corresponding dependencies in various ways. But the question is, will the IoC Service Provider that receives the notification be able to fully understand the intention of the injected object and provide it with the desired dependencies in a timely and effective manner? Sometimes, things may not be as we take them for granted. For the IoC Service Provider that provides dependency injection for the injected object, he needs to know the corresponding relationship between the injected object and the dependent object that he manages and masters.

The IoC Service Provider needs a way to record the correspondence between objects. for example:

  • The corresponding relationship between the injected object and its dependent objects can be recorded through basic text files.
  • These corresponding information can be registered through a highly descriptive XML file format.
  • These corresponding messages can be registered by writing codes.

So, in the actual situation, in what ways do various specific IoC Service Provider implementations record "service information"? We can summarize that the current popular IoC Service Provider products use the following methods of registering object management information:

Direct Encoding

Most of the current IoC containers should support direct encoding, such as PicoContainer, Spring, Avalon, etc. Before the container starts, we can register the injected objects and dependent objects into the container through program coding, and clarify the dependency injection relationship between them.

IoContainer container = ...; 
container.register(FXNewsProvider.class,new FXNewsProvider()); 
container.register(IFXNewsListener.class,new DowJonesNewsListener()); 
... 
FXNewsProvider newsProvider = (FXNewsProvider)container.get(FXNewsProvider.class); 
newProvider.getAndPersistNews();

By specifying the corresponding specific instance for the corresponding class, you can tell the IoC container that when we want this type of object instance, please return the corresponding specific instance registered in the container to us.

If it is interface injection, the pseudocode may look a little more. However, the principle is the same, except that in addition to registering the corresponding object, the "injection flag interface" must be bound to the corresponding dependent object, so that the container can finally know what kind of corresponding relationship it is.

Dependency injection based on interface injection management in direct coding form

IoContainer container = ...;
container.register(FXNewsProvider.class,new FxNewsProvider);
container.register(IFXNewsListener.class,new DowJonesNewsListener);
...
container.bind(IFXNewsListenerCallabe.class,container.get(IFXNewsListener.class));
...
FXNewsProvider newsProvider = (FXNewsProvider)contanier.get(IFXNewsProvider.class);
newProvider.getAndPersistNews();

​ Through the bind method, the object on which the "injected object" depends can be bound to an object instance of type IFXNewsListener registered in the container. Before the container returns the FXNewsProvider object instance, it will inject the object instance registered with the IFXNewsListener in the container into the "injected object" FxNewsProvider according to the binding information, and finally return the assembled FXNewsProvider object.

Therefore, allowing the final IoC Service Provider (that is, each IoC framework or container implementation) to know the "esoteric meaning" of the service through program coding should be the most basic way to manage the dependency binding relationship.

configuration file method

This is a more common way of dependency injection management. Common text files, properties files, XML files, etc., can all be carriers for managing dependency injection. However, the most common way is to manage object registration and dependencies between objects through XML files.

<bean id="newsProvider" class=" ..FXNewsProvider">
	<property name= "newsListener" >
		<ref bean="djNewsListener"/>
    </property>
	<property name="newPersistener">
		<ref bean="djNewsPersister"/>
    </property>
</bean>
<bean id="djNewsListener" class=" ..impl.DowJonesNewsListener" ></bean>
<bean id="djNewsPersister" class=" ..impl.DowJonesNewsPersister"></bean>

metadata method

The representative implementation of this method is Google Guice, which is a set of IOC framework developed by Bob Lee on the basis of java 5 annotations and Generic. We can directly use metadata information in the class to mark the dependencies between objects, and the Guice framework will assemble these objects according to the information provided by these annotations and hand them over to the client object for use.

The definition of FXNewsProvider after using Guice annotations to mark dependencies

public class FXNewsProvider {
    
    
 	private IFXNewsListenernewsListener;private IFXNewsPersister newPersistener ;
    @Inject
    public FXNewsProvider(IFXNewsListener listener,IFXNewsPersister persister) {
    
    
        this.newsListener= listener;
        this.newPersistener = persister ;
    }  
}

Through @Inject, we indicate that IoC Service Provider needs to inject the objects it depends on for FXNewsProvider through constructor injection. As for the remaining dependency-related information, it is provided by the corresponding Module in Guice.

Module implementation used by FXNewsProvider

public class NewsBindingModule extends AbstractModule{
    
    
@Override
protected void configure() {
    
    
	bind (IFXNewsListener.class).to(DowJonesNewsListener.class).in(Scopes.SINGLETON);
    bind (FXNewsPersister.class).to(DowJonesNewsPersister.class).in (Scopes.SINGLETON)
}

After specifying further dependency injection-related information through the Module, we can directly obtain the objects that have finally been injected and are directly available from Guice.

Injector injector = Guice.createInjector(new NewsBindingModule()) ;
FXNewsProvider newsProvider = injector.getInstance (FXNewsProvider.class)newsProvider.getAndPersistNews ( ) ;

Of course, the final injection relationship must be determined through code processing. From this point of view, the annotation method can be regarded as a special case of the coding method.

BeanFactory of Spring's IOC container

We said earlier that Spring's IoC container is an IoC Service Provider, but this is only part of the reason why it is named IoC. What we cannot ignore is the "container". Spring's IoC container is a lightweight container that provides IoC support. In addition to basic IoC support, it also provides support other than IoC as a lightweight container. For example, on top of Spring's IoC container, Spring also provides services such as corresponding AOP framework support and enterprise-level service integration. There is a certain intersection between the services provided by Spring's IoC container and IoC Service Provider, and the relationship between the two is shown in the figure.

The relationship between Spring's IoC container and IoC Service Provider

Spring provides two container types BeanFactory and ApplicationContext

  • The basic type of IOC container of BeanFactory provides complete IOC service support. If not specified, the lazy initialization strategy (lazy-load) is adopted by default. Only when the client object needs to access a managed object in the container will the managed object be initialized and dependency injected. Therefore, relatively speaking, the initial speed of container startup is relatively fast, and the required resources are limited. For scenarios where resources are limited and functional requirements are not very strict, BeanFactory is a more suitable IOC container choice.
  • ApplicationContext. ApplicationContext is built on the basis of BeanFactory, which is a relatively advanced container implementation. In addition to all the support of BeanFactory, ApplicationContext also provides other advanced features, such as event publishing, internationalization information support, etc., which will be detailed later. The objects managed by ApplicationContext are all initialized and bound by default after the container of this type is started. Therefore, compared to BeanFactory, ApplicationContext requires more system resources. At the same time, because all initialization is completed at startup, the container startup time is also longer than BeanFactory. If the system resources are sufficient and more functions are required, the ApplicationContext type container is more suitable.

Relationship between ApplicationContext and Beanfactory

image-20220606103921978

Note: ApplicationContext inherits indirectly from BeanFactory, so it is an IOC container built on top of BeanFactory.

​ BeanFactory is like a car factory. You get auto parts from other auto parts manufacturers or your own parts production department and send them to this car production plant. In the end, you only need to get the finished car from the end of the production line. Similarly, after handing over all the business objects required by the application to the BeanFactory, the only thing left to do is to obtain the final assembled and usable objects directly from the BeanFactory. As for how to assemble the final business object, you don't need to care about it, BeanFactory will do it for you.

​ Therefore, for the client, dealing with BeanFactory is actually very simple. At the most basic level, BeanFactory must expose a method interface for obtaining assembled objects.

Definition of BeanFactory

public interface BeanFactory {
    
    
    String FACTORY_BEAN_PREFIX = "&";
    Object getBean(String name) throws BeansException;
    object getBean(String name,Class requiredType) throws BeansException;
    /**
	 *@since 2.5
	 */
    0bject getBean(String name,Object [] args) throws BeansException;boolean containsBean (string name) ;
    boolean issingleton(String name) throws NoSuchBeanDefinitionException;
    /**
	 *@since 2.0.3
	 */
	boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
    /**
	 * @since 2.0.1
	 */
	boolean isTypeMatch(String name,Class targetType) throws NoSuchBeanDefinitionException;Class getType(String name) throws NoSuchBeanDefinitionException;
	string[] getAliases (string name) ;
}

​ The methods in the above code are basically query-related methods, for example, the method of obtaining an object (getBean), the method of querying whether an object exists in the container (containsBean), or obtaining the state of a bean or type of method etc. Because usually, for a stand-alone application, only the main entry class will be directly coupled with the API of the container.

Guess you like

Origin blog.csdn.net/qq_45473439/article/details/125153679