Day_01
Bean的定义与装配;(XML、Config.java、自动装配 3种方式)
1. 自动装配(注解)-推荐
Spring从两个角度来实现自动化装配:
(1)组件扫描(component scanning):Spring会自动发现应用上下文中所创建的bean。
(2)自动装配(autowiring):Spring自动满足bean之间的依赖。
定义bean?
(1)先定义组件(bean)类的接口(为了解耦);
(2)然后在实现类的类声明上面加一句注解@Component,则这个实现类会被Spring给识别为组件(bean)便于后序的扫描(实际上@Component @Service @Controller @repository都可被识别扫描);也可以指定bean的名字;(也就是说定义为bean的标记加载了bean所在的类的定义上面)在@Component的类里面,可以@value给bean的成员指定初始值;
自动扫描?
(1)Config.java的类声明上面分别加@configuration(表明是个配置类)和@componentScan指定扫描的包路径(默认为当前config.java在的包);
或指定为包中所包含的类或接口:
(2)测试bean的装载;
在test类里面定义一个bean类对象,加上@Autowired注解;然后在test类引入配置的config.java类;然后测试这个bean有没有生成;
装配bean
——把其他bean装入当前bean,像”把多个bean组装起来”?
实现方式:eg:
注意:实际上,@Autowired可以加在当前bean的类的”任意的需要传入其他bean的方法”;
eg:setter或其他需要传参的方法;
当这个作为方法参数传入的bean不存在时,会报错;存在多个匹配的bean的时候,也会报错;解决方法?
(1)当可能不存在匹配的bean;
将required属性设置为false时,Spring会尝试执行自动装配,但是如果没有匹配的bean的话,Spring将会让这个bean处于未装配的状态。但是,把required属性设置为false时,你需要谨慎对待。如果在你的代码中没有进行null检查的话,这个处于未装配状态的属性有可能会出现NullPointerException。
(2)当存在多个bean匹配(使用的是bean类所在的接口,或者确实存在多个同类的bean);
有两种方法:
-
使用@Primary提示首选的bean;
-
或指定bean的名字;
为@Qualifier注解所设置的参数就是想要注入的bean的ID。所有使用@Component注解声明的类都会创建为bean,并且bean的ID为首字母变为小写的类名;
或
测试:
则会导入ID为”cold”的bean;
2. Java方式
这种方式应用于,当采用第三方的库的组件,不能@component来自定义bean
上面这种方式为”自动扫描配置”,以下两种为”显示地手动配置”:Java和XML
因为不自动扫描,所以取消了@componentScan,因此自定义的bean的类@component和@autowired就没用了;
直接在@configuration的config.java里面,手动显示的声明bean(@Bean);
注入其他的bean?(装配bean)
1. 可以传@bean的方法;
看似是调用了sgtPeppers()方法,其实这里只是把这个方法看作是一个已生成的bean,即多次调用这个方法,返回的结果是同一个bean;
2. 也可以不显示的申明传入了一个bean参数;(实际上是在参数前省略了@Aotuwired)
在这里甚至没有要求CompactDisc必须要在JavaConfig中声明,实际上它可以通过组件扫描功能自动发现或者通过XML来进行配置;(当出现多个bean匹配,也可以用类似@primary或@qualifier的方式);
3. XML方式
这种方式最基础;与上一种方式相比,(2)中的@configuration的Config.java对应一个beans.xml文件;为简化xml标签,使用了c-、p-命名空间简写,还有针对集合类的简写;
(1)声明+赋值;eg:
eg:装配(采用构造器-注入其他bean)
(2)c-命名空间;
或者:
对于构造器的常量参数:
(3)集合类;
(4)使用setter注入其他bean,先在bean所在的类定义好setter方法;
(5)p-命名空间;
总结来说,加载bean的3种方式:
- 自动扫描,自定义类@component;然后在config.java + @configuration + @componentScan; 最后在需要用bean的类里面(可以是一个test类,也可以是另一个bean所在的类)使用@autowired来注入/装配”被扫描出来的且被匹配的bean”;
- 与第1种类似,只是config.java里面没有@componentScan,因此就没有了自动扫描;需要显示的在config里面手动@bean+带有返回bean的方法来声明bean;注意,带有@Bean注解的方法可以采用任何必要的Java功能来产生bean实例,构造器和Setter方法只是@Bean方法的两个简单样例;
- 与第二种方式一样,@configuration+config.java文件换成了beans.xml文件,@Bean换成了<beans>.<bean id=.. class=..>...;使用构造器/setter方法初始化来注入其他bean(装配bean),分别对应c-/p-命名空间;
Bean的作用域?
Spring定义了多种作用域,可以基于这些作用域创建bean,包括:
(1)单例(Singleton):在整个应用中,只创建bean的一个实例。(默认)
(2)原型(Prototype):每次注入或者通过Spring应用上下文获取的时候,都会创建一个新的bean实例。
(3)会话(Session):在Web应用中,为每个会话创建一个bean实例。
(4)请求(Rquest):在Web应用中,为每个请求创建一个bean实例。
控制作用域?
使用会话和请求作用域?
在Web应用中,例如,在典型的电子商务应用中,可能会有一个bean代表用户的购物车。如果购物车是单例的话,那么将会导致所有的用户都会向同一个购物车中添加商品。另一方面,如果购物车是原型作用域的,那么在应用中某一个地方往购物车中添加商品,在应用的另外一个地方可能就不可用了,因为在这里注入的是另外一个原型作用域的购物车。
就购物车bean来说,会话作用域是最为合适的,因为它与给定的用户关联性最大。要指定会话作用域,我们可以使用@Scope注解,它的使用方式与指定原型作用域是相同的;
运行时注入?懒加载?条件化注入bean?
(1)运行时注入,在设置bean的属性的时候,可以依赖外部配置.properties文件;
(2)懒加载,对于单例bean,默认IOC容器启动的时候就会加载;可以让其懒加载,即第一次获取(从context容器getBean时)这个bean的时候才加载;直接在@Bean上面加@Lazy即可;
(3)条件加载(Spring 4.0)
直接在@Bean上面加@Condition即可;eg:
这个类要实现Condition接口的boolean match(context, type)方法;实现合适才满足条件;这个context可以获取很多信息:BeanFactory工厂、ClassLoader类加载器、Environment系统环境、bean的注册类...通过这些信息来获取条件;
*Bean的生命周期?定义初始化和销毁方法?
在bean所在的类声明初始化/销毁方法,在@Bean里面调用;对于ProtoType,容器则不会调用@Bean里面的销毁方法;
关于@Autowired方法?(装配bean)
@Autowired可以标注到”构造器、方法、属性、方法参数”上面;
1. 在当前的@component的bean类中,标注在setter方法上面;
2. 有参构造器(当只有一个有参构造器时,构造器上/构造器参数的@Autowired可以省写)
或者
写在config类的@Bean方法上
Profile根据环境”激活”bean?
比如,注册了多个@Bean,根据Profile加载不同的bean;
怎么激活环境?
固定写法: