NullPointerException for Springboot #Autowired component in interceptor [duplicate] - java

This question already has answers here:
Why is my Spring #Autowired field null?
(21 answers)
Closed 8 months ago.
I have a component that's the main job is to return a Jedis instance, and it looks like the following:
#Component
public class JedisConfig {
private Jedis jedis;
public JedisConfig() {
jedis = new Jedis("localhost", 6379);
}
public Jedis getJedis() {return jedis;}
}
I then use the Jedis instance to do some stuff in my interceptor's preHandler:
public class AuthInterceptor implements HandlerInterceptor {
#Autowired
private JedisConfig jc;
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Jedis jedis = jc.getJedis();
// do some other stuff with jedis below
}
}
But I get a NullPointerException when calling jc.getJedis() and I don't understand why it's happening.
On a related note, I did pretty much the exact same thing in a unit test and it runs fine:
#Autowired
private JedisConfig jc;
#Test
public void testJedis(){
Jedis jedis = jc.getJedis();
jedis.set("user", "role");
assertThat(jedis.get("user"),is("role"));
}
Here's How I added the interceptor to the registry:
#Configuration
public class WebConfig implements WebMvcConfigurer {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new AuthInterceptor())
.addPathPatterns("/user/roleChange");
}
}
If anyone knows how to fix this please let me know. Thank you so much!

The AuthInterceptor is not a Spring bean, and the JedisConfig is not autowired and is null. As long as the interceptor is used in the #Configuration class, you can perform autowiring there and encapsulating the JedisConfig interceptor.
#RequiredArgsConstructor // either use Lombok or write the constructor by yourself
public class AuthInterceptor implements HandlerInterceptor {
private final JedisConfig jc;
}
#Configuration
public class WebConfig implements WebMvcConfigurer {
#Autowired
private JedisConfig jc;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new AuthInterceptor(jc))
.addPathPatterns("/user/roleChange");
}
}
Alternatively, you can treat the AuthInterceptor as a component and autowire it in the WebConfig class:
#Component
public class AuthInterceptor implements HandlerInterceptor {
#Autowired
private JedisConfig jc;
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Jedis jedis = jc.getJedis();
...
}
}
#Configuration
public class WebConfig implements WebMvcConfigurer {
#Autowire
private AuthInterceptor authInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authInterceptor)
.addPathPatterns("/user/roleChange");
}
}

Related

#Order annotation vs FilterRegistrationBean.setOrder vs addFilterBefore/addFilterAfter in WebSecurityConfigurerAdapter.configure

I am adding filters in my application and I want to set the order of call for these filters. I can see three ways but I don't know if they're at all related?
Using #Order annotation:
#Component
#Order(1)
public class SampleFilter extends OncePerRequestFilter {
...
}
Register the filter via config:
#Configuration
public class FilterConfig {
#Bean
public FilterRegistrationBean<SampleFilter> perfFilter() {
FilterRegistrationBean<SampleFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new SampleFilter());
registration.setOrder(1);
return registration;
}
}
Via WebSecurityConfigurerAdapter:
public class SampleConfig extends WebSecurityConfigurerAdapter {
#Autowired
private SampleFilter sampleFilter;
#Override
public void configure(HttpSecurity http) throws Exception {
...
http.addFilterBefore(this.sampleFilter, SampleFilterTwo.class);
}
}

How to add spring interceptor only for registered mappings (endpoints)

I have an issue related to HandlerInterceptorAdapter.
Interceptor:
#Component
public class RequestInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler)
throws Exception {
//some logic
return true;
}
}
WebMvcConfigurer
#Configuration
public class FsWebMvcConfigurer implements WebMvcConfigurer {
private static final String API_PATTERN = "/**/api/**/";
private final RequestInterceptor requestInterceptor;
#Autowired
public FsWebMvcConfigurer(final RequestInterceptor requestInterceptor) {
this.requestInterceptor = requestInterceptor;
}
#Override
public void addInterceptors(final InterceptorRegistry registry) {
registry.addInterceptor(requestInterceptor).addPathPatterns(API_PATTERN);
}
}
And it works as expected - every request with pattern /**/api/**/ is getting intercepted.
Unfortunetly it also works for the endpoints that are not registered - I don't want that kind of behaviour. How can I specify that the registered interceptor should only work with registered mappings?
Note: I don't want to specify exactly every existing endpoint by myself (e.g adding all as pattern)

Custom AsyncUncaughtExceptionHandler

