前言 Spring IoC是一种通过描述 来生成或者获取对象的技术,和我们平时在需要时直接new一个对象是不一样的,它就是通过你给的针对对象的描述,如对象的名字或者类型,来生成这个对象(若已生成,就直接取)。在Spring中,这些被管理的对象叫作Bean。Spring会统一管理这些Bean,包括Bean之间的依赖关系,负责管理这些Bean的是IoC容器。
IoC(控制反转)的意思就是,原本是我们自己控制对象,现在变成由Spring控制,我们需要对象的时候要从容器中取。
 
文中出现的Spring源码都是SpringBoot 2.3.0版本。
IoC容器 IoC容器,在程序中就是实现了BeanFactory接口的类。BeanFactory接口中提供了用name和requiredType获取Bean的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public  interface  BeanFactory   {	Object getBean (String name)  throws  BeansException ; 	<T> T getBean (String name, Class<T> requiredType)  throws  BeansException ; 	Object getBean (String name, Object... args)  throws  BeansException ; 	<T> T getBean (Class<T> requiredType)  throws  BeansException ; 	<T> T getBean (Class<T> requiredType, Object... args)  throws  BeansException ; 	 	 } 
 
ApplicationContext接口间接继承了BeanFactory接口,它包含BeanFactory接口的所有方法,还提供了额外功能,Spring将它作为默认的启动容器。
当我们用SpringApplication.run(XXXApplication.class, args);启动时,Spring就会根据依赖的jar包来指定要使用的IoC容器,看SpringApplication类中的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 protected  ConfigurableApplicationContext createApplicationContext ()   {	Class<?> contextClass = this .applicationContextClass; 	if  (contextClass == null ) { 		try  { 			switch  (this .webApplicationType) { 			case  SERVLET: 				contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS); 				break ; 			case  REACTIVE: 				contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);  				break ; 			default : 				contextClass = Class.forName(DEFAULT_CONTEXT_CLASS); 			} 		} 		catch  (ClassNotFoundException ex) { 			throw  new  IllegalStateException( 					"Unable create a default ApplicationContext, please specify an ApplicationContextClass" , ex); 		} 	} 	return  (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass); } 
 
webApplicationType的值由依赖的jar包决定,默认值是SERVLET,所以IoC容器一般是DEFAULT_SERVLET_WEB_CONTEXT_CLASS=AnnotationConfigServletWebServerApplicationContext,这个类间接实现了ApplicationContext接口。
用注解装配Bean SpringBoot多是用注解把Bean装配到IoC容器中,但也可以使用XML配置文件来装配,这里只介绍注解的方式。
@Component 示例:
1 2 3 4 @Component public  class  ApplicationContextUtil  implements  ApplicationContextAware   {	 } 
 
启动类:
1 2 3 4 5 6 @SpringBootApplication public  class  MysqlMultipleDemoApplication   {	public  static  void  main (String[] args)   { 		SpringApplication.run(MysqlMultipleDemoApplication.class , args ) ; 	} } 
 
一个类加上@Component注解后,在SpringBoot启动时,它就会被扫描进IoC容器,原因就是SpringBoot启动类的注解@SpringBootApplication里面包含了@ComponentScan注解,而@ComponentScan的作用就是扫描类当前包及其子包下的被@Component修饰的类。@ComponentScan也可以指定要扫描的包路径。 顺便一提,我们熟悉的@Controller、@Service,以及下面说到的@Configuration都包含了@Component注解,所以这些注解修饰的类都会被装配到IoC容器中。
@Configuration和@Bean组合 示例:
1 2 3 4 5 6 7 @Configuration public  class  BeanConfig   {	@Bean  	ApplicationContextUtil 	applicationContextUtil ()   { 		return  new  ApplicationContextUtil(); 	} } 
 
