Spring5源码解析-Spring框架中的单例和原型bean

最近一直有问我单例和原型bean的一些原理性问题,这里就开一篇来说说的

通过Spring中的依赖注入极大方便了我们的开发。在xml通过<bean>定义(或者通过@Bean在配置类里定义)对象之后,然后只需简单地使用@Autowired注解,就可以使用由Spring上下文管理的每个对象。需要注意的是,所有这些对象在Spring中默认都是单例。

这一次我们会去讨论Spring如何来管理这些定义的bean。在第一部分中,我们将讲解单例和原型作用域的概念。第二部分中,我们将分析单例和原型作用域之间的依赖关系。其后说一下方法注入。最后专门对相关Spring的代码来做下分析,具体看看bean是如何构建出来的。

Spring中的bean默认都是单身贵族

Spring使用单例设计模式来管理bean?不完全是。Singleton设计模式假定它们是由Java的类加载器管理的jvm中给定类的唯一一个实例。在Spring中,还是有点不一样。默认情况下,它们为每个给定的org.springframework.context.ApplicationContext实例存在唯一的一个bean (有点别扭,也就是可以有多个Spring容器,每一个容器内存在唯一bean实例,之前的文章中有涉及例子的)。这意味着如果你有两个或更多上下文,所有这些上下文都由同一Java的类加载器管理(因为在同一个jvm环境中),则可能会有多个给定bean的实例。唯一需要做到的是必须在每个上下文中定义此bean。讲那么多不如代码更有说服力:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class MultipleContextes {
 
public static void main(String[] args) {
try {
// retreive two different contexts
ApplicationContext firstContext = new FileSystemXmlApplicationContext("/home/bartosz/webapp/src/main/resources/META-INF/applicationContext.xml");
ApplicationContext secondContext = new FileSystemXmlApplicationContext("/home/bartosz/webapp/src/main/resources/META-INF/applicationContext.xml");
 
// compare the objects from different contexts
ShoppingCart firstShoppingCart = (ShoppingCart) firstContext.getBean("shoppingCart");
ShoppingCart secondShoppingCart = (ShoppingCart) secondContext.getBean("shoppingCart");
System.out.println("1. Are they the same ? " + (firstShoppingCart == secondShoppingCart));
 
// compare the objects from the same context
ShoppingCart firstShoppingCartBis = (ShoppingCart) firstContext.getBean("shoppingCart");
System.out.println("2. Are they the same ? "+ (firstShoppingCart == firstShoppingCartBis));
} catch (Exception e) {
e.printStackTrace();
}
}
}

通过执行此代码,你应该得到:

1
2
1. Are they the same ? false
2. Are they the same ? true

所以你可以看到,bean只是一个上下文的单例。这就是为什么你不应该将Spring的单例概念与设计模式中的的单例混合在一起。

但是,如果要为一个定义的bean在一个上下文内可以使用不同的实例,应该怎么做?很简单,你应该将此Bean配置为原型作用域:

1
2
<bean id="shoppingCart" class="com.migo.data.ShoppingCart" scope="prototype">
</bean>

现在,在运行以前的代码之后,你可以看到如下输出:

1
2
1. Are they the same ? false
2. Are they the same ? false

我们已经知道两个作用域之间的区别。但在哪种情况下我们应该选择使用单例还是原型?Singleton适用于无状态的bean,即没有状态的bean。比如一个serviceDAO或者controller。他们都没有自己的状态(举个简单的例子,一个函数sin(x),这个函数本身就是无状态的,所以我们现在喜欢的函数式编程也遵循这个理念)。而是根据传输的参数执行一些操作(作为HTTP请求参数)。另一方面,我们可以通过状态bean管理一些状态。比如购物车bean,假如它是一个单例,那么两个不同消费者购买的产品将被放置在同一个对象上。而如果其中一个消费者想要删除一个产品,另一个消费者就铁定不高兴。这也就是状态类对象应该是原型

这里说点题外话,不能确定时间的保证,未来会出一个用Java的代码习惯去解析vue的一些东西,内容已经总结完毕,也应用到自己的项目中了,然后得出的一些方法论,为什么在这里去说,就是因为vue也是遵循这个无状态和状态专门管理的原则的,扯远了,接着进行下一部分。

将原型放在单例中,反之亦然

