Shiro框架:ShiroFilterFactoryBean过滤器源码解析

目录

1.Shiro自定义拦截器SpringShiroFilter

1.1 ShiroFilterFactoryBean解析

1.1.1 实现FactoryBean接口

1.1.2 实现BeanPostProcessor接口

 1.2 SpringShiroFilter解析

1.2.1 OncePerRequestFilter过滤逻辑实现

1.2.2 AbstractShiroFilter过滤逻辑实现

1.2.2.1 创建Subject对象

1.2.2.2 更新Session最后访问时间

1.2.2.3 执行过滤链 

1.2.2.3.1  构造过滤链

1.2.2.3.2  执行构造好的过滤链

2.SpringShiroFilter如何添加到Servlet Filter中

2.1 常见的添加Servlet Filter方式

2.1.1 方式一:显示定义FilterRegistrationBean

2.1.2 方式二:显示定义ShiroFilterFactoryBean

2.2 添加Servlet Filter方式源码解析

2.2.1 addServletContextInitializerBeans

2.2.2 addAdaptableBeans


Shiro通过添加Servlet Filter的方式,提供了登录验证(Authentication)、访问控制(Authorization)以及Session管理等功能,极大的简化了Spring项目中登录鉴权模块的开发工作。

下面通过ShiroFilterFactoryBean作为切入点,详细分析下自定义拦截器SpringShiroFilter的处理流程;并通过源码解析,跟踪SpringShiroFilter是如何添加到Servlet Filter中的;

1.Shiro自定义拦截器SpringShiroFilter

SpringShiroFilter的初始化和构造逻辑是ShiroFilterFactoryBean完成的,首先看一下ShiroFilterFactoryBean的处理逻辑;

1.1 ShiroFilterFactoryBean解析

ShiroFilterFactoryBean同时实现了FactoryBean和BeanPostProcessor接口,因此其具体实现也包含了这2部分功能,下面分别进行说明;

1.1.1 实现FactoryBean接口

    /*** Lazily creates and returns a {@link AbstractShiroFilter} concrete instance via the* {@link #createInstance} method.** @return the application's Shiro Filter instance used to filter incoming web requests.* @throws Exception if there is a problem creating the {@code Filter} instance.*/public Object getObject() throws Exception {if (instance == null) {instance = createInstance();}return instance;}/*** Returns <code>{@link org.apache.shiro.web.servlet.AbstractShiroFilter}.class</code>** @return <code>{@link org.apache.shiro.web.servlet.AbstractShiroFilter}.class</code>*/public Class getObjectType() {return SpringShiroFilter.class;}/*** Returns {@code true} always.  There is almost always only ever 1 Shiro {@code Filter} per web application.** @return {@code true} always.  There is almost always only ever 1 Shiro {@code Filter} per web application.*/public boolean isSingleton() {return true;}/*** This implementation:* <ol>* <li>Ensures the required {@link #setSecurityManager(org.apache.shiro.mgt.SecurityManager) securityManager}* property has been set</li>* <li>{@link #createFilterChainManager() Creates} a {@link FilterChainManager} instance that reflects the* configured {@link #setFilters(java.util.Map) filters} and* {@link #setFilterChainDefinitionMap(java.util.Map) filter chain definitions}</li>* <li>Wraps the FilterChainManager with a suitable* {@link org.apache.shiro.web.filter.mgt.FilterChainResolver FilterChainResolver} since the Shiro Filter* implementations do not know of {@code FilterChainManager}s</li>* <li>Sets both the {@code SecurityManager} and {@code FilterChainResolver} instances on a new Shiro Filter* instance and returns that filter instance.</li>* </ol>** @return a new Shiro Filter reflecting any configured filters and filter chain definitions.* @throws Exception if there is a problem creating the AbstractShiroFilter instance.*/protected AbstractShiroFilter createInstance() throws Exception {log.debug("Creating Shiro Filter instance.");SecurityManager securityManager = getSecurityManager();if (securityManager == null) {String msg = "SecurityManager property must be set.";throw new BeanInitializationException(msg);}if (!(securityManager instanceof WebSecurityManager)) {String msg = "The security manager does not implement the WebSecurityManager interface.";throw new BeanInitializationException(msg);}FilterChainManager manager = createFilterChainManager();//Expose the constructed FilterChainManager by first wrapping it in a// FilterChainResolver implementation. The AbstractShiroFilter implementations// do not know about FilterChainManagers - only resolvers:PathMatchingFilterChainResolver chainResolver = new PathMatchingFilterChainResolver();chainResolver.setFilterChainManager(manager);//Now create a concrete ShiroFilter instance and apply the acquired SecurityManager and built//FilterChainResolver.  It doesn't matter that the instance is an anonymous inner class//here - we're just using it because it is a concrete AbstractShiroFilter instance that accepts//injection of the SecurityManager and FilterChainResolver:return new SpringShiroFilter((WebSecurityManager) securityManager, chainResolver);}protected FilterChainManager createFilterChainManager() {DefaultFilterChainManager manager = new DefaultFilterChainManager();Map<String, Filter> defaultFilters = manager.getFilters();//apply global settings if necessary:for (Filter filter : defaultFilters.values()) {applyGlobalPropertiesIfNecessary(filter);}//Apply the acquired and/or configured filters:Map<String, Filter> filters = getFilters();if (!CollectionUtils.isEmpty(filters)) {for (Map.Entry<String, Filter> entry : filters.entrySet()) {String name = entry.getKey();Filter filter = entry.getValue();applyGlobalPropertiesIfNecessary(filter);if (filter instanceof Nameable) {((Nameable) filter).setName(name);}//'init' argument is false, since Spring-configured filters should be initialized//in Spring (i.e. 'init-method=blah') or implement InitializingBean:manager.addFilter(name, filter, false);}}//build up the chains:Map<String, String> chains = getFilterChainDefinitionMap();if (!CollectionUtils.isEmpty(chains)) {for (Map.Entry<String, String> entry : chains.entrySet()) {String url = entry.getKey();String chainDefinition = entry.getValue();manager.createChain(url, chainDefinition);}}return manager;}

如上,通过初始化SecurityManager,DefaultFilterChainManager以及PathMatchingFilterChainResolver,完成了SpringShiroFilter的单例构造;

1.1.2 实现BeanPostProcessor接口

    /*** Inspects a bean, and if it implements the {@link Filter} interface, automatically adds that filter* instance to the internal {@link #setFilters(java.util.Map) filters map} that will be referenced* later during filter chain construction.*/public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {if (bean instanceof Filter) {log.debug("Found filter chain candidate filter '{}'", beanName);Filter filter = (Filter) bean;applyGlobalPropertiesIfNecessary(filter);getFilters().put(beanName, filter);} else {log.trace("Ignoring non-Filter bean '{}'", beanName);}return bean;}/*** Does nothing - only exists to satisfy the BeanPostProcessor interface and immediately returns the* {@code bean} argument.*/public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {return bean;}

在postProcessBeforeInitialization方法中,通过拦截Filter类型的bean,完成全局属性的注入,包括设置:loginUrl、successUrl、unauthorizedUrl; 

 1.2 SpringShiroFilter解析

SpringShiroFilter类继承结构如下,实现了顶层接口Servlet Filter,因此SpringShiroFilter作为Shiro实现的自定义Filter实现,可以注册到Servlet Filter中执行过滤器逻辑(第2部分进行具体说明);

在如上的继承层次结构中:

  • Filter作为Servlet过滤器顶层接口,声明了doFilter过滤方法

    /* @param request  The request to process* @param response The response associated with the request* @param chain    Provides access to the next filter in the chain for this*                 filter to pass the request and response to for further*                 processing** @throws IOException if an I/O error occurs during this filter's*                     processing of the request* @throws ServletException if the processing fails for any other reason*/public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException;
  • AbstractFilter抽象类支持FilterConfig初始化
  • NameableFilter实现了Nameable,内部实现主要完成了过滤器name的解析
  • OncePerRequestFilter见名知意,实现了“单次请求,一次过滤”的语义
  • AbstractShiroFilter通过注入WebSecurityManager和FilterChainResolver完成Shiro内部的过滤处理逻辑 

下面着重分析下doFilter方法在OncePerRequestFilter和AbstractShiroFilter的过滤逻辑实现;

1.2.1 OncePerRequestFilter过滤逻辑实现

为了实现“单次请求,一次过滤”的语义,OncePerRequestFilter的过滤逻辑中是通过在ServletRequest中记录标志位属性值的方式实现的:

通过判断ServletRequest中是否已经设置了属性Key为alreadyFilteredAttributeName值的方式,判断该filter是否已经执行了过滤逻辑,已执行则跳过,未执行则执行过滤;

并通过引入抽象方法doFilterInternal完成内部过滤逻辑,交由子类具体实现;

具体过滤逻辑实现如下:

    /*** This {@code doFilter} implementation stores a request attribute for* "already filtered", proceeding without filtering again if the* attribute is already there.** @see #getAlreadyFilteredAttributeName* @see #shouldNotFilter* @see #doFilterInternal*/public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)throws ServletException, IOException {String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();if ( request.getAttribute(alreadyFilteredAttributeName) != null ) {log.trace("Filter '{}' already executed.  Proceeding without invoking this filter.", getName());filterChain.doFilter(request, response);} else //noinspection deprecationif (/* added in 1.2: */ !isEnabled(request, response) ||/* retain backwards compatibility: */ shouldNotFilter(request) ) {log.debug("Filter '{}' is not enabled for the current request.  Proceeding without invoking this filter.",getName());filterChain.doFilter(request, response);} else {// Do invoke this filter...log.trace("Filter '{}' not yet executed.  Executing now.", getName());request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);try {doFilterInternal(request, response, filterChain);} finally {// Once the request has finished, we're done and we don't// need to mark as 'already filtered' any more.request.removeAttribute(alreadyFilteredAttributeName);}}}/*** Return name of the request attribute that identifies that a request has already been filtered.* <p/>* The default implementation takes the configured {@link #getName() name} and appends &quot;{@code .FILTERED}&quot;.* If the filter is not fully initialized, it falls back to the implementation's class name.** @return the name of the request attribute that identifies that a request has already been filtered.* @see #getName* @see #ALREADY_FILTERED_SUFFIX*/protected String getAlreadyFilteredAttributeName() {String name = getName();if (name == null) {name = getClass().getName();}return name + ALREADY_FILTERED_SUFFIX;}

1.2.2 AbstractShiroFilter过滤逻辑实现

继承了抽象类OncePerRequestFilter,并实现了方法doFilterInternal完成具体Shiro具体过滤逻辑嵌入,具体实现如下:

    /*** {@code doFilterInternal} implementation that sets-up, executes, and cleans-up a Shiro-filtered request.  It* performs the following ordered operations:* <ol>* <li>{@link #prepareServletRequest(ServletRequest, ServletResponse, FilterChain) Prepares}* the incoming {@code ServletRequest} for use during Shiro's processing</li>* <li>{@link #prepareServletResponse(ServletRequest, ServletResponse, FilterChain) Prepares}* the outgoing {@code ServletResponse} for use during Shiro's processing</li>* <li> {@link #createSubject(javax.servlet.ServletRequest, javax.servlet.ServletResponse) Creates} a* {@link Subject} instance based on the specified request/response pair.</li>* <li>Finally {@link Subject#execute(Runnable) executes} the* {@link #updateSessionLastAccessTime(javax.servlet.ServletRequest, javax.servlet.ServletResponse)} and* {@link #executeChain(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)}* methods</li>* </ol>* <p/>* The {@code Subject.}{@link Subject#execute(Runnable) execute(Runnable)} call in step #4 is used as an* implementation technique to guarantee proper thread binding and restoration is completed successfully.** @param servletRequest  the incoming {@code ServletRequest}* @param servletResponse the outgoing {@code ServletResponse}* @param chain           the container-provided {@code FilterChain} to execute* @throws IOException                    if an IO error occurs* @throws javax.servlet.ServletException if an Throwable other than an IOException*/protected void doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse, final FilterChain chain)throws ServletException, IOException {Throwable t = null;try {final ServletRequest request = prepareServletRequest(servletRequest, servletResponse, chain);final ServletResponse response = prepareServletResponse(request, servletResponse, chain);final Subject subject = createSubject(request, response);//noinspection uncheckedsubject.execute(new Callable() {public Object call() throws Exception {updateSessionLastAccessTime(request, response);executeChain(request, response, chain);return null;}});} catch (ExecutionException ex) {t = ex.getCause();} catch (Throwable throwable) {t = throwable;}if (t != null) {if (t instanceof ServletException) {throw (ServletException) t;}if (t instanceof IOException) {throw (IOException) t;}//otherwise it's not one of the two exceptions expected by the filter method signature - wrap it in one:String msg = "Filtered request failed.";throw new ServletException(msg, t);}}

过滤逻辑主要包含了如下几部分:

  1. 创建Subject对象,标识系统登录用户
  2. 更新Session最后访问时间
  3. 执行过滤链 

下面分别进行说明:

1.2.2.1 创建Subject对象

创建Subject对象底层是通过securityManager完成的,具体实现如下:

    public Subject buildSubject() {return this.securityManager.createSubject(this.subjectContext);}public Subject createSubject(SubjectContext subjectContext) {//create a copy so we don't modify the argument's backing map:SubjectContext context = copy(subjectContext);//ensure that the context has a SecurityManager instance, and if not, add one:context = ensureSecurityManager(context);//Resolve an associated Session (usually based on a referenced session ID), and place it in the context before//sending to the SubjectFactory.  The SubjectFactory should not need to know how to acquire sessions as the//process is often environment specific - better to shield the SF from these details:context = resolveSession(context);//Similarly, the SubjectFactory should not require any concept of RememberMe - translate that here first//if possible before handing off to the SubjectFactory:context = resolvePrincipals(context);Subject subject = doCreateSubject(context);//save this subject for future reference if necessary://(this is needed here in case rememberMe principals were resolved and they need to be stored in the//session, so we don't constantly rehydrate the rememberMe PrincipalCollection on every operation).//Added in 1.2:save(subject);return subject;}

 其中doCreateSubject方法委托给DefaultWebSubjectFactory完成Subject创建

public class DefaultWebSubjectFactory extends DefaultSubjectFactory {public DefaultWebSubjectFactory() {super();}public Subject createSubject(SubjectContext context) {if (!(context instanceof WebSubjectContext)) {return super.createSubject(context);}WebSubjectContext wsc = (WebSubjectContext) context;SecurityManager securityManager = wsc.resolveSecurityManager();Session session = wsc.resolveSession();boolean sessionEnabled = wsc.isSessionCreationEnabled();PrincipalCollection principals = wsc.resolvePrincipals();boolean authenticated = wsc.resolveAuthenticated();String host = wsc.resolveHost();ServletRequest request = wsc.resolveServletRequest();ServletResponse response = wsc.resolveServletResponse();return new WebDelegatingSubject(principals, authenticated, host, session, sessionEnabled,request, response, securityManager);}
1.2.2.2 更新Session最后访问时间

记录Session最后访问时间,

  • 如果是HttpSession,直接返回
  • 如果是Native Session,也即Shiro创建的Session,通过touch方法更新最后访问时间
    /*** Updates any 'native'  Session's last access time that might exist to the timestamp when this method is called.* If native sessions are not enabled (that is, standard Servlet container sessions are being used) or there is no* session ({@code subject.getSession(false) == null}), this method does nothing.* <p/>This method implementation merely calls* <code>Session.{@link org.apache.shiro.session.Session#touch() touch}()</code> on the session.** @param request  incoming request - ignored, but available to subclasses that might wish to override this method* @param response outgoing response - ignored, but available to subclasses that might wish to override this method* @since 1.0*/@SuppressWarnings({"UnusedDeclaration"})protected void updateSessionLastAccessTime(ServletRequest request, ServletResponse response) {if (!isHttpSessions()) { //'native' sessionsSubject subject = SecurityUtils.getSubject();//Subject should never _ever_ be null, but just in case:if (subject != null) {Session session = subject.getSession(false);if (session != null) {try {session.touch();} catch (Throwable t) {log.error("session.touch() method invocation has failed.  Unable to update" +"the corresponding session's last access time based on the incoming request.", t);}}}}}
1.2.2.3 执行过滤链 

该部分具体实现如下,其主要包含了2个步骤:

  1. 构造过滤链
  2. 执行构造好的过滤链

下面分别进行说明

    /*** Executes a {@link FilterChain} for the given request.* <p/>* This implementation first delegates to* <code>{@link #getExecutionChain(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) getExecutionChain}</code>* to allow the application's Shiro configuration to determine exactly how the chain should execute.  The resulting* value from that call is then executed directly by calling the returned {@code FilterChain}'s* {@link FilterChain#doFilter doFilter} method.  That is:* <pre>* FilterChain chain = {@link #getExecutionChain}(request, response, origChain);* chain.{@link FilterChain#doFilter doFilter}(request,response);</pre>** @param request   the incoming ServletRequest* @param response  the outgoing ServletResponse* @param origChain the Servlet Container-provided chain that may be wrapped further by an application-configured*                  chain of Filters.* @throws IOException      if the underlying {@code chain.doFilter} call results in an IOException* @throws ServletException if the underlying {@code chain.doFilter} call results in a ServletException* @since 1.0*/protected void executeChain(ServletRequest request, ServletResponse response, FilterChain origChain)throws IOException, ServletException {FilterChain chain = getExecutionChain(request, response, origChain);chain.doFilter(request, response);}
1.2.2.3.1  构造过滤链

构造过滤链的逻辑如下:

    /*** Returns the {@code FilterChain} to execute for the given request.* <p/>* The {@code origChain} argument is the* original {@code FilterChain} supplied by the Servlet Container, but it may be modified to provide* more behavior by pre-pending further chains according to the Shiro configuration.* <p/>* This implementation returns the chain that will actually be executed by acquiring the chain from a* {@link #getFilterChainResolver() filterChainResolver}.  The resolver determines exactly which chain to* execute, typically based on URL configuration.  If no chain is returned from the resolver call* (returns {@code null}), then the {@code origChain} will be returned by default.** @param request   the incoming ServletRequest* @param response  the outgoing ServletResponse* @param origChain the original {@code FilterChain} provided by the Servlet Container* @return the {@link FilterChain} to execute for the given request* @since 1.0*/protected FilterChain getExecutionChain(ServletRequest request, ServletResponse response, FilterChain origChain) {FilterChain chain = origChain;FilterChainResolver resolver = getFilterChainResolver();if (resolver == null) {log.debug("No FilterChainResolver configured.  Returning original FilterChain.");return origChain;}FilterChain resolved = resolver.getChain(request, response, origChain);if (resolved != null) {log.trace("Resolved a configured FilterChain for the current request.");chain = resolved;} else {log.trace("No FilterChain configured for the current request.  Using the default.");}return chain;}

这里首先获取FilterChainResolver过滤链解析器,并调用getChain获取解析后的过滤链,getChain方法实现如下:

    public FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain) {FilterChainManager filterChainManager = getFilterChainManager();if (!filterChainManager.hasChains()) {return null;}String requestURI = getPathWithinApplication(request);//the 'chain names' in this implementation are actually path patterns defined by the user.  We just use them//as the chain name for the FilterChainManager's requirementsfor (String pathPattern : filterChainManager.getChainNames()) {// If the path does match, then pass on to the subclass implementation for specific checks:if (pathMatches(pathPattern, requestURI)) {if (log.isTraceEnabled()) {log.trace("Matched path pattern [" + pathPattern + "] for requestURI [" + requestURI + "].  " +"Utilizing corresponding filter chain...");}return filterChainManager.proxy(originalChain, pathPattern);}}return null;}

 这里首先获取请求的Url(requestURI),然后和配置好的过滤链进行路径匹配,得到匹配成功的过滤链后,调用filterChainManager的proxy代理方法进行代理,返回代理后的过滤链;

具体代理逻辑如下:

    public FilterChain proxy(FilterChain original, String chainName) {NamedFilterList configured = getChain(chainName);if (configured == null) {String msg = "There is no configured chain under the name/key [" + chainName + "].";throw new IllegalArgumentException(msg);}return configured.proxy(original);}
    public FilterChain proxy(FilterChain orig) {return new ProxiedFilterChain(orig, this);}
public class ProxiedFilterChain implements FilterChain {//TODO - complete JavaDocprivate static final Logger log = LoggerFactory.getLogger(ProxiedFilterChain.class);private FilterChain orig;private List<Filter> filters;private int index = 0;public ProxiedFilterChain(FilterChain orig, List<Filter> filters) {if (orig == null) {throw new NullPointerException("original FilterChain cannot be null.");}this.orig = orig;this.filters = filters;this.index = 0;}public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {if (this.filters == null || this.filters.size() == this.index) {//we've reached the end of the wrapped chain, so invoke the original one:if (log.isTraceEnabled()) {log.trace("Invoking original filter chain.");}this.orig.doFilter(request, response);} else {if (log.isTraceEnabled()) {log.trace("Invoking wrapped filter at index [" + this.index + "]");}this.filters.get(this.index++).doFilter(request, response, this);}}
}

通过过滤链代理类ProxiedFilterChain完成了代码配置好的过滤器链与Sevlet容器中的origin过滤器链的串接,并指定了串接后的过滤器执行顺序;

1.2.2.3.2  执行构造好的过滤链

执行构造好的过滤器链,就是调用上面的过滤链代理类ProxiedFilterChain的doFilter方法,进而完整调用所有相关的过滤器,包括根据Url路径匹配成功的过滤器以及Servlet容器定义的过滤器;

如下是项目中过滤器链常见的配置方式:

后续就是执行匹配成功的过滤器链的过滤逻辑了,由于本文篇幅较大,这点放到后面文章进行解析;

2.SpringShiroFilter如何添加到Servlet Filter中

这部分章节首先介绍下常见的添加Servlet Filter的方式,然后从源码解析的角度进行阐释说明;

2.1 常见的添加Servlet Filter方式

2.1.1 方式一:显示定义FilterRegistrationBean

    @Beanpublic FilterRegistrationBean filterRegistrationBean() {FilterRegistrationBean filterRegistration = new FilterRegistrationBean();filterRegistration.setFilter(new DelegatingFilterProxy("shiroFilter"));filterRegistration.addInitParameter("targetFilterLifecycle", "true");filterRegistration.setEnabled(true);filterRegistration.addUrlPatterns("/*");return filterRegistration;}

2.1.2 方式二:显示定义ShiroFilterFactoryBean

@Configuration
public class ShiroWebFilterConfiguration {@Autowiredprotected SecurityManager securityManager;@Autowiredprotected ShiroFilterChainDefinition shiroFilterChainDefinition;@Value("#{ @environment['shiro.loginUrl'] ?: '/login.jsp' }")protected String loginUrl;@Value("#{ @environment['shiro.successUrl'] ?: '/' }")protected String successUrl;@Value("#{ @environment['shiro.unauthorizedUrl'] ?: null }")protected String unauthorizedUrl;@Beanpublic ShiroFilterFactoryBean shiroFilterFactoryBean() {ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();filterFactoryBean.setLoginUrl(loginUrl);filterFactoryBean.setSuccessUrl(successUrl);filterFactoryBean.setUnauthorizedUrl(unauthorizedUrl);filterFactoryBean.setSecurityManager(securityManager);filterFactoryBean.setFilterChainDefinitionMap(shiroFilterChainDefinition.getFilterChainMap());return filterFactoryBean;}
}

2.2 添加Servlet Filter方式源码解析

添加Servlet Filter的逻辑是在EmbeddedWebApplicationContext容器中实现的,具体如下:

在容器启动时,会调用onRefresh方法,如下:

	@Overrideprotected void onRefresh() {super.onRefresh();try {createEmbeddedServletContainer();}catch (Throwable ex) {throw new ApplicationContextException("Unable to start embedded container",ex);}}private void createEmbeddedServletContainer() {EmbeddedServletContainer localContainer = this.embeddedServletContainer;ServletContext localServletContext = getServletContext();if (localContainer == null && localServletContext == null) {EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory();this.embeddedServletContainer = containerFactory.getEmbeddedServletContainer(getSelfInitializer());}else if (localServletContext != null) {try {getSelfInitializer().onStartup(localServletContext);}catch (ServletException ex) {throw new ApplicationContextException("Cannot initialize servlet context",ex);}}initPropertySources();}

如上会调用onStartup方法:

	private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {return new ServletContextInitializer() {@Overridepublic void onStartup(ServletContext servletContext) throws ServletException {selfInitialize(servletContext);}};}private void selfInitialize(ServletContext servletContext) throws ServletException {prepareEmbeddedWebApplicationContext(servletContext);ConfigurableListableBeanFactory beanFactory = getBeanFactory();ExistingWebApplicationScopes existingScopes = new ExistingWebApplicationScopes(beanFactory);WebApplicationContextUtils.registerWebApplicationScopes(beanFactory,getServletContext());existingScopes.restore();WebApplicationContextUtils.registerEnvironmentBeans(beanFactory,getServletContext());for (ServletContextInitializer beans : getServletContextInitializerBeans()) {beans.onStartup(servletContext);}}/*** Returns {@link ServletContextInitializer}s that should be used with the embedded* Servlet context. By default this method will first attempt to find* {@link ServletContextInitializer}, {@link Servlet}, {@link Filter} and certain* {@link EventListener} beans.* @return the servlet initializer beans*/protected Collection<ServletContextInitializer> getServletContextInitializerBeans() {return new ServletContextInitializerBeans(getBeanFactory());}

如上会创建ServletContextInitializerBeans,其构造方法如下:

	public ServletContextInitializerBeans(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);}

这里主要包含了2部分内容:

  1. 方法addServletContextInitializerBeans完成ServletContextInitializer类型bean的处理
  2. 方法addAdaptableBeans完成Filter类型bean的处理

下面分别对这2部分进行分析;

2.2.1 addServletContextInitializerBeans

方法具体实现如下:

	private void addServletContextInitializerBeans(ListableBeanFactory beanFactory) {for (Entry<String, ServletContextInitializer> initializerBean : getOrderedBeansOfType(beanFactory, ServletContextInitializer.class)) {addServletContextInitializerBean(initializerBean.getKey(),initializerBean.getValue(), beanFactory);}}

这里获取Spring容器中类型为ServletContextInitializer的bean,然后调用addServletContextInitializerBean进行注册: 

	private void addServletContextInitializerBean(String beanName,ServletContextInitializer initializer, ListableBeanFactory beanFactory) {if (initializer instanceof ServletRegistrationBean) {Servlet source = ((ServletRegistrationBean) initializer).getServlet();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 = ((DelegatingFilterProxyRegistrationBean) initializer).getTargetBeanName();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);}}

这里可以看到FilterRegistrationBean(实现了ServletContextInitializer)类型的具体处理逻辑,将FilterRegistrationBean类型的bean注册到initializers中,也即完成了Servlet Filter的注册;

	private 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 beanthis.seen.add(source);}if (ServletContextInitializerBeans.logger.isDebugEnabled()) {String resourceDescription = getResourceDescription(beanName, beanFactory);int order = getOrder(initializer);ServletContextInitializerBeans.logger.debug("Added existing "+ type.getSimpleName() + " initializer bean '" + beanName+ "'; order=" + order + ", resource=" + resourceDescription);}}

2.2.2 addAdaptableBeans

方法体具体实现如下:

这里可以看到针对Filter类型的处理,展开具体实现如下:

	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 seenRegistrationBean registration = adapter.createRegistrationBean(beanName,bean.getValue(), beans.size());registration.setName(beanName);registration.setOrder(order);this.initializers.add(type, registration);if (ServletContextInitializerBeans.logger.isDebugEnabled()) {ServletContextInitializerBeans.logger.debug("Created " + type.getSimpleName() + " initializer for bean '"+ beanName + "'; order=" + order + ", resource="+ getResourceDescription(beanName, beanFactory));}}}}
	/*** {@link RegistrationBeanAdapter} for {@link Filter} beans.*/private static class FilterRegistrationBeanAdapterimplements RegistrationBeanAdapter<Filter> {@Overridepublic RegistrationBean createRegistrationBean(String name, Filter source,int totalNumberOfSourceBeans) {return new FilterRegistrationBean(source);}}

