Shiro的Filter机制详解

首先从spring-shiro.xml的filter配置说起,先回答两个问题:

1, 为什么相同url规则,后面定义的会覆盖前面定义的(执行的时候只执行最后一个)。

2, 为什么两个url规则都可以匹配同一个url,只执行第一个呢。

下面分别从这两个问题入手,最终阅读源码得到解答。

问题一解答

相同url但定义在不同的行,后面覆盖前面

  1. /usr/login.do=test3
  2. /usr/login.do=test1,test2
  3. 不会执行test3filter

要解答第一个问题,需要知道shiro(或者说是spring)是如何扫描这些url规则并保存的。

Web.xml配置shiro以及spring-shiro.xml的核心配置

在web.xml中定义shiroFilter
  1. <filter>

  2. <filter-name>shiroFilter</filter-name>

  3. <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>

  4. <init-param>

  5. <!-- 该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理 -->

  6. <param-name>targetFilterLifecycle</param-name>

  7. <param-value>true</param-value>

  8. </init-param>

  9. </filter>

  10. <filter-mapping>

  11. <filter-name>shiroFilter</filter-name>

  12. <url-pattern>/*</url-pattern>

  13. </filter-mapping>
在spring-shiro.xml中定义shiroFilter

(要和web.xml中的名称一样,因为spring就是依靠名称来获取这个bean的)

  1. <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">

  2. <property name="securityManager" ref="securityManager" />

  3. <property name="loginUrl" value="/login.jsp" />

  4. <property name="unauthorizedUrl" value="/WEB-INF/405.html" />

  5. <property name="filters">

  6. <map>

  7. <entry key="kickout" value-ref="kickoutSessionControlFilter" />

  8. </map>

  9. </property>

  10. <property name="filterChainDefinitions">

  11. <value>

  12. /**=kickout

  13. /usr/login.do=anon

  14. /security/*=anon

  15. /usr/login.do=authc

  16. /usr/test/*=authc

  17. </value>

  18. </property>

  19. </bean>

都定义好之后,分析org.springframework.web.filter.DelegatingFilterProxy发现该filter类的任务是:将具体工作分派给org.apache.shiro.spring.web.ShiroFilterFactoryBean这个类中的静态内部类SpringShiroFilter做。

具体spring内部是怎么将工作委派的,暂时没有分析。

现在关注的是当spring把具体工作委派给ShiroFilterFactoryBean后,该类是怎么工作的。

Spring将配置注入到ShiroFilterFactoryBean

在这之前,spring通过bean注入,将ShiroFilterFactoryBean的相关成员通过set方法注入进去。

前面已经配置了filters和filterChainDefinitions,再次贴出如下所示:

  1. <property name="filters">

  2. <map>

  3. <entry key="kickout" value-ref="kickoutSessionControlFilter" />

  4. </map>

  5. </property>

  6. <property name="filterChainDefinitions">
  7. <value>
  8. /**=kickout

  9. /usr/login.do=anon

  10. /security/*=anon

  11. /usr/login.do=authc

  12. /usr/test/*=authc

  13. </value>

  14. </property>

看一下ShiroFilterFactoryBean是怎么接收他们的。

Filters很简单,只需要map接收就自动完成了。

  1. public void setFilters(Map<String, Filter> filters) {

  2. this.filters = filters;

  3. }

但是filterChainDefinitions是String类型的,需要转换(使用了ini转换方法,内部使用LinkedHashMap保存url和filter的映射关系,保证了顺序)

  1. public void setFilterChainDefinitions(String definitions) {

  2. Ini ini = new Ini();

  3. ini.load(definitions);

  4. //did they explicitly state a 'urls' section? Not necessary, but just in case:

  5. Ini.Section section = ini.getSection(IniFilterChainResolverFactory.URLS);

  6. if (CollectionUtils.isEmpty(section)) {

  7. //no urls section. Since this _is_ a urls chain definition property, just assume the

  8. //default section contains only the definitions:

  9. section = ini.getSection(Ini.DEFAULT_SECTION_NAME);

  10. }

  11. setFilterChainDefinitionMap(section);

  12. }

这两步完成后,filters被注入

filterChianDefinitions也被注入,但是注入方法通过shiro自定义了ini方式,

该方式通过LinkedHashMap保存url规则和对应的权限(键值对),所以当写了相同的url规则,后者会覆盖前者(------现在对HashMap的存储规则遗忘了,需要再看一下)

问题一解答完成

问题二解答:

  1. 同一个url可以匹配不同的规则,但只执行首行
  2. /usr/* =test1,test2
  3. /usr/login.do=test3
  4. url = /usr/login.do请求来了,不会执行test3,因为已经匹配了/usr/* =test1,test2
  5. 要解答该问题,需要知道每个url的FilterChain是如何获取的

接上分析:

有了filter和filterChainDefinitionMap的数据后,下面的工作是构造FilterChainManager

构造FilterChainManager

为什么到这一步呢?

查看spring委托机制,最终找到ShiroFilterFactoryBean的createInstance()方法(这个方法是shiro的filter构造机制的主线),由于ShiroFilterFactoryBean 实现了FactoryBean,spring就是通过这个接口的createInstance方法获取到filter实例的,下面是该方法在ShiroFilterFactoryBean中的实现:

  1. protected AbstractShiroFilter createInstance() throws Exception {

  2. log.debug("Creating Shiro Filter instance.");

  3. SecurityManager securityManager = getSecurityManager();

  4. if (securityManager == null) {

  5. String msg = "SecurityManager property must be set.";

  6. throw new BeanInitializationException(msg);

  7. }

  8. if (!(securityManager instanceof WebSecurityManager)) {

  9. String msg = "The security manager does not implement the WebSecurityManager interface.";

  10. throw new BeanInitializationException(msg);

  11. }

  12. FilterChainManager manager = createFilterChainManager();
  13.     PathMatchingFilterChainResolver chainResolver </span>= <span style="color: #0000ff">new</span><span style="color: #000000"> PathMatchingFilterChainResolver();</br>
  14.     chainResolver.setFilterChainManager(manager);</br>
  15.     </span><span style="color: #0000ff">return</span> <span style="color: #0000ff">new</span><span style="color: #000000"> SpringShiroFilter((WebSecurityManager) securityManager, chainResolver);</br>
  16. }</span></pre>
  17.  

从这里可以知道,首先获取filterChainManager,具体方法如下

  1. protected FilterChainManager createFilterChainManager() {

  2. DefaultFilterChainManager manager = new DefaultFilterChainManager();

  3. Map<String, Filter> defaultFilters = manager.getFilters();

  4. //apply global settings if necessary:

  5. for (Filter filter : defaultFilters.values()) {

  6. applyGlobalPropertiesIfNecessary(filter);

  7. }

  8. //Apply the acquired and/or configured filters:

  9. Map<String, Filter> filters = getFilters();

  10. if (!CollectionUtils.isEmpty(filters)) {

  11. for (Map.Entry<String, Filter> entry : filters.entrySet()) {

  12. String name = entry.getKey();

  13. Filter filter = entry.getValue();

  14. applyGlobalPropertiesIfNecessary(filter);

  15. if (filter instanceof Nameable) {

  16. ((Nameable) filter).setName(name);

  17. }

  18. //'init' argument is false, since Spring-configured filters should be initialized

  19. //in Spring (i.e. 'init-method=blah') or implement InitializingBean:

  20. manager.addFilter(name, filter, false);

  21. }

  22. }
  23. //build up the chains:

  24. Map<String, String> chains = getFilterChainDefinitionMap();

  25. if (!CollectionUtils.isEmpty(chains)) {

  26. for (Map.Entry<String, String> entry : chains.entrySet()) {

  27. String url = entry.getKey();

  28. String chainDefinition = entry.getValue();

  29. manager.createChain(url, chainDefinition);

  30. }

  31. }

  32. return manager;

  33. }

分析后得知,首先在createFilterChainManager()方法中,创建一个DefaultFilterChainManager对象,而这个对象的构造函数在最后会将DefaultFilter中定义的shiro默认的filter映射加入到该对象中。如下代码就是DefaultFilter的定义。

在DefaultFilterChainManager中还做了一件事就是url-filter的映射变成filterChain,这句代码就是执行这个任务(将我们在xml文件中定义的filterChainDefinitions变成filterChain)。

  1. manager.createChain(url, chainDefinition);

作用是将权限分割:如

"authc, roles[admin,user], perms[file:edit]"

将会被分割为

{ "authc", "roles[admin,user]", "perms[file:edit]" }

具体的源代码如下:

  1. public void createChain(String chainName, String chainDefinition) {

  2. //。。。。。。。。

  3.     </span><span style="color: #008000">//</span><span style="color: #008000">parse the value by tokenizing it to get the resulting filter-specific config entries</br>
  4.     </span><span style="color: #008000">//</span></br>
  5.     <span style="color: #008000">//</span><span style="color: #008000">e.g. for a value of</br>
  6.     </span><span style="color: #008000">//</span></br>
  7.     <span style="color: #008000">//</span><span style="color: #008000">     "authc, roles[admin,user], perms[file:edit]"
  8.     </span><span style="color: #008000">//</span></br>
  9.     <span style="color: #008000">//</span><span style="color: #008000"> the resulting token array would equal
  10.     </span><span style="color: #008000">//</span></br>
  11.     <span style="color: #008000">//</span><span style="color: #008000">     { "authc", "roles[admin,user]", "perms[file:edit]" }
  12.     </span><span style="color: #008000">//</br>
  13.  
  14. String[] filterTokens = splitChainDefinition(chainDefinition);


  15. for (String token : filterTokens) {


  16. String[] nameConfigPair = toNameConfigPair(token);


  17. addToChain(chainName, nameConfigPair[0], nameConfigPair[1]);


  18. }


  19. }

并且通过toNameConfigPair(token)将如:roles[admin,user]形式的变成roles,admin,user形式的分割

然后根据url规则 映射 权限和角色

可以发现,每次分割一个token,都会通过addToChain方法接受

分析public void addToChain(String chainName, String filterName, String chainSpecificFilterConfig)方法

  1. public void addToChain(String chainName, String filterName, String chainSpecificFilterConfig) {

  2. if (!StringUtils.hasText(chainName)) {

  3. throw new IllegalArgumentException("chainName cannot be null or empty.");

  4. }

  5. Filter filter = getFilter(filterName);

  6. if (filter == null) {

  7. throw new IllegalArgumentException("There is no filter with name '" + filterName +

  8. "' to apply to chain [" + chainName + "] in the pool of available Filters. Ensure a " +

  9. "filter with that name/path has first been registered with the addFilter method(s).");

  10. }



  11. applyChainConfig(chainName, filter, chainSpecificFilterConfig);
  12.     <span style="color: #ff0000">NamedFilterList chain </span></strong></span><strong><span style="color: #ff0000">=</span></strong><span style="color: #000000"><strong><span style="color: #ff0000"> ensureChain(chainName);
  13.     chain.add(filter);</span></strong></br>
  14. }</span></pre>

分析applyChainConfig(chainName, filter, chainSpecificFilterConfig);

  1. protected void applyChainConfig(String chainName, Filter filter, String chainSpecificFilterConfig) {
  2. //………………………….

  3. if (filter instanceof PathConfigProcessor) {

  4. ((PathConfigProcessor) filter).processPathConfig(chainName, chainSpecificFilterConfig);

  5. } else {

  6. if (StringUtils.hasText(chainSpecificFilterConfig)) {

  7. //they specified a filter configuration, but the Filter doesn't implement PathConfigProcessor

  8. //this is an erroneous config:

  9. String msg = "chainSpecificFilterConfig was specified, but the underlying " +

  10. "Filter instance is not an 'instanceof' " +

  11. PathConfigProcessor.class.getName() + ". This is required if the filter is to accept " +

  12. "chain-specific configuration.";

  13. throw new ConfigurationException(msg);

  14. }

  15. }

  16. }

由于我们自定义的filter都是PathMatchingFilter的子类,所以在applyChainConfig方法中完成的就是将url添加到filter的url表中。

在PathMatchingFilter中可以发现

  1. protected Map<String, Object> appliedPaths = new LinkedHashMap<String, Object>();

processPathConfig 方法的实现如下

  1. public Filter processPathConfig(String path, String config) {

  2. String[] values = null;

  3. if (config != null) {

  4. values = split(config);

  5. }

  6. this.appliedPaths.put(path, values);

  7. return this;

  8. }

基本上在spring-shiro.xml中定义filter的载入过程已经阅读完成,

1, 定义一个DefaultFilterChainManager对象

2, 首先加载默认的filter

3, 加载xml文件中定义的filter

4, 加载xml文件定义的url和filter映射关系

5, 将映射关系解析为以url为键,NamedFilterList为值的键值对。

6, 在解析的过程中,对每个url和对应的过滤条件,都会放到对应filter的appliedPaths中(在PathMatchingFilter中定义)。

现在FilterChainManager的对象已经创建完毕,并且每个filter也已经实例化完毕。

构造SpingShiroFilter

在创建SpringShiroFilter之前还要将刚才创建的FilterChainManager对象包装成一个PathMatchingFilterChainResolver对象(注释的意思是:不直接将FilterChainManager对象暴露给AbstractShiroFilter的实现者,在这里就是SpringShiroFilter。)

PathMatchingFilterChainResolver最重要的作用是:当请求url来的时候,他担任匹配工作(调用该类的getChain方法做匹配,暂时先不分析该方法,等知道在哪里调用该方法时候再分析。其实问题二此时已经可以解答,通过该方法就可以知道,某个url匹配到过滤链的第一个规则时就return了。)

上图最后一句话执行完成后,一个SpringShiroFilter创建完毕。

请求过滤过程分析(上)

下面分析当url请求到来的时候,shiro是如何完成过滤的。首先通过图片大致的了解一下。

现在分析AbstractShiroFilter的doFilterInternal()方法

  1. protected void doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse, final FilterChain chain)
  2. throws ServletException, IOException {

  3. Throwable t = null;

  4. try {

  5. final ServletRequest request = prepareServletRequest(servletRequest, servletResponse, chain);

  6. final ServletResponse response = prepareServletResponse(request, servletResponse, chain);

  7. final Subject subject = createSubject(request, response);

  8. subject.execute(new Callable() {

  9. public Object call() throws Exception {

  10. updateSessionLastAccessTime(request, response);

  11. executeChain(request, response, chain);

  12. return null;

  13. }

  14. });

  15. } catch (ExecutionException ex) {

  16. t = ex.getCause();

  17. } catch (Throwable throwable) {

  18. t = throwable;

  19. }

  20. //…………
  21. }

暂时不关心subject相关的创建等过程,只关心这行代码

executeChain(request, response, chain);

具体实现如下

  1. protected void executeChain(ServletRequest request, ServletResponse response, FilterChain origChain)
  2. throws IOException, ServletException {
  3. FilterChain chain = getExecutionChain(request, response, origChain);
  4. chain.doFilter(request, response);
  5. }

再看getExecutionChain(request, response, origChain);具体实现如下:

  1. protected FilterChain getExecutionChain(ServletRequest request, ServletResponse response, FilterChain origChain) {

  2. FilterChain chain = origChain;
  3.     FilterChainResolver resolver </span>=<span style="color: #000000"> getFilterChainResolver();</br>
  4.     </span><span style="color: #0000ff">if</span> (resolver == <span style="color: #0000ff">null</span><span style="color: #000000">) {</br>
  5.         log.debug(</span>"No FilterChainResolver configured.  Returning original FilterChain."<span style="color: #000000">);</br>
  6.         </span><span style="color: #0000ff">return</span><span style="color: #000000"> origChain;</br>
  7.     }</br>
  8.     <strong><span style="color: #ff0000">FilterChain resolved </span></strong></span><strong><span style="color: #ff0000">= resolver.getChain(request, response, origChain);</br>
  9.     </span></strong><span style="color: #0000ff">if</span> (resolved != <span style="color: #0000ff">null</span><span style="color: #000000">) {</br>
  10.         log.trace(</span>"Resolved a configured FilterChain for the current request."<span style="color: #000000">);</br>
  11.         chain </span>=<span style="color: #000000"> resolved;</br>
  12.     } </span><span style="color: #0000ff">else</span><span style="color: #000000"> {</br>
  13.         log.trace(</span>"No FilterChain configured for the current request.  Using the default."<span style="color: #000000">);</br>
  14.     }</br>
  15.     </span><span style="color: #0000ff">return</span><span style="color: #000000"> chain;
  16. }</span></pre>
  17.  

可以发现,这里用到了我们在创建SpringShiroFilter时传递的FilterChainResolver,至此,我们终于找到了getChain()方法在这里被调用了。其源码实现如下

  1. public FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain) {

  2. FilterChainManager filterChainManager = getFilterChainManager();

  3. if (!filterChainManager.hasChains()) {

  4. return null;

  5. }
  6.     String requestURI </span>=<span style="color: #000000"> getPathWithinApplication(request);</br>
  7.     </span><span style="color: #008000">//</span><span style="color: #008000">the 'chain names' in this implementation are actually path patterns defined by the user.  We just use them
  8.     </span><span style="color: #008000">//</span><span style="color: #008000">as the chain name for the FilterChainManager's requirements</span></br>
  9.     <span style="color: #0000ff">for</span><span style="color: #000000"> (String pathPattern : filterChainManager.getChainNames()) {</br></br>
  10.         </span><span style="color: #008000">//</span><span style="color: #008000"> If the path does match, then pass on to the subclass implementation for specific checks:</span></br>
  11.         <span style="color: #0000ff">if</span><span style="color: #000000"> (pathMatches(pathPattern, requestURI)) {</br>
  12.             </span><span style="color: #0000ff">if</span><span style="color: #000000"> (log.isTraceEnabled()) {</br>
  13.                 log.trace(</span>"Matched path pattern [" + pathPattern + "] for requestURI [" + requestURI + "].  " +</br>
  14.                         "Utilizing corresponding filter chain..."<span style="color: #000000">);</br>
  15.             }
  16.             </span><span style="color: #0000ff">return</span><span style="color: #000000"> filterChainManager.proxy(originalChain, pathPattern);</br>
  17.         }
  18.     }
  19.     </span><span style="color: #0000ff">return</span> <span style="color: #0000ff">null</span><span style="color: #000000">;</br>
  20. }</span></pre>
  21.  

从for循环可以看出,当匹配到第一个url规则,则return一个代表这个url规则的FilterChain给web容器执行。

问题二解答:每个url在匹配他的FilterChain时,当匹配到第一个URL规则时,就返回。

请求过滤过程分析(下)

FilterChain的实现类为org.apache.shiro.web.servlet.ProxiedFilterChain

从该类的doFilter方法可以知道,它会将Filter链的Filter的doFilter方法顺序执行一遍。下图展示了这一过程

现在只需要分析每个Filter的doFilter方法就行了。

先看一下shiro整个filter框架继承关系(图片来自第八章 拦截器机制——《跟我学Shiro》)

上面是它的继承关系:最终的doFilter方法在OncePerRequestFilter中实现,具体代码如下:

  1. public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
  2. throws ServletException, IOException {

  3. String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();

  4. if ( request.getAttribute(alreadyFilteredAttributeName) != null ) {

  5. log.trace("Filter '{}' already executed. Proceeding without invoking this filter.", getName());

  6. filterChain.doFilter(request, response);

  7. } else //noinspection deprecation

  8. if (/* added in 1.2: */ !isEnabled(request, response) ||
  9. /* retain backwards compatibility: */ shouldNotFilter(request) ) {

  10. log.debug("Filter '{}' is not enabled for the current request. Proceeding without invoking this filter.",
  11. getName());

  12. filterChain.doFilter(request, response);

  13. } else {

  14. // Do invoke this filter...

  15. log.trace("Filter '{}' not yet executed. Executing now.", getName());

  16. request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);

  17. try {

  18. doFilterInternal(request, response, filterChain);

  19. } finally {

  20. // Once the request has finished, we're done and we don't
  21. // need to mark as 'already filtered' any more.

  22. request.removeAttribute(alreadyFilteredAttributeName);

  23. }

  24. }

  25. }