通过上面的描述,很多概念都很清楚了吧,但有时候会发生一些更复杂的情况。第一个是在原型bean中放置单例。显然,如果注入的单例对象真的是一个单例的bean(没有状态),这个真的没一点问题。想象一下,对于我们的购物车,我们需要注入产品服务。此服务只会检查添加到购物车的产品是否库存。由于服务没有状态,并且会基于在方法签名中所传递的对象进行验证,因此不存在风险。

另一方面,将原型bean放在单例中需要做更多的工作。我们不能在单例bean中通过使用自动注入(比如@Autowired注解)注入原型bean。当Spring初始化所有具有依赖关系的单例bean时,这些注入只会执行一次。这也就意味着在以下代码,ShoppingCart的实例将始终是相同的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Controller
public class TestController {
@Autowired
private ShoppingCart shoppingCart;
 
@RequestMapping(value = "/addProduct/{productName}")
public String testAdd(@PathVariable(value="productName") String productName) {
Product product = new Product();
product.setName(productName);
this.shoppingCart.addProduct(product);
LOGGER.debug("ShoppingCart is "+this.shoppingCart);
return "test";
}
}

编译此类并进行一些URL调用:http://localhost:8080/addProduct/ice%20tea,http://localhost:8080/addProduct/milk。你将看到如下输出的顺序:

1
2
3
4
// after http://localhost:8080/addProduct/ice%20tea
ShoppingCart is ShoppingCart {products: [Product {ice tea}]}
// after http://localhost:8080/addProduct/milk
ShoppingCart is ShoppingCart {products: [Product {ice tea}, Product {milk}]}

为了在按照我们预想情况下工作(要求不一样的ShoppingCart),我们可以通过bean工厂手动获取ShoppingCart实例(这样就可以再一次生成一个不一样的ShoppingCart实例了):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Controller
public class TestController {
@Autowired
private ApplicationContext context;
 
@RequestMapping(value = "/addProduct/{productName}")
public String testAdd(@PathVariable(value="productName") String productName) {
Product product = new Product();
product.setName(productName);
 
ShoppingCart shoppingCart = (ShoppingCart) context.getBean("shoppingCart");
shoppingCart.addProduct(product);
LOGGER.debug("ShoppingCart is "+shoppingCart);
return "test";
}
}

这样,你就可以日志中看到,每次调用都会有新的ShoppingCart实例的生成:

1
2
3
4
// after http://localhost:8080/addProduct/ice%20tea
ShoppingCart is ShoppingCart {products: [Product {ice tea}]}
// after http://localhost:8080/addProduct/milk
ShoppingCart is ShoppingCart {products: [Product {milk}]}

方法注入

有没有别的方法在每次调用都会产生一个新实例?这就是接下来要说的方法注入的技术。它看起来有点像我们的手动去进行bean的查找,但更优雅。一个可以被上下文所感知(访问应用程序上下文可以得到)的bean将负责在单例bean中生成原型bean实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Service("shoppingCartProvider")
public class ShoppingCartProvider implements ApplicationContextAware {
 
private ApplicationContext context;
 
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
this.context = context;
}
 
public ShoppingCart getInstance() {
return (ShoppingCart) context.getBean("shoppingCart");
}
 
}

经过上面的修改,controller这里相应修改:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Controller
public class TestController {
@Autowired
private ShoppingCartProvider shoppingCartProvider;
 
@RequestMapping(value = "/addProduct/{productName}")
public String testAdd(@PathVariable(value="productName") String productName) {
Product product = new Product();
product.setName(productName);
 
ShoppingCart shoppingCart = shoppingCartProvider.getInstance();
shoppingCart.addProduct(product);
System.out.println("ShoppingCart is "+shoppingCart);
return "test";
}
}

也可以在XML配置文件中定义。里面会有一个属性引用原型bean,并允许在每次调用时创建新的实例。它可以很轻松地在一个bean中混合更多东西:

1
2
3
4
5
6
7
<bean id="shoppingCartProvider" class="com.migo.data.ShoppingCartProvider">
<lookup-method name="getInstance" bean="shoppingCart">
</lookup-method>
</bean>
 
<bean id="shoppingCart" class="com.migo.data.ShoppingCart" scope="prototype">
</bean>
1
2
3
public abstract class ShoppingCartProvider {
public abstract ShoppingCart getInstance();
}