这样就在IoC容器中生成了一个叫”applicationContextUtil”的Bean。Bean的名字可以在@Bean注解中指定,不指定就为方法名。在一个@Configuration类中,可以写多个@Bean。
另外,这样的组合可以控制Bean的加载顺序,控制方法见这篇博客: https://blog.csdn.net/qianshangding0708/article/details/107373538 
PS: 因为@Configuration包含了@Component注解,所以换成 @Component + @Bean 组合也是可以的,效果一样。
 
上面两种方法的区别 从效果上来说没有区别,都可以装配Bean。但后者有一个专门的使用场景,就是装配依赖jar包中的Bean。如上面的示例代码,若ApplicationContextUtil是依赖包中的类,不在自己程序中,就只能用@Configuration和@Bean装配。
依赖注入 类之间免不了有依赖关系,如我们经常在Service类中这样写:
1 2 3 4 5 6 7 8 9 10 @Service public  class  PersonServiceImpl  implements  PersonService  {	@Autowired  	PersonMapper personMapper; 	@Override  	public  void  insertPerson (Person person)   { 		personMapper.insertPerson(person); 	} } 
 
这就说明PersonServiceImpl类依赖PersonMapper类,但这个PersonMapper的实例对象不是我们自己new的,是通过@Autowired注解,让IoC容器为我们注入的,因此称之为“依赖注入” 。
这里有个疑问,PersonMapper一般是接口,无实现类,也没有@Component修饰,为什么会被装配到IoC容器中? 答案是,Mybatis为每个Mapper接口实现了一个动态代理类(即Mapper接口的实现类),这些动态代理类会被MapperFactoryBean保存起来。MapperFactoryBean作为接口类型被添加到IoC容器中,当需要依赖注入Mapper接口的Bean时,调用MapperFactoryBean的getObject()方法得到Mapper的代理类。 这其中的原理足够再写一篇文章,这里就不展开介绍,具体内容可以上网搜索“mapper接口依赖注入”。
 
@Autowired和@Resource @Autowired和@Resource两个注解的作用都是找到对应的Bean注入到依赖它的类中。区别是:
@Autowired是Spring注解,@Resource是JDK注解 
@Autowired先按类型注入,找不到再按名称注入,@Resource相反 
 
注入方式 设值注入(set注入) 设值注入是指IoC容器通过set方法来注入被依赖对象。这种注入方式简单、直观。 把上面的PersonServiceImpl代码改为这样就是设值注入:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Service public  class  PersonServiceImpl  implements  PersonService  {	PersonMapper personMapper; 	 	@Autowired  	public  void  setPersonMapper (PersonMapper personMapper)  { 	    this .personMapper = personMapper; 	} 	@Override  	public  void  insertPerson (Person person)   { 		personMapper.insertPerson(person); 	} } 
 
构造注入 利用构造器来设置依赖对象的方式,被称为构造注入。通俗来说,就是驱动Spring在底层以反射方式执行带指定参数的构造器,当执行带参数的构造器时,就可利用构造器参数对成员变量执行初始化。 PersonServiceImpl代码改为这样就是构造注入:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Service public  class  PersonServiceImpl  implements  PersonService  {	PersonMapper personMapper; 	 	public  PersonServiceImpl (@Autowired PersonMapper personMapper)  { 	    this .personMapper = personMapper; 	} 	@Override  	public  void  insertPerson (Person person)   { 		personMapper.insertPerson(person); 	} } 
 
