Why Spring? This dependency injection method must be used~

foreword

​Spring, as its name suggests, brings spring to developers. Spring is a framework designed to solve the complexity of enterprise-level application development. Its design philosophy is to simplify development.

The core idea in the Spring framework is:

  • IOC (Inversion of Control): That is, the transfer of control of the created object transfers the control of the created object from the developer to the Spring framework.
  • AOP (Aspect Programming): Encapsulate public behaviors (such as logging, permission verification, etc.) into reusable modules, so that the original module only needs to focus on its own personalized behavior.

This article will mainly introduce the dependency injection of IOC in Spring,

Inversion of Control IOC

As far as the IOC itself is concerned, it is not a new technology, but an idea. The core of IOC is to create an object originally. We need to create it directly through new. IOC is equivalent to someone helping people create an object. When you need to use it, you can just take it directly. There are two main ways of implementing IOC:

  • DL (Dependency Lookup): Dependency Lookup.

This means that the container helps us create objects, and when we need to use them, we will actively search for them in the container, such as:

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/application-context.xml");
Object bean = applicationContext.getBean("object");

复制代码
  • DI (Dependency Inject): Dependency Injection.

Dependency injection is an optimization compared to dependency search, that is, we don't need to search by ourselves, just tell the container the object that needs to be injected, and the container will automatically inject (assign) the created object.

Dependency Injection DI

We will not discuss the injection method through xml. Here we mainly discuss the injection method based on annotations. There are usually three types of conventional injection methods based on annotations:

  • property injection
  • Injection based on setter method
  • Constructor-based injection

Three conventional injection methods

Next, let us introduce the three conventional injection methods respectively.

property injection

The method of property injection is very common. This should be a method that everyone is familiar with:

@Service
public class UserService {
    @Autowired
    private Wolf1Bean wolf1Bean;//通过属性注入
}

复制代码

setter method injection

In addition to injection through properties, injection can also be achieved through setter methods:

@Service
public class UserService {
    private Wolf3Bean wolf3Bean;
    
    @Autowired  //通过setter方法实现注入
    public void setWolf3Bean(Wolf3Bean wolf3Bean) {
        this.wolf3Bean = wolf3Bean;
    }
}

复制代码

Constructor injection

When two classes are strongly associated, we can also implement injection through the constructor:

@Service
public class UserService {
  private Wolf2Bean wolf2Bean;
    
     @Autowired //通过构造器注入
    public UserService(Wolf2Bean wolf2Bean) {
        this.wolf2Bean = wolf2Bean;
    }
}

复制代码

interface injection

In the above three conventional injection methods, if we want to inject an interface, and the current interface has multiple implementation classes, then an error will be reported at this time, because Spring cannot know which implementation class should be injected.

For example, the three classes above all implement the same interface IWolf, then directly use the conventional injection method without any annotation metadata to inject the interface IWolf.

@Autowired
private IWolf iWolf;

复制代码

At this point, starting the service will report an error:

This means that there should be one class injected, but Spring found three, so it is impossible to determine which one should be used. How to solve this problem?

There are five main solutions:

Implemented via configuration file and @ConditionalOnProperty annotation

The configuration file can be combined to achieve unique injection through the @ConditionalOnProperty annotation. The following example means that if lonely.wolf=test1 is configured in the configuration file, then Wolf1Bean will be initialized to the container. At this time, because other implementation classes do not meet the conditions, they will not be initialized to the IOC container, so the interface can be injected normally. :

@Component
@ConditionalOnProperty(name = "lonely.wolf",havingValue = "test1")
public class Wolf1Bean implements IWolf{
}

复制代码

Of course, with this configuration method, the compiler may still prompt that there are multiple beans, but as long as we ensure that the conditions of each implementation class are inconsistent, it can be used normally.

Annotated with other @Condition conditions

In addition to the above configuration file conditions, other similar conditions can also be annotated, such as:

  • @ConditionalOnBean: When a Bean exists, initialize this class to the container.
  • @ConditionalOnClass: When a class exists, initialize the container of this class.
  • @ConditionalOnMissingBean: When a bean does not exist, initialize this class to the container.
  • @ConditionalOnMissingClass: When a class does not exist, initialize this class to the container.

