How spring IOC implements dependency injection

Time:2021-3-24

Dependency injection (DI)

DI(Dependency Injection)Spring IOC is not a technology, but an idea, which can guide us to design loosely coupled program code. The function of spring IOC is reflected in two aspects: one is how to assemble beans into a container and how to get beans from the container; the other is how to solve the dependency relationship between beans. In other words, if the IOC container manages the dependency relationship, when a bean needs to rely on another bean, how can IOC container implement such dependency relationship.

The implementation method to solve the dependency between beans in spring is called in the concept of springDependency injection (DI). Generally speaking, there are three ways to implement spring dependency injection: construction method injection, setter method injection and annotation injection. However, as far as I’m concerned, I think it should be divided into two forms – XML based injection and annotation based injection, and then subdivided into the following forms:

DI实现.png

XML based injection is the first way we learn and use, and it is also the most familiar way. Let’s give a brief introduction and give an example.

  • Injection by construction method
public class UserServiceImpl implements UserService {
    
    private UserDao userDao;
    
    public UserServiceImpl(UserDao userDao) {
        this.userDao = userDao;
    }
    
    /**Methods inherited from userservice**/
}

First, define a service layerUserServiceImplAnd then add a reference to the Dao layer inside ituserDao

The next step is to add a constructorpublic UserServiceImpl(UserDao userDao)Let’s wait for spring to use this methoduserDaoInjection instance.

Finally, the corresponding bean instance is injected into the spring XML configuration file.

If there is no corresponding construction method, an error will be reported.

  • Injection through setter method

modifyUserServiceImpl.javaFor:

public class UserServiceImpl implements UserService {

    private UserDao userDao;
    
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
    
    /**Methods inherited from userservice**/
}

Then modify the content of the XML file as follows:

The difference between the two methods is that: 1UserServiceImpl.javaYou don’t need to add a constructor, but there must be a parameterless constructor (such aspublic UserServiceImpl(), which is not written in the example, because Java will provide a parameterless construction method by default for the spring container to register and generate beans (such asuserService)。 2、 In XML file, when the construction method is used to inject, we need to use theThis pair of tags; when the setter method is injected, thelabel.

In the XML injection process, in addition to usingref=""In addition to references, you can also usevalue=""Set specific value, its effect and use annotation@Valuealmost.

Annotation based dependency injection

@Autowired
  • Source code
@Target({ElementType.CONSTRUCTOR,
         ElementType.METHOD,
         ElementType.PARAMETER,
         ElementType.FIELD,
         ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
    boolean required() default true;
}

@AutowiredIs the key point of annotation based dependency injection, its source code is very simple, only one parameterrequest()The function of this parameter is to identify whether the bean must be injected, that is, when the spring container does not find the corresponding bean, if its value istrueIf the value isfalse, there will be no exception, but in the process of using, if the container does not inject beans all the time, there may be null pointer exception.

Another point is that the@TargetThe parameters included are exactly the types of dependency injection methods based on annotations,@Targetdecided@AutowiredCan be marked on which types.

  • Injection by construction method
@Service("userService")
public class UserServiceImpl implements UserService {
    
    private UserDao userDao;
    
    @Autowired
    public UserServiceImpl(UserDao userDao) {
        this.userDao = userDao;
    }
    /**Methods inherited from userservice**/
}

According to the development documents, there is only one construction method. Since spring 4.3, there is no need to add it @AutowiredMark, you can. However, if there are multiple construction methods, you must label one of them @AutowiredOtherwise spring will report an exception.

  • Injection through setter method
@Service("userService")
public class UserServiceImpl implements UserService {

    private UserDao userDao;

	@Autowired
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
    /**Methods inherited from userservice**/
}
  • Field injection
@Service("userService")
public class UserServiceImpl implements UserService {

	@Autowired
    private UserDao userDao;
    
    /**Methods inherited from userservice**/
}
  • Injection of parameters through the method

The above three injection methods are relatively familiar, so I will not elaborate more. Let’s focus on parameter injection. In fact, the method of parameter injection is similar to that of constructor method and setter method, which is equivalent to annotation on constructor method and setter method@AutowiredPut it in the position of the input parameter. It may be abstract. Let’s look at the example

@Component
public class UserDaoImpl implements UserDao {
    //Simply return a user to simulate the database search process
    @Override
    public User getUser(Long id, String name){
        User user = new User();
        user.setId(id);
        user.setName(name);
        user.setAccount("12345678911");
        user.setPassword("******");
        user.setOtherInfo("this is a test account");
        return user;
    }
}
//Userservice class
@Service("userService")
public class UserServiceImpl implements UserService {

    private UserDao userDao;
   
   public UserServiceImpl(@Autowired UserDao userDao,
                          @Autowired User user) {
       System.out.println("UserServiceImpl: "+user);
       this.userDao = userDao;
   }
    
    @Override
    public User getUser(Long id, String name){
        return userDao.getUser(id,name);
    }
}
//Simple configuration class
//The function is to generate bean for the class marked with @ componet (@ service) annotation
//At the same time, the instance is injected into the bean (object) identified by @ Autowired
@Configuration
@ComponentScan
public class DIConfig {
    
    //Used for injection of user in service class
    @Bean
    public User getUser(){
        User u = new User();
        u.setName("user inject into service");
        return u;
    }
}
//Test class
//Note: when testing with junit4, if you need to use @ Autowired injection, you must add
//@Runwith annotation starts in spring mode (or springbootrunner)
//@Contextconfiguration scan configuration class
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = DIConfig.class)
public class DITest {

    //If you do not add two annotations on the test class, the injection fails
    @Autowired
    private UserService userService;

    @Test
    public void testAutowired(){ System.out.println(userService.getUser(1L,"name"));
    }
}

After running the test method, we get the following results:

public UserServiceImpl(@Autowired UserDao userDao,@Autowired User user) The output results in are as follows:

di-test1.png

public void testAutowired()The output results of the test method are as follows:

di-test2.png

Pay attention herepublic UserServiceImpl(@Autowired UserDao userDao,@Autowired User user)In this paper, we introduce a new method

userDaoyesUserServiceImplField, butuserno In other words, we can add any parameter to the construction method. As long as it is needed, it is not necessarily required that the parameter is a property field in the class.

In addition, it should be noted that the method mentioned here is not an arbitrary method, but a construction method or setter methodpublic void initService(@Autowired UserDao userDao)Custom methods cannot be injected.

@Primary and @ qualifier

In the above example, all the beans we inject are only one bean instance in the container. Then, when there are multiple objects of the same type in the containerBeanHow to deal with it?

Modify the configuration class code as follows:

@Configuration
@ComponentScan
public class DIConfig {

    @Bean
    public User getUser(){
        User u = new User();
        u.setName("this is user");
        return u;
    }

    @Bean
    public User getUser2(){
        User u = new User();
        u.setName("this is user2");
        return u;
    }
}

Modify test class:

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = DIConfig.class)
public class DITest {
    