这里通过createRegistrationBean完成了Shiro自定义Filter(SpringShiroFilter)构造为FilterRegistrationBean的逻辑,并添加到initializers中完成Servlet Filter的注册。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/607339.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Alphalens 因子分析 - 以低换手率因子为例(1)

因子分析是量化研究的基本技能之一。通过因子分析,找出有效的因子,通过相关性去重后,就可以通过机器学习、线性回归等方法把因子组合起来,构成交易策略。 这一篇笔记我们就介绍如何使用 Alphalens 来进行单因子分析。我们使用的因子是低换手率因子。 股谚有一种说法,天量…

风速预测 | 基于深度学习的风速预测模型(Matlab)

目录 效果一览基本介绍程序设计参考资料 效果一览 基本介绍 风速预测 | 基于深度学习的风速预测模型&#xff08;Matlab&#xff09; 程序设计 完整程序和数据获取方式&#xff1a;私信博主回复基于深度学习的风速预测模型&#xff08;Matlab&#xff09;。 参考资料 [1] htt…

【Flutter 开发实战】Dart 基础篇:常用运算符

在Dart中&#xff0c;运算符是编写任何程序的基本构建块之一。本文将详细介绍Dart中常用的运算符&#xff0c;以帮助初学者更好地理解和运用这些概念。 1. 算术运算符 算术运算符用于执行基本的数学运算。Dart支持常见的加、减、乘、除、整除以及取余运算。常见的算数运算符如…

