Spring--IOC和Bean管理
IOC
IOC(控制反转),把对象的创建和对象之间的调用过程交给Spring进行管理,可以降低代码耦合度。
IOC容器
IOC思想基于IOC容器实现,IOC容器是 Spring 框架的核心。容器创建对象,把它们连接在一起,配置它们,并管理他们的整个生命周期从创建到销毁。
Spring提供了两种容器:BeanFactory 容器和ApplicationContext 容器。
- BeanFactory 容器是IOC容器的基本实现,一般BeanFactory 容器不提供给开发人员使用,并且BeanFactory 容器在加载配置文件时不会创建对象,在使用对象时才会创建对象。
- ApplicationContext 容器包括BeanFactory 容器所有功能,是BeanFactory接口的实现类,ApplicationContext 容器包含了更多的功能,开发人员一般使用ApplicationContext 容器。
BeanFactory容器
BeanFactory容器构建核心容器时,创建对象时采用的策略是延迟加载的方式,什么时候根据id获取对象,什么时候创建对象。
1 | public class MainApp { |
XmlBeanFactory生成工厂bean,使用ClassPathResource加载Beans.xml配置文件。
ApplicationContext容器
ApplicationContext创建核心容器时,创建对象采用的策略是立即加载的方式,也就是说读取完配置文件就创建配置文件中配置的对象,适合单例模式创建对象使用。
ApplicationContext是BeanFactory的子接口,可以加载配置文件中的bean,并且包含BeanFactory接口中所有功能。ApplicationContext有三种实现方式:
- FileSystemXmlApplicationContext(从XML文件中获取bean,需要完整路径)
- ClassPathXmlApplicationContex (从XML文件中获取bean,不需要完整路径,但是需要CLASSPATH环境变量)
- WebXmlApplicationContext(在一个 web 应用程序的范围内加载在 XML 文件中已被定义的 bean)
1
2
3
4
5public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("bean/user.xml");
User user = context.getBean("user", User.class);
System.out.println(user);
}创建Bean实例
xml方式
1
<bean id="User" class="com.xxx.xxx.User"></bean>
注解方式
- @Component 可以用在任何地方
- @Service 业务层
- @Controller 视图层
- @Repository 数据层
上面4个注解可以用在类上,根据业务进行使用,例如@Service用在业务逻辑层的类上,一般不推荐混用。
以@Component为例:
1 | @Component(value="userService") |
其中@Component中的value值和bean标签中的id值相同,这里也可以忽略value值,默认是类名并且首字母小写。
Bean注入
注入方式:
- 使用构造函数
- 使用set方法
- 使用注解的方式
首先创建User类
1 | public class User { |
构造函数注入
1 | <?xml version="1.0" encoding="UTF-8"?> |
构造函数注入方式使用索引和类型相结合,能更好地适应复杂构造函数传参。
SET方法注入
1 | <?xml version="1.0" encoding="UTF-8"?> |
set方法注入,name为方法传参的名称。
自动注入
Bean标签的属性autowire,配置自动装配:
- byName 注入值bean的id和类属性名称相同;
- byType 注入值bean的类型和类属性的类型相同;注意:不能存在两个类型相同的bean(bean的id不同 )
1
2
3
4
5
6
7public class User{
private Scope scope;
}
<bean id="user" class="com.xxx.xxx.User" autowire="byType"></bean>
<!-- 此时同时存在两个Bean,在autowire的值为byName时会选择第一个bean,如果为byType时,会报错。-->
<bean id="scope" class="com.xxx.xxx.Scope" ></bean>
<bean id="scope1" class="com.xxx.xxx.Scope" ></bean>
基于注解方式实现注入
注解方式实现注入有四个注解能实现:
- @Autowired
- @Qualifier
- @Resource
- @Value
其中最常用的是前三种。
@Autowired:根据属性类型进行自动注入(也就是byType)。
@Qualifier:根据属性名称进行注入,需要和@Autowired一起使用。当接口有多个实现类时,需要使用@Qualifier按照名称指定注入的bean
1 | public class UserService{ |
@Resource:可以根据类型注入,可以根据名称注入。注意:@Resource不是Spring包的,而是javax包下的,因此Spring不推荐使用@Resource注解,可以使用@Qualifier注解。
1 | public class UserService{ |
@Value:注入普通类型属性。
1 | public class UserSerivce{ |
组件扫描
开启自动注入后,需要配置组件扫描。组件扫描相当于告诉Spring需要扫描哪些包才能获取到Bean。
1 | @Configuration //配置类,代替xml文件 |
作用域
Spring支持singleton、prototype、request、session和global session五种作用域。
- singleton是单例类型,在创建容器时就创建bean对象,每次使用时返回同一bean对象。
- prototype 每次使用bean时都会创建一个新的bean对象。
- request 每次Http请求都会创建一个Bean,请求处理完成后会销毁Bean。
- session仅用在WebApplicationContext环境中,每个Http Session创建并使用一个Bean,Session结束后会销毁Bean。
1 | <bean id="user" class="fun.gwt.bean.User"></bean> |
1 | ApplicationContext context = new ClassPathXmlApplicationContext("bean/user.xml"); |
Spring默认使用singleton的方式创建Bean,每次获取的Bean都是相同的。
修改作用域为prototype后,每次创建的Bean对象地址都不同。
1 | <bean id="user" class="fun.gwt.bean.User" scope="prototype"></bean> |
生命周期
Bean的生命周期为:Bean定义-Bean初始化-Bean使用-Bean销毁。使用init-method属性,可以指定一个方法,在实例化Bean时调用方法,destroy-method属性指定一个方法,当容器中移除bean时,调动该方法。
首先定义HelloWorld类。
1 | public class HelloWorld { |
当使用singleton时和prototype不一样,具体区别如下
singleton
Beans.xml文件:
1 | <bean id="helloworld" class="fun.gwt.HelloWorld" scope="singleton" init-method="init" destroy-method="destroy" > |
1 | AbstractApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml"); |
执行上面的语句,控制台输出:
1 | 初始化了 |
虽然获取两次bean对象,但是只初始化一次。
prototype
1 | <bean id="helloworld" class="fun.gwt.HelloWorld" scope="prototype" init-method="init" destroy-method="destroy" > |
使用相同的执行语句,修改scope为prototype,执行后结果为:
1 | 初始化了 |
此时调用两次bean,初始化了两次。但是没有调用destory函数。原因是protorype和destroy-method不能共存,当使用prototype时,Spring容器不能对bean的整个生命周期进行管理,对象的销毁和资源回收由使用者管理。sinleton整个生命周期由容器负责。
BeanPostProcessor Bean后置处理器
BeanPostProcessor可以在初始化前后对bean进行处理。
1 | package fun.gwt; |
创建InitHelloWorld类实现接口BeanPostProcessor。
1 | public class InitHelloWorld implements BeanPostProcessor { |
修改Beans.xml文件为:
1 | <bean id="helloworld" class="fun.gwt.HelloWorld" scope="singleton" init-method="init" destroy-method="destroy" > |
1 | AbstractApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml"); |
最后执行之后,显示:
1 | BeforeInitialization: helloworld |
看到在初始化前后可以进行处理。