Spring java config: bean config after component scan - spring

I have the following configuration:
#Configuration
#ComponentScan("com.xyz.svc")
public class SvcConfig {
#Autowired private Filter filter1;
#Autowired private Filter filter2;
#Autowired private Filter filter3;
#Bean
public List<Filter> filters() {
// Filters are added in the desired order of execution
return ImmutableList.of(
filter1,
filter2,
filter3);
}
}
When leadFilters() method is run all the components that it depends on (ie filter1, filter2, filter3) are null. Basically, these components are registered through #ComponentScan. The problem is leadFilters() method is getting executed before #ComponentScan.
How do I make this work?

Basically, you can't, reliably. A #Configuration class is a #Component that is meant to register bean definitions through #Bean annotated methods. If a request for a bean (handled through a #Bean method) comes in before the BeanPostProcessor that handles #Autowired, then you will see the behavior you are describing.
Note that the following will cause you problems as Spring won't know which to inject.
#Autowired
private Filter filter1;
#Autowired
private Filter filter2;
#Autowired
private Filter filter3;
Assuming this was just an example, you could refactor so that instead of having #Component classes for these filters, you instead declare #Bean methods for them.
#Bean
public Filter filter1() {
return new FilterImpl1();
}
#Bean
public Filter filter2() {
return new FilterImpl2();
}
#Bean
public Filter filter3() {
return new FilterImpl3();
}
You can then use these beans in your other #Bean method
#Bean
public List<Filter> filters() {
// Filters are added in the desired order of execution
return ImmutableList.of(
filter1(),
filter2(),
filter3());
}

Related

Spring Data Rest: #Autowire in Custom JsonDeserializer

I am trying to autowire a component into a custom JsonDeserializer but cannot get it right even with the following suggestions I found:
Autowiring in JsonDeserializer: SpringBeanAutowiringSupport vs HandlerInstantiator
Right way to write JSON deserializer in Spring or extend it
How to customise the Jackson JSON mapper implicitly used by Spring Boot?
Spring Boot Autowiring of JsonDeserializer in Integration test
My final goal is to accept URLs to resources in different microservices and store only the ID of the resource locally. But I don't want to just extract the ID from the URL but also verify that the rest of the URL is correct.
I have tried many things and lost track a bit of what I tried but I believe I tried everything mentioned in the links above. I created tons of beans for SpringHandlerInstantiator, Jackson2ObjectMapperBuilder, MappingJackson2HttpMessageConverter, RestTemplate and others and also tried with setting the SpringHandlerInstantiator in RepositoryRestConfigurer#configureJacksonObjectMapper.
I am using Spring Boot 2.1.6.RELEASE which makes me think something might have changed since some of the linked threads are quite old.
Here's my last attempt:
#Configuration
public class JacksonConfig {
#Bean
public HandlerInstantiator handlerInstantiator(ApplicationContext applicationContext) {
return new SpringHandlerInstantiator(applicationContext.getAutowireCapableBeanFactory());
}
}
#Configuration
public class RestConfiguration implements RepositoryRestConfigurer {
#Autowired
private Validator validator;
#Autowired
private HandlerInstantiator handlerInstantiator;
#Override
public void configureValidatingRepositoryEventListener(ValidatingRepositoryEventListener validatingListener) {
validatingListener.addValidator("beforeCreate", validator);
validatingListener.addValidator("beforeSave", validator);
}
#Override
public void configureJacksonObjectMapper(ObjectMapper objectMapper) {
objectMapper.setHandlerInstantiator(handlerInstantiator);
}
}
#Component
public class RestResourceURLSerializer extends JsonDeserializer<Long> {
#Autowired
private MyConfig config;
#Override
public Long deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
ServiceConfig serviceConfig = config.getServices().get("identity");
URI serviceUri = serviceConfig.getExternalUrl();
String servicePath = serviceUri.getPath();
URL givenUrl = p.readValueAs(URL.class);
String givenPath = givenUrl.getPath();
if (servicePath.equals(givenPath)) {
return Long.parseLong(givenPath.substring(givenPath.lastIndexOf('/') + 1));
}
return null;
}
}
I keep getting a NullPointerException POSTing something to the API endpoint that is deserialized with the JsonDeserializer above.
I was able to solve a similar problem by marking my deserializer constructor accept a parameter (and therefore removing the empty constructor) and marking constructor as #Autowired.
public class MyDeserializer extends JsonDeserializer<MyEntity> {
private final MyBean bean;
// no default constructor
#Autowired
public MyDeserializer(MyBean bean){
this.bean = bean
}
...
}
#JsonDeserialize(using = MyDeserializer.class)
public class MyEntity{...}
My entity is marked with annotation #JsonDeserialize so I don't have to explicitly register it with ObjectMapper.

When to use Qualifier and Primary in Spring