智慧灯杆sip广播可视对讲解决方案

智慧灯杆sip广播可视对讲解决方案 智能路灯以城市公共设施的全面高效利用为出发点&#xff0c;根据路况加载智能照明、新能源汽车充电桩、城市广播、视频监控、WIFI热点、LED/LCD信息发布、环境监测、微型基站、停车场管理、井盖等。监控和USB手机充电灯功能&#xff0c;不仅能…

2024 年 API 安全:预测和趋势

随着技术以前所未有的速度不断进步&#xff0c;API&#xff08;应用程序编程接口&#xff09;安全性的复杂性也随之增加。随着 API 在现代应用程序和服务中的激增&#xff0c;组织将需要更好地了解其 API 环境以及 API 给运营带来的风险。 到 2024 年&#xff0c;预计几个关键…

海外代理IP在游戏中有什么作用?

随着科技的飞速发展&#xff0c;手机和电脑等电子产品已成为互联网连接万物的重要工具&#xff0c;深度融入我们的日常生活&#xff0c;我们借助互联网完成工作、休闲和购物等任务&#xff0c;以求提升生活质量。 不仅如此&#xff0c;网络游戏也是人们心中最爱&#xff0c;它…

前端开发的新纪元:全方位工具栈探索 | 开源专题 No.57