@Primary和@Quelifier 这两个注解解决了一个问题:当@Autowired按类型寻找Bean,找到了多个符合条件的Bean时,应该注入哪一个? 举个例子,SpringBoot的官方给出的“在项目中配置多个数据源”的示例代码中:
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 @Configuration public  class  DataSourceConfig   {    @Bean      @Primary      @ConfigurationProperties ("app.datasource.first" )     public  DataSourceProperties firstDataSourceProperties ()   {         return  new  DataSourceProperties();     }          @Bean      @Primary      @ConfigurationProperties ("app.datasource.first.configuration" )     public  DataSource firstDataSource ()   {         return  firstDataSourceProperties().initializeDataSourceBuilder().type(HikariDataSource.class ).build () ;     }          @Bean      @ConfigurationProperties ("app.datasource.second" )     public  DataSourceProperties secondDataSourceProperties ()   {         return  new  DataSourceProperties();     }          @Bean      @ConfigurationProperties ("app.datasource.second.configuration" )     public  DataSource secondDataSource ()   {         return  secondDataSourceProperties().initializeDataSourceBuilder().type(BasicDataSource.class ).build () ;     } 	 } 
 
这里定义了两个名字不同但类型相同的DataSourceProperties Bean,当有个Bean不指定名字地要求注入它依赖的DataSourceProperties对象,IoC容器就会优先注入被@Primary修饰的那个DataSourceProperties Bean。
当然我们更希望由自己决定注入哪一个Bean,这样更准确。这时就用@Quelifier注解指定要注入的Bean的名字。使用方法类似:
1 2 3 4 @Bean public  DataSourceTransactionManager ds1TransactionManager (@Qualifier("firstDataSource" )  DataSource dataSource)  {    return  new  DataSourceTransactionManager(dataSource); } 
 
Bean的作用域 用@Scope注解可以指定Bean的作用域:
1 2 3 4 5 6 7 8 9 10 @Configuration public  class  BeanConfig1   {	@Bean  	@Scope (ConfigurableBeanFactory.SCOPE_PROTOTYPE) 	MyBean1 myBean1 ()   { 		MyBean1 myBean1 = new  MyBean1(); 		myBean1.setName("hello1" ); 		return  myBean1; 	} } 
 
进入@Scope注解源码,可以看到它支持的作用域定义在ConfigurableBeanFactory和WebApplicationContext中。
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 @Target ({ElementType.TYPE, ElementType.METHOD})@Retention (RetentionPolicy.RUNTIME)@Documented public  @interface  Scope {	@AliasFor ("scopeName" ) 	String value ()  default  "" ; 	 	@AliasFor ("value" ) 	String scopeName ()  default  "" ; 	ScopedProxyMode proxyMode ()  default  ScopedProxyMode.DEFAULT ; 	 } 
 
在ConfigurableBeanFactory接口中,有两个作用域:单例和原型,它们在所有Spring IoC容器都可以使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public  interface  ConfigurableBeanFactory  extends  HierarchicalBeanFactory , SingletonBeanRegistry   {	 	String SCOPE_SINGLETON = "singleton" ; 	 	String SCOPE_PROTOTYPE = "prototype" ; 	 	 } 
 
在WebApplicationContext接口中,有三个作用域:request、session、application。它们只在实现了WebApplicationContext接口的容器中可以使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public  interface  WebApplicationContext  extends  ApplicationContext   {	 	String SCOPE_REQUEST = "request" ; 	 	String SCOPE_SESSION = "session" ; 	 	String SCOPE_APPLICATION = "application" ; 	 } 
 
request session application 单例和原型的含义大家都熟悉,这里先介绍下另外三个作用域。
request: 在一次HTTP请求中,bean是单例的,再来一个HTTP请求就再实例化一个bean(容器中有保留一份bean定义,用bean定义实例化bean)。不同的HTTP请求的bean实例不同,自然也不会互相影响。HTTP请求结束bean实例就销毁 
session: 和request作用于类似,但是范围是一次HTTP session 
application: 在一个ServletContext中,bean是单例的。和singleton的区别是,singleton是指在一个IoC容器中bean是单例的。 
 
ServletContext是什么? 一个Java Web应用都有一个ServletContext,这个应用里的所有servlet都可通过ServletContext获取初始化参数(web.xml中的节点定义的配置)、文件路径等等,同时servlet之间还可通过ServletContext.set/getAttribute()修改和访问共有的属性。ServletContext是servlet之间,以及项目容器(如Tomcat)和WEB项目之间的桥梁。 
 
