How to create a bean after initialization? - spring

I want to provide an easy way to access to some header values, so my idea is:
#Autowired
private MyContext ctx;
problem is that I add infos in a filter at runtime, so I cannot set data inside:
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
servletRequest.getHeader(...)
I found this solution: I save data inside of a servlet attribute and the class just become a wrapper for that:
#Component
public class MyContext{
#Autowired
private HttpServletRequest request;
public DTO getDto(){
return (DTO)request.getAttribute("dto");
}
It works but I don't like that, in my debug, I cannot directly see the DTO object.
So, isn't there a way to create a bean after spring initialization?

i didn't get your question totally but i think so init method can help you. If you want some operation after bean creation just add those operation in init method and add the init method in bean creation configuration.
<bean name="bean" class="Bean" init-method="init"/>

Related

How to access request attributes in SpEL for spring data elastic search

I need to use dynamic index per request in spring data elastic search. I got it working by creating a #RequestScope object
how does singleton bean handle dynamic index
I am trying to get it working without creating #RequestScope object. I set the attributes in request. But I don't know to read it out in SpEL
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
httpRequest.setAttribute("indexName", "indexA");
#Document(indexName = "#${request.getAttribute('indexName')}", createIndex = false) --not working
public class MyDocument{
I also try #Document(indexName = "#${#request.getAttribute('indexName')}", createIndex = false)
Instead of trying to get this into a SpEL expression for the #Document annotation you'd better get the name for the index from the request in the function where you want to use Spring Data Elasticsearch methods - I don't just at them moment with which parameter you would get this.
Then use this name to create a IndexCoordinates object with that name and pass this as parameter to the ElasticsearchOperations method you use.

Why Servlet Filter can be Spring Bean?

What i know about Filter and Interceptor is that Filters as J2EE Specifications are part of the webserver and not the Spring framework. So some older articles explain that it is impossible to register filters as Spring Bean while Interceptor is possible.
But the results I got when I tested today is that Filters can be Spring Bean and also inject Spring Bean on Filters are possible too like Interceptors.
(I tested on SpringBoot Framework)
#Component
public class CustomFilterTest implements Filter {
#Override
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws ServletException, IOException {
chain.doFilter(request, response);
}
#Override
public void init(final FilterConfig filterConfig) throws ServletException {
Filter.super.init(filterConfig);
}
#Override
public void destroy() {
Filter.super.destroy();
}
}
#RestController
#RequiredArgsConstructor
public class ProductController {
private final CustomFilterTest customFilterTest;
#GetMapping("/test")
public ResponseEntity<Void> temp() {
System.out.println(customFilterTest);
return ResponseEntity.noContent().build();
}
}
Can anyone please explain to me?
We have to make a distinction between a regular Spring application and a Spring Boot application here. As with both, you can register a servlet filter as a bean, but the mechanism is a bit different.
Spring Framework
In plain Spring use the DelegatingFilterProxy to achieve this. The task of the DelegatingFilterProxy is to look for a bean with the same name as the filter in the root application context (the ApplicationContext registered through the ContextLoaderListener). This bean has to be your managed servlet filter.
#Configuration
#EnableWebMvc
public class WebConfiguration {
#Bean
public void YourFilter myFilter() { ... }
}
Then for the web application you would register a DelegatingFilterProxy with the name myFilter to make this work.
public class MyWebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
public void onStartup(ServletContext servletContext)
throws ServletException {
super.onStartup(servletContext);
servletContext.addFilter("myFilter", DelegatingFilterProxy.class);
}
Spring Boot
In Spring Boot it is a bit different as Spring Boot is also in control of your servlet container, like Tomcat. It basically means that Tomcat is also a managed bean in your ApplicationContext and Spring Boot can inject dependencies into it. So when Spring Boot detects a bean for the servlet filter it will automatically add it to the filter chain (without the need of a DelegatingFilterProxy).
Which means only an #Bean for your filter is needed.
#Configuration
public class WebConfiguration {
#Bean
public YourFilter myFilter() { ... }
}
Additionally you can configure things like URLs etc. by adding an additional FilterRegistrationBean for this filter.
Conclusion
For plain Spring the DelegatingFilterProxy has been around since Spring 1.2 which was released in 2005. This means if you are reading really, really, really old articles (before 2005) this was true, however with the addition of the DelegatingFilterProxy, this isn't anymore. With the release of Spring Boot, this became even a lesser issue, is it more or less is the only way to register a filter (as a managed bean).

Properties in application.properties not getting loaded in Filter class

I am trying to read the value from application.properties in one of the util library Filter we write.
My code is as below.
public class AuthorizationFilter extends GenericFilterBean
{
#Value ("${application.identifier}")
private String appId;
...
}
However the value appId is not read from application.properties though it is defined.
The issue occurs only with Filter classes.
Any pointers on how to fix this?
Like #M.Deinum said , If you let spring-boot manage the life cycle of the filter bean, then you will be able use the #Value annotation :
#Component
#Order(1)
public class CustomFilter implements Filter {
#Value ("${application.identifier}")
private String appId;
#Override
public void doFilter
ServletRequest request,
ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
LOG.info(
"Starting for req : {}",
req.getRequestURI());
chain.doFilter(request, response);
LOG.info(
"Anything");
}
// other methods
}
Keep in mind that if you provide your filter this way , you won't have to register it manually and if you want it to work for a particular url by registering it manually remember to remove the #Component annotation so that spring-boot won't take it up automatically.
Let the spring manage your filter class. You can register in your filter class like below :
#Bean
public FilterRegistrationBean registerFilter() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(this);
registration.addUrlPatterns("/*");
return registration;
}