可以发现该方法最终会调用doFilterInternal(request, response, filterChain);来完成具体的过滤操作,doFilterInternal方法在 SpringShiroFilter的直接父类AbstractShiroFilter的具体实现过程已经在上面分析过了:具体的就是shiro真正验证授权前的subject,session等初始化的工作,使得后面的过滤以及验证授权工作可以得到subject等然后正常工作。完成后调用其他shiro filter进行继续过滤

而除了shiroFilter之外,其余的filter都是AdviceFilter分支的子类。刚才看了AbstractShiroFilter的doFilterInternal方法,现在看一下AdviceFilter对该方法的实现:

  1. public void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain)
  2. throws ServletException, IOException {

  3. Exception exception = null;

  4. try {

  5. boolean continueChain = preHandle(request, response);

  6. if (log.isTraceEnabled()) {

  7. log.trace("Invoked preHandle method. Continuing chain?: [" + continueChain + "]");

  8. }

  9. if (continueChain) {

  10. executeChain(request, response, chain);

  11. }

  12. postHandle(request, response);

  13. if (log.isTraceEnabled()) {

  14. log.trace("Successfully invoked postHandle method");

  15. }

  16. } catch (Exception e) {

  17. exception = e;

  18. } finally {

  19. cleanup(request, response, exception);

  20. }

  21. }