单例 单例模式 单例模式是一个很出名的设计模式,常见的是懒汉式和饿汉式,但Spring采用的是登记式,且Spring允许你设置是否用懒加载(默认不用)。 饿汉式:
1 2 3 4 5 6 7 8 9 10 11 public  class  Singleton   {	 	private  static  final  Singleton singleton = new  Singleton(); 	 	private  Singleton ()   { 	} 	 	public  static  Singleton getInstance ()   { 		return  singleton; 	} } 
 
懒汉式(线程安全):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public  class  Singleton   {	 	private  static  Singleton singleton = null ; 	 	private  Singleton ()   { 		 	} 	 	public  static  synchronized  Singleton getInstance ()   { 		if (singleton == null ) { 			singleton = new  Singleton(); 		} 		return  singleton; 	} } 
 
登记式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public  class  Singleton   {     	private  static  Map<String, Object> registry = new  HashMap<>(); 	 	protected  Singleton ()   { 	} 	 	public  static  Singleton getInstance ()   { 		Object object = registry.get("singleton" ); 		if (object == null ) { 			try  { 				registry.put("singleton" , new  Singleton()); 			} catch  (Exception e) { 				e.printStackTrace(); 			} 			object = registry.get("singleton" ); 		} 		return  (Singleton) object; 	} } 
 
用这种方式的好处在于,不要求单例类的构造方法是private。因此尽管我们的controller、service未声明private构造方法,在Spring中依然可以是单例。
Spring单例模式的实现 Spring中的单例的概念是,在一个IoC容器中,一个Bean定义只有一个实例。上面说到Spring的单例模式是登记式,有一个bean的“登记表”,保存beanName和bean实例,当登记表中不存在bean实例,就立即创建一个并“登记”,若已有bean实例就直接返回。
“登记表”的接口SingletonBeanRegistry:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public  interface  SingletonBeanRegistry   {	 	void  registerSingleton (String beanName, Object singletonObject)  ; 	 	@Nullable  	Object getSingleton (String beanName)  ; 	 } 
 
