一.关于Spring框架(容器)
Spring框架主要解决了创建对象和管理对象的问题.
实际开发中,可以将创建对象和管理对象的任务交给Spring框架来完成,我们就不必再去关心这些过程,当需要某个对象的时候,直接从Spring中取就可以了
二.Spring框架中创建对象
在开发实际中,有很多对象和配置值需要常驻内存,并且唯一,或需要多处使用,自行维护这些对象很繁琐,通过Spring框架能够极大地简化这些操作.
两种方式:
1.@Bean注解方法,例如:
@Configuration
public class SpringConfig{
@Bean
public Random random(){
return new Random();
}
}
此处@Bean注解必须要在被@Configuration注解的类中,被@Bean注解的方法的返回值类型就是你希望Spring帮你管理的对象的类型.
测试:
a.创建AnnotationConfigApplicationContext对象,将被@Configuration的类对象传给这个对象的构造方法
b.调用getBean方法获取被@Bean注解的方法名
c.测试打印输出
d.调用AnnotationConfigApplicationContext对象的close方法关闭资源
public class SpringRunner{
public static void main(String[] args){
AnnotationConfigApplicationContext ac =
new AnnotationConfigApplicationContext(SpringConfig.class);
Random random = (Random)ac.getBean("random");
System.out.println(random);
ac.close();
}
}
getBean()被重载过很多次,典型的有:
a.Object getBean(String BeanName);使用此方法必须确保传入的BeanName是有效的,否则会抛NoSuchBeanDefinitionException异常
b.T getBean(Class<T> beanClass);此处传入的是被@Configuration注解的类对象,并且有且只有一个对象,如果Spring容器中没有该类型的对象就会抛NoSuchBeanDefinitionException异常,如果超过两个对象,则会抛NoUniqueBeanDefinitionException异常.
c.T getBean(String BeanName,Class<T> beanClass);此处传入的两个参数就是上述的两个参数,Spring会根据传入的类型自动进行类型转换
注意:使用@Bean注解的方法可以传入Sring类型的参数,如果传入参数,那么在调用getBean方法的时候传入的参数必须是在注解的时候传入的参数
2.扫描组件创建对象,例如:
@Configuration
@ComponentScan("包名")
public class SpringConfig{
}
再创建一个组件类,此类是需要Spring创建的对象:
@Component
public class UserMapper{
}
然后再创建测试类:
public class SpringRunner{
public static void main(String[] args){
AnnotationConfigApplicationContext ac =
new AnnotationConfigApplicationContext(SpringConfig.class);
UserMapper userMapper = ac.getBean("userMapper");
System.out.println(userMapper);
ac.close();
}
}
注意事项:
a.@ComponentScan配置的包是要执行组件扫描的根包,Spring会自动扫描配置的包下所有组件类,并创建组件类对象并管理
b.组件类必须在声明之前添加@Component注解,否则Spring扫描不到该类
c.Spring在扫描@ComponentScan配置的包的路径的时候会自动扫描该包以及所有的子孙包
d.当调用getBean方法创建组件类对象的时候,传入的beanName默认的是将组件类的首字母改成小写,并且必须是首字母大写,第二个字母小写的类,否则在调用getBean时传入的参数的名称就是类名(与类名完全相同的字符串)
e.可以配置@Component注解的参数来指定beanName
f.在创建对象的过程中,Spring会自动调用该组件类的构造方法来创建对象,利用了反射技术,所以即使该构造是私有的也不影响创建.
g.关于组件类的其他注解:
@Controller----------用于控制器类
@Service-------------用于处理业务逻辑的类
@Repository---------用于实现数据访问的类
当某个类不具备上述的三个功能的时候,使用通用的@Component注解
特别注意的是,@Configuration注解是@Component注解的元注解,所以此注解也可以视为"组件注解",但是Spring框架对其处理比较特殊(使用了代理模式),所以,仅当某个类的作用是编写各种配置代码的时候,使用该注解
三.如何选择创建对象的方式
1.@Bean注解方式的优点与缺点
优点是:该方法可以完全自定义对象,在@Bean内部仍旧是传统创建对象的语句
缺点是:当需要Spring管理的对象较多的时候,我们需要在这些方法上都添加@Bean注解,比较繁琐,不易于管理
2.组件扫描方式的优点与缺点
优点是:只需要配置一次,给各组件类添加组件即可,且给各组件类添加组件之后还可以增强语义,编码成本和可读性更好
缺点是:只能使用与自定义的类,使用系统的类库的时候就不能使用该方法
3.如何选择
当自定义类的时候使用组件扫描的方式,如果是系统自带的类就使用@Bean注解的方式
四.作用域
默认情况下,Spring Bean的作用域是单例的,即实例唯一,常驻内存
但是可以通过添加@Scope注解来修改作用域,具体方式:
@Configuration
public class SpringConfig{
@Bean
@Scope
public Random random(){
return new Random();
}
}
@Repository
@Scope
public class UserMapper{
}
@Scope常用的值有两个:singleton单例的和prototype原型的,即非单例,其他的值不常用
当使用@Scope("prototype")时,通过getBean方法获取的对象的HashCode的值就不同了(仅在HashCode没有被恶意重写的情况下)
预加载和懒加载
在默认的情况下Spring Bean是预加载的,必要的时候也可以设置成懒加载,这个对象本身必须是单例的
预加载的表现为:在加载Spring环境的时候就创建好对象
懒加载的表现为:在第一次需要获取对象的时候才创建对象
懒加载的实现方式:通过@Lazy注解配置
@Configuration
public class SpringConfig{
@Bean
@Lazy
public Random random(){
return new Random();
}
}
@Lazy
@Repository
public class UserMapper{
}
@Lazy注解的参数值是boolean类型的,表示是否懒加载,默认为true,即添加了该注解就表示是懒加载,如果值为false就说明不懒加载,那么和不添加该注解没有任何区别,所以不需要关心这个注解的参数.
预加载与懒加载的优缺点
预加载:
优点:事先创建好对象,无论何时需要使用对象的时候,都可以直接获取
缺点:程序启动之初就创建对象,对象越多,程序启动的就越慢,并且对象创建出来之后可能很长一段时间都不会被使用,造成了一种资源的浪费
懒加载:
优点:仅在需要对象的时候才创建不会造成浪费
缺点:如果当前系统负荷较重,此时对象仍旧没有创建出来,再来创建对象,就会对系统造成更大的负荷
如何选择?
虽然预加载的启动过程慢,且会造成浪费,但是这种速度和浪费是在我们的承受范围之内的,因为一旦对象创建出来了,之后就再也不用担心没有对象了,而且预加载符合设计大于需求的理念,所以一般选择预加载,因为相比于懒加载更加合理
五.小结
1.Spring框架帮助我们创建对象并管理对象
2.创建对象的两种方式分别是@Bean注解和组件扫描
3.使用组件扫描的时候,在@ComponentScan中配置的包名是组件类的根包,不需要特别精确,但是也不能太模糊
4.根据类的定位来选择@Controller,@Repository,@Service和@Component注解
5.@Configuration是特殊的组件注解,仅使用于配置类,Spring通过代理模式来处理
6.getBean()方法的参数
7.组件扫描的时候getBean的参数
8.Spring Bean的作用域默认是单例的且预加载,可以通过@Scope来配置是否单例,以及通过@Lazy配置是否懒加载