Search code examples
javaspringexceptionconstructor-injection

How to handle global exceptions in java spring?


Currently I am working on a small system to log all the uncaught exceptions and store them into a database for further debugging / development. To do so I am using an UncaughtExceptionHandler for a specific thread:

public class GlobalExceptionHandler implements Thread.UncaughtExceptionHandler{

    @Autowired
    private LoggedExceptionService service;


    public GlobalExceptionHandler() {
    }

    @Override
    public void uncaughtException(Thread t, Throwable e) {
        System.err.println("IN UNCAUGHTEXCEPTION METHOD");
        this.service.saveException(new LoggedException(e));
    }
}

As you can see the field service is injected and when I catch an exception, then I get a NullPointerException because the field is null.

The main problem is the usage of the GlobalExceptionHandler. If I inject using the constructor (like in this code snippet):

private LoggedExceptionService service;

@Autowired
public GlobalExceptionHandler(LoggedExceptionService service) {
this.service = service;
}

then the field is not null, but then I can not declare it as the exception handler, because I can not autowire it to java-native methods. The call would be:

Thread.setDefaultUncaughtExceptionHandler(new GlobalExceptionHandler());

Is there a possibility to autowire the handler to the thread method or what would be a good way to go?


Solution

  • Make it a component and set the default exception handler in an @PostConstruct method.

    @Component
    public class GlobalExceptionHandler implements Thread.UncaughtExceptionHandler{
    
        @Autowired
        private LoggedExceptionService service;
    
    
        public GlobalExceptionHandler() {
        }
    
        @PostConstruct
        public void init(){
            Thread.setDefaultUncaughtExceptionHandler(this);
        }
    
        @Override
        public void uncaughtException(Thread t, Throwable e) {
            System.err.println("IN UNCAUGHTEXCEPTION METHOD");
            this.service.saveException(new LoggedException(e));
        }
    }
    

    This allows you to automatically set the handler as methods annotated with @PostConstruct in components are automatically executed on startup.

    Making GlobalExceptionHandler a spring component also allows to autowire service that would never been set otherwise. Anyways, I would recommend you to use constructor autowiring:

    @Component
    public class GlobalExceptionHandler implements Thread.UncaughtExceptionHandler{
    
        private final LoggedExceptionService service;
    
        @Autowired // @Autowired is actually not necessary if this is the only constructor
        public GlobalExceptionHandler(LoggedExceptionService service) {
            this.service=service
        }
    
        @PostConstruct
        public void init(){
            Thread.setDefaultUncaughtExceptionHandler(this);
        }
    
        @Override
        public void uncaughtException(Thread t, Throwable e) {
            System.err.println("IN UNCAUGHTEXCEPTION METHOD");
            this.service.saveException(new LoggedException(e));
        }
    }