Solution of failed injection of bean into zuulFilter

  • 2021-10-25 06:43:59
  • OfStack

zuulFilter failed to inject bean

1. Why use this

Last week, I wanted to realize the user authentication operation in the gateway layer zuul, that is, I need to call other micro-services in the gateway filter. According to the conventional practice, I annotated an feign interface with @ Autowired in filter, and failed to start 1 straight. After checking and checking with Du Niang Google, I only found one failure similar to "injecting bean into the filter", but all I said was that springMVC was not the gateway layer in springcloud

2. Solutions

After checking for a long time, I finally found the problem. In fact, when the error is reported at startup, the prompt is obvious, and no relevant instance can be found. Yes, the implementation class of feign interface is actually in other micro-services, and naturally it cannot be injected by conventional methods. The solution is actually very simple, that is, adding annotations to the startup class


@EnableFeignClient

Declare that this zuul is also a client that needs feign, and the problem is solved.

Filter use with bean injection

1. Start sequence of each element in web. xml

When the project starts, the listener listener is initialized first, then the filter filter, and finally servlet.

The Spring listener reads the spring configuration file at startup to initialize the spring container. When the dispatcherServlet of springMVC is initialized, the configuration file of springMVC is read to initialize the springMVC container. Each bean is instantiated when the Spring container is initialized. (Personally, I think that when the web container is initialized, all the elements are initialized in the above order, and the web container is initialized only after all the other elements are initialized. However, I have not seen an exact statement of 10 points at present, and I have time to study the source code in the future).

2. Use of filters

A lot of information on the Internet said that bean injected by spring could not be obtained in the filter, because the spring container had not been initialized when the filter was initialized, but it was not. Let's look at one piece of code:

Define filters in web. xml:


<filter>
  <filter-name>demoFilter</filter-name>  
  <filter-class>xx.framework.filter.demoFilter</filter-class>
</filter>
<filter-mapping>  
<filter-name>demoFilter</filter-name>
   <url-pattern>/*</url-pattern>
</filter-mapping>

Then in the filter initialization method init:


@Override
public void init(FilterConfig filterConfig) throws ServletException {
    ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(filterConfig.getServletContext());
    RedisTemplate demoBean = (RedisTemplate)context.getBean("redisTemplate");
    System.out.println(demoBean);
 }

After testing, the redisTemplate in spring can be obtained at this time, which shows that the spring container is indeed initialized before the filter. So back to the problem that bean cannot be injected into the filter, what is the reason? As you can see, here the bean is fetched through the applicationContext rather than directly injected.

My personal understanding is that the filter is defined in servlet specification, and is not managed by spring container, nor can it be directly injected into bean in spring (error will be reported). Of course, there are ways to use filters through spring injection, which are first defined in web. xml:


<filter>
  <filter-name>DelegatingFilterProxy</filter-name> 
  <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
  <init-param>
    <param-name>targetBeanName</param-name>
    <param-value>demoFilter</param-value>
  </init-param>
  <init-param>
    <param-name>targetFilterLifecycle</param-name>
    <param-value>true</param-value>
  </init-param>
</filter>
<filter-mapping>
  <filter-name>DelegatingFilterProxy</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

Then configure demoFilter, the bean, in the spring container:


<bean id="demoFilter" class="xx.framework.filter.demoFilter" />

The injected bean can be obtained in the doFilter method:


@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain filterChain) throws IOException, ServletException {
   System.out.println(redisTemplate.getClientList());
}

Where redisTemplate is injected through the @ Resource annotation.


Related articles: