Spring框架
今日内容
- 能够描述spring框架
- 能够理解spring的IOC的容器
- 能够编写spring的IOC的入门案例
- 能够说出spring的bean标签的配置
- 能够理解Bean的实例化方法
- 能够理解Bean的属性注入方法
- 能够理解复杂类型的属性注入
Spring框架介绍
什么是spring
-
Spring是一个一站式/full-stack(全栈)的框架。意思是使用Spring框架既可以开发web层、也可以开发service层也可以开发dao层
-
本质:Spring就是一个对象的容器,因为在这个容器中有可以处理请求的对象,所以可以用来写web层,因为有service层对象,所以可以用写service层,因为有dao层对象,所以可以用来操作数据库。
-
Spring的开放性和包容性非常强,无缝整合第三方优秀框架,不会重复造轮子。
-
Spring的框架结构
注意:Spring是一个模块化的框架,Spring功能很多很强大,但是并不要求所有的功能我们都得用上,结构图中每一个圆角矩形都对应一个jar,我们使用什么模块什么功能引入对应模块即可。
Spring的发展历程
- 1997年IBM提出了EJB的思想
- 1998年,SUN制定开发标准规范EJB1.0
- 1999年,EJB1.1发布
- 2001年,EJB2.0发布
- 2003年,EJB2.1发布
- 2006年,EJB3.0发布
- Rod Johnson(spring之父)
- Expert One-to-One J2EE Design and Development(2002)
阐述了J2EE使用EJB开发设计的优点及解决方案
Expert One-to-One J2EE Development without EJB(2004)
阐述了J2EE开发不使用EJB的解决方式(Spring雏形)
- Expert One-to-One J2EE Design and Development(2002)
- 2017年9月份发布了spring的最新版本spring 5.0通用版(GA)
Spring优势
- 方便解耦,简化开发
通过Spring提供的IoC容器,可以将对象间的依赖关系交由Spring进行控制,避免硬编码所造成的过度程序耦合。用户也不必再为单例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用 - AOP编程的支持
通过Spring的AOP功能,方便进行面向切面的编程,许多不容易用传统OOP实现的功能可以通过AOP轻松应付。 - 声明式事务的支持
可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务的管理,提高开发效率和质量。 - 方便程序的测试 junit 4.12
可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可做的事情。 - 方便集成各种优秀框架
Spring可以降低各种框架的使用难度,提供了对各种优秀框架(Struts、Hibernate、Hessian、Quartz等)的直接支持。 - 降低JavaEE API的使用难度
Spring对JavaEE API(如JDBC、JavaMail、远程调用等)进行了薄薄的封装层,使这些API的使用难度大为降低。 - Java源码是经典学习范例
Spring的源代码设计精妙、结构清晰、匠心独用,处处体现着大师对Java设计模式灵活运用以及对Java技术的高深造诣。它的源代码无意是Java技术的最佳实践的范例。
IOC的概念和作用
注意:IOC和AOP不是从Spring开始才有的,在Spring之前更趋向于理论化,在Spring这才发扬光大的。
-
IOC的概念
IOC:inverse of control (反转控制/控制反转),我们之前在一个java类中通过new的方式去引入外部资源(其他类等),这叫正向;现在Spring框架帮我们去new,你什么时候需要你去问Spring框架要,这就是反转(创建对象权利的一个反转),我们丧失了一个权利(主动创建对象的权利),但是我们拥有了一个福利(我们不用考虑对象的创建、销毁等问题)。
-
IOC的作用:IOC解决程序耦合问题
- 关于程序耦合(有一个认识即可)
- 耦合:耦合度的意思,程序当中:为了实现一个业务功能,不同的模块之间互相调用就会产生耦合
- 注意:耦合是不能避免的,耦合尽可能去降低
- “高内聚、低耦合”,内聚度过高不利于代码复用,内聚度往往通过增加方法来完成(麻烦自己、方便别人)。
程序的耦合
耦合性(Coupling),也叫耦合度,是对模块间关联程度的度量。耦合的强弱取决于模块间接口的复杂性、调用模块的方式以及通过界面传送数据的多少。模块间的耦合度是指模块之间的依赖关系,包括控制关系、调用关系、数据传递关系。模块间联系越多,其耦合性越强,同时表明其独立性越差( 降低耦合性,可以提高其独立性)。耦合性存在于各个领域,而非软件设计中独有的,但是我们只讨论软件工程中的耦合。 在软件工程中,耦合指的就是就是对象之间的依赖性。对象之间的耦合越高,维护成本越高。因此对象的设计应使类和构件之间的耦合最小。软件设计中通常用耦合度和内聚度作为衡量模块独立程度的标准。划分模块的一个准则就是高内聚低耦合。 它有如下
- 分类:
- (1) 内容耦合。当一个模块直接修改或操作另一个模块的数据时,或一个模块不通过正常入口而转入另一个模块时,这样的耦合被称为内容耦合。内容耦合是最高程度的耦合,应该避免使用之。
- (2) 公共耦合。两个或两个以上的模块共同引用一个全局数据项,这种耦合被称为公共耦合。在具有大量公共耦合的结构中,确定究竟是哪个模块给全局变量赋了一个特定的值是十分困难的。
- (3) 外部耦合 。一组模块都访问同一全局简单变量而不是同一全局数据结构,而且不是通过参数表传递该全局变量的信息,则称之为外部耦合。
- (4) 控制耦合 。一个模块通过接口向另一个模块传递一个控制信号,接受信号的模块根据信号值而进行适当的动作,这种耦合被称为控制耦合。
- (5) 标记耦合 。若一个模块A通过接口向两个模块B和C传递一个公共参数,那么称模块B和C之间存在一个标记耦合。
- (6) 数据耦合。模块之间通过参数来传递数据,那么被称为数据耦合。数据耦合是最低的一种耦合形式,系统中一般都存在这种类型的耦合,因为为了完成一些有意义的功能,往往需要将某些模块的输出数据作为另一些模块的输入数据。
- (7) 非直接耦合 。两个模块之间没有直接关系,它们之间的联系完全是通过主模块的控制和调用来实现的。z
- 总结: 耦合是影响软件复杂程度和设计质量的一个重要因素,在设计上我们应采用以下原则:如果模块间必须存在耦合,就尽量使用数据耦合,少用控制耦合,限制公共耦合的范围,尽量避免使用内容耦合。 内聚与耦合 内聚标志一个模块内各个元素彼此结合的紧密程度,它是信息隐蔽和局部化概念的自然扩展。内聚是从功能角度来度量模块内的联系,一个好的内聚模块应当恰好做一件事。它描述的是模块内的功能联系。耦合是软件结构中各模块之间相互连接的一种度量,耦合强弱取决于模块间接口的复杂程度、进入或访问一个模块的点以及通过接口的数据。 程序讲究的是低耦合,高内聚。就是同一个模块内的各个元素之间要高度紧密,但是各个模块之间的相互依存度却要不那么紧密。
自定义IOC实现程序解耦合
需求:开发dao层和service层模拟保存账户(Account),不具体操作数据库
- pojo对象
public class Account {
private Integer id;
//账户名
private String name;
//账户金额
private Double money;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getMoney() {
return money;
}
public void setMoney(Double money) {
this.money = money;
}
}
- service层
public interface AccountService {
public void saveAccount(Account account);
}
public class AccountServiceImpl implements AccountService {
@Override
public void saveAccount(Account account) {
AccountDao accountDao = (AccountDao) BeanFactory.getInstance("accountDao");
accountDao.saveAccount(account);
}
}
- dao层
public interface AccountDao {
void saveAccount(Account account);
}
public class AccountDaoImpl implements AccountDao {
@Override
public void saveAccount(Account account) {
System.out.println("模拟保存账户");
}
}
- bean工厂
/**
* bean工厂
* 读取xml信息
* 反射创建对象
* 存储到集合Map (对象容器)
*
* 提供一个方法,根据id获取对象
*/
public class BeanFactory {
private static Map<String,Object> map = new HashMap<String, Object>();
/**
* 读取xml配置文件
* 创建对象,存储Map集合
*/
static {
InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");
SAXReader saxReader = new SAXReader();
try {
Document document = saxReader.read(in);
Element rootElement = document.getRootElement();
List<Element> elementList = rootElement.selectNodes("//bean");
if(elementList != null && elementList.size() >0 ){
for(Element element : elementList){
String id = element.attributeValue("id");
String className = element.attributeValue("class");
Class clazz = Class.forName(className);
Object obj = clazz.newInstance();
map.put(id,obj);
}
}
}catch (Exception ex){
ex.printStackTrace();
}
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 根据id返回对象
*/
public static Object getInstance(String id){
return map.get(id);
}
}
- beans.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!--
存储需要实例化的对象
id 唯一标识
class 类全限定名
-->
<beans>
<bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl"></bean>
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"></bean>
</beans>
SpringIOC的使用
- 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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"></bean>
<bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl"></bean>
</beans>
- 测试类
/**
* 测试springIOC容器
*/
@Test
public void testIOC(){
// ApplicationContext对象容器
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// getBean方法获取对象
AccountService service = (AccountService) context.getBean("accountService");
AccountDao dao = (AccountDao)context.getBean("accountDao");
/*
接口class获取实现类对象,接口只有一个实现类
context.getBean(AccountService.class);
*/
/*
接口class获取实现类对象
id唯一标识
context.getBean("accountService",AccountService.class);
*/
System.out.println(service);
System.out.println(dao);
}
}
ApplicationContext和BeanFactory的区别
-
ApplicationContext:立即加载模式,在Spring启动的时候会立即实例化对象,等待使用,推荐使用
-
BeanFactory:使用到对象的时候(getBean等)才去实例化对象(延迟加载模式),不推荐的
-
测试延迟加载
/**
* 测试BeanFactory延迟加载
*/
@Test
public void testBeanFactory(){
// 加载跟目录下的配置文件
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource res = resolver.getResource("applicationContext.xml");
// 创建Bean工厂类 注意 XmlBeanFactory已经过期!
BeanFactory factory = new XmlBeanFactory(res);
AccountDao accountDAO = factory.getBean("accountDao", AccountDao.class);
AccountService accountService = factory.getBean("accountService", AccountService.class);
System.out.println(accountDAO);
System.out.println(accountService);
}
Bean标签
- id:标识符
- class:实例化类的全限定类名,供反射实例化使用
- scope:对象的使用范围
- singleton:默认值,单例,项目全局内存中只有一个对象,一般在使用Spring的时候保持默认值即可
- prototype:原型的意思,在这里是多例,每引用或者getBean一次,Spring框架就返回一个新的对象,比如SqlSession这个特殊的对象
- request:这种配置针对web应用,每一个request范围内只对应一个对象
- session:这种配置针对web应用,每一次会话该bean只有一个对象
- global session:针对的是portlet环境,等同于servlet下的session
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl" scope="prototype">
</bean>
/**
* 测试scope属性
* 对象使用范围
*/
@Test
public void testBeanScope(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// getBean方法获取对象
AccountService service = (AccountService) context.getBean("accountService");
AccountService service1 = (AccountService) context.getBean("accountService");
System.out.println(service);
System.out.println(service1);
}
Bean标签的生命周期
- init-method指定初始化时执行的方法
- destory-method指定销毁时执行的方法
- 销毁执行方法,scope配置为单例子
- 关闭方法在ApplicationContext接口实现类ClassPathXmlApplicationContext中定义
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl" scope="singleton" init-method="init" destroy-method="destroy">
</bean>
public AccountServiceImpl(){
System.out.println("我是构造方法");
}
public void init(){
System.out.println("我是初始化方法");
}
public void destroy(){
System.out.println("我是销毁方法");
}
/**
* 测试对象生命周期
*/
@Test
public void testBeanLife(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// getBean方法获取对象
AccountService service = (AccountService) context.getBean("accountService");
System.out.println(service);
context.close();
}
Bean标签的创建对象方式
-
创建对象:通过class配置的全限定类名创建对象,框架使用反射技术.
-
自己创建对象:加入到IOC容器中
-
静态创建
/** * 静态工厂 * 获取接口实现类对象 */ public class StaticFactory { public static AccountService createAccountService(){ return new AccountServiceImpl(); } }
<!--对象产生方式:静态工厂 --> <bean id="accountService" class="com.itheima.factory.StaticFactory" factory-method="createAccountService"></bean>
-
实例化创建
public class InstanceFactory { public AccountService createAccountService(){ return new AccountServiceImpl(); } }
<!-- 配置实例化的工厂对象--> <bean id="instanceFactory" class="com.itheima.factory.InstanceFactory"> </bean> <!-- 配置工厂创建的对象 factory-bean和id相同--> <bean id="accountService" factory-bean="instanceFactory" factory-method="createAccountService"> </bean>
-
依赖注入(DI)
Dependance Inject(依赖注入的意思)
IOC和DI的区别
IOC和DI是一个事情,只是描述的角度不同(面试题)
Set方法注入,也叫设值注入,这是常用和推荐的用法
- bean子标签property配置
- name:set方法名,去掉set
- ref:创建JavaBean对象的id属性值
<!--
向service层中的成员AccountDao注入对象
-->
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
<!--
name:属性配置方法名
ref:配置创建JavaBean标签的id属性值
-->
<property name="accountDao" ref="accountDao"></property>
</bean>
- service层
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public void saveAccount(Account account) {
accountDao.saveAccount(account);
}
- 测试set方法注入
/**
* 测试set方法依赖注入
*/
@Test
public void testDISet(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
AccountService service = (AccountService) context.getBean("accountService");
service.saveAccount(new Account());
}
简单数据类型注入
- AccountServiceImpl添加成员
private Integer id;
private Double money;
private String name;
public void setId(Integer id) {
this.id = id;
}
public void setMoney(Double money) {
this.money = money;
}
public void setName(String name) {
this.name = name;
}
- property配置
<property name="id" value="123"></property>
<property name="money" value="10.99"></property>
<property name="name" value="张三"></property>
构造方法注入
- AccountServiceImpl添加构造方法
private AccountDao accountDao;
private Integer id;
private Double money;
private String name;
public AccountServiceImpl(AccountDao accountDao, Integer id, Double money, String name){
this.accountDao = accountDao;
this.id = id;
this.money = money;
this.name = name;
}
- property配置
<constructor-arg name="accountDao" ref="accountDao"></constructor-arg>
<constructor-arg name="id" value="99"></constructor-arg>
<constructor-arg name="money" value="8.55"></constructor-arg>
<constructor-arg name="name" value="李四"></constructor-arg>
复杂对象注入
- AccountServiceImpl添加复杂数据类型
private String[] myStrs;
private List<String> myList;
private Set<String> mySet;
private Map<String,String> myMap;
private Properties myPro;
public void setMyStrs(String[] myStrs) {
this.myStrs = myStrs;
}
public void setMyList(List<String> myList) {
this.myList = myList;
}
public void setMySet(Set<String> mySet) {
this.mySet = mySet;
}
public void setMyMap(Map<String, String> myMap) {
this.myMap = myMap;
}
public void setMyPro(Properties myPro) {
this.myPro = myPro;
}
- property标签配置
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
<!--
name:属性配置方法名
ref:配置创建JavaBean标签的id属性值
-->
<property name="accountDao" ref="accountDao"></property>
<property name="id" value="123"></property>
<property name="money" value="10.99"></property>
<property name="name" value="张三"></property>
<!-- 注入数组 -->
<property name="myStrs">
<array>
<value>strs1</value>
<value>strs2</value>
<value>strs3</value>
</array>
</property>
<!-- 注入List集合 -->
<property name="myList">
<list>
<value>list1</value>
<value>list2</value>
<value>list3</value>
</list>
</property>
<!-- 注入Set集合 -->
<property name="mySet">
<set>
<value>set1</value>
<value>set2</value>
<value>set3</value>
</set>
</property>
<!-- 注入Map集合 -->
<property name="myMap">
<map>
<entry key="map1" value="mapvalue1"></entry>
<entry key="map2" value="mapvalue2"></entry>
<entry key="map3" value="mapvalue3"></entry>
</map>
</property>
<!-- 注入properties集合 -->
<property name="myPro">
<props>
<prop key="pro1">provalue1</prop>
<prop key="pro2">provalue2</prop>
<prop key="pro3">provalue3</prop>
</props>
</property>