与AbstractShiroFilter的doFilterInternal方法不同的是,这里通过continueChain变量来判断到底后续的filter会不会被继续执行。而该变量的值由preHandle()函数决定。

基本上所有在系统中用到的filter都是继承PathMatchingFilter类的。看一下该类的preHandle()函数实现,可以发现,我们在xml文件中定义的url匹配,在这里面可以看到匹配原则了:

  1. protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
  2. if (this.appliedPaths == null || this.appliedPaths.isEmpty()) {
  3. if (log.isTraceEnabled()) {

  4. log.trace("appliedPaths property is null or empty. This Filter will passthrough immediately.");

  5. }

  6. return true;

  7. }

  8. for (String path : this.appliedPaths.keySet()) {

  9. // If the path does match, then pass on to the subclass implementation for specific checks
  10. //(first match 'wins'):

  11. if (pathsMatch(path, request)) {

  12. log.trace("Current requestURI matches pattern '{}'. Determining filter chain execution...", path);

  13. Object config = this.appliedPaths.get(path);

  14. return isFilterChainContinued(request, response, path, config);

  15. }

  16. }

  17. //no path matched, allow the request to go through:

  18. return true;

  19. }

继续调用isFilterChainContinued(request, response, path, config)--> onPreHandle(request, response, pathConfig);