Using the following configuration for #Async methods :
#Configuration
#EnableAsync
public class AsyncConfig implements AsyncConfigurer {
#Override
public Executor getAsyncExecutor() {
//Just to experiment
return new SimpleAsyncTaskExecutor();
}
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new CustomAsyncExceptionHandler();
}
}
Is there a way to "get" the ability to autowire (or similar) Services ?
I'd like to use such Services to record errors in database and use common services.
Non working sample :
#Component //seems pointless
public class CustomAsyncExceptionHandler extends ServiceCommons implements AsyncUncaughtExceptionHandler {
protected Logger LOG = LoggerFactory.getLogger(this.getClass());
#Autowired
private MyService myService; //always null
#Override
public void handleUncaughtException(Throwable throwable, Method method, Object... obj) {
//null pointer !
myService.doSomething(throwable);
}
}
When using not in #Async methods, #ControllerAdvice global exception handler allows #Autowired fields. Why not in this case ? Is this because of async thread management ?
I just faced this problem and solved this way:
#Configuration
#EnableAsync
public class MyAsyncConfigurer implements AsyncConfigurer {
private CustomAsyncExceptionHandler customAsyncExceptionHandler;
//...
//other code here
//...
#Autowired
public void setCustomAsyncExceptionHandler(CustomAsyncExceptionHandler customAsyncExceptionHandler) {
this.customAsyncExceptionHandler = customAsyncExceptionHandler;
}
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return this.customAsyncExceptionHandler;
}
}
Custom async exception handler annotated with #Component:
#Component
public class CustomAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
private MyMailService myMailService;
#Autowired
public void setMyMailService(MyMailService myMailService) {
this.myMailService= myMailService;
}
#Override
public void handleUncaughtException(Throwable throwable, Method method, Object... obj) {
myMailService.sendMailToAdmin(throwable, method.getName());
}
}
IoC injects both, myMailService and customAsyncExceptionHandler, correctly whith no errors.
I don't think my solution is the most elegant, but tell me what you think. the idea is to bypass the automatic injection mechanism by using ApplicationContextAware interface. My first attempt was to make my AsyncUncaughtExceptionHandler implementing class to also implement ACAware. But that didn't work. Somehow this class, even annotated as Component or Service seems to live a bit outside the Spring environment. So I did this:
#Configuration
#EnableAsync
public class DemoAsyncConfigurer implements AsyncConfigurer, ApplicationContextAware {
private ApplicationContext applicationContext;
And in the same class:
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
DemoAsyncExceptionHandler demoHandler = new DemoAsyncExceptionHandler(); // you can't add the parameter in this constructor, for some reason...
demoHandler.setApplicationContext(applicationContext);
return demoHandler;
}
/**
*
* {#inheritDoc}
*/
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
My DemoAsyncExceptionHandler has the following:
private ApplicationContext applicationContext;
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public void handleUncaughtException(Throwable throwable, Method method, Object... params) {
UserService userService = this.applicationContext.getBean("userService", UserService.class);
// call userService method
That worked! Hope I have helped

Spring addFormatters not called of WebMvcConfigurerAdapter

I'm trying to implement a converter in a Spring Boot app but for some reason my override of addFormatters(FormatterRegistry formatterRegistry) is never called. What's confusing is that other overridden methods are called, addInterceptors works just fine. Security is enabled for this app.
#EnableWebMvc
#Configuration
#ComponentScan(basePackages = {"com.company.web"})
public class WebMvcConfig extends WebMvcConfigurerAdapter {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
#Autowired
private RequestMappingHandlerAdapter requestMappingHandlerAdapter;
#Inject
private TenantIdentifierInterceptorAdapter multiTenancyInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
logger.info("adding interceptor");
registry.addInterceptor(multiTenancyInterceptor);
}
//THIS IS NOT CALLED
#Override
public void addFormatters(FormatterRegistry formatterRegistry) {
logger.info("adding converters");
formatterRegistry.addConverter(new StringToPersonConverter());
}
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {"classpath:/META-INF/resources/",
"classpath:/resources/", "classpath:/static/", "classpath:/public/"};
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations(CLASSPATH_RESOURCE_LOCATIONS)
.setCachePeriod(3600).resourceChain(true).addResolver(new PathResourceResolver());
}
#PostConstruct
public void init() {
requestMappingHandlerAdapter.setIgnoreDefaultModelOnRedirect(true);
}
}
For some reason if I add this code to the file the formatter code is hit, but I get an error "A ServletContext is required to configure default servlet handling" and the app won't compile.
#Bean
public ResourceBundleMessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("messages");
return messageSource;
}
Have you configured a bean with super class WebMvcConfigurationSupport?
If you did so, it will disable WebMvcAutoConfiguration Bootstrap.
Check this code:
#Configuration
#ConditionalOnWebApplication(
type = Type.SERVLET
)
#ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})
#ConditionalOnMissingBean({WebMvcConfigurationSupport.class})
#AutoConfigureOrder(-2147483638)
#AutoConfigureAfter({DispatcherServletAutoConfiguration.class, ValidationAutoConfiguration.class})
public class WebMvcAutoConfiguration {
I'm not 100% sure but did you try to remove #EnableWebMvc annotation from the class? Because according to documentation it shouldn't be there:
If you want to keep Spring Boot MVC features, and you just want to add additional MVC configuration (interceptors, formatters, view controllers etc.) you can add your own #Configuration class of type WebMvcConfigurerAdapter, but without #EnableWebMvc.
M. Deinum's comment was the answer. I've removed #EnableWebMvc and removed the method addFormatters, then added:
#Bean
StringToPersonConverter stringToPersonConverter() {
return new StringToPersonConverter();
}
I have solved a similar error
EnableWebMvc
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
#Documented
#Import(DelegatingWebMvcConfiguration.class)
public #interface EnableWebMvc {
}
DelegatingWebMvcConfiguration
#Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
#Autowired(required = false)
public void setConfigurers(List<WebMvcConfigurer> configurers) {
if (!CollectionUtils.isEmpty(configurers)) {
this.configurers.addWebMvcConfigurers(configurers);
}
}
//...
#Override
protected void addFormatters(FormatterRegistry registry) {
this.configurers.addFormatters(registry);
}
//...
}
WebMvcConfigurationSupport
public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware {
//...
#Bean
public FormattingConversionService mvcConversionService() {
FormattingConversionService conversionService = new DefaultFormattingConversionService();
addFormatters(conversionService);
return conversionService;
}
/**
* Override this method to add custom {#link Converter}s and {#link Formatter}s.
*/
protected void addFormatters(FormatterRegistry registry) {
}
//...
}
My custom config with multiple WebMvcConfigurerAdapter implements WebMvcConfigurer
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter
{
#Autowired
private ConversionService conversionService;
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers)
{
argumentResolvers.add(new RequestTenantResolverComposite(conversionService));
}
}
The method will not be called if the FormattingConversionService implements ConversionService bean created before WebMvcConfigurer injected.
I solved my question using Lazy annotion for ConversionService with spring verions 4.3.6.