vuejs/vue Stars: 205.6k License: MIT Vue 是一个用于构建用户界面的渐进式框架。它从头开始设计&#xff0c;可以根据不同的使用情况轻松地在库和框架之间进行扩展。Vue 由一个专注于视图层的核心库组成&#xff0c;并且还有一系列支持性库来帮助您处理大型单页应用程序中的…

YOLOv8改进 | 主干篇 | 12月份最新成果TransNeXt特征提取网络(全网首发)

一、本文介绍 本文给大家带来的改进机制是TransNeXt特征提取网络,其发表于2023年的12月份是一个最新最前沿的网络模型,将其应用在我们的特征提取网络来提取特征,同时本文给大家解决其自带的一个报错,通过结合聚合的像素聚焦注意力和卷积GLU,模拟生物视觉系统,特别是对于中…

MySQL 8.0 InnoDB 架构之 日志缓冲区(Log Buffer)和重做日志(Redo Log)

文章目录 MySQL 8.0 InnoDB 架构之 日志缓冲区&#xff08;Log Buffer&#xff09;和重做日志&#xff08;Redo Log&#xff09;REDO相关主要参数innodb_log_buffer_size innodb_redo_log_capacityinnodb_log_group_home_dir参考 【免责声明】文章仅供学习交流&#xff0c;观点…