    @Autowired
    private User user;

    @Test
    public void testAutowiredPriamry(){
        System.out.println(user);
    }
}

When no other treatment is done, the result is as follows

di-test-priority.png

Because there are two user beans(getUser , getUser2If @ bean is not specified, the default method name is bean name, so spring cannot determine which one to use for injection.

Modification method:

  • stay@BeanSet name in, such as@Bean(name="user")When the name matchesprivate User user;The injection can also be completed.
  • takeprivate User userRewrite asgetUserorgetUser2Any one can also be injected. The reason is the same as above. Spring will first match according to the type. If it can’t match, then it will match according to the name. If it can’t match, it will naturally throw an exception.

In addition, spring provides us with two annotations to eliminate the ambiguity of dependency injection.

  • @Primary
@Target({ ElementType.TYPE , // class, interface, enumeration type
         ElementType.METHOD }) // method
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Primary {
}

@PrimaryIt is an annotation that sets the priority of the same type bean, that is, once it is added to a type@PriamryWhen the bean is not specified, the bean will be injected@PriamryThe bean of the identity.

@Configuration
@ComponentScan
public class DIConfig {

    @Primary
    @Bean
    public User getUser(){
        User u = new User();
        u.setName("this is user");
        return u;
    }

    @Bean
    public User getUser2(){
        User u = new User();
        u.setName("this is user2");
        return u;
    }
}

Like the above, in thegetUser()The test method can also run normally by adding the corresponding annotation on the.

But the problem with this approach is that@PriamryIt can be used in many classes, if multiple beans of the same type are labeled@PrimarySo@PriamryIt will lose its due effect.

  • @Qualifier

Therefore, spring provides@QualifierThis annotation is marked directly in the@AutowiredFor the injected bean, a bean is explicitly specified to be injected.

@Target({ElementType.FIELD, 
         ElementType.METHOD, 
         ElementType.PARAMETER, 
         ElementType.TYPE, 
         ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Qualifier {
    String value() default "";
}

@QualifierYou can see anything@AutowiredWhere it can appear, it can be used with it. For example:

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = DIConfig.class)
public class DITest { 

    //Directly specify getuser2 for injection
    @Autowired
    @Qualifier("getUser2")
    private User user;

    @Test
    public void testAutowiredPriamry(){
        System.out.println(user);
    }
}

Both annotations are disambiguated and are recommended@Bean (name = XXX) and @ qualifier (value = XXX)How to use it in combination. However, if there is no ambiguity in the development environment, it is unnecessary to use them.

Of course, it’s just for@AutowiredFor more information, you can check the annotation based container configuration. This reference document has a more detailed and rich introduction.

summary

In general, how does spring implement IOC? First, spring provides an IOC container for getting and managing beans. Then, a set of dependency injection mechanism is provided to help IOC container better manage the dependency relationship among beans, so as to better realize the idea of IOC. It is impossible for a bean to exist independently from the dependency relationship of other beans. When a bean needs the introduction of other beans to initialize, it needs the dependency injection mechanism.

For example, if there is a class a method that wants to call the B interface, or an instance of the B interface is needed.

The traditional program flow is to use a C class to implement B interface, and then create an instance of C class to call its method.

In the process of spring’s dependency injection, class a only needs to add an injection interface (in a broad sense, not an interface)interfaceThis interface can be a constructor method, setter method or other forms. At the same time, add a reference to B interface(private B b;)。

When you really need to generate class a instances, the spring IOC container injects the corresponding bean according to the interface provided by class A, which can be class C(class C implements B{}), or class D(class D implements B{} and so on; the specific person is determined according to the bean assembly strategy and the bean in the IOC container, and is no longer managed by the developer.


Official account: good Linux

How spring IOC implements dependency injection

What’s the gain? I hope the old fellow will take three strikes to show this article to more people.

Recommended Today

Tutorial on configuring OpenSSL in Linux LEMP environment

Today, I’d like to share with you the process of solving the problem of opening OpenSSL function under the environment of source package compilation and installation LEMP. A few days ago, when visiting the page, I suddenly reported an error and asked to turn on the OpenSSL function. What should I do? I first thought […]