SpringBoot(二) starter与servlet容器

Posted by ZhouJ000 on October 10, 2018
最后更新于:2018-10-10

SpringBoot(一) 启动与自动配置
SpringBoot(二) starter与servlet容器
SpringBoot(三) Environment
SpringBoot(四) 集成apollo遇到的事儿
SpringBoot(五) 健康检查(上)
SpringBoot(六) 健康检查(下)

查看spring boot目录,在spring-boot\spring-boot-project下可以看出分类

pom.xml                              spring-boot-cli/           spring-boot-properties-migrator/
spring-boot/                         spring-boot-dependencies/  spring-boot-starters/
spring-boot-actuator/                spring-boot-devtools/      spring-boot-test/
spring-boot-actuator-autoconfigure/  spring-boot-docs/          spring-boot-test-autoconfigure/
spring-boot-autoconfigure/           spring-boot-parent/        spring-boot-tools/

boot-starter

Spring Boot application starters

starter主要的作用是做了包依赖管理,查看spring-boot-starters文件夹下,举例spring-boot-starter-web,其实只有一个pom文件:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starters</artifactId>
		<version>${revision}</version>
	</parent>
	<artifactId>spring-boot-starter-web</artifactId>
	<name>Spring Boot Web Starter</name>
	<description>Starter for building web, including RESTful, applications using Spring
		MVC. Uses Tomcat as the default embedded container</description>
	<properties>
		<main.basedir>${basedir}/../../..</main.basedir>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-json</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-tomcat</artifactId>
		</dependency>
		<dependency>
			<groupId>org.hibernate.validator</groupId>
			<artifactId>hibernate-validator</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
		</dependency>
	</dependencies>
</project>

实现自己的starter

@EnableAutoConfiguration里通过@Import导入了AutoConfigurationImportSelector,在selectImports方法中通过SpringFactoriesLoader读取META-INF/spring.factories的配置。@Enable开头的Annotation可以借助@Import的支持,收集和注册特定场景相关的bean定义。将其中EnableAutoConfiguration对应的配置项通过反射实例化为对应的标注了@Configuration的JavaConfig形式的IOC容器配置类,然后汇总为一个并加载到IOC容器。ConfigurationClassPostProcessor由于实现了BeanDefinitionRegistryPostProcessor,会在Spring容器初始化时被调用postProcessBeanDefinitionRegistry方法,其中会创建ConfigurationClassParser类解析configCandidates,实际是用ConditionEvaluator来判断@Conditional注解

由上一篇知道,自动配置通过META-INF/spring.factories配置与@Conditional注解来生成java配置,通过@ConfigurationProperties注解的配置文件加载默认配置

1 . 创建一个简单的maven项目
2 . pom加入依赖与添加编码配置

<properties>
	<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-autoconfigure</artifactId>
		<version>2.0.5.RELEASE</version>
	</dependency>
</dependencies>

3 . 在resources下新建META-INF文件夹,里面创建spring.factories文件
4 . 加入配置

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.zj.hello-starter.HelloAutoConfiguration

5 . 创建HelloAutoConfiguration文件

// 自动配置类
@Configuration
/*
 *  根据HelloProperties提供的参数,并通过@ConditionalOnClass判断Hello这个类
 *  在类路径中是否存在,且当容器中没有这个Bean的情况下自动配置这个Bean
 */
@EnableConfigurationProperties(HelloProperties.class)
@ConditionalOnClass(Hello.class)
@ConditionalOnProperty(prefix = "hello", value = "enabled", matchIfMissing = true)
public class HelloAutoConfiguration {
	@Autowired
	private HelloProperties helloProperties;
	
	@Bean
	@ConditionalOnMissingBean(Hello.class)
	public Hello hello() {
		Hello hello = new Hello();
		hello.setMsg(helloProperties.getMsg());
		return hello;
	}
}

6 . 创建HelloProperties配置

// 属性配置
@ConfigurationProperties(prefix = "hello")
public class HelloProperties {
	// 默认值
	private static final String MSG = "world";

	private String msg = MSG;

	public String getMsg() {
		return msg;
	}
	public void setMsg(String msg) {
		this.msg = msg;
	}
}

7 . 创建Hello类

// 判断依据类
public class Hello {
	private String msg;

	public String sayHello() {
		return "Hello " + msg;
	}

	public String getMsg() {
		return msg;
	}
	public void setMsg(String msg) {
		this.msg = msg;
	}
}

