目录

Spring Security的引入

AuthenticationConfiguration

WebSecurityConfiguration

引入 FilterChain。

设置FilterChain的配置信息。

WebSecurity

AbstractConfiguredSecurityBuilder

属性

doBuild()方法

WebSecurity

属性

performBuild()

WebSecurityConfigurerAdapter

类图

属性

init()方法

configure()方法

getHttp()方法

HttpSecurity

类图

HttpSecurityBuilder

HttpSecurity

属性

构造函数

performBuild

AuthenticationManagerBuilder配置。


https://github.com/spring-cloud/spring-cloud-security/tree/v2.2.1.RELEASE

Spring Security的引入

在使用Spring Security时,会定义一个WebSecurityConfigurerAdapter的子类,并同时加上注解@EnableWebSecurity

// org.springframework.security.config.annotation.web.configuration;
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter 

@EnableWebSecurity用于引入security功能,WebSecurityConfigurerAdapter实现类用于扩展功能。

@Import({ WebSecurityConfiguration.class,SpringWebMvcImportSelector.class,OAuth2ImportSelector.class })
@EnableGlobalAuthentication
@Configuration
public @interface EnableWebSecurity {/*** Controls debugging support for Spring Security. Default is false.* @return if true, enables debug support with Spring Security*/boolean debug() default false;
}

@EnableWebSecurity又会引入@EnableGlobalAuthentication

@Import(AuthenticationConfiguration.class)
@Configuration
public @interface EnableGlobalAuthentication {
}

因此会导入几个类:

  • AuthenticationConfiguration。
  • WebSecurityConfiguration。引入FilterChain
  • SpringWebMvcImportSelector。判断是否引入了Spring MVC。
  • OAuth2ImportSelector。判断是否引入了OAuth2。

AuthenticationConfiguration

@Import(ObjectPostProcessorConfiguration.class)
public class AuthenticationConfiguration {@Beanpublic AuthenticationManagerBuilder authenticationManagerBuilder(ObjectPostProcessor<Object> objectPostProcessor, ApplicationContext context) {LazyPasswordEncoder defaultPasswordEncoder = new LazyPasswordEncoder(context);AuthenticationEventPublisher authenticationEventPublisher = getBeanOrNull(context, AuthenticationEventPublisher.class);DefaultPasswordEncoderAuthenticationManagerBuilder result = new DefaultPasswordEncoderAuthenticationManagerBuilder(objectPostProcessor, defaultPasswordEncoder);if (authenticationEventPublisher != null) {result.authenticationEventPublisher(authenticationEventPublisher);}return result;}@Beanpublic static GlobalAuthenticationConfigurerAdapter enableGlobalAuthenticationAutowiredConfigurer(ApplicationContext context) {return new EnableGlobalAuthenticationAutowiredConfigurer(context);}@Beanpublic static InitializeUserDetailsBeanManagerConfigurer initializeUserDetailsBeanManagerConfigurer(ApplicationContext context) {return new InitializeUserDetailsBeanManagerConfigurer(context);}@Beanpublic static InitializeAuthenticationProviderBeanManagerConfigurer initializeAuthenticationProviderBeanManagerConfigurer(ApplicationContext context) {return new InitializeAuthenticationProviderBeanManagerConfigurer(context);}}

WebSecurityConfiguration

引入 FilterChain。

创建名称为springSecurityFilterChain的Filter

