In Spring programming, filters are built with the following annotations:

  • @ServletComponentScan
  • @WebFilter

So it looks like you can just use these two notes and get on with it. But after the production, still can encounter fancy problems:

  • Not working
  • The order is wrong
  • Execute multiple waits

Most of them thought it was easy to use and didn’t care. It is still necessary to be proficient in the process and principles of filter execution.

The @webFilter filter cannot be automatically injected

To count interface time, implement a filter:The filter is marked@WebFilter. So the launcher annotates the scan@ServletComponentScanPut it into effect:Then, provide a UserController:Failed to start an applicationTimeCostFilter looks like a normal Bean. Why can’t it be injected automatically?

The source code parsing

Essentially, the filter is@WebFilterAfter decoration, the TimeCostFilter is only wrapped as a FilterRegistrationBean, and the TimeCostFilter itself is only instantiated as an InnerBean, This means that the TimeCostFilter instance is not registered with the Spring container as a Bean.So when we try to automatically inject TimeCostFilter, we fail. With this conclusion in mind, we can clarify some of the key logic with two questions:

What is a FilterRegistrationBean? How is it defined

javax.servlet.annotation.WebFilter So it’s not Spring, it’s a Servlet specification. Spring Boot uses it when the Spring Boot project uses itorg.springframework.boot.web.servlet.FilterRegistrationBeanWrap an instance of the @webFilter tag. Implementatively, the FilterRegistrationBean#Filter attribute is an instance of the @webfilter tag. You can see this in the screenshot above.

When we define a Filter class, we might expect to automatically generate an instance of it and point to it with the Filter name as the Bean name. However, debugging revealed that in Spring Boot, the Bean name was indeed correct, except that the Bean instance was actually FilterRegistrationBean.

How was the FilterRegistrationBean originally obtained?

You have to trace back to how the @Webfilter annotation was handled.

How does @webFilter work

When using @webFilter, the Filter is loaded with two conditions:

  • The @ WebFilter declaration
  • In a path that can be scanned by @ServletComponentScan

Direct search pair@WebFilterThe use of can be foundWebFilterHandlerUse it:Therefore, we choose indoHandle()Breaking pointDebug starts, observing the call stack:Visible on the@WebFilterThe processing is done when SB starts, inServletComponentRegisteringPostProcessorTriggered to scan and process the following annotations:

  • @WebFilter
  • @WebListener
  • @WebServlet

The WebFilterHandler handles the use of @webfilter:

In the end,WebServletHandlerBy the parent classServletComponentHandlerThe template method pattern handles all of the by@WebFilterAnnotated classes:You can see that the FilterRegistrationBean you eventually register is your custom WebFilter.

Look at the second question:

When to instantiate TimeCostFilter

When was the TimeCostFilter instantiated? Why isn’t it a normal Bean? We can add breakpoints to the TimeCostFilter constructor for quick initialization timing:Combined with the source code, you can find:

  • When Tomcat starts (onstartUp), the FilterRegistrationBean is created
  • When the FilterRegistrationBean is created (createBean) is createdTimeCostFilterAssembling itself, whileTimeCostFilterIs through theResolveInnerBeanTo create the
  • The TimeCostFilter instance is ultimately a kind of InnerBean

So the TimeCostFilter instance ends up being an InnerBean and cannot be injected automatically.

correction

Find the root cause, and you’ll know how to solve it.

A. use B. use C. use D. use@WebFilterWhen you decorate the filter, beans of type TimeCostFilter are not registered with the Spring container; FilterRegistrationBean is actually registered. Considering that there may be more than one Filter, you can modify it as follows:

  • Inject the FilterRegistrationBean type instead of the TimeCostFilter type
  • The injected name is a fully qualified name that contains the package name and cannot be used directlyTimeCostFilter, so that there are multiple filters, can be accurate match.

conclusion

A Filter built in the @webFilter fashion cannot be injected automatically directly based on the Filter definition type, because the Filter itself is rendered as an internal Bean and is eventually rendered to Spring via the FilterRegistrationBean. So auto-assembly can be done by automatically injecting the FilterRegistrationBean type.