8 . 执行’mvn clean install’编译打包到本地mvn_lib
9 . 在自己的应用中加入依赖

<dependency>
	<groupId>com.zj</groupId>
	<artifactId>hello-starter</artifactId>
	<version>1.0-SNAPSHOT</version>
</dependency>

10 . 可以在application.yml中添加配置hello.msg: spring boot starter
11 . 写个测试类运行下

@RunWith(SpringRunner.class)
@SpringBootTest
public class Test6 {

    @Autowired
    public Hello hello;

    @Test
    public void test() {
        System.out.println(hello.sayHello());
    }
}

// 打印 Hello spring boot starter

自带servlet容器

从上面spring-boot-starter-web的pom文件中可以看到,引入了spring-boot-starter-tomcat依赖,所以启动web时就有了tomcat容器。除了tomcat,还可以通过exclusion子标签排除tomcat,加入spring-boot-starter-jetty来使用jetty容器

通过spring.factories文件可以发现了web.servlet包下的自动配置类

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
...
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
...

查看ServletWebServerFactoryAutoConfiguration类

@Configuration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
		ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
		ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
		ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration { // ... }


@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {}

发现import了ServletWebServerFactoryConfiguration.EmbeddedTomcat,继续点进去查看,是一个内部类,很简单,创建一个tomcat工厂

@Configuration
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
public static class EmbeddedTomcat {

	@Bean
	public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
		return new TomcatServletWebServerFactory();
	}
}

tomcatServletWebServerFactory 另一方面,import也导入了ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar,它是在ConfigurationClassPostProcessor.postProcessBeanFactory方法中会调用processConfigBeanDefinitions方法,然后调用loadBeanDefinitionsForConfigurationClass方法到loadBeanDefinitionsFromRegistrars方法里,其中之一是通过BeanPostProcessorsRegistrar去注册几个bean

public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
		BeanDefinitionRegistry registry) {
	if (this.beanFactory == null) {
		return;
	}
	// ----> 
	registerSyntheticBeanIfMissing(registry,
			"webServerFactoryCustomizerBeanPostProcessor",
			WebServerFactoryCustomizerBeanPostProcessor.class);
	registerSyntheticBeanIfMissing(registry,
			"errorPageRegistrarBeanPostProcessor",
			ErrorPageRegistrarBeanPostProcessor.class);
}

其中WebServerFactoryCustomizerBeanPostProcessor类实现了BeanPostProcessor接口,在实例化ServletWebServerFactoryConfiguration初始化时候,会调用applyBeanPostProcessorsBeforeInitialization方法,其中获取BeanPostProcessors执行其postProcessBeforeInitialization方法,会进行一些设值(通过ServerProperties)

public Object postProcessBeforeInitialization(Object bean, String beanName)
		throws BeansException {
	if (bean instanceof WebServerFactory) {
		postProcessBeforeInitialization((WebServerFactory) bean);
	}
	return bean;
}
	
private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {
	LambdaSafe
		.callbacks(WebServerFactoryCustomizer.class, getCustomizers(),
				webServerFactory)
		.withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)
		// customize会为factory设值(TomcatWebServerFactoryCustomizer、TomcatServletWebServerFactoryCustomizer)
		.invoke((customizer) -> customizer.customize(webServerFactory));
}

启动

那么tomcat容器是在什么时候被创建且启动的呢?
因为webApplicationType是SERVLET,所以创建了AnnotationConfigServletWebServerApplicationContext,在进行refresh方法中,有一个onRefresh方法,其注释是Initialize other special beans in specific context subclasses。正好AnnotationConfigServletWebServerApplicationContext继承的ServletWebServerApplicationContext实现了这个方法

protected void onRefresh() {
	super.onRefresh();
	try {
		// ----> 创建webServer
		createWebServer();
	}
	catch (Throwable ex) {
		throw new ApplicationContextException("Unable to start web server", ex);
	}
}

1 . 创建webServer

private void createWebServer() {
	WebServer webServer = this.webServer;
	ServletContext servletContext = getServletContext();
	if (webServer == null && servletContext == null) {
		// ----> 2. 获取WebServer工厂
		ServletWebServerFactory factory = getWebServerFactory();
		// ----> 3. 获取webServer
		this.webServer = factory.getWebServer(getSelfInitializer());
	}
	else if (servletContext != null) {
		try {
			getSelfInitializer().onStartup(servletContext);
		}
		catch (ServletException ex) {
			throw new ApplicationContextException("Cannot initialize servlet context",
					ex);
		}
	}
	// 初始化PropertySources
	initPropertySources();
}

