Spring controls inversion and dependency injection


Controls the type of inversion

Inversion of control (IOC) aims to provide a simpler mechanism to set the dependencies of components and manage them throughout the life cycle. Generally, control inversion can be divided into two subtypes: dependency injection (DI) and dependency lookup (DL). These subtypes can be further decomposed into the specific implementation of IOC services

1. Dependency search

1.1 dependent pull

Dependency Pull refers to extracting dependencies from the registry as needed. The following code shows the Spring based Dependency Pull

public class DependencyPull {

    public static void main(String[] args) {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring/app-context.xml");
        ctx.getBean("renderer", MessageRenderer.class);
    }
}
1.2 context dependent search

Contextual Dependency lookup (CDL) also belongs to the subtype of dependency lookup, which is somewhat similar to dependency pull. However, in CDL, the lookup is performed for the container for managing resources, which is usually provided by the application server or framework (Tomcat, JBoss, Spring), For example, the following code shows a container interface that provides a dependency lookup service

public interface Container {
    // Get the corresponding dependencies according to the key
    Object getDependency(String key);
}

Let the components work through the CDL interface

public interface ManagedComponent {
    void performLookup(Container container);
}

The component needs to implement this interface. When the Container is ready to pass dependencies to the component, it will call the performLookup() method of each component in turn, and then the component can use the Container interface to find the required dependencies

public class ContextualizedDependencyLookup implements ManagedComponent {

    private Dependency dependency;

    @Override
    public void performLookup(Container container) {
        this.dependency = (Dependency) container.getDependency("myDependency");
    }

    @Override
    public String toString() {
        return dependency.toString();
    }
}

2. Dependency injection

2.1 constructor injection

Constructor dependency injection occurs when a dependency is provided in a component's constructor

public class ConstructorInjection {

    private Dependency dependency;

    public ConstructorInjection(Dependency dependency) {
        this.dependency = dependency;
    }

    @Override
    public String toString() {
        return dependency.toString();
    }
}
2.2 setter function injection

The Ioc container injects the dependencies of components through JavaBean style setter methods

public class SetterInjection {

    private Dependency dependency;

    public void setDependency(Dependency dependency) {
        this.dependency = dependency;
    }

    @Override
    public String toString() {
        return dependency.toString();
    }
}

In Spring, another injection type called field injection is also supported. This injection type will be introduced later when you learn to use @ Autowire annotation for automatic assembly


Inversion of control in Spring

1. Bean and BeanFactory

The core of Spring's dependency injection container is BeanFactory, which is responsible for managing components, including dependencies and their life cycle. If we want to obtain a component (Bean), we must create an instance that implements the BeanFactory interface and configure it

Although BeanFactory can be configured programmatically, it is more common to use a configuration file to configure it externally. Bean configuration can be represented by an instance of a class that implements the BeanDefinition interface. For any BeanFactory implementation class that implements the BeanDefinitionReader interface, you can use PropertiesBeanDefinitionReader or XmlBeanDefinitionReader to read BeanDefinition data from the configuration file

Define a set of interfaces:

public interface Oracle {
    String output();
}

public class OracleImpl implements Oracle {

    @Override
    public String output() {
        return "hello world";
    }
}

Next, let's take a look at how Spring's BeanFactory is initialized and used to obtain Bean instances

public class XmlConfigWithBeanFactory {

    public static void main(String[] args) {
        // DefaultListableBeanFactory is one of the two main BeanFactory implementations provided by Spring
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        XmlBeanDefinitionReader rdr = new XmlBeanDefinitionReader(factory);
        // Use XmlBeanDefinitionReader to read BeanDefinition information from XML file
        rdr.loadBeanDefinitions(new ClassPathResource("spring/xml-bean-factory-config.xml"));
        // Use the name oracle configured in the XML configuration file to get the bean
        Oracle oracle = (Oracle) factory.getBean("oracle");
        System.out.println(oracle.getInfo());
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="oracle" name="oracle" class="com.example.OracleImpl"/>

</beans>

ApplicationContext interface is an extension of BeanFactory. In addition to DI services, it also provides other services such as transaction and AOP. When developing Spring based applications, it is recommended to interact with Spring through the ApplicationContext interface

2. Set Spring configuration

2.1 XML configuration

For XML configuration, you need to declare the basic namespace information provided by Spring required by the application. The configuration shown below only declares the namespace used to define the bean

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="provider" class="com.example.HelloWorldMessageProvider"/>

    <bean id="render" class="com.example.StandardOutMessageRender"
          p:messageProvider-ref="provider"/>

</beans>
2.2 annotation configuration

To use Spring's annotation support in your application, you need to declare it in the XML configuration

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.example" />

</beans>

The < context: Component scan > tag tells Spring to scan the code to find the beans injected with @ Component and other annotations, and beans that support the use of @ Autowire and other annotations under the specified package (and all its sub packages)

2.3 Java configuration

The Configuration class uses @ Configuration annotation and contains methods annotated with @ bean. These methods are directly called by the IOC container to instantiate the bean, and the bean name is the same as the name of the method used to create it

@Configuration
public class HelloWorldConfiguration {

    @Bean
    public MessageProvider provider() {
        return new HelloWorldMessageProvider();
    }

    @Bean
    public MessageRender render() {
        StandardOutMessageRender render = new StandardOutMessageRender();
        render.setMessageProvider(provider());
        return render;
    }
}

If you want to read configuration information from this class, you need a different ApplicationContext implementation

public class HelloWorldSpringAnnotated {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx
                = new AnnotationConfigApplicationContext(HelloWorldConfiguration.class);
        MessageRender render = ctx.getBean("render", MessageRender.class);
        render.render();
    }
}

3. setter injection

To configure setter injection using XML configuration, you need to specify the < property > tag under the < bean > tag and inject a dependency for it

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="provider" class="com.example.HelloWorldMessageProvider"/>

    <bean id="render" class="com.example.StandardOutMessageRender">
        <property name="messageProvider" ref="provider"/>
    </bean>

</beans>

If you use annotations, you only need to add an @ Autowired annotation to the setter method

@Service("render")
public class StandardOutMessageRender implements MessageRender {

	...

    @Override
    @Autowired
    public void setMessageProvider(MessageProvider messageProvider) {
        this.messageProvider = messageProvider;
    }
}

4. Constructor injection

public class ConfigurableMessageProvider implements MessageProvider {

    private String message;

    public ConfigurableMessageProvider(String message) {
        this.message = message;
    }

    @Override
    public String getMessage() {
        return null;
    }
}

Using XML injection

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:c="http://www.springframework.org/schema/c"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="oracle" name="oracle" class="com.example.OracleImpl"/>

    <!-- use <constructor-arg> sign -->
    <bean id="messageProvider" class="com.example.ConfigurableMessageProvider">
        <constructor-arg value="hello world" />
    </bean>

    <!-- use c Namespace -->
    <bean id="provider" class="com.example.ConfigurableMessageProvider"
            c:message="hello world"/>

</beans>

Use annotation method

@Service
public class ConfigurableMessageProvider implements MessageProvider {

    private String message;

    @Autowired
    public ConfigurableMessageProvider(
            @Value("hello world") String message) {
        this.message = message;
    }

    @Override
    public String getMessage() {
        return null;
    }
}

Keywords: Java Spring Back-end

Added by figo2476 on Sun, 20 Feb 2022 02:19:55 +0200