Autowired in CustomInterceptor getting null(Spring Boot) [duplicate]

This question already has answers here:
Why is my Spring #Autowired field null?
(21 answers)
Closed 7 years ago.
I want to make a check in database when i receive a request. So i did a Interceptor like below,
CustomInterceptor.java
#Component
public class CustomInterceptor extends HandlerInterceptorAdapter {
#Autowired
private DatabaseService databaseService;
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//Set Request Attribute(TODO)
LogService.info(this.getClass().getName(), "New Request URI is:" + request.getRequestURI());
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
String authToken = request.getHeader("AuthToken");
boolean isValidRequest = databaseService.checkIfTokenIsValid(authToken);
}
}
Application.class:
#SpringBootApplication
public class Application extends SpringBootServletInitializer {
// protected Properties props = new Properties();
//
// public Application() {
// props.setProperty("error.path", "/error");
//// props.setProperty("error.whitelabel.enabled", "false");
//// props.setProperty("org.springframework.web", "DEBUG");
// }
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
// application.properties(props);
return application.sources(Application.class);
}
#Override
public void onStartup(final ServletContext servletContext) throws ServletException {
LogService.info(Application.class.getName(), "Loading Service...");
super.onStartup(servletContext);
LogService.info(Application.class.getName(), "Service Started");
}
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(Application.class, args);
}
DatabasService.java
#Service
public class DatabaseService {
#Autowired
private ApplicationProperties properties;
private final JdbcTemplate defaultJdbcTemplate;
#Autowired
public DatabaseService(
#Qualifier("dataSource") DataSource dataSource) {
defaultJdbcTemplate = new JdbcTemplate(dataSource);
}
public boolean checkIfTokenIsValid() {
//Perform Check
}
}
CustomWebConfiguration.java
#Configuration
#EnableWebMvc
public class CustomWebConfiguration extends WebMvcConfigurerAdapter {
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {
"classpath:/META-INF/resources/", "classpath:/resources/",
"classpath:/static/", "classpath:/public/"};
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!registry.hasMappingForPattern("/**")) {
registry.addResourceHandler("/**").addResourceLocations(
CLASSPATH_RESOURCE_LOCATIONS);
}
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new CustomInterceptor())
.addPathPatterns("/**");
}
}
But i get NullPointer At: boolean isValidRequest = databaseService.checkIfTokenIsValid(authToken);
What is wrong here, why cannot spring Autowire the Databaseservice in Interceptor?
Note: Autowire works fine everywhere else, but not in the interceptor.
Solution (Thanks to M. Deinum)
Change the CustomWebConfiguration.java like below;
#Configuration
#EnableWebMvc
public class CustomWebConfiguration extends WebMvcConfigurerAdapter {
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {
"classpath:/META-INF/resources/", "classpath:/resources/",
"classpath:/static/", "classpath:/public/"};
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!registry.hasMappingForPattern("/**")) {
registry.addResourceHandler("/**").addResourceLocations(
CLASSPATH_RESOURCE_LOCATIONS);
}
}
#Bean
public CustomInterceptor customInterceptor() {
return new CustomInterceptor();
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(customInterceptor())
.addPathPatterns("/**");
}
}
Spring will only autowire beans it knows about, you are creating the instance yourself outside the control of Spring.
Either inject a CustomInterceptor into your configuration class or add a #Bean method to make it a Spring managed instance. Then use that instance to add it to the list of interceptors.
It is OK to use #SpringBootApplication on your main class, however don't you miss #EnableAutoConfiguration and #ComponentScan on the same class to tell Spring Boot to look for other components and services automatically?

Categories

Resources