java框架开发技术之Spring——IOC
Spring是开源的、轻量级的框架,是为了解决企业应用开发的复杂性而创建的,它贯穿于表现层,业务层,持久层,致力于J2EE应用各层的解决方案,不是只专注于某一个层面。
Spring包括IOC(控制反转/依赖注入)和AOP(面向切面)两大技术点。本文主要介绍IOC技术,AOP技术的介绍请参考下一篇。
首先说一下Spring技术是优点:
① 轻量级的框架;
② IOC(控制反转);
③ 面向切面编程;
④ Spring容器,主要是用来管理对象。
Spring中使用IOC技术
IOC技术,控制反转又名依赖注入,主要借助于Spring容器来创建对象,并设置和维护各个对象之间的依赖关系,即将面向接口编程中创建任务转移到容器中,实现程序的解耦合。容器是Spring框架实现功能的核心,容器不只是帮我们创建了对象那么简单,它负责了对象整个的生命周期的管理——创建、装配、销毁。Spring容器也就是一个bean工厂(BeanFactory)。应用中bean的实例化,获取,销毁等都是由这个bean工厂管理的。
从代码层面,Spring容器其实就是一个ApplicationContext ,org.springframework.context.ApplicationContext接口用于完成容器的配置,初始化,管理bean。一个Spring容器就是某个实现了ApplicationContext接口的类的实例。
在普通的JAVA工程中,我们可以通过代码显式new一个ClassPathXmlApplicationContext或者FileSystemXmlApplicationContext来初始化一个Spring容器。在Web工程中,我们一般是通过配置web.xml的方式来初始化Spring容器。
使用Spring IOC解耦合的过程如下
① 创建工程并添加Spring支持即导入Spring相关jar包;
② 创建业务层和持久层接口;
③ 编写业务层和持久层实现类;
④ 在业务层实现类中添加持久层接口引用和访问器;
⑤ 编写Spring配置文件,设置对象依赖关系 ;
⑥ 测试,通过ApplicationContext接口的实现类获取组件。
在第五步中对象依赖注入的方式有两种,一种使用set方式进行注入,一种使用构造方法进行注入。
- set方式注入
语法:使用property节点<property name="属性名" ref="设置依赖的对象"></property>
name:指定属性名称
ref:用来设置依赖的对象(该对象必须存在于IOC容器中)
value:设置普通属性的值
注意:必须要为属性提供set方法。
标签属性说明
① bean标签: 用来实例化对象,相当于new
② id: 用来设置对象的唯一标识(在整个IOC容器中不能重复)
③ class: 设置要实例化对象的类(完全限定)
④ name: 设置对象的别名,可以设置多个,用逗号分开
对象别名的设置还可以使用alias标签(一次只能设置一个别名)<alias name="service" alias="service4"/>
⑤ lazy-init: 设置延时加载的功能
default-lazy-init=“true”:可以设置整个IOC容器默认的延时加载方式
true:开启延时加载
false: 关闭延时加载
在IOC容器中默认会在一开始加载所有的对象。
当一个非延时加载的对象依赖了一个延时加载的对象,这时延时加载对象的延时加载功能失效。
⑥ scope:bean对象的作用域
singleton: 单例模式,(默认值)代表该对象在整个容器中实例只有一份。
prototype:多例模式,该对象在容器中会被创建多次,也就是用户调用一次getBean()方法就会创建一个实例。
request: 和request的存储范围有关
session: 和session的存储范围有关
例如:
定义顾客接口ICustomer,包含用餐方法haveDinner
package com.xixw.springTest;
/**
* 顾客类接口
* @author Administrator
*/
public interface ICustomer {
public void haveDinner();//用餐方法
}
定义厨师接口IKitchener,包含做饭方法doCooking
package com.xixw.springTest;
/**
* 厨师类接口
* @author Administrator
*/
public interface IKitchener {
public String doCooking();//做饭方法
}
定义顾客接口的实现类,添加姓名属性及厨师接口类型的属性,相关访问器
package com.xixw.springTest
/**
* 顾客实体类
* @author Administrator
*/
public class Customer implements ICustomer {
private String name;//姓名
private IKitchener kitchener;//厨师
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public IKitchener getKitchener() {
return kitchener;
}
public void setKitchener(IKitchener kitchener) {
this.kitchener = kitchener;
}
@Override
public void haveDinner() {
// TODO Auto-generated method stub
System.out.println("顾客"+this.name + "吃到了"+kitchener.doCooking());
}
}
定义厨师接口的实现类,添加姓名及拿手菜属性以及相关访问器
package com.xixw.springTest;
/**
* 厨师实体类
* @author Administrator
*/
public class Kitchener implements IKitchener{
private String name;//姓名
private String specialty;//拿手菜
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSpecialty() {
return specialty;
}
public void setSpecialty(String specialty) {
this.specialty = specialty;
}
@Override
public String doCooking() {
System.out.println("厨师"+name + "为您服务");
return this.specialty;
}
}
在Spring配置文件中定义顾客和厨师对象并将厨师注入给顾客
<?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.1.xsd">
<!-- 定义厨师对象
<property name="属性名" ref="设置依赖的对象"></property>
name:指定属性名称
ref:用来设置依赖的对象(该对象必须存在于IOC容器中)
value:设置普通属性的值
-->
<bean id="kitchener" class="com.xixw.springTest.Kitchener">
<!-- 设置属性值(普通属性) -->
<property name="name" value="张三"></property>
<property name="specialty" value="红烧肉"></property>
</bean>
<!-- 定义顾客对象 -->
<bean id="customer" class="com.xixw.springTest.Customer">
<!-- 设置普通属性值 -->
<property name="name" value="李四"></property>
<!-- 设置属性值(依赖对象) -->
<property name="kitchener" ref="kitchener"></property>
</bean>
</beans>
测试通过ApplicationContext加载顾客对象
package com.xixw.springTest;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* 测试类
* @author Administrator
*/
public class Test {
public static void main(String[] args) {
//加载IOC容器
ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
//获取IOC容器中的对象
ICustomer customer=(ICustomer) ac.getBean("customer");
customer.haveDinner();
}
}
结果
- 构造方式注入
语法:使用constructor-arg节点<constructor-arg index="索引" ref="依赖的对象"></constructor-arg>
index: 参数的位置
ref: 用来设置依赖的对象(该对象必须存在于IOC容器中)
value: 设置普通属性的值
注意:必须在类中已经提供了相应的构造方法
★ 两种注入方式对比
set方法注入比较灵活,可以被子类继承,但是在对象创建完成之后不能立马使用该对象。
构造方法注入在对象创建完成之后就可以立马使用该对象,但是当属性过多的时候不够灵活。
以上例为例使用构造方法注入过程
(其他同上)
定义顾客接口的实现类,添加姓名属性及厨师接口类型的属性,使用构造方法
package com.xixw.springTest
/**
* 顾客实体类
* @author Administrator
*/
public class Customer implements ICustomer {
private String name;//姓名
private IKitchener kitchener;//厨师
public Kitchener(String name, String specialty) {
this.name = name;
this.specialty = specialty;
}
public Kitchener() {
}
@Override
public void haveDinner() {
// TODO Auto-generated method stub
System.out.println("顾客"+this.name + "吃到了"+kitchener.doCooking());
}
}
定义厨师接口的实现类,添加姓名及拿手菜属性以及相关访问器
package com.xixw.springTest;
/**
* 厨师实体类
* @author Administrator
*/
public class Kitchener implements IKitchener{
private String name;//姓名
private String specialty;//拿手菜
public Customer(String name, IKitchener kitchener) {
this.name = name;
this.kitchener = kitchener;
}
@Override
public String doCooking() {
System.out.println("厨师"+name + "为您服务");
return this.specialty;
}
}
在Spring配置文件中定义顾客和厨师对象并将厨师注入给顾客(使用构造方法注入)
<?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.1.xsd">
<!-- 定义厨师对象
<constructor-arg index="索引" ref="依赖的对象"></constructor-arg>
index:参数的位置
ref:用来设置依赖的对象(该对象必须存在于IOC容器中)
value:设置普通属性的值
-->
<bean id="kitchener" class="com.xixw.springTest.Kitchener">
<!-- 设置属性值(普通属性) -->
<constructor-arg index="0" value="张三"></constructor-arg>
<constructor-arg index="1" value="红烧肉"></constructor-arg>
</bean>
<!-- 定义顾客对象 -->
<bean id="customer" class="com.xixw.springTest.Customer">
<!-- 设置普通属性值 -->
<constructor-arg index="0" value="李四"></constructor-arg>
<!-- 使用构造方法设置对象依赖关系 -->
<constructor-arg index="1" ref="kitchener"></constructor-arg>
</bean>
</beans>
结果
简化Spring IOC配置
(1)使用p命名空间注入属性
p命名空间注入是在Spring2.0之后引入进来的用来扩展Spring的配置。可以简化的注入方式。
语法:
注入普通属性:p:属性名=属性值
注入依赖的对象:p:对象名-ref=”依赖的对象名”
注意:在使用p命名空间注入的时候要在Spring配置文件中添加p命名空间的声明,现在p命名空间已经包含在Spring的核心包中了,所以不需要单独引入。
例如
在Spring配置文件中定义顾客和厨师对象并将厨师注入给顾客(使用p命名空间注入)
<?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.1.xsd">
<!-- 使用p命名空间注入 -->
<!-- 定义厨师对象 -->
<bean id="kitchener" class="com.xixw.springTest.Kitchener" p:name="张三" p:specialty="红烧肉"></bean>
<!-- 定义顾客对象 -->
<bean id="customer" class="com.xixw.springTest.Customer" p:name="李四" p:kitchener-ref="kitchener"></bean>
</beans>
(2)自动注入(重要)使用autowire进行设置
自动注入类型 | 说明 |
no(默认) | 不进行自动注入 |
byName | 根据属性的名称在IOC容器中寻找一个同名的对象,如果找到则注入成功,如果找不到则注入失败 |
byType | 根据属性的数据类型在IOC容器中寻找同一类型的对象,若找到唯一的对象则注入成功,若找不到或找到多个则注入失败(同一个父类都属于同一类型的对象) |
constructor | 通过构造方法进行注入,先使用byType进行匹配,如果没有找到或找到多个则使用byName进行匹配,若匹配成功则注入成功,否则注入失败 |
注意:
① IOC容器中默认不进行自动注入,可以使用default-autowire=“byName”来修改。
② 自动注入使得配置文件非常简洁但同时也造成组件之间的依赖关系不明确,容易引发一些潜在的错误,在实际项目中要谨慎使用。
例如
<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.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd"
default-lazy-init="true" default-autowire="byName">
<!--<bean id="service" class="com.xixw.TestService" autowire="constructor">
</bean>-->
</beans>
(3)基于注解的配置
为了简化Spring IOC容器的配置文件,在Spring2.0之后引入了注解配置。
配置步骤
① 在主配置文件的头文件中添加context命名空间和验证文件
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.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd">
② 在主配置文件中添加自动扫描注解的配置
<context:component-scan base-package="com.xixw"></context:component-scan>
③ 在spring中给我们提供了四个注解进行bean对象的管理
都是用来创建对象,并将对象放到IOC容器中,功能相当于bean节点
常用的注解 | 说明 |
@Repository | 主要标注在持久层(dao)的类上,用来创建持久层的对象 |
@Service | 主要标注在业务层的类上,用来创建业务层的对象 |
@Controller | 主要标注在控制层的类上,用来创建控制层对象 |
@Component | 主要标注在其他受控制的一些类上 |
例如编写javaBean类
@Repository: 一般标注在持久层的类上面,用来定义一个Bean对象
@Repository("dao")
public class UserDao {
public UserVo getUsers(){
ApplicationContext act=new ClassPathXmlApplicationContext("applicationContext.xml");
UserVo user=(UserVo) act.getBean("user");
return user;
}
}
@Service:一般标注在业务层的类上面,用来定义一个业务Bean对象
@Service("service")
public class UserService {
/*@Resource(name="dao")*/
@Autowired
private UserDao dao1;
public void setDao(UserDao dao) {
this.dao1 = dao;
}
public UserVo getUsers(){
return dao1.getUsers();
}
}
@Controller:一般标注在控制层的类上面
@Controller("servlet")
public class UserServlet {
@Resource(name="service",type=UserService.class)
private UserService service;
public void setService(UserService service) {
this.service = service;
}
public void getUsers(){
UserVo user=service.getUsers();
System.out.println(user.getName());
}
}
@Component:一般标注在除了上面出现的类之外的一些其他的类上面
@Component("user")
public class UserVo {
@Value(value="zhangsan")
private String name;
@Value(value="123456")
private String password;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
自动注入的注解(主要写在属性上)
属性的注解可以写在属性上或者该属性的set方法上,建议写在set方法上。
方式一
@Autowired 默认只能用byType的方式进行自动注入
@Qualifier(“对象名”) 先通过Autowired找同类型的对象,再通过Qualifier指定名字进行筛选。
方式二
@Resource(name=“对象名”,type=类型)
name和type同时指定,即从容器中寻找一个名称和类型都匹配的对象;
如果只指定name,则只根据名字进行寻找同名的对象。
如果只指定type,则只根据类型寻找同类型的对象。
Name和type都省略,默认使用byName的方式进行自动注入,若找不到则使用byType方式进行匹配。
Spring bean元素的作用域?
当通过Spring容器创建一个Bean实例的时候,不仅可以完成bean实例的实例化,还可以为bean指定作用域。Spring bean元素的支持以下5种作用域:
- Singleton:单例模式,在整个spring IOC容器中,使用singleton定义的bean将只有一个实例。
- Prototype:多例模式,每次通过容器中的getBean方法获取prototype定义的beans时,都会产生一个新的bean的实例。
- Request:对于每次Http请求,使用request定义的bean都会产生一个新的实例,只有在web应用时候,该作用域才会有效。
- Session:对于每次Http Session,使用session定义的Bean都将产生一个新的实例。
- Globalsession:每个全局的Http Sesisonn,使用session定义的本都将产生一个新的实例。
Spring框架中都用到了哪些设计模式?
- 单例模式 : Spring 中的 Bean 默认都是单例的。
- 工厂模式 : Spring使用工厂模式通过 BeanFactory、ApplicationContext 创建 bean 对象。
- 模板方法模式 : Spring 中 jdbcTemplate、hibernateTemplate 等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。
- 代理模式 : Spring AOP 功能的实现。
- 观察者模式: Spring 事件驱动模型就是观察者模式很经典的一个应用。
- 适配器模式 :Spring AOP 的增强或通知(Advice)使用到了适配器模式、spring MVC 中也是用到了适配器模式适配Controller。
- 装饰者模式 : 我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。