分析onPreHandle(),PathMatchingFilter自己并没有实现,只是简单的返回true。所以当我们自定义filter的时候,要将具体的逻辑实现在该方法中,或者实现该类的子类AccessControlFilter(该类对onPreHandle()方法进行了更细致的划分,大部分一般会继承该类)

有兴趣的可以分析一下shiro自带的这些filter

Shiro的Filter机制详解---源码分析(转)的更多相关文章

  1. Shiro的Filter机制详解---源码分析

    Shiro的Filter机制详解 首先从spring-shiro.xml的filter配置说起,先回答两个问题: 1, 为什么相同url规则,后面定义的会覆盖前面定义的(执行的时候只执行最后一个). ...

  2. ArrayList详解-源码分析

    ArrayList详解-源码分析 1. 概述 在平时的开发中,用到最多的集合应该就是ArrayList了,本篇文章将结合源代码来学习ArrayList. ArrayList是基于数组实现的集合列表 支 ...

  3. LinkedList详解-源码分析

    LinkedList详解-源码分析 LinkedList是List接口的第二个具体的实现类,第一个是ArrayList,前面一篇文章已经总结过了,下面我们来结合源码,学习LinkedList. 基于双 ...

  4. Java开源生鲜电商平台-盈利模式详解(源码可下载)

    Java开源生鲜电商平台-盈利模式详解(源码可下载) 该平台提供一个联合买家与卖家的一个平台.(类似淘宝购物,这里指的是食材的购买.) 平台有以下的盈利模式:(类似的平台有美菜网,食材网等) 1. 订 ...

  5. quartz集群调度机制调研及源码分析---转载

    quartz2.2.1集群调度机制调研及源码分析引言quartz集群架构调度器实例化调度过程触发器的获取触发trigger:Job执行过程:总结:附: 引言 quratz是目前最为成熟,使用最广泛的j ...

  6. 定时组件quartz系列<三>quartz调度机制调研及源码分析

    quartz2.2.1集群调度机制调研及源码分析引言quartz集群架构调度器实例化调度过程触发器的获取触发trigger:Job执行过程:总结:附: 引言 quratz是目前最为成熟,使用最广泛的j ...

  7. (1)quartz集群调度机制调研及源码分析---转载

    quartz2.2.1集群调度机制调研及源码分析 原文地址:http://demo.netfoucs.com/gklifg/article/details/27090179 引言quartz集群架构调 ...

  8. 微服务生态组件之Spring Cloud LoadBalancer详解和源码分析

    Spring Cloud LoadBalancer 概述 Spring Cloud LoadBalancer目前Spring官方是放在spring-cloud-commons里,Spring Clou ...

  9. Go Revel - Filter(过滤器)源码分析

    在 Go Revel - server.go 源码分析 http://www.cnblogs.com/hangxin1940/p/3265538.html 说到revel框架很多重要的东西都Filte ...

