Spring框架概述
Spring是什么(官网: spring.io)
- Spring 是分层的 Java SE/EE 应用 full-stack 轻量级开源框架
- 以 IoC(Inverse Of Control: 反转控制)和 AOP(Aspect Oriented Programming:面向切面编程)为内核,提供了展现层 Spring MVC 和持久层 Spring JDBC 以及业务层事务管理等众多的企业级应用技术
- 还能整合开源世界众多 著名的第三方框架和类库,逐渐成为使用最多的 Java EE 企业应用开源框架
Spring的两大核心
- IoC(Inverse Of Control: 反转控制)
- AOP(Aspect Oriented Programming:面向切面编程)
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 雏形)
- 2017 年 9 月份发布了 spring 的最新版本 spring 5.0 通用版(GA)
Spring的优势
- 方便解耦,简化开发
- 通过 Spring 提供的 IoC 容器,可以将对象间的依赖关系交由 Spring 进行控制,避免硬编码所造 成的过度程序耦合。用户也不必再为单例模式类、属性文件解析等这些很底层的需求编写代码,可 以更专注于上层的应用。
- AOP 编程的支持
- 通过 Spring 的 AOP 功能,方便进行面向切面的编程,许多不容易用传统 OOP 实现的功能可以通过 AOP 轻松应付。
- 声明式事务的支持
- 可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务的管理, 提高开发效率和质量。
- 方便程序的测试
- 可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可 做的事情。
- 方便集成各种优秀框架
- Spring 可以降低各种框架的使用难度,提供了对各种优秀框架(Struts、Hibernate、Hessian、Quartz 等)的直接支持。
- 降低 JavaEE API 的使用难度
- Spring 对 JavaEE API(如 JDBC、JavaMail、远程调用等)进行了薄薄的封装层,使这些 API 的 使用难度大为降低。
- Java 源码是经典学习范例
- Spring 的源代码设计精妙、结构清晰、匠心独用,处处体现着大师对 Java 设计模式灵活运用以 及对 Java 技术的高深造诣。它的源代码无意是 Java 技术的最佳实践的范例。
Spring的体系结构
程序的耦合以及解耦
- 程序的耦合
- 耦合:程序间的依赖关系
- 类之间的依赖
- 方法间的依赖
- 解耦:降低程序间的依赖关系
- 实际开发中应做到编译器不依赖,运行时才依赖
- 耦合:程序间的依赖关系
- 程序的解耦
- 使用反射机制来创建对象,而避免使用类机制
- 通过读取配置文件来获取要创建的对象全限定类名
曾经案例中的问题
- 接口
public interface IUserDao{ public void save(); }
- 接口实现类
public class IUserDaoImpl implements IUserDao{ public void save(){ //... } }
- 表现层调用
public class Client{ //此处耦合 IUserDao ud = new IUserDaoImpl();//有new ,依赖了别的类 ud.save(); }
- 如果实现类被拷走,就是编译时的错误,无法通过编译
工厂模式解偶
-
Bean:在计算机英语中,有可重用组件的含义
-
JavaBean:用java语言编写的可重用组件
- javabean != 实体类
- 实体类只是可重用组件的一层
-
一个创建Bean对象的工厂
- 创建service和dao对象的
- 需要一个配置文件来配置service和dao
- 配置的内容,唯一标示=全限定类名(key=value)
- 通过读取配置文件中内容,反射创建对象
-
配置文件可以是xml也可以是properties
-
配置文件代码示例
accountService=com.itheima.service.impl.AccountServiceImpl accountDao=com.itheima.dao.impl.AccountDaoImpl
-
读取配置文件代码示例
public class BeanFactory { //定义一个Properties对象 private static Properties props; //定义一个Map,用于存放我们要创建的对象。我们把它称之为容器 private static Map<String,Object> beans; //使用静态代码块为Properties对象赋值 static { try { //实例化对象 props = new Properties(); //获取properties文件的流对象 InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties"); props.load(in); //实例化容器 beans = new HashMap<String,Object>(); //取出配置文件中所有的Key Enumeration keys = props.keys(); //遍历枚举 while (keys.hasMoreElements()){ //取出每个Key String key = keys.nextElement().toString(); //根据key获取value String beanPath = props.getProperty(key); //反射创建对象 Object value = Class.forName(beanPath).newInstance(); //把key和value存入容器中 beans.put(key,value); } }catch(Exception e){ throw new ExceptionInInitializerError("初始化properties失败!"); } } /** * 根据bean的名称获取对象 * @param beanName * @return */ public static Object getBean(String beanName){ return beans.get(beanName); }
-
表现层(用于调用业务层)
/** * 模拟一个表现层,用于调用业务层 */ public class Client { public static void main(String[] args) { //IAccountService as = new AccountServiceImpl(); for(int i=0;i<5;i++) { IAccountService as = (IAccountService) BeanFactory.getBean("accountService"); System.out.println(as); as.saveAccount(); } } }
IOC概念和Spring中的IOC
- 控制反转,把创建对象的权利交给框架,是框架的重要特征,你并非面向对象编程的专用术语。它包括依赖注入和依赖查找
- 明确ioc作用:削减计算机程序的耦合(解除代码中的依赖关系 )
Spring中基于xml的IOC环境搭建
- 如上面的Sprig结构图,Core Container里的Beans,Core,Context,SpEL就是Spring容器所必须的jar包,再加上一个日志容器(把Apache的日志组件又封装了一下集成进来),一共5个jar包
- Spring对bean的管理细节
- 第一种:使用默认构造函数的创建
- 在Spring的配置文件中使用bean标签,配以id属性之后,且并没有其他属性和标签时。
- 采用的是默认的构造函数创建bean对象,此时如果类中没有默认构造函数,则对象无法创建
<bean id="accoutService" class="com.my.service.impl.AccountServiceImpl"></bean>
- 第二种(别人jar包里的类改变不了):使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入spring容器)
<bean id="instanceFactory" class="com.my.factory.InstanceFactory"></bean>
<bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService"></bean>
- 第三种:使用工厂中的静态方法创建对象(使用某个类中的静态方法创建对象并存入spring容器)
<bean id="instanceFactory" class="com.my.factory.StaticFactory" factory-method="getAccountService"></bean>
- 第一种:使用默认构造函数的创建
- bean的作用范围调整
- bean标签的scope属性
- 作用:用于指定bean的作用范围
- 取值(常用的是单例的和多例的): singleton:单例的(默认值)、prototype:多例的、request:作用于web应用的请求范围、session:作用于web应用的会话范围、global-session:作用于集群环境的会话范围(全局会话范围),当不是集群环境时,它就是session
- bean标签的scope属性
- 全局session扩展
- bean对象的生命周期
- 单例对象(立即加载)
- 出生:当容器创建时出生
- 活着:只要容器还在,对象一直活着
- 死亡:容器销毁,对象消亡
- 总结:单例对象的生命周期和容器相同
- 多例对象(延迟加载))
- 出生:当使用对象时spring框架为我们创建
- 活着:对象只要是在使用过程中就一直活着
- 死亡:当对象长时间不用且没有别的对象引用时,由java的垃圾回收器回收(spring不知道什么时候用完的)
- 单例对象(立即加载)
- 示例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"> <!--把对象的创建交给spring来管理--> <!--创建Bean的三种方式 --> <!-- 第一种方式:使用默认构造函数创建。--> <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"></bean> <!-- 第二种方式: 使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入spring容器)--> <bean id="instanceFactory" class="com.itheima.factory.InstanceFactory"></bean> <bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService"></bean> <!-- 第三种方式:使用工厂中的静态方法创建对象(使用某个类中的静态方法创建对象,并存入spring容器) --> <bean id="accountService" class="com.itheima.factory.StaticFactory" factory-method="getAccountService"></bean> <!-- 设置bean的作用范围 --> <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl" scope="prototype"></bean> <!-- 设置初始化和销毁方法 --> <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl" scope="prototype" init-method="init" destroy-method="destroy"></bean> </beans>
- 示例表现层
public class Client { /** * * @param args */ public static void main(String[] args) { //1.获取核心容器对象 // ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml"); ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml"); //2.根据id获取Bean对象 IAccountService as = (IAccountService)ac.getBean("accountService"); as.saveAccount(); //手动关闭容器 ac.close(); } }
依赖注入(Dependency Injection)(DI)
- 依赖注入:Dependency injection
- IOC的作用:降低程序见的耦合(依赖关系)
- 依赖关系的管理:以后都交给了spring来维护
- 在当前类中需要用到其他类的对象,由Spring为我们提供,我们只需要在配置文件中说明
- 依赖关系的维护:即依赖注入
- 依赖注入:
- 能注入的数据(三类):
- 基本类型和string
- 其他bean类型(在配置文件中或者注解配置过的bean)
- 复杂类型/集合类型
- 注入的方式(三种):
- 使用构造函数提供
- 使用set方法提供
- 使用注解提供
- 能注入的数据(三类):
注入的方式详解
-
构造函数注入:
- 如果是经常变化的数据,并不适合此种方式
- 使用标签:
<constructor-arg></constructor-arg>
- 标签出现的位置:bean标签的内部
- 标签中的属性:
- type;用于指定要注入的数据的数据类型,该数据类型也是构造函数中某个或某些参数的类型
- index:用于指定要注入的数据给构造函数中指定索引位置的参数赋值。索引的位置是从0开始的
- name:用于指定给构造函数中指定名称的参数赋值
- 以上三个用于指定给构造函数中哪个参数赋值
- value:用于提供基本类型和string类型的数据
- ref:用于指定其他的bean类型数据,它指的就是在spring的ioc核心容器中出现过的bean对象(比如配一个日期bean对象,就可以引用了)
- 优势:
- 在获取bean对象时,注入数据是必须的操作,否则对象无法创建成功
- 弊端:
- 改变了bean对象的实例化方式,使我们在创建对象时,即使用不到这些数据,也必须提供
- 配置代码:
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"> <constructor-arg name="name" value="泰斯特"></constructor-arg> <constructor-arg name="age" value="18"></constructor-arg> <constructor-arg name="birthday" ref="now"></constructor-arg> </bean> <!-- 配置一个日期对象 --> <bean id="now" class="java.util.Date"></bean>
-
set方法注入: (更常用)
- 涉及的标签:property
- 出现的位置:bean标签的内部
- 标签的属性:
name:用于指定注入时所调用的set方法名称
value:用于提供基本类型和string 类型的数据
ref:用于指定其他的bean类型数据。他指的就是在spring的ioc核心容器中出现过的bean对象 - 优势:
- 创建对象时没有没有明确的限制,可以直接使用默认构造函数
- 弊端;
- 如果有某个成员必须有值,则获取对象是有可能set方法没有执行
- 配置代码:
<bean id="accountService2" class="com.itheima.service.impl.AccountServiceImpl2"> <property name="name" value="TEST" ></property> <property name="age" value="21"></property> <property name="birthday" ref="now"></property> </bean>
-
注入集合数据(也是调的set方法)
- 用于给list结构集合注入的标签:list, array, set
- 用于给Map结构集合注入的标签 map, props
- 结构相同,标签可以互换
<bean id="accountService3" class="com.itheima.service.impl.AccountServiceImpl3"> <property name="myStrs"> <set> <value>AAA</value> <value>BBB</value> <value>CCC</value> </set> </property> <property name="myList"> <array> <value>AAA</value> <value>BBB</value> <value>CCC</value> </array> </property> <property name="mySet"> <list> <value>AAA</value> <value>BBB</value> <value>CCC</value> </list> </property> <property name="myMap"> <props> <prop key="testC">ccc</prop> <prop key="testD">ddd</prop> </props> </property> <property name="myProps"> <map> <entry key="testA" value="aaa"></entry> <entry key="testB"> <value>BBB</value> </entry> </map> </property> </bean>