Spring Solution for Static Variable Injection

  • 2021-10-24 22:59:04
  • OfStack

Spring cannot inject static variables

Problem

Today, in the process of learning, I want to write an JDBCUtils tool class bound by connection and thread, but I found that 1 directly reported null pointer exception during the test. After checking on the Internet, Spring does not support injecting static member variables, so it is definitely impossible to try @ Autowired alone.

However, when we write tool classes, we must use static variables and methods. I summarize that I have used methods that can inject static member variables.


@Component
public class JDBCUtils {
    @Autowired
    private static ComboPooledDataSource dataSource;
    private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
    public static Connection getThreadConnection(){
        Connection conn = tl.get();
        if (conn == null){
            conn = getConnection();
            tl.set(conn);
        }
        return conn;
    }
    public static DataSource getDataSource(){
        return dataSource;
    }
    public static Connection getConnection(){
        Connection connection = null;
        try {
            connection = dataSource.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return connection;
    }
    public static void removeThreadConnection(){
        tl.remove();
    }
}

set method injection

Annotation mode

Add @ Component annotation before the class and @ Autowired annotation on the set method. Note two points here

1. The parameters of the variable are already configured in the configuration file

2. When static variables automatically generate set methods, there will be static modification, which should be removed, otherwise it will still not be injected


@Component
public class JDBCUtils {
    private static ComboPooledDataSource dataSource;
    @Autowired
    public void setDataSource(ComboPooledDataSource dataSource) {
        JDBCUtils.dataSource = dataSource;
    }
xml mode

Also pay attention to removing static from set method


public class JDBCUtils {
    private static   ComboPooledDataSource dataSource;
    public void setDataSource(ComboPooledDataSource dataSource) {
        this.dataSource = dataSource;
    }
    private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
    public static Connection getThreadConnection(){
        Connection conn = tl.get();
        if (conn == null){
            conn = getConnection();
            tl.set(conn);
        }
        return conn;
    }
    public static DataSource getDataSource(){
        return dataSource;
    }
    public static Connection getConnection(){
        Connection connection = null;
        try {
            connection = dataSource.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return connection;
    }
    public static void removeThreadConnection(){
        tl.remove();
    }
}

   <bean id="JDBCUtils" class="com.cc.utils.JDBCUtils">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

@ PostConstruct annotation mode injection

Append @ PostConstruct to the init method, which is executed after class initialization to assign values to member variables. Before we do this, we'll revamp the 1 tool class to remove the static modifier that we want to inject into the variable, so we can inject it with @ Autowired.

Then add a static reference object to the class itself, which we can get when we want variables.


@Component
public class JDBCUtils {
    @Autowired
    private  ComboPooledDataSource dataSource;
    private static JDBCUtils jdbcUtils;
    @PostConstruct
    public void init(){
        jdbcUtils = this;
        this.dataSource = dataSource;
    }
    private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
    public static Connection getThreadConnection(){
        Connection conn = tl.get();
        if (conn == null){
            conn = getConnection();
            tl.set(conn);
        }
        return conn;
    }
    public static DataSource getDataSource(){
        return jdbcUtils.dataSource;
    }
    public static Connection getConnection(){
        Connection connection = null;
        try {
            connection = jdbcUtils.dataSource.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return connection;
    }
    public static void removeThreadConnection(){
        tl.remove();
    }
}

Of course, this initialization method can also be configured with xml, and the principle is 1.


public class JDBCUtils {
    private  ComboPooledDataSource dataSource;
    public void setDataSource(ComboPooledDataSource dataSource) {
        this.dataSource = dataSource;
    }
    private static JDBCUtils jdbcUtils;
    public void init(){
        jdbcUtils = this;
        this.dataSource = dataSource;
    }
    private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
    public static Connection getThreadConnection(){
        Connection conn = tl.get();
        if (conn == null){
            conn = getConnection();
            tl.set(conn);
        }
        return conn;
    }
    public static DataSource getDataSource(){
        return jdbcUtils.dataSource;
    }
    public static Connection getConnection(){
        Connection connection = null;
        try {
            connection = jdbcUtils.dataSource.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return connection;
    }
    public static void removeThreadConnection(){
        tl.remove();
    }
}

<bean id="JDBCUtils" class="com.cc.utils.JDBCUtils" init-method="init">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

Failure reason of static method injection bean

Today, when writing a tool class of redission, write the following code conveniently


package com.wt.redission.wtredission.utils;  
import org.redisson.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; 
import javax.annotation.PostConstruct; 
@Component
public class RedissionUtilserror { 
    @Autowired
   private static RedissonClient redissonClient; 
 
    public static RLock getRLock(String objectName) {
        RLock rLock =redissonClient.getLock(objectName);
        return rLock;
    } 
 
    // Get by name map
    public static  <K, V> RMap<K, V> getRMap(String objectName) {
        RMap<K, V> map = redissonClient.getMap(objectName);
        return map;
    }
 
    // Set by name and value map
    public static void setMap(String objectName,Object key,Object value){
        RMap<Object, Object> map =redissonClient.getMap(objectName);
        map.put(key,value);
    } 
 
    // Get by name set
    public static <V> RSet<V> getSet(String objectName) {
        RSet<V> set = redissonClient.getSet(objectName);
        return set;
    }
 
    // Set by name and value set
    public static void setSet(String objectName,Object value){
        RSet<Object> set = redissonClient.getSet(objectName);
        set.add(value);
    }
 
    // Get by name list
    public static  <V> RList<V> getRList(String objectName) {
        RList<V> rList = redissonClient.getList(objectName);
        return rList;
    } 
 
    // Set by name and value list
    public static void setList(String objectName, int  index,Object element ){
        RList<Object> objectRList = redissonClient.getList(objectName);
        objectRList.set(index,element);
    }
 
