单例模式
-
@Scope默认是单例模式,即scope=“singleton”。
-
单例模式肯定是线程不安全的,一般来说只有类的属性/全局变更会导致多线程问题,而方法内的局部变量不会有并发问题
-
Spring的Bean中的自定义的成员变量可以用threadlocal封装,变成线程安全的
-
Spring提供的template等类没有多线程的问题
prototype
-
prototype作用域部署的bean,每一次请求(将其注入到另一个bean中,或者以程序的方式调用容器的
getBean()方法)都会产生一个新的bean实例,相当与一个new的操作 -
使用@Autowired没有实现多个实例,即使使用@Scope(BeanDefinition.SCOPE_PROTOTYPE)将Bean声明为prototype:
1.外层的类是singleton
2.使用@Autowired注入
这样的话仍然指挥一个实例,举例:
@Component
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class ClassB {
private Integer num = Integer.valueOf(0); //全局变量
public Integer getNum() {
return num;
}
public void setNum(Integer num) {
this.num = num;
}
}
使用@Autowired注入到ClassA中
/**
* Created by ASUS on 2017/5/24.
*/
@Component
public class ClassA {
@Autowired
private ClassB classB;
public Integer addNum(){
classB.setNum(classB.getNum()+1);
System.out.println(classB.getNum());
return classB.getNum();
}
}
通过Controller调用,用@Autowire将ClassA注入。
@RestController
public class Controller {
@Autowired
private ClassA classA;
@RequestMapping("/")
public String print(){
return classA.addNum().toString();
}
}
每一次访问都会导致num+1
ClassB并没有被多次实例化,ClassA本身是一个单例,单例只会实例化一次,这样其属性自然也就只会被实例化一次。
- ** 解决方法**
直接在方法中声明局部变量
@Component
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class ClassA {
public Integer addNum(){
//不使用@Autowired,直接在方法中声明
ClassB classB = new ClassB();
classB.setNum(classB.getNum()+1);
System.out.println(classB.getNum());
return classB.getNum();
}
注意:
这样做的问题在于,如果ClassB中需要使用@Autowired,则这个@Autowired会失效。
比如想在ClassB中使用Spring管理的JdbcTemplate,就需要使用@Autowired。如果ClassB不是通过@Autowired实例化的,ClassB中的JdbcTemplate就会注入失败,导致NullPointerException。
使用ThreadLocal管理属性
还要一个方法实现和多实例“类似”的功能,即使用ThreadLocal来管理类中的属性。例如对于上面的例子:
@Component
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class ClassB {
//使用ThreadLocal管理属性,每个线程都操作一个新的副本
private static ThreadLocal<Integer> integerThreadLocal = new ThreadLocal<Integer>(){
@Override
protected Integer initialValue() {
return 0;
}
};
public ThreadLocal<Integer> getIntegerThreadLocal() {
return integerThreadLocal;
}
}
在ClassB中就可以正常使用@Autowired进行注入。
@Component
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class ClassA {
@Autowired
ClassB classB;
public Integer addNum(){
Integer integer = classB.getIntegerThreadLocal().get();
integer++;
return integer;
}
}
-
request
request表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP request内有效, -
session
session作用域表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP session内有效
-
global session
global session作用域类似于标准的HTTP Session作用域,不过它仅仅在基于portlet的web应用中才有意义。Portlet规范定义了全局Session的概念,它被所有构成某个 portlet web应用的各种不同的portlet所共享。在global session作用域中定义的bean被限定于全局portlet Session的生命周期范围内。如果你在web中使用global session作用域来标识bean,那么web会自动当成session类型来使用。
request,sessuib.gobal session 正常宰项目中很少使用