Spring框架概述 --- 控制反转, 依赖注入, 容器和Bean
Spring框架的好处
- 轻量: Sprinh是轻量的, 基础版本大约2MB
- 控制反转: Spring 通过控制反转实现了松散耦合
- AOP: Spring支持面向切面的编程
- MVC容器: Spring的Web框架
- 事务管理: Spring提供一个持续的事务管理接口, 可以拓展到上至本地事务下至全局事务
- 异常处理: Spring提供方便的API把具体技术的相关异常转化为一致的unchecked异常
控制反转
- 一种设计思想,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制
- 具体来说: 在传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对 象的创建;
- 传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;
因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了
Spring IOC容器和Bean
- Bean是Spring管理的基本单位,在基于Spring的Java EE应用中,所有的组件都被当成Bean处理,包括数据源、Hibernate的SessionFactory、事务管理器等。在Spring中,Bean的是一个非常广义的概念,任何的Java对象、Java组件都被当成Bean处理。
- Spring框架的核心是Spring IOC容器. 容器创建对象, 将它们装配在一起, 配置它们并管理生命周期.
BeanFactory
- BeanFactory是ApplicationContext 的父类, BeanFactory实际上可以说是Spring的核心容器
- 这是一个用来访问 Spring 容器的 root 接口,要访问 Spring 容器,我们将使用 Spring 依赖注入功能,使用 BeanFactory 接口和它的子接口。
- 通常情况,BeanFactory 的实现是使用懒加载的方式,这意味着 beans 只有在我们通过 getBean() 方法直接调用它们时才进行实例化。
ApplicationContext
- ApplicationContext 是 Spring 应用程序中的中央接口,用于向应用程序提供配置信息。它继承了 BeanFactory 接口,所以 ApplicationContext 包含 BeanFactory 的所有功能以及更多功能!它的主要功能是支持大型的业务应用的创建。
- 与 BeanFactory 懒加载的方式不同,它是预加载,所以,每一个 bean 都在 ApplicationContext 启动之后实例化。
Bean的作用域
- singleton: 单例模式, 在整个Spring IoC容器中,使用singleton定义的Bean将只有一个实例
- protoype: 原型模式, 每次通过容器的getBean方法获取prototype定义的Bean时,都将产生一个新的Bean实例
- request: 对于每次HTTP请求,使用request定义的Bean都将产生一个新实例,即每次HTTP请求将会产生不同的Bean实例。只有在Web应用中使用Spring时,该作用域才有效
- session: 对于每次HTTP Session,使用session定义的Bean豆浆产生一个新实例。同样只有在Web应用中使用Spring时,该作用域才有效
- globalseesion: 每个全局的HTTP Session,使用session定义的Bean都将产生一个新实例。典型情况下,仅在使用portlet context的时候有效。同样只有在Web应用中使用Spring时,该作用域才有效
在Spring中设置bean的作用域
- 采用XML配置方式时,可以用标签中的scope属性可以指定bean的作用范围
<bean id="ajaxGetWarningDataAction" class="com.mln.pems.collect.actions.AjaxGetWarningDataAction" scope="prototype">
<property name="daoSrv" ref="daoSrv" />
<property name="emRunSrv" ref="emRunSrv" />
</bean>
- 采用注解方式时,可以用@Scope(value = “singleton”)来指定
@Scope(value = “prototype”)
public class AccountServiceImpl implements AccountService {
......
}
依赖注入 — 控制反转的实现方法
- Class A中用到了Class B的对象b,一般情况下,需要在A的代码中显式的new一个B的对象。采用依赖注入技术之后,A的代码只需要定义一个私有的B对象,不需要直接new来获得这个对象,而是通过相关的容器控制程序来将B对象在外部new出来并注入到A类里的引用中
依赖注入的三种方式 — 以下使用注解@Autowire的装配方法
- 属性注入 – Attributed-based DI
@Service
public class BService {
@Autowired
AService aService;
}
- setter注入 – setter-based DI
@Service
public class BService {
AService aService;
@Autowired
public void setaService(AService aService) {
this.aService = asService;
}
}
- 构造方法注入 – constructor-based DI
@Service
public class BService {
AService aService;
@Autowired
public BService(AService aService) {
this.aService = asService;
}
}
setter注入和构造器注入的区别
- 部分依赖:假设一个类中有3个属性,有一个有三个arg的构造函数和三个setter方法。在这种情况下,如果您只想传递一个属性的信息,则只能通过setter方法
- 覆盖:Setter注入会覆盖构造函数注入。如果我们同时使用构造函数和setter注入,IOC容器将使用setter注入。
- 变化:我们可以通过二次注射轻松更改值。它不会像构造函数一样创建新的bean实例。因此,setter注入比构造函数注入更灵活。
推荐使用构造器注入
- 依赖不可变:这个好理解,通过构造方法注入依赖,在对象创建的时候就要注入依赖,一旦对象创建成功,以后就只能使用注入的依赖而无法修改了,这就是依赖不可变(通过 set 方法注入将来还能通过 set 方法修改)。
- 依赖不为空:通过构造方法注入的时候,会自动检查注入的对象是否为空,如果为空,则注入失败;如果不为空,才会注入成功。
- 完全初始化:由于获取到了依赖对象(这个依赖对象是初始化之后的),并且调用了要初始化组件的构造方法,因此最终拿到的就是完全初始化的对象了。
Spring中bean的装配 ---- 定义bean并描述bean之间的依赖关系
基于XML形式的手动装配
- 手动装配,通常在XML配置文件中实现
//创建实体类: People
package com.lisi.pojo;
public class People {
private Cat cat;
private Dog dog;
private String name;
....
....
}
手动装配
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="dog" class="com.lisi.pojo.Dog"/>
<bean id="cat" class="com.lisi.pojo.Cat"/>
<bean id="people" class="com.lisi.pojo.People">
<property name="cat" ref="cat"/>
<property name="dog" ref="dog"/>
<property name="name" value="haha"/>
</bean>
</beans>
基于XML形式的自动装配
//创建实体类: People
package com.lisi.pojo;
public class People {
private Cat cat;
private Dog dog;
private String name;
....
....
}
- byName: bean的id与属性名,一致则能赋值
<bean id="cat" class="com.lisi.pojo.Cat"></bean>
<bean id="dog" class="com.lisi.pojo.Dog"></bean>
<bean id="people" class="com.lisi.pojo.People" autowire="byName"></bean>
- byType: 根据类型判断是否能装配,
- spring容器中bean的类型为兼容性的属性赋值, 兼容性:父类与实现的接口可以
- 使用autowire byType首先需要保证:同一类型的对象,在spring容器中唯一。如果不唯一,会报不唯一的异常
- 也就是不能再加入
<bean id="cat1" class="com.lisi.pojo.Cat"></bean>
, 这样有两个Cat对象
<bean id="cat" class="com.lisi.pojo.Cat"></bean>
<bean id="dog" class="com.lisi.pojo.Dog"></bean>
<bean id="people" class="com.lisi.pojo.People" autowire="byType"></bean>
基于注解形式的自动装配
Spring 提供了以下多个注解,这些注解可以直接标注在 Java 类上,将它们定义成 Spring Bean
@Component
:
- 该注解用于描述 Spring 中的 Bean,它是一个泛化的概念,仅仅表示容器中的一个组件(Bean),并且可以作用在应用的任何层次,例如 Service 层、Dao 层等. 使用时只需将该注解标注在相应类上即可。
@Repository
: 该注解用于将数据访问层(Dao 层)的类标识为 Spring 中的 Bean,其功能与 @Component 相同。@Service
: 该注解通常作用在业务层(Service 层),用于将业务层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。@Controller
: 该注解通常作用在控制层(如 Struts2 的 Action、SpringMVC 的 Controller),用于将控制层的类标识为 Spring 中的 Bean,其功能与 @Component 相同
可以通过以下注解将定义好 Bean 装配到其它的 Bean 中
@Autowired
:
- 可以应用到 Bean 的属性变量、setter 方法、非 setter 方法及构造函数等,默认按照 Bean 的类型进行装配。
- @Autowired 注解默认按照 Bean 的类型进行装配,默认情况下它要求依赖对象必须存在,如果允许 null 值,可以设置它的 required 属性为 false。如果我们想使用按照名称(byName)来装配,可以结合 @Qualifier 注解一起使用
@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 注解的参数指定。
@Service
public class CategoryService {
private static final Logger LOG = LoggerFactory.getLogger(CategoryService.class);
@Resource
private CategoryMapper categoryMapper;
@Resource
private SnowFlake snowFlake;
.....
.....
.....
基于Java配置类的装配
- 不使用xml文件配置Bean,而是单独写一个配置类来配置Bean
@Bean
注解扮演与 元素相同的角色。用到方法上,表示当前方法的返回值是一个bean@Configuration
类允许通过简单地调用同一个类中的其他 @Bean 方法来定义 Bean 间依赖关系。相当于spring的配置文件XML
@Configuration
public class ServiceConfiguration {
@Bean
public InventoryService inventoryService() {
return new InventoryService();
}
@Bean
public ProductService productService() {
return new ProductService(inventoryService());
}
}