web前端(html)练习

第一题 1. 用户名为文本框&#xff0c;名称为 UserName&#xff0c;长度为 15&#xff0c;最大字符数为 20。 2. 密码为密码框&#xff0c;名称为 UserPass&#xff0c;长度为 15&#xff0c;最大字符数为 20。 3. 性别为两个单选按钮&#xff0c;名称为 sex&#xff0c;值分…

链表--141.环形链表/easy C级理解

141.环形链表 1、题目2、题目分析3、解题步骤4、复杂度最优解代码示例5、抽象与扩展 1、题目 给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链…

750ml离心瓶进口国产离心机通用750ml离心杯高低速离心机瓶

750ml低速离心瓶系列&#xff1a; 产品货号&#xff1a;ZY1136222 材质&#xff1a;PPCO 容量&#xff08;ml&#xff09;&#xff1a;750 尺寸&#xff08;O.DxH,mm&#xff09;&#xff1a;98.5x147 最大离心力&#xff08;xg&#xff09;&#xff1a;6000 产品货号&…

CloudCompare——点云空间圆拟合

目录 1.概述2.软件实现3.完整操作4.相关代码 本文由CSDN点云侠原创&#xff0c;CloudCompare——点云空间圆拟合&#xff0c;爬虫自重。如果你不是在点云侠的博客中看到该文章&#xff0c;那么此处便是不要脸的爬虫与GPT生成的文章。 1.概述 CloudCompare软件中的Tools——>…