I have read that #Qualifier can be used in Injection phase whereas #Primary is used in Configuration phase. Am still unclear when to use which.
Also I have below doubts
can #Primary and #Qualifier be used together? if yes does #Qualifier take precedence?
can #Primary be used with #Autowired?
How is the Injection phase different from Configuration phase, this in respect to Spring beans
#Primary indicates that a bean should be given preference when multiple candidates
are qualified to autowire a single-valued dependency.
#Qualifier indicates specific bean should be autowired when there are multiple candidates.
For example, we have two beans both implement the same interface.
public interface BeanInterface {
String getName();
}
public class Bean1 implements BeanInterface {
#Override
public String getName() {
return "bean 1";
}
}
public class Bean2 implements BeanInterface {
#Override
public String getName() {
return "bean2";
}
}
Here is our service.
#Service
public class BeanService {
#Autowired
private BeanInterface bean;
}
And our configuration.
#Configuration
public class Config {
#Bean("bean1")
public BeanInterface bean1() {
return new Bean1();
}
#Bean("bean2")
public BeanInterface bean2() {
return new Bean2();
}
}
When Spring starts, it will find there are two beans("bean1" and "bean2") both can be autowired to BeanService since they implement the same interface BeanInterface. It reports an error in my Idea.
Could not autowire. There is more than one bean of 'BeanInterface' type.
Beans: bean1   (Config.java)
bean2   (Config.java)
And without a hint, Spring does not know which one to use.
So in our case, when we add #Primary to Config.bean1().
#Bean("bean1")
#Primary
public BeanInterface bean1() {
return new Bean1();
}
It tells Spring, "when you find more than one beans that both can be autowired, please use the primary one as your first choose." So, Spring will pick bean1 to autowire to BeanService.
Here is another way to autowire bean1 to BeanService by using #Qualifier in BeanService.class.
#Service
public class BeanService {
#Autowired
#Qualifier("bean1")
private BeanInterface bean;
}
#Qualifier will tell Spring, "no matter how many beans you've found, just use the one I tell you."
So you can find both #Qualifier and #Primary are telling Spring to use the specific bean when multiple candidates are qualified to autowire. But #Qualifier is more specific and has high priority. So when both #Qualifier and #Primary are found, #Primary will be ignored.
Here is the test.
#Configuration
public class Config {
#Bean("bean1")
#Primary
public BeanInterface bean1() {
return new Bean1();
}
#Bean("bean2")
public BeanInterface bean2() {
return new Bean2();
}
}
#Service
public class BeanService {
#Autowired
#Qualifier("bean2")
private BeanInterface bean;
#PostConstruct
public void test() {
String name = bean.getName();
System.out.println(name);
}
}
The output is "bean2".
Also, need to remember that #Qualifier as bigger priority then #Primary, that's means that it's waste to define both of the annotations.
#Primary means default implementation, while #Qualifier is the specific implementation.
You can review my blog I wrote regarding this annotations -
http://shaikezam.com/#/spring_qualifier
Yes, #Qualifier takes precedence.
Yes we can user #Primary and #Autowired together.
#Configuration Class related to Application Context which can be use to configure application level configuration.
To access beans with the same type we usually use #Qualifier(“beanName”) annotation. We apply it at the injection point along with #Autowired. In our case, we select the beans at the configuration phase so #Qualifier can't be applied here.
To resolve this issue Spring offers the #Primary annotation along with #Bean annotation.
Source

spring inject logback TurboFilter

I use spring to inject DemoService has always been null, there is no problem with the filter inject of servlet, in the class of extends TurboFilter, how can I get the DemoService object?
https://stackoverflow.com/questions/30662641/inject-spring-bean-into-custom-logback-filter
I have tried the answer to this connection and did not solve the problem of inject.
public class ErrorLogTurboFilter extends TurboFilter {
#Autowired
private DemoService demoService;
#Override
public FilterReply decide(Marker marker, Logger logger, Level level, String s, Object[] objects, Throwable throwable) {
// todo
return FilterReply.NEUTRAL;
}
}
Problem: Logback starts up before the Spring context. Therefore you need to lazy initialize the Filter with the to be injected bean. Apart from that the Filter will not be called as a Spring bean, but as a Turbofilter, that does not know any injections and so on.
What you could try is define that Filter as a Spring bean in your context, that contains the DemoService. Inject the bean via a Setter for the service, but declare the field static, so you are able to access it from the logging context.
Now during the execution you need to check if the static field is already initialized, if so you can use it without a problem.
You are not trying the answer you are quoting, because your extended filter "ErrorLogTurboFilter" does not have a "#Named("errorLogTurboFilter")" which is the standard annotation to make your filter a spring bean.
see : What is javax.inject.Named annotation supposed to be used for?
#markusw According to your prompt, this is my solution,and thank you.
#Configuration
public class WebConfig {
#Bean
public DemoService demoService() {
return new DemoService();
}
}
public class ErrorLogTurboFilter extends TurboFilter {
private ApplicationContext ctx = new AnnotationConfigApplicationContext(WebConfig.class);
private DemoService demoService = ctx.getBean(DemoService.class);
#Override
public FilterReply decide(Marker marker, Logger logger, Level level, String s, Object[] objects, Throwable throwable) {
// todo
return FilterReply.NEUTRAL;
}
}

Multiple Spring Configuration files (one per Profile)

I'm a Spring rookie and trying to benefit from the advantages of the easy 'profile' handling of Spring. I already worked through this tutorial: https://spring.io/blog/2011/02/14/spring-3-1-m1-introducing-profile and now I'd like to adapt that concept to an easy example.
I've got two profiles: dev and prod. I imagine a #Configuration class for each profile where I can instantiate different beans (implementing a common interface respectively) depending on the set profile.
My currently used classes look like this:
StatusController.java
#RestController
#RequestMapping("/status")
public class StatusController {
private final EnvironmentAwareBean environmentBean;
#Autowired
public StatusController(EnvironmentAwareBean environmentBean) {
this.environmentBean = environmentBean;
}
#RequestMapping(method = RequestMethod.GET)
Status getStatus() {
Status status = new Status();
status.setExtra("environmentBean=" + environmentBean.getString());
return status;
}
}
EnvironmentAwareBean.java
public interface EnvironmentAwareBean {
String getString();
}
EnvironmentAwareBean.java
#Service
public class DevBean implements EnvironmentAwareBean {
#Override
public String getString() {
return "development";
}
}
EnvironmentAwareBean.java
#Service
public class ProdBean implements EnvironmentAwareBean {
#Override
public String getString() {
return "production";
}
}
DevConfig.java
#Configuration
#Profile("dev")
public class DevConfig {
#Bean
public EnvironmentAwareBean getDevBean() {
return new DevBean();
}
}
ProdConfig.java
#Configuration
#Profile("prod")
public class ProdConfig {
#Bean
public EnvironmentAwareBean getProdBean() {
return new ProdBean();
}
}
Running the example throws this exception during startup (SPRING_PROFILES_DEFAULT is set to dev):
(...) UnsatisfiedDependencyException: (...) nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [EnvironmentAwareBean] is defined: expected single matching bean but found 3: prodBean,devBean,getDevBean
Is my approach far from a recommended configuration? In my opinion it would make more sense to annotate each Configuration with the #Profile annotation instead of doing it for each and every bean and possibly forgetting some variants when new classes are added later on.
Your implementations of EnvironmentAwareBean are all annotated with #Service.
This means they will all be picked up by component scanning and hence you get more than one matching bean. Do they need to be annotated with #Service?
Annotating each #Configuration with the #Profile annotation is fine. Another way as an educational exercise would be to not use #Profile and instead annotate the #Bean or Config classes with your own implementation of #Conditional.

Spring JavaConfig + WebMvcConfigurerAdapter + #Autowired => NPE

I have an application with 2 Contexts. Parent for web agnostic business logic and ChildContext (implicitly created by dispatcher servlet) for web logic.
My setup loks like
#Configuration
public class BusinessConfig {
#Bean
public ObjectMapper jacksonMapper() { return new ObjectMapper() }
}
and
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Autowired
private ObjectMapper objectMapper; // <- is null for some reason
#Override
public configureMessageConverters(List<HttpMessageConverter<?>> converters) {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(objectMapper); // <- bang!
messageConverters.add(converter);
}
}
I need the the object mapper in the parent context, as I use it also in security configuration. But can someone explain me, why the #Autowired objectMapper is null? Its created in the parent context (the fact that the parent exists is even logged by spring at startup). Also #Autowired has required=true by default, so it should not blow up in the configure method (it should have blown up in construction of the context, if the bean wasn't there for some reason).
It seems to me that there might be some lifecycle problem in spring - in a sense that it calls the overridden methods first, and then #Autowires the dependencies... I have also tried to #Autowire the BusinessConfig (should be perfectly legal according to documentation - the result was the same (null)).
What should I do to make this working?
Thanks in advance!
EDIT - ISSUE FOUND
I found the issue. Unfortunately it had nothing to do with WebMvcConfigurerAdapter nor #Configuration. It was caused by premature initialization of context triggered by missing static modifier for propertyPlaceholderConfigurer... I have created issue in Spring core jira (https://jira.spring.io/browse/SPR-14382)
What about simply renaming the bean declaration method to match with the autowired bean?
#Configuration
public class BusinessConfig {
#Bean
public ObjectMapper objectMapper() { return new ObjectMapper() }
}
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Autowired
private ObjectMapper objectMapper;
[...]
}

Resources