7. Spring管理对象的作用域
由Spring管理的对象,默认情况下,是单例的!所以,其作用域就非常久!
在Spring管理对象的情况下,讨论对象的作用域,其本质就是讨论其是否单例!
在创建对象的方法之前,添加@Scope注解,并配置注解参数为prototype,就可以使得该对象不是单例的:
@Scope("prototype") @Bean public User user() { return new User(); }
由Spring管理的对象,如果是单例模式的,默认情况下,是饿汉式的!在创建对象的方法之前,添加@Lazy注解,就可以调整为懒汉式的:
@Bean @Lazy public User user() { return new User(); }
一般,在开发项目时,极少调整对象的作用域!
8. 当天小结:
Spring的主要作用:创建对象,管理对象;
如果某个方法是用于给Spring框架创建对象的,这个方法就必须添加@Bean注解;
所有添加了@Bean注解的方法,其所在的类应该添加@Configuration注解,凡添加了@Configuration注解的类称之为配置类;
默认情况下,由Spring管理的对象是单例的,使用@Scope注解可以将Spring管理的对象调整为“非单例”的;
默认情况下,由Spring管理的单例的对象是是“饿汉式”的,使用@Lazy可以将它们改为“懒汉式”的。
附1:设计模式之单例模式
单例模式的特点:在同一时期,某个类的对象一定最多只有1个!也许会尝试多次的获取对象,但是,获取到的一定是同一个对象!
假设项目中有King类:
public class King { }
很显然,目前它并不是单例的,因为,可以:
King k1 = new King();
King k2 = new King();
King k3 = new King();
以上代码就创建了3个King类型的对象!如果要实现单例,首先,就必须限制构造方法的访问,例如:
public class King { private King() { } }
每个类中都可以有若干个构造方法,如果某个类没有显式的声明任何构造方法,编译器就会自动添加1个公有的、无参数的构造方法!如果类中已经声明任何构造方法,则编译器不会自动添加构造方法!
由于将构造方法声明为私有的,则原有的King k1 = new King();这类代码就不能用于创建对象了!
限制构造方法的访问,其目的是“不允许随意创建对象”,并不是“不允许创建对象”,在King类的内部,还是可以创建对象的,可以添加方法,返回内部创建的对象:
public class King { private King king = new King(); private King() { } public King getInstance() { return king; } }
所以,当需要King类型的对象时,可以通过getInstance()方法来获取!
但是,以上代码是不可行的!因为,如果要调用getInstance()方法,必须先获取King的对象,而获取King对象的唯一方式就是调用getInstance()方法!为了解决这个问题,必须在getInstance()方法的声明之前添加static修饰符,最终,就可以通过类名.方法名()的语法格式来调用方法了!同时,由于“被static修饰的成员,不可以访问其它未被static修饰的成员”,所以,全局属性king也必须被static修饰:
public class King { private static King king = new King(); private King() { } public static King getInstance() { return king; } }
至此,基本的单例模式的代码就设计完成了!
以上代码是“饿汉式”的单例模式,另外,还有“懒汉式”的单例模式!
基本的懒汉式单例模式的代码是:
public class King { private static King king = null; private King() { } public static King getInstance() { if (king == null) { king = new King(); } return king; } }
注意:以上代码是多线程不安全的!
在开发领域中,只要数据的产生、变化不是开发人员预期的,就称之为“不安全”,也就是“数据安全问题”。
为了保障线程安全,应该为以上创建对象的代码片断“加锁”,例如:
public class King { private static King king = null; private King() { } public static King getInstance() { synchronized ("hello") { if (king == null) { king = new King(); } } return king; } }
当然,无论是哪个线程在什么时候执行以上代码,都必须先“锁住”代码片断后才能开始执行,是没有必要的,“锁”的性能消耗是浪费的,所以,可以进一步调整为:
public class King { private static King king = null; private King() { } public static King getInstance() { if (king == null) { // 判断有没有必要锁定接下来的代码 synchronized ("java") { if (king == null) { // 判断有没有必要创建对象 king = new King(); } } } return king; } }
至此,懒汉式的单例模式就完成了!