Spring之IOC
一、Spring的IoC容器
IOC概念:控制反转其实是控制权的转移,被调用类的创建原本由调用类进行管理,当前管理权转移到由Spring容器进行管理。
Spring 提供了两种 IoC 容器,分别为 BeanFactory 和 ApplicationContext;
Spring 容器是 Spring 框架的核心。容器将创建对象,把它们连接在一起,配置它们,并管理他们的整个生命周期从创建到销毁。Spring 容器使用依赖注入(DI)来管理组成一个应用程序的组件。这些对象被称为 Spring Beans。
- BeanFactory 是基础类型的 IoC 容器,它由
org.springframework.beans.facytory.BeanFactory
接口定义,并提供了完整的 IoC 服务支持。BeanFactory 和相关的接口,比如BeanFactoryAware、DisposableBean、InitializingBean,仍旧保留在 Spring 中,主要目的是向后兼容已经存在的和那些 Spring 整合在一起的第三方框架。
简单来说,BeanFactory 就是一个管理 Bean 的工厂,它主要负责初始化各种 Bean,并调用它们的生命周期方法。 BeanFactory 接口有多个实现类,最常见的是org.springframework.beans.factory.xml.XmlBeanFactory
,它是根据 XML 配置文件中的定义装配 Bean 的。· - ApplicationContext 是 BeanFactory 的子接口,也被称为应用上下文。该接口的全路径为 org.springframework.context.ApplicationContext,它不仅提供了 BeanFactory 的所有功能,还添加了对 i18n(国际化)、资源访问、事件传播等方面的良好支持。
通常在 Java 项目中,会采用通过 ClassPathXmlApplicationContext 类实例化 ApplicationContext 容器的方式,
而在 Web 项目中,ApplicationContext 容器的实例化工作会交由 Web 服务器完成。Web 服务器实例化 ApplicationContext 容器通常使用基于 ContextLoaderListener 实现的方式,它只需要在 web.xml 中添加如下代码:
<!--指定Spring配置文件的位置,有多个配置文件时,以逗号分隔-->
<context-param>
<param-name>contextConfigLocation</param-name>
<!--spring将加载spring目录下的applicationContext.xml文件-->
<param-value>
classpath:spring/applicationContext.xml
</param-value>
</context-param>
<!--指定以ContextLoaderListener方式启动Spring容器-->
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
二、Spring Bean
Spring 容器可以被看作一个大工厂,而 Spring 容器中的 Bean 就相当于该工厂的产品。如果希望这个大工厂能够生产和管理 Bean,这时则需要告诉容器需要哪些 Bean,以及需要以何种方式将这些 Bean 装配到一起。
1. Spring Bean配置
Spring 的配置文件名称是可以自定义的,通常情况下,都会将配置文件命名为 applicationContext.xml;
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">
<!-- 由 Spring容器创建该类的实例对象 -->
<bean id="personDao" class="com.mengma.ioc.PersonDaoImpl" />
<bean id="personService" class="com.mengma.ioc.PersonServiceImpl">
<!-- 将personDao实例注入personService实例中 -->
<property name="personDao" ref="personDao"/>
</bean>
</beans>
约束配置:beans的属性是Sring的约束配置;
xmlns="http://www.springframework.org/schema/beans", 默认命名空间:它没有空间名,用于Spring Bean的定义;
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance", xsi命名空间:这个命名空间用于为每个文档中命名空间指定相应的Schema样式文件,是标准组织定义的标准命名空间。
创建实例:Spring 容器中创建一个 id 为 personDao 的 bean 实例,其中 id 表示文件中的唯一标识符,class 属性表示指定需要实例化 Bean 的实全限定类名(包名+类名)
属性名称 | 描述 |
---|---|
id | 是一个 Bean 的唯一标识符,Spring 容器对 Bean 的配置和管理都通过该属性完成 |
name | Spring 容器同样可以通过此属性对容器中的 Bean 进行配置和管理,name 属性中可以为 Bean 指定多个名称,每个名称之间用逗号或分号隔开 |
class | 该属性指定了 Bean 的具体实现类,它必须是一个完整的类名,使用类的全限定名 |
scope | 用于设定 Bean 实例的作用域,其属性值有 singleton(单例)、prototype(原型)、request、session 和 global Session。其默认值是 singleton |
constructor-arg | |
property | |
ref | 和 等元素的子元索,该元素中的 bean 属性用于指定对 Bean 工厂中某个 Bean 实例的引用 |
value | 和 等元素的子元素,用于直接指定一个常量值 |
list | 用于封装 List 或数组类型的依赖注入 |
set | 用于封装 Set 类型属性的依赖注入 |
map | 用于封装 Map 类型属性的依赖注入 |
entry |
2. Spring Bean的实例化
在面向对象的程序中,要想调用某个类的成员方法,就需要先实例化该类的对象。在 Spring 中,实例化 Bean 有三种方式,分别是构造器实例化、静态工厂方式实例化和实例工厂方式实例化。
构造器实例化是指 Spring 容器通过 Bean 对应的类中默认的构造函数实例化 Bean。大多数情况下常用的实例化方式。
其他两种不常用,暂时不作为重点,参考:http://c.biancheng.net/view/4256.html
3. Spring Bean的作用域
当在 Spring 中定义一个 bean 时,你必须声明该 bean 的作用域的选项。
例如,为了强制 Spring 在每次需要时都产生一个新的 bean 实例,你应该声明 bean 的作用域的属性为 prototype。同理,如果你想让 Spring 在每次需要时都返回同一个bean实例,你应该声明 bean 的作用域的属性为 singleton。
Spring 框架支持以下五个作用域,分别为singleton、prototype、request、session和global session,5种作用域说明如下所示,
作用域 | 描述 |
---|---|
singleton | 在spring IoC容器仅存在一个Bean实例,Bean以单例方式存在,默认值 |
prototype | 每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行newXxxBean() |
request | 每次HTTP请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext环境 |
session | 同一个HTTP Session共享一个Bean,不同Session使用不同的Bean,仅适用于WebApplicationContext环境 |
global-session | 一般用于Portlet应用环境,该运用域仅适用于WebApplicationContext环境 |
3.1、singleton
<bean id="book" class="com.dreamice.scope.Book" scope="singleton"/>
System.out.println(applicationContext.getBean("book"));
System.out.println(applicationContext.getBean("book"));
输出:
com.dreamice.scope.Book@43814d18
com.dreamice.scope.Book@43814d18
3.2、prototype
<bean id="book" class="com.dreamice.scope.Book" scope="prototype"/>
System.out.println(applicationContext.getBean("book"));
System.out.println(applicationContext.getBean("book"));
输出:
com.dreamice.scope.Book@2758fe70
com.dreamice.scope.Book@1f36e637
4、Spring Bean的生命周期
http://c.biancheng.net/view/4261.html
三、Spring的依赖注入(DI)和装配
依赖注入(Dependency Injection,DI)和控制反转含义相同,它们是从两个角度描述的同一个概念。
依赖注入主要有两种实现方式,分别是属性 setter 注入和构造方法注入;
1、属性 setter 注入
// 定义接口声明
private PersonDao personDao;
// 提供set()方法,用于依赖注入
public void setPersonDao(PersonDao personDao) {
this.personDao = personDao;
}
配置文件中,实例注入到实例<property name="personDao" ref="personDao"/>
必须有,不然会报NPE;所以说setter要和配置文件同步一致才可以。
2、构造方法注入
3、注入和装配的区别
Bean 的装配可以理解为依赖关系注入,Bean 的装配方式也就是 Bean 的依赖注入方式。Spring 容器支持多种形式的 Bean 的装配方式,如基于 XML 的 Bean 装配、基于 Annotation 的 Bean 装配和自动装配等。(http://c.biancheng.net/view/4264.html)
依赖注入的本质就是装配,装配是依赖注入的具体行为。(https://blog.csdn.net/u012843873/article/details/52399206)
可以理解为:注入就是装配
4、基于Annotation装配Bean
主要注解有:
注解 | 作用 |
---|---|
@Component | 可以使用此注解描述 Spring 中的 Bean,但它是一个泛化的概念,仅仅表示一个组件(Bean),并且可以作用在任何层次。使用时只需将该注解标注在相应类上即可。 |
@Repository | 用于将数据访问层(DAO层)的类标识为 Spring 中的 Bean,其功能与 @Component 相同。 |
@Service | 通常作用在业务层(Service 层),用于将业务层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。 |
@Controller | 通常作用在控制层(如 Struts2 的 Action),用于将控制层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。 |
@Autowired | 用于对 Bean 的属性变量、属性的 Set 方法及构造函数进行标注,配合对应的注解处理器完成 Bean 的自动配置工作。默认按照 Bean 的类型进行装配。 |
@Resource | 其作用与 Autowired 一样。其区别在于 @Autowired 默认按照 Bean 类型装配,而 @Resource 默认按照 Bean 实例名称进行装配;@Resource 中有两个重要属性:name 和 type。Spring 将 name 属性解析为 Bean 实例名称,type 属性解析为 Bean 实例类型。如果指定 name 属性,则按实例名称进行装配;如果指定 type 属性,则按 Bean 类型进行装配。如果都不指定,则先按 Bean 实例名称装配,如果不能匹配,则再按照 Bean 类型进行装配;如果都无法匹配,则抛出 NoSuchBeanDefinitionException 异常。 |
@Qualifier | 与 @Autowired 注解配合使用,会将默认的按 Bean 类型装配修改为按 Bean 的实例名称装配,Bean 的实例名称由 @Qualifier 注解的参数指定。 |
不仅是@Resource,使用@Autoware注解也是不需要set和get方法的。因为spring在实例化对象时,是通过java的反射机制注入实例。(https://q.cnblogs.com/q/80710/)
4.1、@Resource的使用
@Service("bookService")
public class BookServiceImpl implements BookService {
@Resource(name = "bookDao")
private BookDao bookDao;
@Override
public void add() {
bookDao.add();
}
}
配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.dreamice"/>
</beans>
@Autowired和@Qualifier的使用参考:https://www.cnblogs.com/caoyc/p/5626365.html
5、Spring Beans 自动装配
四、实际项目问题
问题1、使用@Autowired注入后报错为null
- 调用类B在使用自动注入被调用类C时,必须保证自身是受Spring容器进行管理的,因此需要在类上添加@Service、@Component等注解,否则实例C使用时会为null;
- 调用类A的类也要保证是通过@Autowired的方式注入B,即使通过new的方式实例化B,也要添加@Autowired,否则B类不再受Spring容器管理。
@Controller
public class A}{
//自动注入
@Autowired
private B b;
//项目中有使用new的方式,这种情况添加@Autowired,才能保证实例c不为null
B b = new B();
}
@Service
public class B{
@Autowired
private C c;
public void test(){
c.test();
}
}
@Repositroy
public class C}{
public void test(){
}
}
参考资料:
1、C语言中文网-Java Spring框架入门教程
2、慕课网-Spring框架小白的蜕变
3、W3Cschool-Spring 教程
4、依赖注入与自动装配