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 modeAdd @ 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