Controller interceptor that process endpoint annotation in WebFlux

My team is in the middle of migrating our Spring MVC extensions to WebFlux.
We've got a feature that lets our clients customize metric of controller method. To do that we've created our annotation that is processed by HandlerInterceptorAdapter.
The problem is that I can't see any equivalent of this in Spring WebFlux. I can't use WebFilter because Spring does not know yet which endpoint will be called. How can I implement that?
The closest workaround I found is to use RequestMappingHandlerMapping and somehow build a map of Map<String(path), HandlerMethod>, but this is cumbersome and error prone in my opinion.
Is there any better way to solve this?
Edit:
It goes like this
public class MeteredHandlerInterceptor extends HandlerInterceptorAdapter {
public MeteredHandlerInterceptor() {
}
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// I save start time of method
return true;
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// I read endpoint method from the HandlerMethod, I apply any customisation by our custom #MeteredEndpoint annotation (for example custom name) and I save it in MeterRegistry
}
}
I haven't coded workaround yet because I didn't want to invest time in it, but I see that I could obtain HandlerMethod for path, but I'm not sure I will receive same HandlerMethod as I normally would when the controller is called.
Maybe little bit late, but it can still be useful for someone...
I have not found an easy way to do that, the best I was able to create is a HandlerAdapter bean that intercepts handling in the following way:
#Bean
#Order(Ordered.HIGHEST_PRECEDENCE)
public HandlerAdapter handlerAdapter(RequestMappingHandlerAdapter requestMappingHandlerAdapter) {
return new HandlerAdapter() {
#Override
public boolean supports(Object handler) {
return handler instanceof HandlerMethod;
}
#Override
public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {
// your stuff here...
// e.g. ((HandlerMethod) handler).getMethod().getAnnotations()...
return requestMappingHandlerAdapter.handle(exchange, handler);
}
};
}
The idea is that this adapter is used for all HandlerMethod handlers (those are the ones created by collecting annotated methods from #Controllers) and delegates the handling to the RequestMappingHandlerAdapter (that would be used directly for HandlerMethod handlers in normal case, notice the #Order annotation here).
The point is you can put your code before/after the invocation of the handle method and you are aware of the method being invoked at this point.
Solution:
#Component
class AuditWebFilter(
private val requestMapping: RequestMappingHandlerMapping
): WebFilter {
override fun filter(exchange: ServerWebExchange, chain: WebFilterChain): Mono<Void> {
// if not to call - then exchange.attributes will be empty
// so little early initialize exchange.attributes by calling next line
requestMapping.getHandler(exchange)
val handlerFunction = exchange.attributes.get(HandlerMapping.BEST_MATCHING_HANDLER_ATTRIBUTE) as HandlerMethod
val annotationMethod = handlerFunction.method.getAnnotation(MyAnnotation::class.java)
// annotationMethod proccesing here
}
}

Add more field into header using interceptor of spring boot not work

I'm using spring boot.
I want to add a field into header of every response. So that, i using interceptor. The code is:
#Component
public class ApiVersionInterceptor extends HandlerInterceptorAdapter{
private final Logger log = LoggerFactory.getLogger(ApiVersionInterceptor.class);
#Autowired
private Environment environment;
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception arg3) throws Exception {
String apiVersion = environment.getProperty(ApiVersion.VERSION_KEY.getKey());
log.debug("api-version:"+apiVersion);
response.addHeader("Api-Version", apiVersion);
}
}
And the configuration is:
#Configuration
public class InterceptorsConfiguration extends WebMvcConfigurerAdapter {
#Autowired
private ApiVersionInterceptor apiVersionInterceptor;
/**
* Add interceptor
*/
#Override
public void addInterceptors(final InterceptorRegistry registry) {
//Add api-version field to header of response
registry.addInterceptor(apiVersionInterceptor);
}
}
To make sure this snipped code is run because of:
2017-12-06 02:35:10,392 DEBUG [] [http-nio-8080-exec-7] ApiVersionInterceptor: api-version:1.9.0
But i don't understand, i don't see this field in the header of any response.
Update
My app use Restful webservice, so don't have view phase.
Thanks for help.
You should add header in a earlier phase, override the preHandle method in your ApiVersionInterceptor. Because in afterCompletion response is already committed and skip header changes.

Resources