2 . 获取WebServer工厂

protected ServletWebServerFactory getWebServerFactory() {
	// Use bean names so that we don't consider the hierarchy
	// TomcatServletWebServerFactory正是继承了ServletWebServerFactory
	String[] beanNames = getBeanFactory()
			.getBeanNamesForType(ServletWebServerFactory.class);
	if (beanNames.length == 0) {
		throw new ApplicationContextException(
				"Unable to start ServletWebServerApplicationContext due to missing "
						+ "ServletWebServerFactory bean.");
	}
	if (beanNames.length > 1) {
		throw new ApplicationContextException(
				"Unable to start ServletWebServerApplicationContext due to multiple "
						+ "ServletWebServerFactory beans : "
						+ StringUtils.arrayToCommaDelimitedString(beanNames));
	}
	return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
}

3 . 获取webServer

public WebServer getWebServer(ServletContextInitializer... initializers) {
	// 创建tomcat
	Tomcat tomcat = new Tomcat();
	File baseDir = (this.baseDirectory != null ? this.baseDirectory
			: createTempDir("tomcat"));
	tomcat.setBaseDir(baseDir.getAbsolutePath());
	// org.apache.coyote.http11.Http11NioProtocol
	Connector connector = new Connector(this.protocol);
	// 设置connector
	tomcat.getService().addConnector(connector);
	// 设置端口等信息
	customizeConnector(connector);
	tomcat.setConnector(connector);
	tomcat.getHost().setAutoDeploy(false);
	configureEngine(tomcat.getEngine());
	for (Connector additionalConnector : this.additionalTomcatConnectors) {
		tomcat.getService().addConnector(additionalConnector);
	}
	// ----> 4. 准备上下文
	prepareContext(tomcat.getHost(), initializers);
	// ----> 5. 创建TomcatWebServer封装tomcat,启动
	return getTomcatWebServer(tomcat);
}


// StandardService
public void addConnector(Connector connector) {
	synchronized (connectorsLock) {
		connector.setService(this);
		Connector results[] = new Connector[connectors.length + 1];
		System.arraycopy(connectors, 0, results, 0, connectors.length);
		results[connectors.length] = connector;
		connectors = results;
		if (getState().isAvailable()) {
			try {
				connector.start();
			} catch (LifecycleException e) {
				log.error(sm.getString(
						"standardService.connector.startFailed",
						connector), e);
			}
		}
		// Report this property change to interested listeners
		support.firePropertyChange("connector", null, connector);
	}
}

protected void customizeConnector(Connector connector) {
	int port = (getPort() >= 0 ? getPort() : 0);
	connector.setPort(port);
	if (StringUtils.hasText(this.getServerHeader())) {
		connector.setAttribute("server", this.getServerHeader());
	}
	if (connector.getProtocolHandler() instanceof AbstractProtocol) {
		customizeProtocol((AbstractProtocol<?>) connector.getProtocolHandler());
	}
	if (getUriEncoding() != null) {
		connector.setURIEncoding(getUriEncoding().name());
	}
	// Don't bind to the socket prematurely if ApplicationContext is slow to start
	connector.setProperty("bindOnInit", "false");
	if (getSsl() != null && getSsl().isEnabled()) {
		customizeSsl(connector);
	}
	TomcatConnectorCustomizer compression = new CompressionConnectorCustomizer(
			getCompression());
	compression.customize(connector);
	for (TomcatConnectorCustomizer customizer : this.tomcatConnectorCustomizers) {
		customizer.customize(connector);
	}
}

4 . 准备上下文

protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
	File documentRoot = getValidDocumentRoot();
	// 创建TomcatEmbeddedContext
	TomcatEmbeddedContext context = new TomcatEmbeddedContext();
	if (documentRoot != null) {
		context.setResources(new LoaderHidingResourceRoot(context));
	}
	context.setName(getContextPath());
	context.setDisplayName(getDisplayName());
	context.setPath(getContextPath());
	File docBase = (documentRoot != null ? documentRoot
			: createTempDir("tomcat-docbase"));
	context.setDocBase(docBase.getAbsolutePath());
	context.addLifecycleListener(new FixContextListener());
	context.setParentClassLoader(
			this.resourceLoader != null ? this.resourceLoader.getClassLoader()
					: ClassUtils.getDefaultClassLoader());
	resetDefaultLocaleMapping(context);
	addLocaleMappings(context);
	context.setUseRelativeRedirects(false);
	configureTldSkipPatterns(context);
	WebappLoader loader = new WebappLoader(context.getParentClassLoader());
	loader.setLoaderClass(TomcatEmbeddedWebappClassLoader.class.getName());
	loader.setDelegate(true);
	context.setLoader(loader);
	if (isRegisterDefaultServlet()) {
		addDefaultServlet(context);
	}
	if (shouldRegisterJspServlet()) {
		addJspServlet(context);
		addJasperInitializer(context);
	}
	context.addLifecycleListener(new StaticResourceConfigurer(context));
	ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);
	host.addChild(context);
	// 设置上下文,会创建一个TomcatStarter,并加入到context的initializers列表中
	configureContext(context, initializersToUse);
	// 留给子类扩展,空方法
	postProcessContext(context);
}

protected void configureContext(Context context,
		ServletContextInitializer[] initializers) {
	TomcatStarter starter = new TomcatStarter(initializers);
	if (context instanceof TomcatEmbeddedContext) {
		// Should be true
		((TomcatEmbeddedContext) context).setStarter(starter);
	}
	context.addServletContainerInitializer(starter, NO_CLASSES);
	for (LifecycleListener lifecycleListener : this.contextLifecycleListeners) {
		context.addLifecycleListener(lifecycleListener);
	}
	for (Valve valve : this.contextValves) {
		context.getPipeline().addValve(valve);
	}
	for (ErrorPage errorPage : getErrorPages()) {
		new TomcatErrorPage(errorPage).addToContext(context);
	}
	for (MimeMappings.Mapping mapping : getMimeMappings()) {
		context.addMimeMapping(mapping.getExtension(), mapping.getMimeType());
	}
	configureSession(context);
	for (TomcatContextCustomizer customizer : this.tomcatContextCustomizers) {
		customizer.customize(context);
	}
}

5 . 封装并启动

public TomcatWebServer(Tomcat tomcat, boolean autoStart) {
	Assert.notNull(tomcat, "Tomcat Server must not be null");
	this.tomcat = tomcat;
	this.autoStart = autoStart;
	initialize();
}


private void initialize() throws WebServerException {
	TomcatWebServer.logger
			.info("Tomcat initialized with port(s): " + getPortsDescription(false));
	synchronized (this.monitor) {
		try {
			addInstanceIdToEngineName();

			Context context = findContext();
			context.addLifecycleListener((event) -> {
				if (context.equals(event.getSource())
						&& Lifecycle.START_EVENT.equals(event.getType())) {
					// Remove service connectors so that protocol binding doesn't
					// happen when the service is started.
					removeServiceConnectors();
				}
			});

			// Start the server to trigger initialization listeners
			// 启动tomcat
			this.tomcat.start();

			// We can re-throw failure exception directly in the main thread
			rethrowDeferredStartupExceptions();

			try {
				ContextBindings.bindClassLoader(context, context.getNamingToken(),
						getClass().getClassLoader());
			}
			catch (NamingException ex) {
				// Naming is not enabled. Continue
			}

			// Unlike Jetty, all Tomcat threads are daemon threads. We create a
			// blocking non-daemon to stop immediate shutdown
			// 创建一个阻塞的非守护线程阻止立即关闭
			startDaemonAwaitThread();
		}
		catch (Exception ex) {
			stopSilently();
			throw new WebServerException("Unable to start embedded Tomcat", ex);
		}
	}
}

疑问

在设置上下文时,新建了一个TomcatStarter,并放入上下文的initializers中,那是什么时候被调用的?这里的初始化又是做什么的?

在创建TomcatWebServer时,执行initialize方法,期间会启动tomcatthis.tomcat.start()

// Tomcat
public void start() throws LifecycleException {
	getServer();
	getConnector();
	// ---->
	server.start();
}