“登记表”的接口实现类DefaultSingletonBeanRegistry:
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 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 public  class  DefaultSingletonBeanRegistry  extends  SimpleAliasRegistry  implements  SingletonBeanRegistry   {	 	 	private  final  Map<String, Object> singletonObjects = new  ConcurrentHashMap<>(256 ); 	 	private  final  Map<String, ObjectFactory<?>> singletonFactories = new  HashMap<>(16 ); 	 	private  final  Map<String, Object> earlySingletonObjects = new  HashMap<>(16 ); 	 	 	private  final  Set<String> registeredSingletons = new  LinkedHashSet<>(256 ); 	 	@Override  	public  void  registerSingleton (String beanName, Object singletonObject)  throws  IllegalStateException  { 		Assert.notNull(beanName, "Bean name must not be null" ); 		Assert.notNull(singletonObject, "Singleton object must not be null" ); 		synchronized  (this .singletonObjects) { 			Object oldObject = this .singletonObjects.get(beanName); 			if  (oldObject != null ) { 				throw  new  IllegalStateException("Could not register object ["  + singletonObject + 						"] under bean name '"  + beanName + "': there is already object ["  + oldObject + "] bound" ); 			} 			addSingleton(beanName, singletonObject); 		} 	} 	 	protected  void  addSingleton (String beanName, Object singletonObject)   { 		synchronized  (this .singletonObjects) { 			this .singletonObjects.put(beanName, singletonObject); 			this .singletonFactories.remove(beanName); 			this .earlySingletonObjects.remove(beanName); 			this .registeredSingletons.add(beanName);  		} 	} 	 	 	@Override  	@Nullable  	public  Object getSingleton (String beanName)   { 		return  getSingleton(beanName, true ); 	} 	@Nullable  	protected  Object getSingleton (String beanName, boolean  allowEarlyReference)   { 		Object singletonObject = this .singletonObjects.get(beanName); 		if  (singletonObject == null  && isSingletonCurrentlyInCreation(beanName)) { 			synchronized  (this .singletonObjects) { 				singletonObject = this .earlySingletonObjects.get(beanName); 				if  (singletonObject == null  && allowEarlyReference) { 					ObjectFactory<?> singletonFactory = this .singletonFactories.get(beanName); 					if  (singletonFactory != null ) { 						singletonObject = singletonFactory.getObject();  						this .earlySingletonObjects.put(beanName, singletonObject); 						this .singletonFactories.remove(beanName); 					} 				} 			} 		} 		return  singletonObject; 	} 	 	 	public  Object getSingleton (String beanName, ObjectFactory<?> singletonFactory)   { 		Assert.notNull(beanName, "Bean name must not be null" ); 		synchronized  (this .singletonObjects) { 			Object singletonObject = this .singletonObjects.get(beanName);  			if  (singletonObject == null ) { 				if  (this .singletonsCurrentlyInDestruction) { 					throw  new  BeanCreationNotAllowedException(beanName, 							"Singleton bean creation not allowed while singletons of this factory are in destruction "  + 							"(Do not request a bean from a BeanFactory in a destroy method implementation!)" ); 				} 				if  (logger.isDebugEnabled()) { 					logger.debug("Creating shared instance of singleton bean '"  + beanName + "'" ); 				} 				beforeSingletonCreation(beanName); 				boolean  newSingleton = false ; 				boolean  recordSuppressedExceptions = (this .suppressedExceptions == null ); 				if  (recordSuppressedExceptions) { 					this .suppressedExceptions = new  LinkedHashSet<>(); 				} 				try  { 					singletonObject = singletonFactory.getObject();  					newSingleton = true ; 				} 				catch  (IllegalStateException ex) { 					 					 					singletonObject = this .singletonObjects.get(beanName); 					if  (singletonObject == null ) { 						throw  ex; 					} 				} 				catch  (BeanCreationException ex) { 					if  (recordSuppressedExceptions) { 						for  (Exception suppressedException : this .suppressedExceptions) { 							ex.addRelatedCause(suppressedException); 						} 					} 					throw  ex; 				} 				finally  { 					if  (recordSuppressedExceptions) { 						this .suppressedExceptions = null ; 					} 					afterSingletonCreation(beanName); 				} 				if  (newSingleton) { 					addSingleton(beanName, singletonObject);  				} 			} 			return  singletonObject; 		} 	}	 	 } 
 
那么,bean实例是何时被注册的?在IoC容器启动,和程序中使用ApplicationContext.getBean()方法时。
IoC容器启动时,调用入口在IoC容器基类AbstractApplicationContext.finishBeanFactoryInitialization()方法:
1 2 3 4 5 6 protected  void  finishBeanFactoryInitialization (ConfigurableListableBeanFactory beanFactory)   {	 	 	beanFactory.preInstantiateSingletons(); } 
 
程序中使用ApplicationContext.getBean()方法时:
1 2 3 4 5 @Override public  Object getBean (String name)  throws  BeansException  {	assertBeanFactoryActive(); 	return  getBeanFactory().getBean(name); } 
 