    // Get by name bucket
    public static <T> RBucket<T> getRBucket(String objectName) {
        RBucket<T> bucket = redissonClient.getBucket(objectName);
        return bucket;
    }
 
    // By name and value   Set the corresponding bucket
    public static  <T> T setBucket(String objectName,String value){
        RBucket<Object> bucket = redissonClient.getBucket(objectName);
        bucket.set(value);
        T t= (T) bucket.get(); // The value type is determined by the return value 
        return  t;
    } 
}

At first glance, it seems that there is no problem. I write a static method and then use the static variable redissonClient in the method. Wow, 1 cut looks so normal

When I started the test, NPE

Finally, it was found that the foundation was not firm..........., and the class loading mechanism of jvm was almost not considered. Briefly, the reason for the error was

jvm in the class loading, first will load class variables, class methods, which I modified by static method, and then when I call static methods for use, will use redissionClient, note that this redissionClient is through autowired in, the key problem is here, the bottom of autowired is through the constructor and set method into bean

redissionClient is decorated with static and is still an interface that is definitely not instantiated when called

Here are three ways to use it correctly

Mode 1


package com.wt.redission.wtredission.utils;  
import org.redisson.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; 
import java.util.List; 
@Component
public class RedissionUtils { 
    private static RedissonClient redissonClient; 
    @Autowired
    public  RedissionUtils(RedissonClient redissonClient){
        RedissionUtils.redissonClient=redissonClient;
    }  
 
    public static RLock getRLock(String objectName) {
        RLock rLock = redissonClient.getLock(objectName);
        return rLock;
    } 
 
    // Get by name map
    public static  <K, V> RMap<K, V> getRMap(String objectName) {
        RMap<K, V> map = redissonClient.getMap(objectName);
        return map;
    }
 
    // Set by name and value map
    public static void setMap(String objectName,Object key,Object value){
        RMap<Object, Object> map =redissonClient.getMap(objectName);
        map.put(key,value);
    } 
 
    // Get by name set
    public static <V> RSet<V> getSet(String objectName) {
        RSet<V> set = redissonClient.getSet(objectName);
        return set;
    }
 
    // Set by name and value set
    public static void setSet(String objectName,Object value){
        RSet<Object> set = redissonClient.getSet(objectName);
        set.add(value);
    }
 
    // Get by name list
    public static  <V> RList<V> getRList(String objectName) {
        RList<V> rList = redissonClient.getList(objectName);
        return rList;
    } 
 
    // Set by name and value list
    public static void setList(String objectName, int  index,Object element ){
        RList<Object> objectRList = redissonClient.getList(objectName);
        objectRList.set(index,element);
    }
 
    // Get by name bucket
    public static <T> RBucket<T> getRBucket(String objectName) {
        RBucket<T> bucket = redissonClient.getBucket(objectName);
        return bucket;
    }
 
    // By name and value   Set the corresponding bucket
    public static  <T> T setBucket(String objectName,String value){
        RBucket<Object> bucket = redissonClient.getBucket(objectName);
        bucket.set(value);
        T t= (T) bucket.get(); // The value type is determined by the return value 
        return  t;
    } 
}

Mode 2


package com.wt.redission.wtredission.utils;  
import org.redisson.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; 
import javax.annotation.PostConstruct; 
@Component
public class RedissionUtils2 {
 
    @Autowired
    RedissonClient redissonClient; 
    public  static RedissionUtils2 redissionUtils; 
    @PostConstruct
    public  void  init(){
        redissionUtils=this;
        redissionUtils.redissonClient=this.redissonClient;
    } 
 
    public static RLock getRLock(String objectName) {
        RLock rLock = redissionUtils.redissonClient.getLock(objectName);
        return rLock;
    } 
 
    // Get by name map
    public static  <K, V> RMap<K, V> getRMap(String objectName) {
        RMap<K, V> map = redissionUtils.redissonClient.getMap(objectName);
        return map;
    }
 
    // Set by name and value map
    public static void setMap(String objectName,Object key,Object value){
        RMap<Object, Object> map =redissionUtils.redissonClient.getMap(objectName);
        map.put(key,value);
    } 
 
    // Get by name set
    public static <V> RSet<V> getSet(String objectName) {
        RSet<V> set = redissionUtils.redissonClient.getSet(objectName);
        return set;
    }
 
    // Set by name and value set
    public static void setSet(String objectName,Object value){
        RSet<Object> set = redissionUtils.redissonClient.getSet(objectName);
        set.add(value);
    }
 
    // Get by name list
    public static  <V> RList<V> getRList(String objectName) {
        RList<V> rList = redissionUtils.redissonClient.getList(objectName);
        return rList;
    } 
 
    // Set by name and value list
    public static void setList(String objectName, int  index,Object element ){
        RList<Object> objectRList = redissionUtils.redissonClient.getList(objectName);
        objectRList.set(index,element);
    }
 
    // Get by name bucket
    public static <T> RBucket<T> getRBucket(String objectName) {
        RBucket<T> bucket = redissionUtils.redissonClient.getBucket(objectName);
        return bucket;
    }
 
    // By name and value   Set the corresponding bucket
    public static  <T> T setBucket(String objectName,String value){
        RBucket<Object> bucket = redissionUtils.redissonClient.getBucket(objectName);
        bucket.set(value);
        T t= (T) bucket.get(); // The value type is determined by the return value 
        return  t;
    } 
}

Mode 3 is obtained through spring context


@Component
public class JDBCUtils {
    private static ComboPooledDataSource dataSource;
    @Autowired
    public void setDataSource(ComboPooledDataSource dataSource) {
        JDBCUtils.dataSource = dataSource;
    }
0

Related articles: