When using Spring Web, in this case for rest endpoints & using Spring Boot 2, I can configure interceptors for my app using by implementing the WebMvcConfigurer interface:
#Configuration
public class SpringWebConfig implements WebMvcConfigurer
{
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor).addPathPatterns("/api/endpoint/**");
}
}
I added this interceptor to most of my apps in an automatic fashion by doing the following:
Create a "common-jar", and put the above interface under package
com.company.api.
In every app, add the package com.company.api to
the api scan.
This common package also contains the Interceptor and utility classes to make this interceptor work, so in effect, adding this common-jar would automatically add he interceptor to all operations in the app, which is a similar concept as to what Spring itself does: adding dependencies changes the default configuration of Spring.
The problem I'm facing now is this approach cannot be extended to a second interceptor in a second jar, because I already used the WebMvcConfigurer implementation. and I cannot have two.
I was thinking about maybe using some kind of composite-configurer pattern where we loop over every configurer, collect all interceptors, and then add them once, but unfortunately Spring doesn't allow this. What are my options?
Currently, the approach I took is duplicating the WebMvcConfigurer interface in every app that requires it. I feel sad when something changes, and I have to change the same snippet of code in every single app.
If I understand your question correctly , basically you want to define some common Interceptors in multiple JARs such that an application can activate these Interceptors by simply including these JARs into their app ?
I was thinking about maybe using some kind of composite-configurer
pattern where we loop over every configurer, collect all interceptors,
and then add them once, but unfortunately Spring doesn't allow this.
What are my options?
Well, if implementation A returns a registry with only interceptor A,
and implementation B returns a registry with only interceptor B, would
spring combine both registries into one super registry containing both
A and B, or would it just pick one, or would it throw an error that
there was no unique bean definition ?
Actually , Spring has already implement this feature. When there are multiple WebMvcConfigurer beans , Spring simply loop them one by one and calls their configuration methods. So the end-result is that InterceptorRegistry will contain all interceptors.
If the client application need to activate certain WebMvcConfigurer only, it can simply exclude those JARs containing the WebMvcConfigurer that they don't want.
To take this idea further which allow the application to control which Interceptors to activate down to interceptor level , you could even do the following in each common JAR :
#Configuration
public class SpringWebConfig implements WebMvcConfigurer {
//Make sure the HandlerInterceptor implementation in this JAR is a bean (e.g mark it as #Component)
#Autowired
private List<HandlerInterceptor> interceptors;
#Override
public void addInterceptors(InterceptorRegistry registry) {
for(HandlerInterceptor interceptor : interceptors){
registry.addInterceptor(interceptor).addPathPatterns("/api/endpoint/**");
}
}
}
In the client application , use includeFilters / excludeFilters in #ComponentScan to customise which to interceptors to include. For example, to disable certain Interceptors, you could do :
#ComponentScan(
basePackages = {"com.company.api"},
excludeFilters={
#ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE, value=com.company.common.jar1.Inteceptor1.class) ,
#ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE, value=com.company.common.jar2.Inteceptor1.class)
})
If I understand your problem correctly, you don't want to implement all the methods of WebMvcConfigurer in every app. You just want to add relevant interceptors and be done with it.
My approach would be to create an AbstractWebMvcConfigurerImpl by implementing the WebMvcConfigurer in the Common module. Just leave the addInterceptors() abstract and implement other methods. You can then extend that abstract implementation in your every Spring Boot project and just override the addInterceptors() method as per your need.
Also, you can have as many implementations of WebMvcConfigurer as you want in a Spring project. So, if you need to define some common interceptors in the Common module, you can extend the AbstractWebMvcConfigurerImpl in the common module also.
Please note that all your implementations of AbstractWebMvcConfigurerImpl should be annotated with #Configuration
Related
I have a project which is expanding quickly and now has over a hundred components. For unit testing, I have a #Configuration class to substitute stubbed classes, such as a database service with limited functionality:
#Configuration
public class SpringDevTestConfiguration {
#Bean
#Profile("unittest")
public DatabaseService databaseService() {
// this one is needed for the test
return new TestDatabaseServiceImpl();
}
#Bean
#Profile("unittest")
public RobotControlService robotControlService() {
// Why should I do this on every service?
return new RobotControlServiceImpl();
}
... etc.
The vast majority of components don't need test alternatives, so we want the default one that exists and is picked up at runtime. But whenever a new component is added to the system, we have to add it to all of the #Configuration classes, or we get the "NoSuchBeanDefinitionException: No qualifying bean of type..." exception. That's a lot of boilerplate code to return Impl classes, and prevents us from making implementation classes package-protected (because the configuration class needs to be able to instantiate them).
I have tried adding #ComponentScan to the class, but that results in BeanDefinitionOverrideException. If I allow bean overrides with:
spring.main.allow-bean-definition-overriding=true
... then I get NoUniqueBeanDefinitionException because of the multiple database service implementations.
I'm sure I must be missing something. How can I have short, simple #Configuration class that only instantiates the test implementations I need for the test, then allows the other services to be discovered automatically without explicitly creating them?
Good day, guys. I have a question about autowiring services into my classes when using Springboot. All of the examples I have seen on the Internet as well as in the Springboot specification do something of the like (taking an excerpt from the Springboot version 1.5.7 specification):
package com.example.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
#Service
public class DatabaseAccountService implements AccountService {
private final RiskAssessor riskAssessor;
#Autowired
public DatabaseAccountService(RiskAssessor riskAssessor) {
this.riskAssessor = riskAssessor;
}
// ...
}
This is a class that injects a property through its constructor, by means of #Autowiring the constructor. Another form is to #Autowire the property like this:
#Autowired
private final RiskAssessor riskAssessor
But, where I work, for these two methods to work, I have been told that I need to use this method:
applicationContext.getAutowireCapableBeanFactory().autowireBean(Object.class)
They have told me that I need this in order for the #Autowired annotation to work.
Now my question to you is: why is there no simple annotation that allows the #Autowire to function correctly? (Something like #AutowiredClass). The above method is too verbose and hard to remember, so surely there must be a better way to make #Autowired work on classes in order to inject services, just like we do in Grails where we just say def someService and it is automatically injected.
If you want properly use #Autowired in your spring-boot application, you must do next steps:
Add #SpringBootApplicationto your main class
Add #Service or #Component annotation to class you want inject
Use one of two ways that you describe in question, to autowire
If you don't have any wiered package structure and the main class package includes all other classes you want spring to instantiate (directly or in the subpackages) a simple annotation #ComponentScan on your main class will help you save all those boiler plate code. Then spring will do the magic, it will go and scan the package(and subpackages) and look for classes annotated with #Service, #Component etc and instantiate it.
Even better, use #SpringBootApplication in your main class, this will cover #Configuration as well. If it is a green field project , I would encourage to start from start.spring.io - a template generation/scaffolding tool for spring
Now my question to you is: why is there no simple annotation that allows the #Autowire to function correctly?
There is: #SpringBootApplication
If you put this at the root of your application (file that contains the main class) and as long as your services are at the same package or a sub-package, Spring will auto-discover, instantiate, and inject the proper classes.
There's an example in this walk-through: REST Service with Spring Boot
As described in that page:
#SpringBootApplication is a convenience annotation that adds all of the following:
#Configuration tags the class as a source of bean definitions for the application context.
#EnableAutoConfiguration tells Spring Boot to start adding beans based on classpath settings, other beans, and various property settings.
#ComponentScan tells Spring to look for other components, configurations, and services in the hello package, allowing it to find the controllers.
You need to annotate the implementation of RestService as a #Service or #Component so Spring would pick it up.
#Service
public class MyRiskAssessorImpl implements RiskAssessor {
///
}
#Autowired almost works out of the box. Just do your component scanning of the class you want to autowire and you are done. Just make sure your main class (or main configuration class) uses #ComponentScan("{com.example.app}") or #SpringBootApplication (main class). The docs explain this stuff pretty good
When I setup Spring with XML a can override component definitions in XML files that are loaded later.
It's very usefull for tests - I create default config set and than load it with addition test configuration that replaces some of components with specials (stubs, mocks, and so on).
Now i start to migrate to annotation based configurations and it causes some problems.
The direct way to use annotations is auto-discovering of packages with #Component
So I have
#Configuration
#ComponentScan({"some.pack1", "some.pack2"})
public class ProductConfig{}
And when
#Configuration
#Import({ProductConfig.class})
#ComponentScan({"test.pack"})
public class TestConfig{}
But it will cause conflict if I try to override components in test.pack
And what I can do?
After some investigations where are 3 answers with some issues on them
Worst - i can use #Filter on ComponentScan - it's worst way,
i must not import existed config (that can has some additional beans)
i must rescan all components, and explicitly define set of filters
i can use #Profile and activeProfiles - it's better, while it's more sophistical, implict, but
it means that i must to know at product classes that they can be disabled in some tests
not to use #ComponentScan on override Config and using #Bean insted of it
it's maybe well on test configurations, but it means that I lost ability to use #Component annotation
use setParent on contexts - it works well, but
it's explicit operation on implementation of ApplicationContext not on interface
it's not hard to setup if overriding services has #Autwire dependency on some components from overriden config - require manual register and refresh
What is best and standard way to override conigurations??? When I used XML-based it was not a problem...
#profile plays a crucial role while implementing the testing strategy for your service/code.
For example, in development, you may have:
public interface DataSource{
public String getHost();
}
Default implementation is
#Component
#Profile("Prod")
public class DevDataSource implements DataSource {
public String getHost(){
// return actual value
}
And the implementation for component tests(Fake impl)
#Component
#Profile("test")
public class StubbyDataSource implements DataSource {
public String getHost(){
return "some-host"; // return mocked data
}
Now you can write a test here which can act as integration test, unit test and component tests (https://martinfowler.com/bliki/ComponentTest.html)
In that way, your testing strategy would be much more elegant, concise and easy to maintain. Just by changing the profile, the same test can point to different environments (real or fake).
I am trying to stay declarative, convention-base and XML-less.
So, I have no web.xml, and I have no context configuration XMLs. Unfortunately, Google is spammed with old-fashion Spring examples and it is impossible to find modern answer.
My question is: is it possible to declare interceptor with annotations in Spring? Apparently it would be possible to do the same way as it done with controllers (controller class is annotated with #Controller and it's methods -- with #RequestMapping).
The best way I found is here https://stackoverflow.com/a/16706896/258483
Unfortunately, it is not declarative.
Using Java configuration and the #Configuration annotation it looks like you create an interceptor and register it as described here. It's not as simple as annotating a class as an interceptor but may still adhere to your stipulations.
EDIT:
In java configuration class, we need to extend
WebMvcConfigurerAdapter. To add our interceptor, we override
WebMvcConfigurerAdapter. addInterceptors() method. Find the code
snippet.
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoggingInterceptor());
registry.addInterceptor(new TransactionInterceptor()).addPathPatterns("/person/save/*");
}
Using Spring, one can define classes that implement MessageSourceAware. The container will then inject a MessageSource automatically (see the tail end of this documentation).
Is this a special case or can I use this concept to inject other dependencies too? For instance, can my classes implement the following interface:
public interface MyServiceAware {
void setMyService(MyService service);
}
and then somehow be auto-wired with a suitable bean?
Note: I am using XML configuration exclusively at the moment and I'd be reluctant to move to Java-based annotations.
The *Aware interfaces are handled by a BeanPostProcessor that is automatically registered in the application context (org.springframework.context.support.ApplicationContextAwareProcessor).
You could create and register your own postprocessor to handle MyServiceAware beans.
Remember also that the XML configuration does support autowiring for appropriately configured beans.