Controller的代码与实现ApplicationContextAware接口的provider的那个例子是一样的。而区别也仅在于provider的bean定义和实现。该定义包含一个标签查找方法。它指定必须使用哪个方法来获取bean属性中指定的bean的新实例。在我们的这个例子中,我们通过调用ShoppingCartProvider类的getInstance方法来寻找新的ShoppingCart的实例。需要注意的一点,类和方法都可以是抽象的。通过这样做,你可以让Spring生成将实现该方法并返回所需bean的子类。如果这个方法不是抽象的,Spring会重写覆盖它。

Spring中的Bean类

单例的源码实现主要存在于org.springframework.beansorg.springframework.context包中。首先,从Bean包中查看BeanFactory接口。它包含两个我们绝对感兴趣的方法,可用来确定bean是单例还是原型:

  • boolean isSingleton(String name)throws NoSuchBeanDefinitionException
  • boolean isPrototype(String name)throws NoSuchBeanDefinitionException

接下来,我们来深入一下AbstractFactoryBean,从这个类的注释可以知道它是作为“FactoryBean实现的简单模板超类(还是直白翻译下比较好,说默认实现也觉得不靠谱)”。它包含一个用来返回单例或创建原型bean的getObject方法的实现。原型和单例是通过createInstance方法在不同的时间段进行的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/**
* Simple template superclass for {@link FactoryBean} implementations that
* creates a singleton or a prototype object, depending on a flag.
*
* <p>If the "singleton" flag is {@code true} (the default),
* this class will create the object that it creates exactly once
* on initialization and subsequently return said singleton instance
* on all calls to the {@link #getObject()} method.
*
* <p>Else, this class will create a new instance every time the
* {@link #getObject()} method is invoked. Subclasses are responsible
* for implementing the abstract {@link #createInstance()} template
* method to actually create the object(s) to expose.
*
* @author Juergen Hoeller
* @author Keith Donald
* @since 1.0.2
* @see #setSingleton
* @see #createInstance()
*/
public abstract class AbstractFactoryBean<T>
implements FactoryBean<T>, BeanClassLoaderAware, BeanFactoryAware, InitializingBean, DisposableBean {
/**
* Expose the singleton instance or create a new prototype instance.
* @see #createInstance()
* @see #getEarlySingletonInterfaces()
*/
@Override
public final T getObject() throws Exception {
if (isSingleton()) {
return (this.initialized ? this.singletonInstance : getEarlySingletonInstance());
}
else {
return createInstance();
}
}
...
/**
* Template method that subclasses must override to construct
* the object returned by this factory.
* <p>Invoked on initialization of this FactoryBean in case of
* a singleton; else, on each {@link #getObject()} call.
* @return the object returned by this factory
* @throws Exception if an exception occurred during object creation
* @see #getObject()
*/
protected abstract T createInstance() throws Exception;
...
}

另一个我们会感兴趣的一个点是BeanDefinition接口bean如其名,它定义了一个bean属性,例如:scope,class name,factory method name,properties或constructor arguments。

org.springframework.beans.factory.config.BeanDefinition

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* A BeanDefinition describes a bean instance, which has property values,
* constructor argument values, and further information supplied by
* concrete implementations.
*
* <p>This is just a minimal interface: The main intention is to allow a
* {@link BeanFactoryPostProcessor} such as {@link PropertyPlaceholderConfigurer}
* to introspect and modify property values and other bean metadata.
*
* @author Juergen Hoeller
* @author Rob Harrop
* @since 19.03.2004
* @see ConfigurableListableBeanFactory#getBeanDefinition
* @see org.springframework.beans.factory.support.RootBeanDefinition
* @see org.springframework.beans.factory.support.ChildBeanDefinition
*/
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
//限于篇幅,请自行查看源码,能发现很多有用的东西
}

想要看到bean被初始化的位置,我们需要跳转到context包中,更准确地说就是在AbstractApplicationContext类(这个类我们已经接触过好多次了)中。在它的public void refresh()throws BeansException,IllegalStateException我们可以找到一些关于bean创建的片段,特别是:

  • protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory):实现org.springframework.beans.factory.config .BeanFactoryPostProcessor接口的所有bean 都被初始化和调用。这种类型bean允许修改另一个bean的属性或构造函数参数(请看PostProcessorRegistrationDelegate的相应代码可以知道,使用BeanFactoryPostProcessor来处理我们所要用beanFactory生成的bean,这里可以直接把beanFactory看成是我们需要的bean即可)。但是请注意,在此阶段只能修改bean定义。“正常”bean实例尚未创建。关于这块会请参考文章Spring中的bean工厂后置处理器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* Instantiate and invoke all registered BeanFactoryPostProcessor beans,
