首页 > 学院 > 开发设计 > 正文

Spring Boot 之web Filter --支持排序的使用扩展

2019-11-08 01:32:26
字体:
来源:转载
供稿:网友

SPRing Boot 之web Filter --支持排序的使用扩展

  

为了spring boot支持注解@WebFilter("/*")的web filter组件排序,我们需要对其扩展。

  本博客对web filter的排序支持注解@Order(Integer.MAX_VALUE),也支持spring的Ordered接口。跟踪源码,web fiter的注册主要由ServletContextInitializerBeans实现,但由于sprirng对其某些方法访问权限的控制,我只能copy一份,修改方法权限和利用反射技术实现。

   
package com.sdcuike.spring.extend.web;import java.lang.reflect.Field;import java.util.AbstractCollection;import java.util.ArrayList;import java.util.Collections;import java.util.Comparator;import java.util.EventListener;import java.util.HashSet;import java.util.Iterator;import java.util.LinkedHashMap;import java.util.List;import java.util.Map;import java.util.Map.Entry;import java.util.Set;import javax.servlet.Filter;import javax.servlet.MultipartConfigElement;import javax.servlet.Servlet;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import org.springframework.aop.scope.ScopedProxyUtils;import org.springframework.beans.factory.ListableBeanFactory;import org.springframework.beans.factory.support.BeanDefinitionRegistry;import org.springframework.boot.web.servlet.DelegatingFilterProxyRegistrationBean;import org.springframework.boot.web.servlet.FilterRegistrationBean;import org.springframework.boot.web.servlet.RegistrationBean;import org.springframework.boot.web.servlet.ServletContextInitializer;import org.springframework.boot.web.servlet.ServletContextInitializerBeans;import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;import org.springframework.boot.web.servlet.ServletRegistrationBean;import org.springframework.core.annotation.AnnotationAwareOrderComparator;import org.springframework.util.LinkedMultiValueMap;import org.springframework.util.MultiValueMap;/** * copy自{@link ServletContextInitializerBeans},主要解决了反射解决spring的小bug及访问控制权限,以利于自定义扩展 *  * @author sdcuike *         <p> *         Created on 2017.02.13 *         <p> */public class ServletContextInitializerBeansModify extends AbstractCollection<ServletContextInitializer> {    private static final String                                        DISPATCHER_SERVLET_NAME = "dispatcherServlet";    protected static final Log                                         logger                  = LogFactory            .getLog(ServletContextInitializerBeansModify.class);    /**     * Seen bean instances or bean names.     */    protected final Set<Object>                                        seen                    = new HashSet<Object>();    protected final MultiValueMap<Class<?>, ServletContextInitializer> initializers;    private List<ServletContextInitializer>                            sortedList;    public ServletContextInitializerBeansModify(ListableBeanFactory beanFactory) {        this.initializers = new LinkedMultiValueMap<Class<?>, ServletContextInitializer>();        addServletContextInitializerBeans(beanFactory);        addAdaptableBeans(beanFactory);        List<ServletContextInitializer> sortedInitializers = new ArrayList<ServletContextInitializer>();        for (Map.Entry<?, List<ServletContextInitializer>> entry : this.initializers                .entrySet()) {            AnnotationAwareOrderComparator.sort(entry.getValue());            sortedInitializers.addAll(entry.getValue());        }        this.sortedList = Collections.unmodifiableList(sortedInitializers);    }    private void addServletContextInitializerBeans(ListableBeanFactory beanFactory) {        for (Entry<String, ServletContextInitializer> initializerBean : getOrderedBeansOfType(                beanFactory, ServletContextInitializer.class)) {            addServletContextInitializerBean(initializerBean.getKey(),                    initializerBean.getValue(), beanFactory);        }    }    private void addServletContextInitializerBean(String beanName,                                                  ServletContextInitializer initializer, ListableBeanFactory beanFactory) {        if (initializer instanceof ServletRegistrationBean) {            Servlet source = getServlet((ServletRegistrationBean) initializer);            addServletContextInitializerBean(Servlet.class, beanName, initializer,                    beanFactory, source);        } else if (initializer instanceof FilterRegistrationBean) {            Filter source = ((FilterRegistrationBean) initializer).getFilter();            addServletContextInitializerBean(Filter.class, beanName, initializer,                    beanFactory, source);        } else if (initializer instanceof DelegatingFilterProxyRegistrationBean) {            String source = getTargetFilterName((DelegatingFilterProxyRegistrationBean) initializer);            addServletContextInitializerBean(Filter.class, beanName, initializer,                    beanFactory, source);        } else if (initializer instanceof ServletListenerRegistrationBean) {            EventListener source = ((ServletListenerRegistrationBean<?>) initializer)                    .getListener();            addServletContextInitializerBean(EventListener.class, beanName, initializer,                    beanFactory, source);        } else {            addServletContextInitializerBean(ServletContextInitializer.class, beanName,                    initializer, beanFactory, initializer);        }    }    protected void addServletContextInitializerBean(Class<?> type, String beanName,                                                    ServletContextInitializer initializer, ListableBeanFactory beanFactory,                                                    Object source) {        this.initializers.add(type, initializer);        if (source != null) {            // Mark the underlying source as seen in case it wraps an existing bean            this.seen.add(source);        }        if (ServletContextInitializerBeansModify.logger.isDebugEnabled()) {            String resourceDescription = getResourceDescription(beanName, beanFactory);            int order = getOrder(initializer);            ServletContextInitializerBeansModify.logger.debug("Added existing "                    + type.getSimpleName() + " initializer bean '" + beanName                    + "'; order=" + order + ", resource=" + resourceDescription);        }    }    protected final String getResourceDescription(String beanName,                                                  ListableBeanFactory beanFactory) {        if (beanFactory instanceof BeanDefinitionRegistry) {            BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;            return registry.getBeanDefinition(beanName).getResourceDescription();        }        return "unknown";    }    @SuppressWarnings("unchecked")    private void addAdaptableBeans(ListableBeanFactory beanFactory) {        MultipartConfigElement multipartConfig = getMultipartConfig(beanFactory);        addAsRegistrationBean(beanFactory, Servlet.class,                new ServletRegistrationBeanAdapter(multipartConfig));        addAsRegistrationBean(beanFactory, Filter.class,                new FilterRegistrationBeanAdapter());        for (Class<?> listenerType : ServletListenerRegistrationBean                .getSupportedTypes()) {            addAsRegistrationBean(beanFactory, EventListener.class,                    (Class<EventListener>) listenerType,                    new ServletListenerRegistrationBeanAdapter());        }    }    private MultipartConfigElement getMultipartConfig(ListableBeanFactory beanFactory) {        List<Entry<String, MultipartConfigElement>> beans = getOrderedBeansOfType(                beanFactory, MultipartConfigElement.class);        return (beans.isEmpty() ? null : beans.get(0).getValue());    }    private <T> void addAsRegistrationBean(ListableBeanFactory beanFactory, Class<T> type,                                           RegistrationBeanAdapter<T> adapter) {        addAsRegistrationBean(beanFactory, type, type, adapter);    }    private <T, B extends T> void addAsRegistrationBean(ListableBeanFactory beanFactory,                                                        Class<T> type, Class<B> beanType, RegistrationBeanAdapter<T> adapter) {        List<Map.Entry<String, B>> beans = getOrderedBeansOfType(beanFactory, beanType,                this.seen);        for (Entry<String, B> bean : beans) {            if (this.seen.add(bean.getValue())) {                int order = getOrder(bean.getValue());                String beanName = bean.getKey();                // One that we haven't already seen                RegistrationBean registration = adapter.createRegistrationBean(beanName,                        bean.getValue(), beans.size());                registration.setName(beanName);                registration.setOrder(order);                this.initializers.add(type, registration);                if (ServletContextInitializerBeansModify.logger.isDebugEnabled()) {                    ServletContextInitializerBeansModify.logger.debug(                            "Created " + type.getSimpleName() + " initializer for bean '"                                    + beanName + "'; order=" + order + ", resource="                                    + getResourceDescription(beanName, beanFactory));                }            }        }    }    private final int getOrder(Object value) {        return new AnnotationAwareOrderComparator() {            @Override            public int getOrder(Object obj) {                return super.getOrder(obj);            }        }.getOrder(value);    }    private <T> List<Entry<String, T>> getOrderedBeansOfType(                                                             ListableBeanFactory beanFactory, Class<T> type) {        return getOrderedBeansOfType(beanFactory, type, Collections.emptySet());    }    private <T> List<Entry<String, T>> getOrderedBeansOfType(                                                             ListableBeanFactory beanFactory, Class<T> type, Set<?> excludes) {        List<Entry<String, T>> beans = new ArrayList<Entry<String, T>>();        Comparator<Entry<String, T>> comparator = new Comparator<Entry<String, T>>() {            @Override            public int compare(Entry<String, T> o1, Entry<String, T> o2) {                return AnnotationAwareOrderComparator.INSTANCE.compare(o1.getValue(),                        o2.getValue());            }        };        String[] names = beanFactory.getBeanNamesForType(type, true, false);        Map<String, T> map = new LinkedHashMap<String, T>();        for (String name : names) {            if (!excludes.contains(name) && !ScopedProxyUtils.isScopedTarget(name)) {                T bean = beanFactory.getBean(name, type);                if (!excludes.contains(bean)) {                    map.put(name, bean);                }            }        }        beans.addAll(map.entrySet());        Collections.sort(beans, comparator);        return beans;    }    @Override    public Iterator<ServletContextInitializer> iterator() {        return this.sortedList.iterator();    }    @Override    public int size() {        return this.sortedList.size();    }    /**     * Adapter to convert a given Bean type into a {@link RegistrationBean} (and hence a {@link ServletContextInitializer}.     */    protected interface RegistrationBeanAdapter<T> {        RegistrationBean createRegistrationBean(String name, T source,                                                int totalNumberOfsourceBeans);    }    /**     * {@link RegistrationBeanAdapter} for {@link Servlet} beans.     */    private static class ServletRegistrationBeanAdapter            implements RegistrationBeanAdapter<Servlet> {        private final MultipartConfigElement multipartConfig;        ServletRegistrationBeanAdapter(MultipartConfigElement multipartConfig) {            this.multipartConfig = multipartConfig;        }        @Override        public RegistrationBean createRegistrationBean(String name, Servlet source,                                                       int totalNumberOfSourceBeans) {            String url = (totalNumberOfSourceBeans == 1 ? "/" : "/" + name + "/");            if (name.equals(DISPATCHER_SERVLET_NAME)) {                url = "/"; // always map the main dispatcherServlet to "/"            }            ServletRegistrationBean bean = new ServletRegistrationBean(source, url);            bean.setMultipartConfig(this.multipartConfig);            return bean;        }    }    /**     * {@link RegistrationBeanAdapter} for {@link Filter} beans.     */    private static class FilterRegistrationBeanAdapter            implements RegistrationBeanAdapter<Filter> {        @Override        public RegistrationBean createRegistrationBean(String name, Filter source,                                                       int totalNumberOfSourceBeans) {            return new FilterRegistrationBean(source);        }    }    /**     * {@link RegistrationBeanAdapter} for certain {@link EventListener} beans.     */    private static class ServletListenerRegistrationBeanAdapter            implements RegistrationBeanAdapter<EventListener> {        @Override        public RegistrationBean createRegistrationBean(String name, EventListener source,                                                       int totalNumberOfSourceBeans) {            return new ServletListenerRegistrationBean<EventListener>(source);        }    }    private Servlet getServlet(ServletRegistrationBean object) {        try {            Field field = object.getClass().getDeclaredField("servlet");            field.setaccessible(true);            return (Servlet) field.get(object);        } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {            throw new RuntimeException(e);        }    }    private String getTargetFilterName(DelegatingFilterProxyRegistrationBean object) {        try {            Field field = object.getClass().getDeclaredField("targetBeanName");            field.setAccessible(true);            return (String) field.get(object);        } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {            throw new RuntimeException(e);        }    }}类的名字没有起好,先这样凑合吧。利用反射破除sprirng某些方法的访问,有下面两个方法:
private Servlet getServlet(ServletRegistrationBean object) {        try {            Field field = object.getClass().getDeclaredField("servlet");            field.setAccessible(true);            return (Servlet) field.get(object);        } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {            throw new RuntimeException(e);        }    }    private String getTargetFilterName(DelegatingFilterProxyRegistrationBean object) {        try {            Field field = object.getClass().getDeclaredField("targetBeanName");            field.setAccessible(true);            return (String) field.get(object);        } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {            throw new RuntimeException(e);        }    }因为启动过程中就会注册,所以反射没加缓存,不影响性能吧。修改了下面的方法的访问权限,以便子类复写(为了区别是spring的代码和自己扩展的代码,我写了两个类,便于区分,没写到一个类中):
protected void addServletContextInitializerBean(Class<?> type, String beanName,                                                    ServletContextInitializer initializer, ListableBeanFactory beanFactory,                                                    Object source)我们开始对web filter的注册做处理:
package com.sdcuike.spring.extend.web;import javax.servlet.Filter;import org.springframework.beans.factory.ListableBeanFactory;import org.springframework.boot.web.servlet.FilterRegistrationBean;import org.springframework.boot.web.servlet.ServletContextInitializer;import org.springframework.core.Ordered;import org.springframework.core.annotation.OrderUtils;/** * @author sdcuike *         <p> *         Created on 2017.02.13 *         <p> */public class ServletContextInitializerBeansExtend extends ServletContextInitializerBeansModify {    public ServletContextInitializerBeansExtend(ListableBeanFactory beanFactory) {        super(beanFactory);    }    @Override    protected void addServletContextInitializerBean(Class<?> type, String beanName, ServletContextInitializer initializer, ListableBeanFactory beanFactory, Object source) {        Integer order = OrderUtils.getOrder(source.getClass());        if (order == null && source instanceof Ordered) {            order = ((Ordered) source).getOrder();        }        if (Filter.class == type && order != null) {            FilterRegistrationBean filterRegistrationBean = (FilterRegistrationBean) initializer;            filterRegistrationBean.setOrder(order);        }        super.addServletContextInitializerBean(type, beanName, initializer, beanFactory, source);    }}主要对继承filter接口的类做处理即注册fiter:排序的规则注解@Order优先于接口Ordered 的值。为了使用我们扩展的注册类,我们还需要实现一个EmbeddedWebapplicationContext :
package com.sdcuike.spring.extend.web;import java.util.Collection;import org.springframework.boot.context.embedded.EmbeddedWebApplicationContext;import org.springframework.boot.web.servlet.ServletContextInitializer;/** * 自定义EmbeddedWebApplicationContext,主要为了支持@WebFilter 支持 @Order 排序 *  * @author sdcuike *         <p> *         Created on 2017.02.13 *         <p> */public class EmbeddedWebApplicationContextExtend extends EmbeddedWebApplicationContext {    @Override    protected Collection<ServletContextInitializer> getServletContextInitializerBeans() {        return new ServletContextInitializerBeansExtend(getBeanFactory());    }}启动spring boot应用:
package com.sdcuike.practice;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import com.sdcuike.spring.extend.web.EmbeddedWebApplicationContextExtend;/** * @author sdcuike *         <p> *         Created on 2016.12 *         <p> *         We generally recommend that you locate your main application class in a root package above other classes. <br> *         <p> *         We recommend that you follow Java’s recommended package naming conventions and use a reversed domain name (for example, com.example.project) */@SpringBootApplicationpublic class Application {    public static void main(String[] args) {        SpringApplication springApplication = new SpringApplication(Application.class);        springApplication.setApplicationContextClass(EmbeddedWebApplicationContextExtend.class);        springApplication.run(args);    }}相关源码:https://github.com/sdcuike/spring-boot-practice/tree/master/src/main/java/com/sdcuike/spring/extend/webhttps://github.com/sdcuike/spring-boot-practice/blob/master/src/main/java/com/sdcuike/practice/Application.java<spring-boot.version>1.5.1.RELEASE</spring-boot.version>
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表