//public static final String DEFAULT_FILTER_NAME = "springSecurityFilterChain";
@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)public Filter springSecurityFilterChain() throws Exception {
//判断是否自定义了SecurityConfigurerboolean hasConfigurers = webSecurityConfigurers != null&& !webSecurityConfigurers.isEmpty();if (!hasConfigurers) {
//如果没有自定义,则使用默认的。WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor.postProcess(new WebSecurityConfigurerAdapter() {});webSecurity.apply(adapter);}
//builder 返回一个Filterreturn webSecurity.build();}

设置FilterChain的配置信息。

创建了一个WebSecurity。

@Autowired(required = false)public void setFilterChainProxySecurityConfigurer(ObjectPostProcessor<Object> objectPostProcessor,@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)throws Exception {
//创建webSecurity webSecurity = objectPostProcessor.postProcess(new WebSecurity(objectPostProcessor));if (debugEnabled != null) {webSecurity.debug(debugEnabled);}
//webSecurityConfigurers排序。通过@Order注解。webSecurityConfigurers.sort(AnnotationAwareOrderComparator.INSTANCE);//对Order进行比较是否有相同的(保证不相同),由于前面进行了排序,只要比较前后有相同的就可以Integer previousOrder = null;Object previousConfig = null;for (SecurityConfigurer<Filter, WebSecurity> config : webSecurityConfigurers) {Integer order = AnnotationAwareOrderComparator.lookupOrder(config);if (previousOrder != null && previousOrder.equals(order)) {throw new IllegalStateException("@Order on WebSecurityConfigurers must be unique. Order of "+ order + " was already used on " + previousConfig + ", so it cannot be used on "+ config + " too.");}previousOrder = order;previousConfig = config;}for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
//webSecurity 应用配置。webSecurity.apply(webSecurityConfigurer);}this.webSecurityConfigurers = webSecurityConfigurers;}

获取configurers。一般返回的就是自定义的WebSecurityConfigurer

@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers
 public List<SecurityConfigurer<Filter, WebSecurity>> getWebSecurityConfigurers() {List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers = new ArrayList<>();
//获取所有WebSecurityConfigurer的实现类。Map<String, WebSecurityConfigurer> beansOfType = beanFactory.getBeansOfType(WebSecurityConfigurer.class);for (Entry<String, WebSecurityConfigurer> entry : beansOfType.entrySet()) {webSecurityConfigurers.add(entry.getValue());}return webSecurityConfigurers;}

WebSecurity

Filter的创建,最终是通过WebSecurity的实例的build()方法实现。

WebSecurity的类继承如下图:

  • SecurityBuilder定义了构建的接口标准
  • AbstractSecurityBuilder实现build方法,用AtomicBoolean的变量building保证多线程情况下,操作的原子性。此处采用的是模板模式。定义了doBuild()抽象方法
  • AbstractConfiguredSecurityBuilder 继承AbstractSecurityBuilder实现doBuild()方法,也采用模板模式,定义了实现的具体的步骤。
public interface SecurityBuilder<O> {O build() throws Exception;
}
public abstract class AbstractSecurityBuilder<O> implements SecurityBuilder<O> {
//用于控制并发。private AtomicBoolean building = new AtomicBoolean();private O object;public final O build() throws Exception {if (this.building.compareAndSet(false, true)) {this.object = doBuild();return this.object;}throw new AlreadyBuiltException("This object has already been built");}public final O getObject() {if (!this.building.get()) {throw new IllegalStateException("This object has not been built");}return this.object;}//由子类具体实现build。protected abstract O doBuild() throws Exception;
}

AbstractConfiguredSecurityBuilder

属性

public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBuilder<O>>extends AbstractSecurityBuilder<O> {//SecurityConfigurer 列表。private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers = new LinkedHashMap<>();private final List<SecurityConfigurer<O, B>> configurersAddedInInitializing = new ArrayList<>();private final Map<Class<?>, Object> sharedObjects = new HashMap<>();private final boolean allowConfigurersOfSameType;
//当前状态。private BuildState buildState = BuildState.UNBUILT;private ObjectPostProcessor<Object> objectPostProcessor;
}

doBuild()方法

doBuild()方法的步骤:

  1. beforeInit。空实现,可由子类覆盖。
  2. init。
  3. beforeConfigure。空实现,可由子类覆盖。
  4. configure
  5. performBuild。由子类实现。
@Overrideprotected final O doBuild() throws Exception {synchronized (configurers) {buildState = BuildState.INITIALIZING;beforeInit();init();buildState = BuildState.CONFIGURING;beforeConfigure();configure();buildState = BuildState.BUILDING;O result = performBuild();buildState = BuildState.BUILT;return result;}}
 private Collection<SecurityConfigurer<O, B>> getConfigurers() {List<SecurityConfigurer<O, B>> result = new ArrayList<>();for (List<SecurityConfigurer<O, B>> configs : this.configurers.values()) {result.addAll(configs);}return result;}

init()方法

 private void init() throws Exception {Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();//循环所有的configurer ,调用其初始化。for (SecurityConfigurer<O, B> configurer : configurers) {configurer.init((B) this);}for (SecurityConfigurer<O, B> configurer : configurersAddedInInitializing) {configurer.init((B) this);}}

configure()方法

 private void configure() throws Exception {Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
//循环调用configurer 的configure()方法。for (SecurityConfigurer<O, B> configurer : configurers) {configurer.configure((B) this);}}

apply()方法

apply方法用于设置SecurityConfigurer的属性,并加入到此builder的configurers中。

    public <C extends SecurityConfigurerAdapter<O, B>> C apply(C configurer) throws Exception {configurer.addObjectPostProcessor(objectPostProcessor);configurer.setBuilder((B) this);add(configurer);return configurer;}public <C extends SecurityConfigurer<O, B>> C apply(C configurer) throws Exception {add(configurer);return configurer;}

WebSecurity

属性

//忽略权限控制的请求 Matcher。
private final List<RequestMatcher> ignoredRequests = new ArrayList<>();
//构造SecurityFilterChain 的Buidler列表。private final List<SecurityBuilder<? extends SecurityFilterChain>> securityFilterChainBuilders = new ArrayList<>();private IgnoredRequestConfigurer ignoredRequestRegistry;private FilterSecurityInterceptor filterSecurityInterceptor;private HttpFirewall httpFirewall;private boolean debugEnabled;private WebInvocationPrivilegeEvaluator privilegeEvaluator;private DefaultWebSecurityExpressionHandler defaultWebSecurityExpressionHandler = new DefaultWebSecurityExpressionHandler();private SecurityExpressionHandler<FilterInvocation> expressionHandler = defaultWebSecurityExpressionHandler;private Runnable postBuildAction = () -> {};

performBuild()

真实构造FilterChain:FilterChainProxy


@Overrideprotected Filter performBuild() throws Exception {Assert.state(!securityFilterChainBuilders.isEmpty(),() -> "At least one SecurityBuilder<? extends SecurityFilterChain> needs to be specified. "+ "Typically this done by adding a @Configuration that extends WebSecurityConfigurerAdapter. "+ "More advanced users can invoke "+ WebSecurity.class.getSimpleName()+ ".addSecurityFilterChainBuilder directly");int chainSize = ignoredRequests.size() + securityFilterChainBuilders.size();//securityFilterChainsList<SecurityFilterChain> securityFilterChains = new ArrayList<>(chainSize);//如果是ignoredRequest类型的,那么久添加默认过滤器链(DefaultSecurityFilterChain)       for (RequestMatcher ignoredRequest : ignoredRequests) {securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));}//如果是securityFilterChainBuilder类型的,那么通过securityFilterChainBuilder的build()方法来构建过滤器链for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {securityFilterChains.add(securityFilterChainBuilder.build());}//将过滤器链交给一个过滤器链代理对象,此代理对象里面存储的是所有FilterChain。FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);if (httpFirewall != null) {filterChainProxy.setFirewall(httpFirewall);}filterChainProxy.afterPropertiesSet();Filter result = filterChainProxy;if (debugEnabled) {result = new DebugFilter(filterChainProxy);}//构造完后处理。postBuildAction.run();return result;}

WebSecurityConfigurerAdapter

类图

public interface SecurityConfigurer<O, B extends SecurityBuilder<O>> {void init(B builder) throws Exception;void configure(B builder) throws Exception;
}

WebSecurityConfigurer构造的是Filter的实例。

public interface WebSecurityConfigurer<T extends SecurityBuilder<Filter>> extendsSecurityConfigurer<Filter, T> {}

属性

使用WebSecurity作为Filter的builder

@Order(100)
public abstract class WebSecurityConfigurerAdapter implementsWebSecurityConfigurer<WebSecurity> {private ApplicationContext context;private ContentNegotiationStrategy contentNegotiationStrategy = new HeaderContentNegotiationStrategy();private ObjectPostProcessor<Object> objectPostProcessor = new ObjectPostProcessor<Object>() {public <T> T postProcess(T object) {throw new IllegalStateException(ObjectPostProcessor.class.getName()+ " is a required bean. Ensure you have used @EnableWebSecurity and @Configuration");}};private AuthenticationConfiguration authenticationConfiguration;private AuthenticationManagerBuilder authenticationBuilder;private AuthenticationManagerBuilder localConfigureAuthenticationBldr;private boolean disableLocalConfigureAuthenticationBldr;private boolean authenticationManagerInitialized;private AuthenticationManager authenticationManager;private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();private HttpSecurity http;
//是否可使用默认值。private boolean disableDefaults;

init()方法

通过getHttp()方法构造HttpSecurity,并设置为WebSecurity的builder。

 public void init(final WebSecurity web) throws Exception {
//构造 HttpSecurity final HttpSecurity http = getHttp();
//添加chain  builder,类型为SecurityBuilder<? extends SecurityFilterChain>。web.addSecurityFilterChainBuilder(http)
.postBuildAction(() -> {FilterSecurityInterceptor securityInterceptor = http.getSharedObject(FilterSecurityInterceptor.class);web.securityInterceptor(securityInterceptor);});}

configure()方法

空实现。子类可覆盖。

 public void configure(WebSecurity web) throws Exception {}

getHttp()方法

protected final HttpSecurity getHttp() throws Exception {if (http != null) {return http;}
//S1:DefaultAuthenticationEventPublisher eventPublisher = objectPostProcessor.postProcess(new DefaultAuthenticationEventPublisher());localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);
//S2:AuthenticationManager authenticationManager = authenticationManager();authenticationBuilder.parentAuthenticationManager(authenticationManager);authenticationBuilder.authenticationEventPublisher(eventPublisher);Map<Class<?>, Object> sharedObjects = createSharedObjects();
//S3:http = new HttpSecurity(objectPostProcessor, authenticationBuilder,sharedObjects);if (!disableDefaults) {
//S4:// @formatter:offhttp.csrf().and().addFilter(new WebAsyncManagerIntegrationFilter()).exceptionHandling().and().headers().and().sessionManagement().and().securityContext().and().requestCache().and().anonymous().and().servletApi().and().apply(new DefaultLoginPageConfigurer<>()).and().logout();// @formatter:onClassLoader classLoader = this.context.getClassLoader();
//加载所有的AbstractHttpConfigurer子类。并应用apply方法。List<AbstractHttpConfigurer> defaultHttpConfigurers =SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader);for (AbstractHttpConfigurer configurer : defaultHttpConfigurers) {http.apply(configurer);}}
//S5:configure(http);return http;}

步骤:

S1:
S2:构造AuthenticationManager 。
S3:构造HttpSecurity。

S4:如果不禁用默认值,则使用默认configurer设置HttpSecurity。

S5:配置HttpSecurity

 protected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().anyRequest().authenticated().and().formLogin().and().httpBasic();}

HttpSecurity

HttpSecurity是一个Builder,用于构造FilterChain。

类图

HttpSecurityBuilder

用于构造DefaultSecurityFilterChain。可维护Builder的属性。提供了设置 用于认证的bean的方法。

public interface HttpSecurityBuilder<H extends HttpSecurityBuilder<H>> extendsSecurityBuilder<DefaultSecurityFilterChain> {<C extends SecurityConfigurer<DefaultSecurityFilterChain, H>> C getConfigurer(Class<C> clazz);<C extends SecurityConfigurer<DefaultSecurityFilterChain, H>> C removeConfigurer(Class<C> clazz);<C> void setSharedObject(Class<C> sharedType, C object);<C> C getSharedObject(Class<C> sharedType);H authenticationProvider(AuthenticationProvider authenticationProvider);H userDetailsService(UserDetailsService userDetailsService) throws Exception;H addFilterAfter(Filter filter, Class<? extends Filter> afterFilter);H addFilterBefore(Filter filter, Class<? extends Filter> beforeFilter);H addFilter(Filter filter);
}

HttpSecurity

属性

//请求匹配过滤的配置信息
private final RequestMatcherConfigurer requestMatcherConfigurer;
//过滤器列表private List<Filter> filters = new ArrayList<>();
//匹配任何请求的匹配器private RequestMatcher requestMatcher = AnyRequestMatcher.INSTANCE;
//过滤器比较。private FilterComparator comparator = new FilterComparator();

FilterComparator 

用于Filter排序。并定义了常用Filter的Order顺序。初始值100,步长100。可以通过 方法在某个Filter前面后者后面加Filter。

final class FilterComparator implements Comparator<Filter>, Serializable {public int compare(Filter lhs, Filter rhs) {Integer left = getOrder(lhs.getClass());Integer right = getOrder(rhs.getClass());return left - right;}
}
 private static final int INITIAL_ORDER = 100;private static final int ORDER_STEP = 100;
FilterComparator() {Step order = new Step(INITIAL_ORDER, ORDER_STEP);put(ChannelProcessingFilter.class, order.next());put(ConcurrentSessionFilter.class, order.next());put(WebAsyncManagerIntegrationFilter.class, order.next());put(SecurityContextPersistenceFilter.class, order.next());put(HeaderWriterFilter.class, order.next());
... ...
 private final Map<String, Integer> filterToOrder = new HashMap<>();public void registerAfter(Class<? extends Filter> filter,Class<? extends Filter> afterFilter) {Integer position = getOrder(afterFilter);if (position == null) {throw new IllegalArgumentException("Cannot register after unregistered Filter " + afterFilter);}put(filter, position + 1);}

RequestMatcher 

用于判断request是否匹配,并返回匹配结果。

public interface RequestMatcher {boolean matches(HttpServletRequest request);default MatchResult matcher(HttpServletRequest request) {boolean match = matches(request);return new MatchResult(match, Collections.emptyMap());}class MatchResult {private final boolean match;private final Map<String, String> variables;MatchResult(boolean match, Map<String, String> variables) {this.match = match;this.variables = variables;}
}}

默认实现了很多个RequestMatcher。

public final class AnyRequestMatcher implements RequestMatcher {public static final RequestMatcher INSTANCE = new AnyRequestMatcher();public boolean matches(HttpServletRequest request) {return true;}
}public final class AndRequestMatcher implements RequestMatcher {private final List<RequestMatcher> requestMatchers;public AndRequestMatcher(List<RequestMatcher> requestMatchers) {
... ... this.requestMatchers = requestMatchers;}public AndRequestMatcher(RequestMatcher... requestMatchers) {this(Arrays.asList(requestMatchers));}public boolean matches(HttpServletRequest request) {for (RequestMatcher matcher : requestMatchers) {if (logger.isDebugEnabled()) {logger.debug("Trying to match using " + matcher);}if (!matcher.matches(request)) {logger.debug("Did not match");return false;}}return true;}
}

构造函数

构造函数,保存了AuthenticationManagerBuilder实例,并保存了一些共用对象。

public HttpSecurity(ObjectPostProcessor<Object> objectPostProcessor,AuthenticationManagerBuilder authenticationBuilder,Map<Class<?>, Object> sharedObjects) {super(objectPostProcessor);Assert.notNull(authenticationBuilder, "authenticationBuilder cannot be null");setSharedObject(AuthenticationManagerBuilder.class, authenticationBuilder);for (Map.Entry<Class<?>, Object> entry : sharedObjects.entrySet()) {setSharedObject((Class<Object>) entry.getKey(), entry.getValue());}ApplicationContext context = (ApplicationContext) sharedObjects.get(ApplicationContext.class);this.requestMatcherConfigurer = new RequestMatcherConfigurer(context);}

performBuild

 @Overrideprotected DefaultSecurityFilterChain performBuild() {
//排序filters.sort(comparator);
//构造DefaultSecurityFilterChain。会在每个configurer的configure()方法中,把filter加入到Filtersreturn new DefaultSecurityFilterChain(requestMatcher, filters);}

Filters通过addFilter 添加。

 public HttpSecurity addFilter(Filter filter) {Class<? extends Filter> filterClass = filter.getClass();if (!comparator.isRegistered(filterClass)) {throw new IllegalArgumentException("The Filter class "+ filterClass.getName()+ " does not have a registered order and cannot be added without a specified order. Consider using addFilterBefore or addFilterAfter instead.");}this.filters.add(filter);return this;}

AuthenticationManagerBuilder配置。

AuthenticationManagerBuilder 配置AuthenticationProviderUserDetailsService

 public HttpSecurity authenticationProvider(AuthenticationProvider authenticationProvider) {getAuthenticationRegistry().authenticationProvider(authenticationProvider);return this;}/** (non-Javadoc)** @see* org.springframework.security.config.annotation.web.HttpSecurityBuilder#userDetailsService* (org.springframework.security.core.userdetails.UserDetailsService)*/public HttpSecurity userDetailsService(UserDetailsService userDetailsService)throws Exception {getAuthenticationRegistry().userDetailsService(userDetailsService);return this;}private AuthenticationManagerBuilder getAuthenticationRegistry() {return getSharedObject(AuthenticationManagerBuilder.class);}

FilterChainProxy生成时序图

Spring Security源码解析(二)——引入相关推荐

  1. Spring Security源码解析(一)——认证和鉴权

    目录 认证过程 AuthenticationManager Authentication AbstractAuthenticationToken UsernamePasswordAuthenticat ...

  2. Spring Security源码解析(四)—— 过滤器

    目录 FilterChainProxy 属性 构造函数 执行Filter 获取Filter VirtualFilterChain 默认过滤器 默认Filter WebAsyncManagerInteg ...

  3. Spring Security源码解析(三)—— HttpSecurity

    目录 SecurityConfigurerAdapter AbstractHttpConfigurer AnonymousConfigurer AbstractAuthenticationFilter ...

  4. Spring AOP源码解析-拦截器链的执行过程

    一.简介 在前面的两篇文章中,分别介绍了 Spring AOP 是如何为目标 bean 筛选合适的通知器,以及如何创建代理对象的过程.现在得到了 bean 的代理对象,且通知也以合适的方式插在了目标方 ...

  5. spring事务源码解析

    前言 在spring jdbcTemplate 事务,各种诡异,包你醍醐灌顶!最后遗留了一个问题:spring是怎么样保证事务一致性的? 当然,spring事务内容挺多的,如果都要讲的话要花很长时间, ...

  6. 【若依】开源框架学习笔记 07 - 登录认证流程(Spring Security 源码)

    文章目录 一.概述 二.登录过程代码实现 三.用户验证流程(Spring Security 源码) 1.处理用户认证逻辑过滤器 `UsernamePasswordAuthenticationFilte ...

  7. spring boot 源码解析23-actuate使用及EndPoint解析

    前言 spring boot 中有个很诱人的组件–actuator,可以对spring boot应用做监控,只需在pom文件中加入如下配置即可: <dependency><group ...

  8. 【深度学习模型】智云视图中文车牌识别源码解析(二)

    [深度学习模型]智云视图中文车牌识别源码解析(二) 感受 HyperLPR可以识别多种中文车牌包括白牌,新能源车牌,使馆车牌,教练车牌,武警车牌等. 代码不可谓不混乱(别忘了这是职业公司的准产品级代码 ...

  9. Spring IOC源码解析笔记

    小伙伴们,你们好,我是老寇 Spring最重要的概念就算IOC和AOP,本篇主要记录Spring IOC容器的相关知识,本文适合有spring相关基础并且想了解spring ioc的相关原理的人看 本 ...

最新文章

  1. bit,Byte、KB、MB、GB、TB、PB、EB之间的关系
  2. go 公众号 关注 监听_荐号丨推荐五个适合法学院关注公众号
  3. RX学习笔记:正则表达式
  4. CentOS 6.0安装ipvsadm 1.26错误笔录
  5. [C++11]字符串原始字面量
  6. 「ROI 2017 Day 2」反物质(单调队列优化dp)
  7. MLSQL解决了什么问题
  8. 从 Amazon Graviton3 发布,看 2022 云计算的核心方向
  9. Windows Server 2008/2008 R2 各版本内存支持概要
  10. php学习五:数组操作
  11. 华为荣耀立方中播放群晖nas中保存的视频
  12. 全国计算机二级vb试题库,全国计算机等级考试题库之二级VB试题
  13. 安卓开发中wifi连接打印机打印图片
  14. 朋友圈集赞万能截图生成器微信小程序源码下载
  15. 谷歌浏览器黑色主题设置
  16. C++第2次实验2-三角形类
  17. 搭建p2p文件服务器,p2p服务器搭建
  18. 将RTSP网络摄像机进行网页和微信直播的方案
  19. 超全汇总 | 基于Camera的3D目标检测算法综述!(单目/双目/伪激光雷达)
  20. Python从zip文件里导入包

热门文章

  1. jpa之PagingAndSortingRepository带分页查询
  2. 【大话Hibernate】Hibernate的核心接口和类
  3. WARNING:Your password has expired --linux 用户密码过期
  4. Python模块包中__init__.py文件的作用(转载)
  5. jQuery四大选择器以及过滤选择器:的详解
  6. 模板代码复用的三种方式: 宏, 继承, 包含
  7. 随笔-546 评论-829 文章-21 2015年第15本:天才在左,疯子在右
  8. Linux uniq命令
  9. Linux下学C语言开发要学些什么‏
  10. exchange和域得命名