* respecting explicit order if given.
* <p>Must be called before singleton instantiation.
*/
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
 
// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
// (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
}

org.springframework.context.support.PostProcessorRegistrationDelegate

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
* Delegate for AbstractApplicationContext's post-processor handling.
*
* @author Juergen Hoeller
* @since 4.0
*/
class PostProcessorRegistrationDelegate {
 
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
 
// Invoke BeanDefinitionRegistryPostProcessors first, if any.
Set<String> processedBeans = new HashSet<>();
 
if (beanFactory instanceof BeanDefinitionRegistry) {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
List<BeanFactoryPostProcessor> regularPostProcessors = new LinkedList<>();
List<BeanDefinitionRegistryPostProcessor> registryPostProcessors =
new LinkedList<>();
 
for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
BeanDefinitionRegistryPostProcessor registryPostProcessor =
(BeanDefinitionRegistryPostProcessor) postProcessor;
registryPostProcessor.postProcessBeanDefinitionRegistry(registry);
registryPostProcessors.add(registryPostProcessor);
}
else {
regularPostProcessors.add(postProcessor);
}
}
...
}
  • protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory):这里上下文实例化并调用实现了org.springframework.beans.factory.config.BeanPostProcessor接口的所有bean 。实现此接口的bean包含可以在其他bean初始化之前或之后调用的回调。因为内容比较多,关于这块会请参考文章Spring中的bean工厂后置处理器
1
2
3
4
5
6
7
8
/**
* Instantiate and invoke all registered BeanPostProcessor beans,
* respecting explicit order if given.
* <p>Must be called before any instantiation of application beans.
*/
protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
}
  • protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory):主要调用定义在org.springframework.beans.factory.config.ConfigurableListableBeanFactory接口内的preInstantiateSingletons方法。该方法的目的是实例化所有被定义为非延迟加载的bean。如果在应用程序上下文加载时遇到BeansException异常,则可能来自此方法。当bean无法创建时,它会抛出BeansException异常。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/**
* Finish the initialization of this context's bean factory,
* initializing all remaining singleton beans.
*/
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// Initialize conversion service for this context.
if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
beanFactory.setConversionService(
beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
}
 
// Register a default embedded value resolver if no bean post-processor
// (such as a PropertyPlaceholderConfigurer bean) registered any before:
// at this point, primarily for resolution in annotation attribute values.
if (!beanFactory.hasEmbeddedValueResolver()) {
beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
}
 
// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
for (String weaverAwareName : weaverAwareNames) {
getBean(weaverAwareName);
}
 
// Stop using the temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(null);
 
// Allow for caching all bean definition metadata, not expecting further changes.
beanFactory.freezeConfiguration();
 
// Instantiate all remaining (non-lazy-init) singletons.
beanFactory.preInstantiateSingletons();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
 
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
 
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
 
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
 
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
 
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
 
// Initialize message source for this context.
initMessageSource();
 
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
 
// Initialize other special beans in specific context subclasses.
onRefresh();
 
// Check for listener beans and register them.
registerListeners();
 
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
 
// Last step: publish corresponding event.
finishRefresh();
}
 
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
 
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
 
// Reset 'active' flag.
cancelRefresh(ex);
 
// Propagate exception to caller.
throw ex;
}
 
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}

总结

首先,我们讲了单例和原型作用域之间的区别。第一个为每个容器创建一个对象,而第二个在每个请求时创建一个新的bean对象。单例和原型都可以一起交叉使用,但原型不能通过@Autowired或其他注入方式来解决。它们应该使用getBean()方法方法查找来生成新实例。最后随意说了一说关于bean及其初始化的内容。