随机推荐

  1. Excel的版本

    https://en.wikipedia.org/wiki/Microsoft_Excel 取自维基百科,需要特别注意的是,从v12开始,有很大的改变.后缀名从xls变为xlsx Versions 5 ...

  2. vue --- 解读vue的中webpack.base.config.js

    const path = require('path') const utils = require('./utils')// 引入utils工具模块,具体查看我的博客关于utils的解释,utils ...

  3. js---- localStorage的基本用法

    <body> <div> <span>用户名</span> <input type="text" class='usernam ...

  4. SQL 锁 lock

    http://www.cnblogs.com/huangxincheng/p/4292320.html 关于sql 中的锁. 1 排他锁 sql中在做 insert update delete 会存在 ...

  5. python 内存中写入文件(read读取不到文件解决)

    from io import StringIO a = StringIO.StringIO('title') a.write('content1\n') a.write('content2') a.s ...

  6. pytest使用问题总结

    问题一.AttributeError: module 'pytest' has no attribute 'allure'解决方法:pip3 uninstall pytest-allure-adapt ...

  7. CMDB学习之四 ——DEBUG模式

    定义一个debug,进行解析调试,到测试文件 配置文件,配置debug模式,定义环境变量, #!/usr/bin/env python # -*- coding:utf-8 -*- import os ...

  8. ubuntu mysql 无法启动 简单排查

    自己的站点非常久没有去上了,想打开发现竟然打不开了.所以就找了一系列的原因. vps不行了 dns解析出问题了 域名出问题了 简单排查之后,我的vps服务商用的是 ***(bandwagonhost) ...

  9. android 自己定义控件属性(TypedArray以及attrs解释)

    近期在捣鼓android 自己定义控件属性,学到了TypedArray以及attrs.在这当中看了一篇大神博客Android 深入理解Android中的自己定义属性.我就更加深入学习力一番.我就沿着这 ...

  10. 6lession-基本数据类型

    因为自己是根据网上教程学习的,所以以下内容参考自 http://www.w3cschool.cc/python/python-variable-types.html python支持物种数据类型,分别 ...