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/*");
}
Related
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
I have a collection of classes which I want to be injected into the Spring application context. However, these classes can only be guaranteed to be annotated with one of a group of annotations I have written - i.e. I can assume it will be annotated with #MyAnnotation, but not #Component.
However, #MyAnnotation forms part of an API for my project, and I don't want to state an explicit dependency of this API on Spring. Thus, I can't annotate #MyAnnotation with #Component in order to have it be transitively picked up by Spring.
Is there a way to tell Spring to additionally include #MyAnnotation in its classpath scanning without adding this dependency to my API?
Currently I'm manipulating the bean definition registry to 'manually' add each class annotated with #MyAnnotation, but I'd prefer to rely on Spring's inbuilt support.
Thanks in advance.
It's possible if you create your own BeanDefinitionRegistryPostProcessor to register your own beans. If you implement the postProcessBeanDefinitionRegistry method, you can add beans to the registry by yourself, for example:
#Component
public class FooFactoryBean implements BeanDefinitionRegistryPostProcessor {
#Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
registry.registerBeanDefinition(..);
}
}
To obtain these bean definitions, you can use the ClassPathScanningCandidateComponentProvider class, which will create BeanDefinition objects for all classes found for a specific filter. In this case, an AnnotationTypeFilter will work:
ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
scanner.addIncludeFilter(new AnnotationTypeFilter(Foo.class));
Set<BeanDefinition> definitions = scanner.findCandidateComponents("com.example.my");
In this example, it will find all classes annotated with #Foo in the com.example.my package.
#Configuration classes and XML based configuration should work for you. Have a look at this tutorial: https://www.tutorialspoint.com/spring/spring_java_based_configuration.htm
But to get your #MyAnnotations picked up is more difficult (see #g00glen00b's answer), and I'm not sure it makes sense if the above mentioned solutions are available.
To use jamon in spring, it's described to use JamonPerformanceMonitorInterceptor and put it to springs AOP-mechanism via a applicationContext.xml. It's explained, and there's an example within the tests in it's sources. Unfortunately, I want to build a spring-boot application without any xml-configuration.
Is it possible to use some annotations to include the JamonPerformanceMonitorInterceptor to spring?
Better late than never...
I had the very same situation: I needed to configure JAMon without any XML configuration. Most of the examples online (including the comments in the JAMon source code) advertise XML configuration flexibility, but I couldn't find any examples with annotation based configuration. Also annotation-based configs are not necessarily less flexible, they just need to be conceptually separated and not confused with functional parts of the application. I think such advisor can be a good example:
#Component
public class MonitoringAdvisor extends AbstractPointcutAdvisor {
private final StaticMethodMatcherPointcut pointcut = new StaticMethodMatcherPointcut() {
#Override
public boolean matches(Method method, Class<?> targetClass) {
return targetClass.isAnnotationPresent(RestController.class);
}
};
#Override
public Pointcut getPointcut() {
return this.pointcut;
}
#Override
public Advice getAdvice() {
return new JamonPerformanceMonitorInterceptor(true, true);
}
}
This advisor would let Spring/AOP know to run JAMon monitoring advice on any method of Spring bean annotated with #RestContrller. This advisor should be configured/added to the same Spring context as rest controllers.
Note, that in my case I specifically wanted to monitor my rest controllers. One can adapt the advisor according to his/her own needs. (In my code I use a more advanced/configurable version of the presented advisor)
Is this Spring Boot sample application helpful?
Here is the relevant part of the Spring AOP manual.
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.
I am new to Spring AOP.
Using annotation based Spring configuration:
#Configuration
#EnableAspectJAutoProxy(proxyTargetClass=true)
#ComponentScan({"sk.lkrnac"})
Aspect:
#Aspect
#Component
public class TestAspect {
#Before("execution(* *(..))")
public void logJoinPoint(JoinPoint joinPoint){
....
}
}
Spring compoment:
package sk.lkrnac.testaop;
#Component
public class TestComponent{
#PostConstruct
public void init(){
testMethod();
}
public void testMethod() {
return;
}
}
How can I intercept all public methods that are called by Spring framework itself? (e.g. TestComponent.init() during creation of the TestComponent instance by Spring)
Currently I am able to intercept only TestComponent.testMethod() by invoking:
TestComponent testComponent = springContext.getBean(TestComponent.class);
testComponent.testMethod();
This is a common issue you run into with Spring AOP. Spring accomplishes AOP by proxying advised classes. In your case, your TestComponent instances will be wrapped in a run-time proxy class that provides the "hooks" for any aspect advice to be applied. This works very well when methods are called from outside the class, but as you have discovered it doesn't work on internal calls. The reason is that internal calls will not pass the proxy barrier, thus will not trigger the aspect.
There are primarily two ways around this. One is to fetch an instance of the (proxied) bean from the context. This is what you have already tried with success.
The other way is to use something called load-time weaving. When using this, AOP advices are added to a class ("weaved" into it) by a custom class-loader by injecting byte-code into the class definition. The Spring documentation has more on this.
There is a third way, which is called "compile time weaving". In this scenario, your AOP advices are statically weaved into each advised class when you compile it.
You can't intercept init() without any explicit means, please see the SpringSource Jira for details.
You can also try to call inner testMethod() from init() by self via proxy object like Don explained in https://stackoverflow.com/a/5786362/6786382.