Similar to this implementation, dynamic configuration can also be implemented very flexibly.

However, the methods described above seem to be able to inject only one implementation class at a time. If we just want to inject multiple classes at the same time, different scenarios can be dynamically switched without restarting or modifying the configuration file. How to implement it? ?

Dynamically obtained through the @Resource annotation

If you don't want to get it manually, we can also get it by dynamically specifying the BeanName in the form of the @Resource annotation:

@Component
public class InterfaceInject {
    @Resource(name = "wolf1Bean")
    private IWolf iWolf;
}

复制代码

As shown above, only the implementation class whose BeanName is wolf1Bean will be injected.

Injection through collections

In addition to specifying Bean injection, we can also inject all implementation classes of the interface at one time by means of a collection:

@Component
public class InterfaceInject {
    @Autowired
    List<IWolf> list;

    @Autowired
    private Map<String,IWolf> map;
}

复制代码

Both forms above will inject all the implementing classes in IWolf into the collection. If the List collection is used, then we can take it out and use the instanceof keyword to determine the type; and through the Map collection injection, Spring will store the Bean name (the default class name in lowercase) as the key, so we can You can dynamically get the implementation class you want when you need it.

@Primary annotation implements default injection

In addition to the above methods, we can also add the @Primary annotation to one of the implementation classes to indicate that when multiple beans meet the conditions, the bean currently annotated with @Primary will be injected first:

@Component
@Primary
public class Wolf1Bean implements IWolf{
}

复制代码

In this way, Spring will inject wolf1Bean by default, while at the same time we can still manually obtain other implementation classes through the context, because other implementation classes also exist in the container.

Several ways to obtain beans manually

In a Spring project, to obtain beans manually, you need to use the ApplicationContext object. At this time, you can obtain them in the following five ways:

direct injection

The easiest way is to obtain the ApplicationContext object through direct injection, and then you can obtain the Bean through the ApplicationContext object:

@Component
public class InterfaceInject {
    @Autowired
    private ApplicationContext applicationContext;//注入

    public Object getBean(){
        return applicationContext.getBean("wolf1Bean");//获取bean
    }
}

复制代码

Obtained through the ApplicationContextAware interface

Obtain the ApplicationContext object by implementing the ApplicationContextAware interface, thereby obtaining the Bean. It should be noted that the class implementing the ApplicationContextAware interface also needs to be annotated so that it can be handed over to Spring for unified management (this method is also a method used more in the project):

@Component
public class SpringContextUtil implements ApplicationContextAware {
    private static ApplicationContext applicationContext = null;

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

    /**
     * 通过名称获取bean
     */
    public static <T>T getBeanByName(String beanName){
        return (T) applicationContext.getBean(beanName);
    }

    /**
     * 通过类型获取bean
     */
    public static <T>T getBeanByType(Class<T> clazz){
        return (T) applicationContext.getBean(clazz);
    }
}

复制代码

After encapsulation, we can directly call the corresponding method to get the Bean:

Wolf2Bean wolf2Bean = SpringContextUtil.getBeanByName("wolf2Bean");
Wolf3Bean wolf3Bean = SpringContextUtil.getBeanByType(Wolf3Bean.class);

复制代码

Obtained through ApplicationObjectSupport and WebApplicationObjectSupport

Among these two objects, WebApplicationObjectSupport inherits ApplicationObjectSupport, so there is no substantial difference.

Similarly, the following tool class also needs to be annotated for unified management by Spring:

@Component
public class SpringUtil extends /*WebApplicationObjectSupport*/ ApplicationObjectSupport {
    private static ApplicationContext applicationContext = null;

    public static <T>T getBean(String beanName){
        return (T) applicationContext.getBean(beanName);
    }

    @PostConstruct
    public void init(){
        applicationContext = super.getApplicationContext();
    }
}

复制代码

With the tool class, it can be called directly in the method:

@RestController
@RequestMapping("/hello")
@Qualifier
public class HelloController {
    @GetMapping("/bean3")
    public Object getBean3(){
        Wolf1Bean wolf1Bean = SpringUtil.getBean("wolf1Bean");
        return wolf1Bean.toString();
    }
}