这两个调用点,都会进入到AbstractBeanFactory.doGetBean()方法:
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 protected  <T> T doGetBean (final  String name, @Nullable final  Class<T> requiredType,  		@Nullable final  Object[] args, boolean  typeCheckOnly)  throws  BeansException  {	final  String beanName = transformedBeanName(name); 	Object bean; 	 	Object sharedInstance = getSingleton(beanName);  	if  (sharedInstance != null  && args == null ) { 		if  (logger.isTraceEnabled()) { 			if  (isSingletonCurrentlyInCreation(beanName)) { 				logger.trace("Returning eagerly cached instance of singleton bean '"  + beanName + 						"' that is not fully initialized yet - a consequence of a circular reference" ); 			} 			else  { 				logger.trace("Returning cached instance of singleton bean '"  + beanName + "'" ); 			} 		} 		bean = getObjectForBeanInstance(sharedInstance, name, beanName, null ); 	} 	else  { 		 		try  { 			 			if  (mbd.isSingleton()) { 				 				sharedInstance = getSingleton(beanName, () -> { 					try  { 						return  createBean(beanName, mbd, args); 					} 					catch  (BeansException ex) { 						destroySingleton(beanName); 						throw  ex; 					} 				}); 				bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); 			} 			 			else  if  (mbd.isPrototype()) { 				Object prototypeInstance = null ; 				try  { 					beforePrototypeCreation(beanName); 					prototypeInstance = createBean(beanName, mbd, args); 				} 				finally  { 					afterPrototypeCreation(beanName); 				} 				bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd); 			}				 			 		} 		catch  (BeansException ex) { 			cleanupAfterBeanCreationFailure(beanName); 			throw  ex; 		} 	} 	 	return  (T) bean; } 
 
Spring为什么默认单例模式 单例bean的优势: 
减少了新生成实例的消耗。新生成实例消耗包括两方面,第一spring会通过反射或者cglib来生成bean实例,这都是耗性能的操作,第二给对象分配内存也会涉及复杂算法 
减少jvm垃圾回收。由于不会给每个请求都新生成bean实例,所以自然回收的对象少了 
可以快速获取到bean。因为单例的获取bean操作除了第一次生成之外其余的都是从缓存里获取的,所以很快 
 
单例bean的劣势:  单例的bean一个很大的劣势就是他不能做到线程安全。下面详细说明。
单例模式的线程安全问题 
线程安全问题都是由单例bean的成员变量及静态变量引起的 
若每个线程中对成员变量、静态变量只有读操作,而无写操作,那么不存在线程安全问题;若有写操作,就存在线程安全问题 
成员变量、静态变量的线程安全问题是因为成员变量在堆中,静态变量在方法区中,堆和方法区是线程共享的(局部变量在栈中,是线程私有的) 
当多个线程调用方法时会不会出现线程安全问题呢?答案是不会,方法存在虚拟机栈中,是线程私有的 
 
这也是为什么我们声明的bean,如controller、service,基本上不含除了依赖bean以外的成员变量,也不会去修改成员变量值。
如果一定要在单例bean中使用会被修改的成员变量,可以在类中定义一个ThreadLocal成员变量,将需要的可变成员变量保存在ThreadLocal中。
ThreadLocal是线程局部变量,所谓的线程局部变量,就是仅仅只能被本线程访问,不能在线程之间进行共享访问的变量。
 
举例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 private  static  final  ThreadLocal threadSession = new  ThreadLocal();public  static  Session getSession ()  throws  InfrastructureException  {	Session s = (Session) threadSession.get(); 	try  { 		if  (s == null ) { 			s = getSessionFactory().openSession(); 			threadSession.set(s); 		} 	} catch  (HibernateException ex) { 		throw  new  InfrastructureException(ex); 	} 	return  s; } 
 
附上ThreadLocal的普通用法。在一个线程中,controller经常要传参给service,如果固定要传某个参数,方法形参的声明会很臃肿,所以可以把一些必须要传的参数写到ThreadLocal中。 举例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public  class  BusiUtil   {	 	private  static  ThreadLocal<String> threadLocal_logindex = new  ThreadLocal<>(); 	 	public  static  String getLogIndex ()   { 		return  threadLocal_logindex.get(); 	} 	 	public  static  void  setLogIndex (String logIndex)   { 		threadLocal_logindex.set(logIndex); 	} 	 	public  static  void  removeLogIndex ()   { 		threadLocal_logindex.remove(); 	} 	 	 	 }