[SpringUtil] 2. In-depth understanding

1. Nonsense

After understanding the basic content of SpringUtil, I still have a lot of doubts about SpringUtil, so I will answer them one by one in the form of questions here.
PS: It is mainly my personal understanding, if there is any problem, please correct me.

Two, SpringUtil

For the convenience of viewing the code, put the same SpringUtil code here.

@Component
public class SpringUtil implements ApplicationContextAware {
    
    
    /** 静态成员*/
    private static ApplicationContext context;

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

    public static <T> T getBean(Class<T> beanClass) {
    
    
        return context.getBean(beanClass);
    }
}

Three, the problem

Here we will take the introduction of the ShiroUtil class into RedisSessionDAO as an example, and mainly take static static members as an example.

1. When do you need to use SpringUtil?

When you need to use the Bean in the Spring container in the Bean that is not managed by the Spring container, you can use the SpringUtil class to obtain the Bean object managed by Spring.
[Suppose there is a non-Spring-managed tool class (Bean, such as ShiroUtil) that needs to use some objects in the Spring container (for example, RedisSessionDAO).
insert image description here
Because it is not within the scope of Spring management, we cannot use the @Autowired annotation to inject objects,
so we first need to use @Configuration and @Bean in ShiroConfig to register RedisSessionDAO in the Spring container.

@Configuration
public class ShiroConfig {
    
    

	@Bean
    public SecurityManager securityManager() {
    
    
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 自定义Ssession管理
        securityManager.setSessionManager(sessionManager());
        // 自定义Cache实现
        securityManager.setCacheManager(cacheManager());
        // 自定义Realm验证
        securityManager.setRealm(shiroRealm());
        return securityManager;
    }
	@Bean
    public RedisSessionDAO redisSessionDAO() {
    
    
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        return redisSessionDAO;
    }
	@Bean
    public SessionManager sessionManager() {
    
    
        ShiroSessionManager shiroSessionManager = new ShiroSessionManager();
        shiroSessionManager.setSessionDAO(redisSessionDAO());
        return shiroSessionManager;
    }
	···
}

Obtain the object through reflection, and then call the properties and methods inside
[ the imported jar package needs to be registered in the Spring container first, and then you can directly use SpringUtil to obtain the object ]

public class ShiroUtils {
    
    
	/** 采用SpringUtil静态成员方法**/
	private static RedisSessionDAO redisSessionDAO = SpringUtil.getBean(RedisSessionDAO.class);

	private static ...{
    
    }
	···
}

Supplement: The reason for using static here is that the methods of ShiroUtil here are all static methods. The purpose is to directly use the class .
[SpringUtil] 1. Basic content

Supplement 2:
The same point: the result of both is to register the Bean for the spring container.
The difference:
@Component is usually automatically detected and automatically assembled into the Spring container through class path scanning.
The @Bean annotation is usually the logic that we define to generate this bean in the method marked with this annotation.
The difference between @Component and @Bean article 1
The difference between @Component and @Bean article 2

2. In addition to using the getBean method rewritten by SpringUtil, can you use @Autowired to get the object?

Can
① Static member static [ mainly used ]

@Component
public class ShiroUtils {
    
    
	/** set方法依赖注入**/
    private static RedisSessionDAO redisSessionDAO;
    @Autowired
    public void setRedisSessionDAO(RedisSessionDAO redisSessionDao){
    
    
        redisSessionDAO = redisSessionDao;
    }

	···
}
  1. Because RedisSeeionDAO is not part of Spring container management, you need to use @Component to introduce ShiroUtil into Spring container management.
  2. Since the static member variable (private static RedisSessionDAO redisSessionDAO) is used here, @Autowired cannot be directly added to the property, and dependency injection needs to be performed by adding @Autowired to the set method.

②Non-static member [ not recommended ]
Because it has not been tested, there is a high probability that there are some loading pits in it in use! ! !

@Component
public class ShiroUtils {
    
    
	/** @Autowired依赖注入**/
    @Autowired
    private RedisSessionDAO redisSessionDAO;
	···
}

Note:
1. After using non-static members, none of the following methods can use static methods.
Non-static variables and methods cannot be accessed in static methods ; non-static methods can access static and non-static variables and methods
2. When other classes call ShiroUtil of this non-static member RedieSessionDAO, they cannot directly use the class . method , and need to create an object to use .
[SpringUtil] 1. Basic content

3. Why can't @Autowired be added to static static member variables? And need to add @Autowired to the set method for dependency injection?

①Why can't it be added?

1. The problem of initialization order: Static member variables belong to the class level, and they are initialized and assigned when the class is loaded, while the spring container only starts working at runtime. If a static member variable is automatically assembled by the @Autowired annotation, it has not been initialized when it is used, and the static member variable does not know what to assign, which will cause a NullPointerException.
2. The management scope of the Spring container:Static member variables are class- level,andThe Spring container manages the object level, that is, the objects that the Spring container can manage are all instantiated class objects, not the class itself. Therefore, if @Autowired is used for static member variables, it is not actually managed by the container, which is inconsistent with the original intention of the Spring framework.
3. To sum up, @Autowired cannot act on static member variables, because the initialization order of static member variables is before the Spring container, and the Spring container can only manage instantiated objects. Therefore, it is recommended to use the @Autowired annotation on instance variables.

②Why add it to the set method?

1. Static member variables do not depend on the instantiation of objects , so dependency injection cannot be performed by passing objects.
2. Static member variables depend on the definition and loading of classes . To achieve dependency injection of static member variables, you need to set the set method and add @Autowired to achieve it through reflection.
[When the Spring container instantiates ShiroUtil, it will automatically inject the RedieSessionDAO instance and assign it to the static member variable redisSessionDAO through the set method] 3. When the
Spring container is initialized, it will scan the @Autowired annotation, and then the Spirng framework will look for the annotated class Object, through the reflection mechanism, obtains the instance of this type of object, and assigns it to the variable that needs to be injected.

Supplement: Reflection is mainly to obtain the object of the class. Obtaining its instance through the class, in the running of the program, the order is prior to new (class loading precedes object instantiation)

4. Why does SpringUtil need to set static static ApplicationContext?

Purely personal understanding, please correct me if I have any questions.
Because SpringUtil needs to set the getBean method, the purpose is to obtain the instance of the object through the class like reflection, and it is convenient, so it needs to be completed when the class is loaded in order to use static.

5. In addition to the methods mentioned in question 2, can static member variables be directly instantiated to classes or use SpringUtil non-static member methods to obtain objects?

It is not possible
to instantiate the class directly

public class ShiroUtils {
    
    
	/** 直接实例化   不行,没有进行配置 会为null**/
    private static RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
	···
}

Instantiating static member variables directly instead of letting Spring inject them may cause other dependencies to fail to be set.
If it is a class that does not need to be configured, it can be instantiated directly.

②Adopt SpringUtil non-static member method
Here, SpringUtil needs to use non-static method

@Component
public class SpringUtil implements ApplicationContextAware {
    
    
    /** 非静态成员*/
    private ApplicationContext context;

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

    public <T> T getBean(Class<T> beanClass) {
    
    
        return context.getBean(beanClass);
    }
}

Because the SpringUtil used here is non-static, it cannot directly class. Method , so using getBean here needs to instantiate SpringUtil.

public class ShiroUtils {
    
    
	/** 采用SpringUtil非静态成员方法     不行 会为null**/
  private static RedisSessionDAO redisSessionDAO = new SpringUtil().getBean(RedisSessionDAO.class);
	···
}
  1. Because the static member is initialized when the class is loaded , the static member variable is assigned new here. This whole sentence of code should be completed when the class is loaded. However, RedisSessionDAO has not been registered by @Configuration and @Bean in ShiroConfig; in addition, When the class is loaded, SpringUtil cannot be instantiated yet, so static members cannot directly instantiate the class.
  2. Static member variables are at the class level, while Spring container management is at the object level; static member variables are initialized before the instantiation of the Spring container, which means that the ApplicationContext has not been loaded and cannot be instantiated.

6. Why can't the ApplicationContext be instantiated before it is loaded?


The Spring project will start after the class is
loaded These configuration files define which components the Spring container should include, how to perform dependency injection, and so on.
2. Create and initialize ApplicationContext : When the Spring configuration file is loaded, Spring will create and initialize the ApplicationContext instance according to the definition in it. This process will perform some preprocessing operations, such as parsing placeholders, parsing attributes, and so on.
3. Create and initialize Bean : After the ApplicationContext is created and initialized, Spring will create each Bean (also called instantiated Bean) according to the definition of the configuration file , and inject it into the container. In this process, Spring will sort these beans according to the dependencies to ensure that the dependencies are injected correctly. [Can be seen in the log INFO]
4. Expose ApplicationContext to the external environment : After the ApplicationContext initialization is completed, Spring will expose it to the external environment so that other components can use it.
[This place is the setApplicationContext method we saw]

The main reason:
the bean is not managed by the container, and the relevant dependencies are not injected. As a result, a bean is an incomplete instance and cannot work normally, and a null pointer exception will occur.

The detailed reasons may be:
1. When instantiating, the ApplicationContext has not been loaded (the Spring context has not been initialized), which will cause Spring to not scan the package, or the construction and dependency injection have not been completed. [Because the static member variable is initialized when the class is loaded, the bean has not been created/instantiated, so the above reasons will occur]
2. Directly instantiate the static member variable instead of letting Spring inject it, which may cause other dependencies to fail to be set.

7. What exactly is a bean?

Official explanation:
In Spring, the objects that form the backbone of the application and are managed by the Spring IoC container are called beans. A bean is an object instantiated, assembled and managed by the Spring IoC container.
Explanation of Bean in Spring

Summary:
Bean is a term in Spring that refers to an instantiated object managed by the Spring IOC container , which can be a POJO (pure Java object), a business class, a data access object, a service object, and so on. Bean is an object .

Replenish:

  1. Instantiate Bean: Bean created by the Spring container is an object in the Spring container, that is, according to the class new object, it exists in the Spring container, and it can be directly called out if it is needed, and it does not need to be in the new object.
  2. Instantiate static member variables directly: directly (private static a = new class).

8. Add @Component to the SpringUtil class, how does the setApplicationContext (ApplicationContext applicationContext) in it realize the dependency injection of the applicationContext?

After the SpringUtil class is annotated with @Component, this class will be scanned by the Spring container and added to the container. At the same time, Spring will also create an instance object for it and bring its object into the management of the container.
When the Spring container is initialized, it will automatically call the setApplicationContext method of @Override, and set the ApplicationContext instance object in the current container.

Explain: the ApplicationContext instance object in the current container
When the class being instantiated is the Aware type and the ApplicationContextAware type, the setApplicationContext(this.applicationContext) method will be called to set the spring context. That is, the ApplicationContextAware interface is implemented.
ps: I haven't read this part in detail, I just know the ultimate purpose of this.
The setApplicationContext method call process of the ApplicationContextAware interface

insert image description here

9. Why can SpringUtil add @Component and @Override to the set method to automatically inject ApplicationContext?

The setApplicationContext method in SpringUtil can also rely on injection without @Autowired.
Because when the Spring container starts, it will scan all @Component annotations, instantiate them and add them to the BeanFactory of the container for management. At the same time, Spring will inject the ApplicationContext instance of the container itself into the Bean that implements the ApplicationContextAware interface (that is, SpringUtil). Therefore, Spring can automatically call the setApplicationContext method during initialization and pass in the ApplicationContext instance of the current container.

In general, SpringUtil injects the ApplicationContext object into the setApplicationContext method of the SpringUtil object through the ApplicationContextAware interface , and completes dependency injection.

10. Why some classes can be directly injected with @Autowired, and some need to add @Autowired to the set method for injection?

Mainly the difference between non-static and static.

  1. Non-static member variables:
    can be injected directly using @Autowired.
    When the Spring container is instantiated, the Bean (object in Spring) will be instantiated for the loaded class; through the @Autowired
    annotation, the Bean object is obtained from the Spring container and injected into the variable.
  2. Static member variables:
    initialization is done when the class is loaded, prior to the instantiation of the Spring container, so @Autowired cannot get the Bean from the container when the static member variables are initialized.
    First define static member variables, then you need to add objects not managed by the Spring container to the Spring container, then write the setter method and add @Autowired to the method.
    insert image description here

11. Why does Spring inject the ApplicationContext instance of the container itself into the Bean that implements the ApplicationContextAware interface?

Somewhat similar to question 9, this one explains in more detail.

The Spring container will inject the ApplicationContext instance of the container itself into the Bean that implements the ApplicationContextAware interface, because ApplicationContextAware is an interface provided by Spring, which is used to provide a reference to the ApplicationContext instance to the Bean object after the Bean is instantiated, so that the Bean can access The function of the Spring container itself. In this way, beans can access other beans in the Spring container in their own code, as well as other services provided by Spring, such as configuration files, resource files, and so on.

In fact, ApplicationContextAware is a Marker interface, which does not define any methods. By implementing this interface, the Bean does not register any callback function or event listener with the Spring container, but only indicates to the Spring container that this Bean has a certain dependence on the Spring container and needs the Spring container to inject an ApplicationContext instance. When the Spring container initializes the bean, if it finds that the bean implements the ApplicationContextAware interface, it will call the bean's setApplicationContext method to inject the ApplicationContext instance into the bean to meet the bean's dependency requirements.

Guess you like

Origin blog.csdn.net/weixin_42516475/article/details/130188299