// LifecycleBase
public final synchronized void start() throws LifecycleException {
	if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) ||
			LifecycleState.STARTED.equals(state)) {

		if (log.isDebugEnabled()) {
			Exception e = new LifecycleException();
			log.debug(sm.getString("lifecycleBase.alreadyStarted", toString()), e);
		} else if (log.isInfoEnabled()) {
			log.info(sm.getString("lifecycleBase.alreadyStarted", toString()));
		}
		return;
	}

	if (state.equals(LifecycleState.NEW)) {
		init();
	} else if (state.equals(LifecycleState.FAILED)) {
		stop();
	} else if (!state.equals(LifecycleState.INITIALIZED) &&
			!state.equals(LifecycleState.STOPPED)) {
		invalidTransition(Lifecycle.BEFORE_START_EVENT);
	}

	try {
		setStateInternal(LifecycleState.STARTING_PREP, null, false);
		// ----> 启动组件,并且实现所需的要求,中间一步就是执行initializers里ServletContainerInitializer的onStartup方法
		startInternal();
		if (state.equals(LifecycleState.FAILED)) {
			// This is a 'controlled' failure. The component put itself into the
			// FAILED state so call stop() to complete the clean-up.
			stop();
		} else if (!state.equals(LifecycleState.STARTING)) {
			// Shouldn't be necessary but acts as a check that sub-classes are
			// doing what they are supposed to.
			invalidTransition(Lifecycle.AFTER_START_EVENT);
		} else {
			setStateInternal(LifecycleState.STARTED, null, false);
		}
	} catch (Throwable t) {
		// This is an 'uncontrolled' failure so put the component into the
		// FAILED state and throw an exception.
		ExceptionUtils.handleThrowable(t);
		setStateInternal(LifecycleState.FAILED, null, false);
		throw new LifecycleException(sm.getString("lifecycleBase.startFail", toString()), t);
	}
}

TomcatStarter正是继承ServletContainerInitializer,查看其onStartup方法

public void onStartup(Set<Class<?>> classes, ServletContext servletContext)
		throws ServletException {
	try {
		for (ServletContextInitializer initializer : this.initializers) {
			initializer.onStartup(servletContext);
		}
	}
	catch (Exception ex) {
		this.startUpException = ex;
		// Prevent Tomcat from logging and re-throwing when we know we can
		// deal with it in the main thread, but log for information here.
		if (logger.isErrorEnabled()) {
			logger.error("Error starting Tomcat context. Exception: "
					+ ex.getClass().getName() + ". Message: " + ex.getMessage());
		}
	}
}

分别调用一些方法,具体的看不太懂

// 1. AbstractServletWebServerFactory
// 工具方法,希望将指定的ServletContextInitializer参数与此实例中定义的参数组合的子类可以使用该方法
protected final ServletContextInitializer[] mergeInitializers(
		ServletContextInitializer... initializers) {
	List<ServletContextInitializer> mergedInitializers = new ArrayList<>();
	mergedInitializers.add((servletContext) -> this.initParameters
			.forEach(servletContext::setInitParameter));
	mergedInitializers.add(new SessionConfiguringInitializer(this.session));
	mergedInitializers.addAll(Arrays.asList(initializers));
	mergedInitializers.addAll(this.initializers);
	return mergedInitializers.toArray(new ServletContextInitializer[0]);
}


// 2. AbstractServletWebServerFactory
public void onStartup(ServletContext servletContext) throws ServletException {
	if (this.session.getTrackingModes() != null) {
		servletContext
				.setSessionTrackingModes(unwrap(this.session.getTrackingModes()));
	}
	// 设置cookie
	configureSessionCookie(servletContext.getSessionCookieConfig());
}


// 3. ServletWebServerApplicationContext
private void selfInitialize(ServletContext servletContext) throws ServletException {
	prepareWebApplicationContext(servletContext);
	ConfigurableListableBeanFactory beanFactory = getBeanFactory();
	ExistingWebApplicationScopes existingScopes = new ExistingWebApplicationScopes(
			beanFactory);
	// 注册一些web特定的scope("request", "session", "globalSession", "application")
	WebApplicationContextUtils.registerWebApplicationScopes(beanFactory,
			getServletContext());
	existingScopes.restore();
	WebApplicationContextUtils.registerEnvironmentBeans(beanFactory,
			getServletContext());
	// dispatcherServlet、characterEncodingFilter、hiddenHttpMethodFilter、httpPutFormContentFilter、requestContextFilter
	for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
		beans.onStartup(servletContext);
	}
}

// RegistrationBean
public final void onStartup(ServletContext servletContext) throws ServletException {
	String description = getDescription();
	if (!isEnabled()) {
		logger.info(StringUtils.capitalize(description)
				+ " was not registered (disabled)");
		return;
	}
	// 动态添加filter,配置注册设置
	register(description, servletContext);
}