Spring IoC原理

前言

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注入到依赖它的类中。区别是:

  1. @Autowired是Spring注解,@Resource是JDK注解
  2. @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注解源码,可以看到它支持的作用域定义在ConfigurableBeanFactoryWebApplicationContext中。

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 "";

/**
* Specifies the name of the scope to use for the annotated component/bean.
* <p>Defaults to an empty string ({@code ""}) which implies
* {@link ConfigurableBeanFactory#SCOPE_SINGLETON SCOPE_SINGLETON}.
* @since 4.2
* @see ConfigurableBeanFactory#SCOPE_PROTOTYPE
* @see ConfigurableBeanFactory#SCOPE_SINGLETON
* @see org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST
* @see org.springframework.web.context.WebApplicationContext#SCOPE_SESSION
* @see #value
*/
@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 {

/**
* Scope identifier for the standard singleton scope: {@value}.
* <p>Custom scopes can be added via {@code registerScope}.
* @see #registerScope
*/
String SCOPE_SINGLETON = "singleton";

/**
* Scope identifier for the standard prototype scope: {@value}.
* <p>Custom scopes can be added via {@code registerScope}.
* @see #registerScope
*/
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 {

/**
* Scope identifier for request scope: "request".
* Supported in addition to the standard scopes "singleton" and "prototype".
*/
String SCOPE_REQUEST = "request";

/**
* Scope identifier for session scope: "session".
* Supported in addition to the standard scopes "singleton" and "prototype".
*/
String SCOPE_SESSION = "session";

/**
* Scope identifier for the global web application scope: "application".
* Supported in addition to the standard scopes "singleton" and "prototype".
*/
String SCOPE_APPLICATION = "application";

// 省略其他属性和方法
}

request session application

单例和原型的含义大家都熟悉,这里先介绍下另外三个作用域。

  1. request: 在一次HTTP请求中,bean是单例的,再来一个HTTP请求就再实例化一个bean(容器中有保留一份bean定义,用bean定义实例化bean)。不同的HTTP请求的bean实例不同,自然也不会互相影响。HTTP请求结束bean实例就销毁
  2. session: 和request作用于类似,但是范围是一次HTTP session
  3. 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 {
// 存放beanName和bean实例的map,就像bean的登记表
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 {
/**
* Register the given existing object as singleton in the bean registry
*/
void registerSingleton(String beanName, Object singletonObject);

/**
* Return the (raw) singleton object registered under the given name.
*/
@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 {

// 下面3个Map都是用于缓存
/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

// 登记表
/** Set of registered singletons, containing the bean names in registration order. */
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); // 登记
}
}

/**
* getSingleton方法1
*/
@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;
}

/**
* getSingleton方法2
*/
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) {
// Has the singleton object implicitly appeared in the meantime ->
// if yes, proceed with it since the exception indicates that state.
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
// 初始化单例且非懒加载的bean,beanFactory=DefaultListableBeanFactory
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// ...
// Instantiate all remaining (non-lazy-init) singletons.
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;

// Eagerly check singleton cache for manually registered singletons.
Object sharedInstance = getSingleton(beanName); //调用getSingleton方法1
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()) {
//调用getSingleton方法2,后一个参数是createBean()方法返回的bean实例(且已初始化)
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
// 可对比看下原型作用域,就是一次getBean就会调用createBean()方法返回bean实例(且已初始化)
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的优势:

  1. 减少了新生成实例的消耗。新生成实例消耗包括两方面,第一spring会通过反射或者cglib来生成bean实例,这都是耗性能的操作,第二给对象分配内存也会涉及复杂算法
  2. 减少jvm垃圾回收。由于不会给每个请求都新生成bean实例,所以自然回收的对象少了
  3. 可以快速获取到bean。因为单例的获取bean操作除了第一次生成之外其余的都是从缓存里获取的,所以很快

单例bean的劣势:
单例的bean一个很大的劣势就是他不能做到线程安全。下面详细说明。

单例模式的线程安全问题

  1. 线程安全问题都是由单例bean的成员变量及静态变量引起的
  2. 若每个线程中对成员变量、静态变量只有读操作,而无写操作,那么不存在线程安全问题;若有写操作,就存在线程安全问题
  3. 成员变量、静态变量的线程安全问题是因为成员变量在堆中,静态变量在方法区中,堆和方法区是线程共享的(局部变量在栈中,是线程私有的)
  4. 当多个线程调用方法时会不会出现线程安全问题呢?答案是不会,方法存在虚拟机栈中,是线程私有的

这也是为什么我们声明的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();

// 这个方法只能访问本线程内的sqlSession
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();
}

// 在controller中调用BusiUtil.setLogIndex("123456789")
// 在service中调用BusiUtil.getLogIndex()
}