Assume I have a following code in Java EE / EJB / JAX-RS:
#POST
#Path("some/path")
#MyAnnotation
public MyResponse createActivation(MyRequest request, CustomValue value) {
// ...
}
How do I check for the presence of custom #MyAnnotation annotation and populate CustomValue value method parameter based on some request context parameters in case the annotation is present?
Note: I already have this code in Spring using HandlerInterceptorAdapter and HandlerMethodArgumentResolver. Now I need to do the same without Spring. I have already discovered the ContainerRequestFilter and I use it to check for the annotation, but now I am struggling with injecting the method parameter.
Custom method parameter injection is handled a little differently from normal (i.e. field, constructor) injection. With Jersey, this requires the implementation of a ValueFactoryProvider. For your case it would look something like
public class MyAnnotationParamValueProvider implements ValueFactoryProvider {
#Inject
private ServiceLocator locator;
#Override
public Factory<?> getValueFactory(Parameter parameter) {
if (parameter.getAnnotation(MyAnnotation.class) != null
&& parameter.getRawType() == CustomValue.class) {
final Factory<CustomValue> factory
= new AbstractContainerRequestValueFactory<CustomValue>() {
#Override
public CustomValue provide() {
final ContainerRequest request = getContainerRequest();
final String value = request.getHeaderString("X-Value");
return new CustomValue(value);
}
};
locator.inject(factory);
return factory;
}
return null;
}
#Override
public PriorityType getPriority() {
return Priority.NORMAL;
}
}
Then you need to register it with the ResourceConfig
public class AppConfig extends ResourceConfig {
public AppConfig() {
register(new AbstractBinder() {
#Override
protected void configure() {
bind(MyAnnotationParamValueProvider.class)
.to(ValueFactoryProvider.class)
.in(Singleton.class);
}
});
}
}
See a complete example in this Gist
See also:
Custom Method Parameter Injection with Jersey. It shows another way to do this, where you don't need to explicitly inject, and also you will be able to inject the value in all three areas (field, constructor, and method param).
Related
I see that with Spring boot is really simple create filters. Just follow post like this one https://www.baeldung.com/spring-boot-add-filter
What I have not able to find, is how to create annotations that subscribe specifics endpoints in the controller to one filter.
Something like in Jax-RS it would looks like
#GET
#Path("jax-rs-single")
#Reactive(ttlRequest = 2000)
#Produces(MediaType.APPLICATION_JSON)
public Single getSingle() {
return Single.just("Hello world single");
}
Where #Reactive it would trigger the ReactiveFilter implementation per request.
I also saw the #WebFlow annotation, but it's not what I want. I want to create a library where the consumers decide which filter use, just adding the annotation in the controller.
Any idea how to do something similar with Spring boot/MVC ?
Regards
I will try to describe here more about Custom annotation and the processor in Spring.
I don't know what you want or what you need, but I will give an generic example.
You have 2 options:
BeanProcessor
HandlerInterceptor
BeanProcessor
You need to build 3 things basically: Annotaton, BeanProcessor and a Callback to execute your logic if annotated. Here is an example of it and how it works:
1 - Create the annotation
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
#Documented
public #interface Reactive {
Integer ttlRequest;
}
2 - Implement a BeanPostProcessor
#Component
public class ReactiveAnnotationProcessor implements BeanPostProcessor {
private ConfigurableListableBeanFactory configurableBeanFactory;
#Autowired
public ReactiveAnnotationProcessor(ConfigurableListableBeanFactory beanFactory) {
this.configurableBeanFactory = beanFactory;
}
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
this.scanReactiveAnnotation(bean, beanName);
return bean;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
protected void scanReactiveAnnotation(Object bean, String beanName) {
this.configureMethodInjection(bean);
}
private void configureMethodInjection(Object bean) {
Class<?> managedBeanClass = bean.getClass();
MethodCallback methodCallback =
new ReactiveMethodCallback(configurableBeanFactory, bean);
ReflectionUtils.doWithMethod(managedBeanClass, methodCallback);
}
}
3 - Create the method callback (here is the logic to execute)
public ReactiveMethodCallback implements MethodCallback {
private ConfigurableListableBeanFactory configurableBeanFactory;
private Object bean;
public ReactiveMethodCallback(ConfigurableListableBeanFactory bf, Object bean) {
configurableBeanFactory = bf;
this.bean = bean;
}
#Override
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
if (!method.isAnnotationPresent(Reactive.class)){
return;
}
//YOUR LOGIC HERE
}
}
Here is a good source about annotation processing, it is about FieldProcessing but you can just change the interfaces to implement what you need if you have doubts: https://www.baeldung.com/spring-annotation-bean-pre-processor
[UPDATED] You can also create a HandlerInterceptor instead:
HandlerInterceptor
public class ReactiveFilterHandlerInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws
Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
// Test if the controller-method is annotated with #CustomFilter
Reactive filter = handlerMethod.getMethod().getAnnotation(Reactive.class);
if (filter != null) {
// ... do the filtering, or call the Component for filtering
}
}
return true;
}
}
And register your handler:
#Configuration
public class WebMvcConfig extends WebMvcConfigurer {
#Autowired
ReactiveFilterHandlerInterceptor reactiveFilterHandlerInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(reactiveFilterHandlerInterceptor);
}
}
If I understand what you want correctly the main problem is how to apply filter based on custom annotation.
So first of all, yes you can use a regular Spring filter (WebFilter in case of Spring Webflux or Filter in case of Spring MVC), but you'll need to write some custom logic.
To do filtering based on annotation you should:
Use RequestMappingHandlerMapping#getHandlerInternal() method to retrieve a reference to the method that handles the request (the getSingle() in your case)
When you manage to retrieve the HandlerMethod then you can check if that method has your custom annotation applied with hasMethodAnnotation(Class<A> annotationType) method.
When you know that, then you can react accordingly: either chain.doFilter(request, response) without performing any actions, or apply your custom logic, and then trigger the rest of the filter chain.
Let's say I have a custom ConstraintValidator:
public class FooValidator implements ConstraintValidator<ValidFoo, String> {
#Override
public void initialize(final ValidFoo foo) {
// No-op
}
#Override
public boolean isValid(final String foo, final ConstraintValidatorContext context) {
}
}
I'd like to be able to initialize this class by passing some configuration from the ServiceConfiguration in Dropwizard run or initialize.
Is this possible?
First, it's worth noting that the upcoming Dropwizard 2.0.0 release has built in support for this
For now, the process is a bit involved. You basically want to re-bootstrap the Hibernate validation but with a custom constraint validator factory that would support injection.
It's gonna involve about 4 custom classes, so bear with me. Here goes:
First, we start by registering a custom feature to wrap this functionality, into our Application class:
public void run(MainConfiguration config, Environment environment) throws Exception {
// ...
environment.jersey().register(InjectingValidationFeature.class);
}
Now we define the feature: InjectingValidationFeature - it basically registers our custom implementations within the service container:
public class InjectingValidationFeature implements Feature {
#Override
public boolean configure(FeatureContext context) {
context.register(new AbstractBinder() {
#Override
protected void configure() {
bindFactory(ValidatorFactory.class).to(Validator.class).in(Singleton.class);
bind(InjectingConfiguredValidator.class).to(ConfiguredValidator.class).in(Singleton.class);
bind(InjectingConstraintValidatorFactory.class).to(ConstraintValidatorFactory.class).in(Singleton.class);
}
});
return true;
}
}
Now we define those classes that we are registering above. Let's start with the core piece, the InjectingConstraintValidatorFactory which is what Hibernate Validator will actually use to create the constraint validators. Note that because we are registering them in the container, we can actually start injecting stuff already, here is our custom ConstraintValidatorFactory making use of the service locator to make dependency injection possible:
public class InjectingConstraintValidatorFactory implements ConstraintValidatorFactory {
private final ServiceLocator serviceLocator;
#Inject
public InjectingConstraintValidatorFactory(ServiceLocator serviceLocator) {
this.serviceLocator = serviceLocator;
}
#Override
public <T extends ConstraintValidator<?, ?>> T getInstance(Class<T> key) {
return this.serviceLocator.createAndInitialize(key);
}
#Override
public void releaseInstance(ConstraintValidator<?, ?> instance) {
this.serviceLocator.preDestroy(instance);
}
}
Now our factory for the central javax.validation.Validator interface:
public class ValidatorFactory implements Factory<Validator> {
private final ConstraintValidatorFactory constraintValidatorFactory;
#Inject
public ValidatorFactory(ConstraintValidatorFactory constraintValidatorFactory) {
this.constraintValidatorFactory = constraintValidatorFactory;
}
#Override
public Validator provide() {
return Validation.byDefaultProvider().configure().constraintValidatorFactory(
this.constraintValidatorFactory).buildValidatorFactory()
.getValidator();
}
#Override
public void dispose(Validator instance) {
// Nothing
}
}
And finally, our InjectingConfiguredValidator, notice how it's just using DropwizardConfiguredValidator but with an #Inject which would allow us to receive the validator from our ValidatorFactory above:
public class InjectingConfiguredValidator extends DropwizardConfiguredValidator {
#Inject
public InjectingConfiguredValidator(Validator validator) {
super(validator);
}
}
That's it. With the above, we managed to both register an injection-aware Validator with Jersey and also into our service container so you can also #Inject Validator anywhere and use it however you like.
I have one interface and two implementations for this interface
Interface definition:
public interface DoSomething {}
Two implementations:
public ImplementationOne implements DoSomething{}
public ImplementationTwo implements DoSomething{}
Then inside another class, I want to get a different implementaion (either ImplementationOne or ImplementationTwo) based on the condition, how can I do that using Spring?
Something like..
Public ServiceManager {
Private DoSomething doSomething = null;
Public void do() {
If (condition1) {
doSomething = new ImplementationOne();
} else {
doSomething = new ImplementationTwo();
}
}
}
You should definitely auto wire ApplicationContext type using #Autowire annotation. Then if you did it like this:
#Autowire
ApplicationContext context
Then you should get your desired bean like this:
context.getBean(yourDesiredType.class)
Like that you can get any bean you want to be placed under any matching type according to your example.
Another option to consider is have a configuration bean - for example -
#Configuration
public class EntityRepositoryConfiguration {
private Map<Entity, EntityRepository> entityEntityRepositoryMap = new HashMap<>();
protected EntityRepositoryConfiguration() {
entityEntityRepositoryMap.put(Entity.Book, new BookRepository());
}
#Bean
public EntityRepository getByEntityType(Entity entity) {
return entityEntityRepositoryMap.get(entity);
}
}
And then inject the configuration bean to your other beans and use the getEntityType method (for example) to get beans injected.
I'm using the HK2 container in my Jersey application . I need to use my custom factory method to get the injected instance from the HK2 container.
For example ,
// Here I declare the IOC binding.
public class ApplicationBinder extends AbstractBinder {
#Override
protected void configure() {
bind(Logger.class).to(ILogger.class).in(Singleton.class);;
bind(MySqlRepository.class).to(IRepository.class).in(Singleton.class);
}
}
public class MyApplication extends ResourceConfig {
public static ApplicationBinder binder ;
public MyApplication () {
binder = new ApplicationBinder();
register(binder);
packages(true, "com.myapplication.server");
}
}
Here is my code :
public class BusinessLogic
{
//#Inject
//ILogger logger ;
//Instead
ILogger logger = DependencyResolver .resolve(ILogger.class) // resolve will get ILogger from HK2 container
}
The reason I need to do this way is for sometimes , I allocate classes manually which has dependencies , so in this way each use of #Inject return null.
For example, if I use new BusinessLogic() , then the logger with #Inject is null. I have to bind businesslogic also and use IOC in order to get the ILogge.
I need something like this:
public class DependencyResolver {
public static <T> T resolve(Class<T> beanClass){
return instance;
}
}
I need to use the DependencyResolver in order to get the instances I registered in MyApplication.
Any suggestions.
Thanks in advance...
I'm not 100% sure what exactly you want to do, but ...
I think you misunderstood AbstractBinder.bind(...) or bindings itself. Also, afaig you can't inject something into an instance which is not kinda managed component (like your BusinessLogic).
See jersey.java.net - ioc for examples regarding your BusinessLogic. You may have a look at ComponentProvider and/or InjectableProvider
For your ILogger I would suggest to create and bind a Factory like this:
public class LoggerFactory implements Factory<ILogger> {
// inject stuff here if you need (just an useless example)
#Inject
public LoggerFactory(final UriInfo uriInfo) {
this.uriInfo = uriInfo;
}
#Override
public ILogger provide() {
// here you resolve you ilogger
return MyLocator.resolve(ILogger.class);
}
#Override
public void dispose(ILogger instance) {
// ignore
}
}
Bind Factory
public class ApplicationBinder extends AbstractBinder {
#Override
protected void configure() {
bindFactory(LoggerFactory.class).to(ILogger.class).in(PerLookup.class /* or other scopeAnnotation if needed */);
// what's you Logger.class ?
// bind(Logger.class).to(ILogger.class).in(Singleton.class);
// bind(MySqlRepository.class).to(IRepository.class).in(Singleton.class);
}
}
Hope this was helpful somehow. Maybe someone is willing to write something about Providers for your case.
Is there any way to have spring call a factory with a runtime parameter of the type of the parent class of a variable it is trying to autowire?
For example, let's say I have something like this:
interface IConfig {
}
interface IConfigProvider {
IConfig getConfig(Class<?> type)
}
class MyClass {
#Autowired
private IConfig _config;
}
Is there anyway to have spring, when autowiring MyClass._config to essentially call IConfigProvider.getConfig(MyClass.class) (well the concrete version that is in the context) at runtime to wire the variable?
I know I could autowire the factory and call it myself, I could even "hide" it in a base class but I am trying to avoid this.
NOTE: I am very new to Spring so if I am asking something really stupid/not using the right terminology, I apologise.
You would need to create a FactoryBean for that. Something like this should do the trick.
class IConfigFactoryBean implements FactoryBean<IConfig> {
#Autowired
private IConfigProvider configProvider;
private IConfig config;
#PostConstruct
public void initialize() {
config = configProvider.getConfig(...);
}
#Override
public IConfig getObject() throws Exception {
return config;
}
#Override
public Class<?> getObjectType() {
return ...;
}
#Override
public boolean isSingleton() {
return true;
}
}
I am not sure what the argument would be. Note that if you have several instances of IConfig you will need to qualify them as Spring won't be able to know which one it has to inject based on a simple #Autowired annotation. Check the javadoc of #Qualifier for more information.