Implement a filter that supports regular matching and the problems encountered with spring management filter

Source: Internet
Author: User

It is believed that many people will worry about the Url-pattern of HTTP filter without supporting the regular. For example, in a Web project, struts, when we want to filter all or an action, and not filter other actions, especially if you do not want to filter the static resources, if your struts configured the action suffix may be better, Unfortunately, my project is not set suffix, all can not use Url-pattern only a few rules supported, so I implemented an abstract class, to support the configurable filter and do not need to filter the URL rules, and support the regular, the specific source is visible below.

Import Java.io.ioexception;import Java.util.regex.pattern;import Javax.servlet.filter;import Javax.servlet.filterchain;import Javax.servlet.filterconfig;import Javax.servlet.servletexception;import Javax.servlet.servletrequest;import Javax.servlet.servletresponse;import javax.servlet.http.httpservletrequest;/ * * <p> * Support using regular configuration filter Specify URL * * web. XML configuration When adding Init-params:include: Configure URL rules that need to be filtered, support regular, multiple ', ' split * Exclude: Configure URLs that do not need to be filtered Rules, support regular, multiple have ', ' split * </p> * * @author Vicky * @date 2015-5-13 */public abstract class Patternfilter implements Filt ER {protected pattern[] Includepattern = null;protected pattern[] Excludepattern = null;public final void init (FILTERCONFI G Filterconfig) throws Servletexception {String include = Filterconfig.getinitparameter ("include"); String exclude = Filterconfig.getinitparameter ("exclude"); if (null! = include &&! "). Equals (include)) {string[] arr = Include.split (","); Includepattern = new Pattern[arr.length];for (int i = 0; i < Arr.le Ngth; i++){Includepattern[i] = Pattern.compile (Arr[i]);}} if (null! = Exclude &&! "". Equals (exclude)) {string[] arr = Exclude.split (","); Excludepattern = new Pattern[arr.length];for (int i = 0; i < Arr.le Ngth; i++) {Excludepattern[i] = Pattern.compile (Arr[i]);}} Innerinit (filterconfig);} /** * Subclass Initialization Method * * @param filterconfig */public abstract void Innerinit (Filterconfig filterconfig) throws Servletexcepti On;public void Destroy () {//TODO auto-generated Method stub}/** * Filter methods, final subclasses are not overwritten, regular matching rules are implemented, subclasses cover Innerdofilter */ Public final void DoFilter (ServletRequest servletrequest, Servletresponse servletresponse, Filterchain filterchain) Throws IOException, servletexception {String url = ((httpservletrequest) servletrequest). Getservletpath (); if ( Checkexclude (URL) | | !checkinclude (URL)) {//without filtering the request, Passfilterchain.dofilter (ServletRequest, servletresponse); return;} Call Innerdofilter to filter innerdofilter (ServletRequest, Servletresponse, Filterchain); return;} /** * Requires sub-class overlay to implement filtering logic * * @param servleTrequest * @param servletresponse * @param filterchain */public abstract void Innerdofilter (ServletRequest servletrequest , Servletresponse Servletresponse,filterchain Filterchain) throws IOException, servletexception;/** * Verify that the access request is in the Include list * * @param requesturl * @return */public Final boolean checkinclude (String requesturl) {Boolean flag = True;if (Null = = Includepattern | | includepattern.length = = 0) {return flag;} for (Pattern Pat:includepattern) {if (flag = Pat.matcher (Requesturl). matches ()); return flag;}  /** * Verify that the access request is in the Exclude list * * @param requesturl * @return */public Final boolean checkexclude (String requesturl) {Boolean Flag = false;if (null = = Excludepattern | | excludepattern.length = = 0) {return flag;} for (Pattern Pat:excludepattern) {if (flag = Pat.matcher (Requesturl). matches ()); return flag;}}
The source code is actually very simple, only the basic need to pay attention to the place: Java Matcher class is thread insecure, all in the filter must not be shared, but pattern can, so we initialize the specified rules into pattern cache up, Avoid initializing pattern for each invocation, and improve efficiency by using the final modification method to prevent subclasses from overwriting Dofilter's implementation logic and by invoking an abstract methodInnerdofilter () to invoke the filter logic of the subclass.

Here's a look at how to use the filter. Very simple, first write a filter inherits from this class, implement Innerdofilter () and Innerinit () method, Then add the configuration of the filter in Web. config, you can specify two Init-param when configured, see below for configuration.

Filter><filter-name>loginfilter</filter-name><filter-class> Org.springframework.web.filter.delegatingfilterproxy</filter-class><init-param><description> Specify Spring Management filter life cycle </description><param-name>targetFilterLifecycle</param-name>< param-value>true</param-value></init-param><init-param><description> Configure the URL rules that need to be filtered, Need to use regular, multiple between have ', ' split </description><param-name>include</param-name><param-value></ Param-value></init-param><init-param><description> configuration does not need to filter the URL rules, need to use regular, multiple have ', ' split </ Description><param-name>exclude</param-name><param-value>/css/.*,/js/.*,/images/.*,/fonts /.*,/plugin/.*</param-value></init-param></filter><filter-mapping><filter-name> Loginfilter</filter-name><url-pattern>/*</url-pattern></filter-mapping>
From the above configuration file can be seen, added two initialization parameters, meaning configuration is described in detail. However, my configuration might be different from the normal filter configuration because I used spring to manage the filter, so the fliter-class here is a class of spring (Org.springframework.web.filter.DelegatingFilterProxy), this class defaults to Filter-name to find the corresponding bean to initialize to filter. Here's a look at spring's classDelegatingfilterproxy.

The init () method needs to be called from the Patternfilter code, but if you use spring's Delegatingfilterproxy class to manage the filter, the filter's init is not called by default ( ) method, because spring does not manage the life cycle of the filter by default, so the init () and Destory () methods are not called. The following is a simple analysis of Delegatingfilterproxy This class of source code, to see where is the key to solve the problem.

First open the Delegatingfilterproxy class source code, you can see inherit from Genericfilterbean, and Genericfilterbean implemented the filter, and implemented the Init () method.

Public final void Init (Filterconfig filterconfig) throws Servletexception {Assert.notnull (filterconfig, "filterconfig Must not is null "), if (logger.isdebugenabled ()) {Logger.debug (" Initializing filter "+ filterconfig.getfiltername () +" ' ");} This.filterconfig = filterconfig;//Set Bean properties from init parameters.try {propertyvalues PVs = new Filterconfigpro Pertyvalues (Filterconfig, this.requiredproperties); Beanwrapper bw = propertyaccessorfactory.forbeanpropertyaccess (this); Resourceloader Resourceloader = new Servletcontextresourceloader (Filterconfig.getservletcontext ()); Bw.registercustomeditor (Resource.class, New Resourceeditor (Resourceloader, this.environment)); InitBeanWrapper (BW) ; Bw.setpropertyvalues (PVs, True);} catch (Beansexception ex) {String msg = "Failed to set beans properties on filter '" +filterconfig.getfiltername () + "':" + Ex.getmessage (); Logger.error (msg, ex); throw new Nestedservletexception (MSG, ex);} Let subclasses do whatever initialization they like.initfilteRbean (), if (logger.isdebugenabled ()) {Logger.debug ("Filter" + filterconfig.getfiltername () + "' configured Successfully ");}}
As you can see from the code aboveGenericfilterbean'sInit () method by calling theInitfilterbean () to invoke the subclass's Init () method, let's look at the Delegatingfilterproxy Initfilterbean () method.

protected void Initfilterbean () throws Servletexception {synchronized (this.delegatemonitor) {if (this.delegate = = null) {//If no target bean name specified, use filter name.if (this.targetbeanname = = null) {This.targetbeanname = Getfilternam E ();} Fetch Spring Root Application context and initialize the delegate early,//if possible. If the root application context would be a started after this//filter proxy, we'll have to resort to lazy initialization. Webapplicationcontext WAC = Findwebapplicationcontext (); if (WAC! = null) {this.delegate = Initdelegate (WAC);}}}}
The method internally calls theinitdelegate () method.

Protected filter initdelegate (Webapplicationcontext WAC) throws servletexception {Filter delegate = Wac.getbean ( Gettargetbeanname (), Filter.class), if (Istargetfilterlifecycle ()) {Delegate.init (Getfilterconfig ());} return delegate;}
The method internally invokes the class that is being proxied (that is, our own filter) init () method. Note, however, that there is an if judgment at the time of invocation, that is, whether spring is allowed to manage the life cycle of the filter (istargetfilterlifecycle ()), and look at the inside of this method if it is judged.

/** * Return whether to invoke the {@code Filter.init} and * {@code Filter.destroy} lifecycle methods on the target bean. */protected Boolean istargetfilterlifecycle () {return this.targetfilterlifecycle;}
The inside of the method is very simple, just to determine the value of a variable, the value of the variable determines whether to allow spring to manage the filter's life cycle, the default is false, so when found here, we already know what caused the cause. So it is natural to see if there is a way to set the value of the variable, of course, there must be (Settargetfilterlifecycle ()), and then find out where this method was called, the results found no place to call the method, note is displayed, So for the time being, so it is natural to think of inheriting the class to meet their own needs, but the total feeling is wrong, so read the comments of this class, the results are very happy to find the note mentioned in the value of this variable if modified.

* <p><b>NOTE:</b> The lifecycle methods defined by the Servlet Filter interface * would by default <i& Gt;not</i> is delegated to the target bean, relying on the * Spring application context to manage the lifecycle of T Hat Bean. Specifying * the "targetfilterlifecycle" filter Init-param as "true" would enforce invocation * of the {@code Filter.init} and {@code Filter.destroy} lifecycle methods * on the target bean, letting the servlet container manage the Filter LIFECYC Le.
It is simple to add a parameter to the Init-param when configuring the filter, calledTargetfilterlifecycle, the value is true, so try, the result is OK. Although the problem solved, but still want to see where exactly is called settargetfilterlifecycle () This method, after all, there is no place to display the call, so go back to the source.
We go back to the init () method of the Genericfilterbean class and see where there is a place where you want to set the value of the variable: bw.setpropertyvalues (PVs, true); The result is an in-depth look at the Settargetfilterlifecycle value set in this place , which is to get all the Init-param parameters in the filter, then get the corresponding set method by reflection based on the parameter name, and call the assignment. There is a lot of confusion about the specific code, but it does not affect the understanding of the problem. According to the above logic,all variables in the Delegatingfilterproxy class should be specified by Init-param only if the corresponding set method is present.

These are the filter classes of configurable rules that I implement myself and the problems encountered by spring management filter.



Implement a filter that supports regular matching and the problems encountered with spring management filter

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.