How to Fix Spring Static Injection NPE: Real-World Demo & Solutions

This article explains why static fields injected with @Autowired in Spring can become null, causing NPEs in production, and presents six practical solutions—including removing static fields, using @PostConstruct, static setters, ApplicationContext, singleton patterns, and @Lazy loading—to ensure reliable static dependency injection.

Selected Java Interview Questions
Selected Java Interview Questions
Selected Java Interview Questions
How to Fix Spring Static Injection NPE: Real-World Demo & Solutions

Spring Static Injection Real-World Demo

Sometimes we wrap a bean into a utility class and expose it as a default method, but the static field ends up null, causing a NullPointerException in production.

Why does RemoteEBRpcInvoker.getEbFormIdUtil sometimes return NULL?
@Component
public class RemoteEBRpcInvoker {
    private static final String DEFAULT_RPC_GROUP = "ebuilderform";
    private static PublishKitRuntimeUtil publishKitRuntimeUtil;
    private static GetEbFormIdUtil getEbFormIdUtil;

    @Autowired
    public RemoteEBRpcInvoker(GetEbFormIdUtil getEbFormIdUtil,
                              PublishKitRuntimeUtil publishKitRuntimeUtil) {
        RemoteEBRpcInvoker.getEbFormIdUtil = getEbFormIdUtil;
        RemoteEBRpcInvoker.publishKitRuntimeUtil = publishKitRuntimeUtil;
    }

    /**
     * Build RPC service instance by appId.
     */
    public static <T> T newInstance(Class<T> tClass, Long appId) {
        Long appId = getEbFormIdUtil.getAppId(TagConstant.CS_APPID_TAG, tenantKey);
        return publishKitRuntimeUtil.buildRpcService(tClass, DEFAULT_RPC_GROUP, String.valueOf(appId));
    }
}

When calling newInstance(), getEbFormIdUtil is null because static injection failed.

Reason 1: Static variable initialization order

Static fields are initialized before instance fields; @Autowired works on instance creation, so static fields may remain null.

Reason 2: Spring lifecycle and static fields

Spring injects dependencies into instance fields; static fields are not tied to the bean lifecycle, leading to possible null values.

Solutions:

Solution

Method 1: Remove static fields (breaks original intent)

@Component
public class RemoteEBRpcInvoker {
    private static final String DEFAULT_RPC_GROUP = "ebuilderform";
    private PublishKitRuntimeUtil publishKitRuntimeUtil;
    private GetEbFormIdUtil getEbFormIdUtil;

    @Autowired
    public RemoteEBRpcInvoker(GetEbFormIdUtil getEbFormIdUtil,
                              PublishKitRuntimeUtil publishKitRuntimeUtil) {
        this.getEbFormIdUtil = getEbFormIdUtil;
        this.publishKitRuntimeUtil = publishKitRuntimeUtil;
    }
}

This works but defeats the goal of a static utility.

Method 2: Use @PostConstruct

Initialize static variables after injection.

@Component
public class RemoteEBRpcInvoker {
    private static final String DEFAULT_RPC_GROUP = "ebuilderform";
    private static PublishKitRuntimeUtil publishKitRuntimeUtil;
    private static GetEbFormIdUtil getEbFormIdUtil;

    @Autowired
    public RemoteEBRpcInvoker(GetEbFormIdUtil getEbFormIdUtil,
                              PublishKitRuntimeUtil publishKitRuntimeUtil) {
        this.getEbFormIdUtil = getEbFormIdUtil;
        this.publishKitRuntimeUtil = publishKitRuntimeUtil;
    }

    @PostConstruct
    public void init() {
        RemoteEBRpcInvoker.getEbFormIdUtil = this.getEbFormIdUtil;
        RemoteEBRpcInvoker.publishKitRuntimeUtil = this.publishKitRuntimeUtil;
    }
}

This ensures static variables are assigned after dependencies are injected.

Method 3: Use static @Autowired setter

@Component
public class RemoteEBRpcInvoker {
    private static final String DEFAULT_RPC_GROUP = "ebuilderform";
    private static PublishKitRuntimeUtil publishKitRuntimeUtil;
    private static GetEbFormIdUtil getEbFormIdUtil;

    @Autowired
    public static void setPublishKitRuntimeUtil(PublishKitRuntimeUtil publishKitRuntimeUtil) {
        RemoteEBRpcInvoker.publishKitRuntimeUtil = publishKitRuntimeUtil;
    }

    @Autowired
    public static void setGetEbFormIdUtil(GetEbFormIdUtil getEbFormIdUtil) {
        RemoteEBRpcInvoker.getEbFormIdUtil = getEbFormIdUtil;
    }
}

Static setter methods let Spring inject dependencies without relying on an instance.

Method 4: Use ApplicationContext to fetch beans

@Component
public class RemoteEBRpcInvoker implements ApplicationContextAware {
    private static final String DEFAULT_RPC_GROUP = "ebuilderform";
    private static ApplicationContext applicationContext;

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

    public static GetEbFormIdUtil getGetEbFormIdUtil() {
        return applicationContext.getBean(GetEbFormIdUtil.class);
    }

    public static PublishKitRuntimeUtil getPublishKitRuntimeUtil() {
        return applicationContext.getBean(PublishKitRuntimeUtil.class);
    }
}

Manually retrieve beans from the context in static methods.

Method 5: Singleton pattern with manual injection

@Component
public class RemoteEBRpcInvoker {
    private static final String DEFAULT_RPC_GROUP = "ebuilderform";
    private static RemoteEBRpcInvoker instance;

    private PublishKitRuntimeUtil publishKitRuntimeUtil;
    private GetEbFormIdUtil getEbFormIdUtil;

    @Autowired
    public RemoteEBRpcInvoker(GetEbFormIdUtil getEbFormIdUtil,
                              PublishKitRuntimeUtil publishKitRuntimeUtil) {
        this.getEbFormIdUtil = getEbFormIdUtil;
        this.publishKitRuntimeUtil = publishKitRuntimeUtil;
        instance = this;
    }

    public static RemoteEBRpcInvoker getInstance() {
        return instance;
    }

    public GetEbFormIdUtil getGetEbFormIdUtil() {
        return this.getEbFormIdUtil;
    }

    public PublishKitRuntimeUtil getPublishKitRuntimeUtil() {
        return this.publishKitRuntimeUtil;
    }
}

The singleton stores the bean instance in a static field, allowing static access to non‑static members.

Method 6: Use @Lazy for lazy loading

@Component
public class RemoteEBRpcInvoker {
    private static final String DEFAULT_RPC_GROUP = "ebuilderform";
    private static PublishKitRuntimeUtil publishKitRuntimeUtil;
    private static GetEbFormIdUtil getEbFormIdUtil;

    @Autowired
    @Lazy
    public RemoteEBRpcInvoker(GetEbFormIdUtil getEbFormIdUtil,
                              PublishKitRuntimeUtil publishKitRuntimeUtil) {
        RemoteEBRpcInvoker.getEbFormIdUtil = getEbFormIdUtil;
        RemoteEBRpcInvoker.publishKitRuntimeUtil = publishKitRuntimeUtil;
    }
}

@Lazy defers dependency initialization until the first actual use, preventing premature static initialization.

Summary

Avoid static fields; prefer instance fields (recommended).

Initialize static variables with @PostConstruct.

Inject static dependencies via static setter methods.

Manually obtain beans using ApplicationContext.

Apply a singleton pattern to access non‑static dependencies.

Use @Lazy to lazily load dependencies.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaspringSpring Bootdependency-injectionStatic Injection
Selected Java Interview Questions
Written by

Selected Java Interview Questions

A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.