【DevOps-07-2】Sonarqube基本使用

一、简要说明 Sonar Qube的使用方式很多,Maven可以整合,也可以采用sonar-scanner的方式,再查看Sonar Qube的检测效果 Sonarqube集成在Maven实现代码检测使用sonar-scanner客户端的方式二、Sonarqube管理后台安装中文插件 1、登录Sonarqube管理后台 示例:http://192.168.95…

Python+requests搭建接口自动化测试框架

一、接口自动化的意义&#xff08;为什么做这个框架&#xff09; 新版本上线时之前版本的功能需要进行回归测试&#xff0c;导致大量的重复性手工测试。引入自动化测试可以使用自动化技术代替部分手工的回归性测试&#xff0c;解放更多人力做其它更有必要的事情。但目前项目UI变…

RDD算子——转换操作(Transformations )【map、flatMap、reduceByKey】

一、map map 算子 # spark-shell sc.parallelize(Seq(1, 2, 3)).map( num > num * 10).collect()# IDEA Test def mapTest(): Unit {// 1. 创建RDDval rdd1 sc.parallelize(Seq(1, 2, 3))// 2. 执行 map 操作val rdd2 rdd1.map(item > item * 10)// 3. 得到结果val re…

【基础工具篇使用】ADB 的安装和使用