复制代码

Get through HttpServletRequest

Through the HttpServletRequest object, combined with the tool class WebApplicationContextUtils provided by Spring itself, the ApplicationContext object can also be obtained, and the HttpServletRequest object can be obtained actively (the following getBean2 method) or passively (the following getBean1 method):

@RestController
@RequestMapping("/hello")
@Qualifier
public class HelloController {

    @GetMapping("/bean1")
    public Object getBean1(HttpServletRequest request){
        //直接通过方法中的HttpServletRequest对象
        ApplicationContext applicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(request.getServletContext());
        Wolf1Bean wolf1Bean = (Wolf1Bean)applicationContext.getBean("wolf1Bean");

        return wolf1Bean.toString();
    }

    @GetMapping("/bean2")
    public Object getBean2(){
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();//手动获取request对象
        ApplicationContext applicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(request.getServletContext());

        Wolf2Bean wolf2Bean = (Wolf2Bean)applicationContext.getBean("wolf2Bean");
        return wolf2Bean.toString();
    }
}

复制代码

Obtained by other means

Of course, in addition to the methods mentioned above, we can also use the code example in the DL mentioned at the beginning to manually new an ApplicationContext object, but this means re-initialization once, so it is not recommended to do this, but when writing This method is more suitable for unit testing.

Talk about the difference between @Autowrite and @Resource and @Qualifier annotations

As we have seen above, injecting a bean can be injected through @Autowrite or @Resource annotation. What is the difference between these two annotations?

  • @Autowrite: Through type injection, it can be used for constructor and parameter injection. When we inject an interface, all its implementation classes belong to the same type, so there is no way to know which implementation class to choose to inject.
  • @Resource: By default, it is injected by name and cannot be used for constructor and parameter injection. If a unique bean cannot be found by name, it will be looked up by type. A unique implementation can be determined by specifying a name or type as follows:
@Resource(name = "wolf2Bean",type = Wolf2Bean.class)
 private IWolf iWolf;

复制代码

The @Qualifier annotation is used to identify qualifiers. When @Autowrite and @Qualifier are used together, it is equivalent to identifying uniqueness by name:

@Qualifier("wolf1Bean")
@Autowired
private IWolf iWolf;

复制代码

Then some people may say, I just use @Resource directly, why is it so troublesome to combine two annotations, so it seems that the @Qualifier annotation is a bit redundant?

Is the @Qualifier annotation redundant?

Let's first look at the following scenario of declaring a bean. Here, a method is used to declare a bean (MyElement), and the parameters in the method have a Wolf1Bean object, then Spring will automatically inject Wolf1Bean for us at this time:

@Component
public class InterfaceInject2 {
    @Bean
    public MyElement test(Wolf1Bean wolf1Bean){
        return new MyElement();
    }
}

复制代码

However, if we change the above code a little bit, change the parameter to an interface, and the interface has multiple implementation classes, then an error will be reported:

@Component
public class InterfaceInject2 {
    @Bean
    public MyElement test(IWolf iWolf){//此时因为IWolf接口有多个实现类,会报错
        return new MyElement();
    }
}

复制代码

The @Resource annotation cannot be used in parameters, so at this time, you need to use the @Qualifier annotation to confirm the only implementation (for example, when configuring multiple data sources, the @Qualifier annotation is often used to implement):

@Component
public class InterfaceInject2 {
    @Bean
    public MyElement test(@Qualifier("wolf1Bean") IWolf iWolf){
        return new MyElement();
    }
}

复制代码

Summarize

This article mainly describes how to use a flexible way to implement the injection method of various scenarios in Spring, and focuses on how to inject when an interface has multiple implementation classes. Finally, it also introduces several commonly used injection annotations. The difference, through this article, I believe you will be more familiar with how to use dependency injection in Spring.

Author: public account_IT brother

Link: https://juejin.cn/post/7069564336468918280

Source: The copyright of rare earth nuggets belongs to the author. For commercial reprints, please contact the author for authorization, and for non-commercial reprints, please indicate the source.

Guess you like

Origin blog.csdn.net/wdjnb/article/details/124450945