Spring5源码解析-Spring框架中的单例和原型bean的更多相关文章

  1. 【Spring】8、Spring框架中的单例Beans是线程安全的么

    看到这样一个问题:spring框架中的单例Beans是线程安全的么? Spring框架并没有对单例bean进行任何多线程的封装处理.关于单例bean的线程安全和并发问题需要开发者自行去搞定.但实际上, ...

  2. Spring框架中的单例bean是线程安全的吗?

    不,Spring框架中的单例bean不是线程安全的.

  3. Spring 框架中的单例 bean 是线程安全的吗?

    不,Spring 框架中的单例 bean 不是线程安全的.

  4. Spring5源码,Spring Web中的处理程序执行链

    一.什么是Spring中的处理程序执行链? 二.HandlerExecutionChain类 三.自定义处理程序执行链 Spring的DispatcherServlet假如缺少几个关键元素将无法分派请 ...

  5. Spring框架中的单例Beans是线程安全的么

    Spring框架并没有对单例bean进行任何多线程的封装处理.关于单例bean的线程安全和并发问题需要开发者自行去搞定.但实际上,大部分的Spring bean并没有可变的状态(比如Serview类和 ...

  6. Spring框架中的单例Beans是线程安全的么?

    Spring框架并没有对单例bean进行任何多线程的封装处理.关于单例bean的线程安全和并发问题需要开发者自行去搞定.但实际上,大部分的Spring bean并没有可变的状态(比如Serview类和 ...

  7. Spring5源码解析-论Spring DispatcherServlet的生命周期

    Spring Web框架架构的主要部分是DispatcherServlet.也就是本文中重点介绍的对象. 在本文的第一部分中,我们将看到基于Spring的DispatcherServlet的主要概念: ...

  8. 3.3 Spring5源码---循环依赖过程中spring读取不完整bean的最终解决方案

    根据之前解析的循环依赖的源码, 分析了一级缓存,二级缓存,三级缓存的作用以及如何解决循环依赖的. 然而在多线程的情况下, Spring在创建bean的过程中, 可能会读取到不完整的bean. 下面, ...

  9. Spring5源码解析_IOC之容器的基本实现

    前言: 在分析源码之前,我们简单回顾一下SPring核心功能的简单使用: 容器的基本用法 Bean是Spring最核心的东西,Spring就像是一个大水桶,而Bean就是水桶中的水,水桶脱离了水就没有 ...

随机推荐

  1. 微信支付-微信公众号支付,微信H5支付,微信APP支付,微信扫码支付

    在支付前,如果使用第三方MVC框架,则使用重写模式,服务器也需要配置该项 if (!-e $request_filename){ rewrite ^/(.*)$ /index.php/$ last; ...

  2. 算法 -- 四种方法获取的最长“回文串”,并对时间复杂进行分析对比&PHP

    https://blog.csdn.net/hongyuancao/article/details/82962382 “回文串”是一个正读和反读都一样的字符串,比如“level”或者“noon”等等就 ...

  3. Scala集合(二)

    将函数映射到集合 map方法 val names = List("Peter" , "Paul", "Mary") names.map(_. ...

  4. 如何在CentOS 7中安装最新Git(源码安装)

    如何在CentOS 7中安装最新Git 2017年05月20日 11:49:53 阅读数:1624 Git是在今天的软件开发行业一个非常有用的版本控制工具.我一直使用Git.于是为Linux公社的读者 ...

  5. 【LeetCode每天一题】Find First and Last Position of Element in Sorted Array(找到排序数组中指定元素的开始和结束下标)

    Given an array of integers nums sorted in ascending order, find the starting and ending position of ...

  6. Python 初始函数

    python之路——初识函数   阅读目录 为什么要用函数 函数的定义与调用 函数的返回值 函数的参数 本章小结 为什么要用函数 我们就想啊,要是我们能像使用len一样使用我们这一大段“计算长度”的代 ...

  7. c#中取整和取余

    "%"为取余. "/"号整型运算是取整,浮点运算时为除法运算.如54/10结果为5,54.0/10.0结果为5.4.而且取整时不进行四舍五入只取整数部分,如54 ...

  8. [Java in NetBeans] Lesson 16. Exceptions.

    这个课程的参考视频和图片来自youtube. 主要学到的知识点有: We want to handle the bad Error. (e.g bad input / bugs in program) ...

  9. 如何使用Hive&R从Hadoop集群中提取数据进行分析

    一个简单的例子! 环境:CentOS6.5 Hadoop集群.Hive.R.RHive,具体安装及调试方法见博客内文档. 1.分析题目 --有一个用户数据样本(表名huserinfo)10万数据左右: ...

  10. Elasticsearch集群管理工具head插件安装

    Elasticsearch-head是一个elasticsearch的集群管理工具,它是完全由html5编写的独立网页程序,你可以通过插件把它集成到es.或直接下载源码,在本地打开index.html ...