文章目录 ADB的命令安装ADB 命令使用查看帮助 ——adb help查看连接设备 ADB的命令安装 ADB 命令的全称为“Android Debug Bridge”&#xff0c;从英文中看出主要是用作安卓的调试工具。ADB 命令在嵌入式开发中越来越常用了 在 Windows 上按“win”“R”组合件打开运行, 输入 …

astadmin安装querylist插件Puppeteer

我本来是想在linux服务器上安装&#xff0c;折腾了一天也没安装成功&#xff0c;由于急着用&#xff0c;就先做window10上安装了&#xff0c;以后有时间再研究centos7上安装 一 首先需要安装fastadmin 框架和querylist插件 这个大家可以自行安装&#xff0c;querylist安装地址…

LitJson-Json字符串转对像时:整型与字符串或字符串转:整型进的类型不一致的处理

目录 问题描述上代码测试代码各位看官&#xff0c;打赏个1元吧 Json数据格式是大家在游戏开中常量用的一种数据格式&#xff0c;某种程度上可以说是必备的。对unity开发来说&#xff0c;LitJson这个json库应该是被使用最多的json库了。 问题描述 今天说要的其中的这个api: Jso…

Arcgis像元统计数据

目录 单幅影像统计多幅影像统计 单幅影像统计 现有一幅NDVI影像&#xff0c;如何知道影像中NDVI的分布情况呢&#xff1f; 先栅格转点&#xff0c;然后在属性表中查看汇总情况 还有一种方法就是在ENVI中打开&#xff0c; -0.3-0.338占据了99% 多幅影像统计 现有多幅NDVI影…