spring官网:https://spring.io/
一.Spring概述
Spring是分层的 Java SE/EE应用 full-stack(全栈) 轻量级开源框架。
Spring的核心是 IOC(Inverse Of Control:控制反转)和 AOP(Aspect Oriented Programming:面向切面编程)
Spring一个全栈应用框架, 提供了表现层 Spring MVC 和持久层 Spring JDBC 以及业务层事务管理等众多应用技术
Spring还能整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的 Java EE 企业应用开源框架
Spring官网:https://spring.io/
(1)发展历程:
* EJB
1997 年,IBM提出了EJB 的思想
1998 年,SUN制定开发标准规范 EJB1.0
1999 年,EJB1.1 发布
2001 年,EJB2.0 发布
2003 年,EJB2.1 发布
2006 年,EJB3.0 发布
* Spring
Rod Johnson( Spring 之父)
改变Java世界的大师级人物
2002年编著《Expert one on one J2EE design and development》
指出了JavaEE和EJB组件框架中的存在的一些主要缺陷;提出普通java类依赖注入更为简单的解决方案。
2004年编著《Expert one-on-one J2EE Development without EJB》
阐述了JavaEE开发时不使用EJB的解决方式(Spring 雏形)
同年4月spring1.0诞生
2006年10月,发布 Spring2.0
2009年12月,发布 Spring3.0
2013年12月,发布 Spring4.0
2017年9月, 发布最新 Spring5.0 通用版(GA)
(2)Spring开发优势:
1. 方便解耦,简化开发
通过Spring提供的 IOC 容器,可以将对象间的依赖关系交由 Spring 进行控制,避免硬编码所造成的过度耦合。 用户也不必再为单例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。
2. AOP编程的支持
通过Spring的AOP功能,方便进行面向切面编程,许多不容易用传统 OOP 实现的功能可以通过 AOP 轻松实现。
3. 声明式事务的支持
可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务管理,提高开发效率和质量。
4. 方便程序的测试
可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可做的事情。
5. 方便集成各种优秀框架
Spring对各种优秀框架(Struts、Hibernate、Hessian、Quartz等)的支持。
6. 降低JavaEE API的使用难度
Spring对JavaEEAPI(如JDBC、JavaMail、RPC等)进行了薄薄的封装层,使这些 API 的使用难度大为降低。
7. Java源码经典学习范例
Spring的源代码设计精妙、结构清晰、匠心独用,处处体现着大师对Java 设计模式灵活运用以及对 Java技术的高深造诣。它的源代码无意是 Java 技术的最佳实践的范例。
(3)Spring体系结构:
二.初识IOC(控制反转)
(1)介绍:
控制反转(Inverse Of Control)不是什么技术,而是一种设计思想。它的目的是指导我们设计出更加松耦合的程序。
控制:在java中指的是对象的控制权限(创建、销毁)
反转:指的是对象控制权由原来 由开发者在类中手动控制 反转到 由Spring容器控制
比如:
* 原来我们需要一个对象,需要自己手动new出来
UserServlet
UserService us = new UserServiceImpl();
* 学习了spring之后,需要一个对象,从spring的ioc容器获取
UserServlet
UserService us = spring的IOC容器.getBean("userService");
* IOC的核心思想:松耦合
需求:实现service层和dao层代码解耦合
(2)案例:
新建idea项目:
项目完毕:
统一进行设置:
- 创建Maven的java模块:
导入maven 的jar包
<!--依赖管理-->
<dependencies>
<!--dom4j-->
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
<!--xpath-->
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>1.1.6</version>
</dependency>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
(2)实现方案比较:
==版本1==原始版本
编写UserDao接口和实现类:快捷键alt+insert
编写UserService接口和实现类:
编写UserTest
问题总结;
==版本2==工厂解耦
我们需要在项目中 定义BeanFactory + xml解析,实现各层代码之间解耦合
编写beans.xml
编写BeanFactory
public class BeanFactory {
public static Object getBean(String id) {
Object object = null;
try {
// 1.通过类加载器读取 beans.xml
InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");
// 2.创建dom4j核心解析器对象
SAXReader saxReader = new SAXReader();
Document document = saxReader.read(in);
// 3.编写xpath表达式
// String xpath = "//bean[@id='userDao']";
String xpath = "//bean[@id='" + id + "']";
// 4.获取指定id的标签对象
Element element = (Element) document.selectSingleNode(xpath);
// 5.获取全限定名
String className = element.attributeValue("class");
// 6.通过反射创建对象实例
object = Class.forName(className).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
// 7.返回对象实例
return object;
}
}
修改UserServiceImpl
问题:
==版本3== 工厂优化:
使用单例模式:
修改BeanFactory
public class BeanFactory {
// 声明存储对象的容器(map集合)
private static Map<String, Object> ioc = new HashMap<>();
// 静态代码块,初始化ioc容器
static {
String id = null;
String className = null;
Object object = null;
try {
// 1.通过类加载器读取 beans.xml
InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");
// 2.创建dom4j核心解析器对象
SAXReader saxReader = new SAXReader();
Document document = saxReader.read(in);
// 3.编写xpath表达式
String xpath = "//bean";
// 4.获取所有的bean标签对象
List<Element> list = document.selectNodes(xpath);
// 5.遍历集合,创建对象实例,设置到ioc容器中
for (Element element : list) {
id = element.attributeValue("id");
className = element.attributeValue("class");
object = Class.forName(className).newInstance();
// 设置到map集合
ioc.put(id, object);
}
} catch (Exception e) {
e.printStackTrace();
}
}
// 从ioc容器中获取指定id的对象实例
public static Object getBean(String id) {
return ioc.get(id);
}
}
总结
对象的创建由原来的 使用 new关键字在类中主动创建 变成了 从工厂中获取, 而对象的创建过程由工厂内部来实现, 而这个工厂就是 Spring的IOC容器, 也就是以后我们的对象不再自己创建,而是直接向Spring要, 这种思想就是IOC
ioc目的:松耦合...,你可以简单的认为就是一个map集合(ioc容器)
三.Spring入门:
流程图:
代码实现
创建maven的java模块:
导入spring相关坐标:
<!--依赖管理-->
<dependencies>
<!--spring的核心坐标-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
编写UserDao接口和实现类:
创建spring的核心配置文件,导入约束
官方推荐:applicationContext.xml
编写bean标签:(id,class)
测试:模拟service层
public class UserTest {
// spring的快速入门
@Test
public void test01() throws Exception {
// 1.通过spring的api读取配置文件
ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
// 2.获取指定id的对象实例
UserDao userDao = (UserDao) app.getBean("userDao");
userDao.save();
}
}
四/Spring相关API
spring的api体系非常的庞大,我们先介绍核心的内容
(1)两个接口
* BeanFactory
介绍:
这是IOC容器的顶级接口 它定义了IOC的最基础的功能, 但是其功能比较简单,一般面向Spring自身使用
特点:
在第一次使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化[用的时候再创建]
懒汉设计
* ApplicationContext
介绍:
这是在BeanFactory基础上衍生出的接口,它扩展了BeanFactory的功能,一般面向程序员使用
特点:
在容器启动时,一次性创建并加载了所有的Bean [初始化的时候全创建好]
恶汉设计
* 小结:上面两种方式创建的对象都是单例, 只是创建对象的时机不同
(2)三个实现类:
* ClassPathXmlApplicationContext
功能:
读取类路径(classpath)下的xml配置文件
* FileSystemXmlApplicationContext
功能:
读取本地磁盘下的xml配置文件
* AnnotationConfigApplicationContext
功能:
读取java配置类加载配置
(3)一个方法:
* public Object getBean(String name) throws BeansException;
功能:
通过指定id获取对象的实例,需要手动强转
* public <T> T getBean(Class<T> requiredType);
功能:
通过指定类型获取对象的实例,不需要强转
* public <T> T getBean(String name, Class<T> requiredType);
功能:
通过指定id和类型获取对象的实例
// getBean方法介绍
@Test
public void test02() throws Exception {
// 1.通过spring的api读取配置文件
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
// 方式一: 通过指定id获取对象的实例,需要手动强转
/*
UserDao userDao1 = (UserDao) app.getBean("userDao");
userDao1.save();
*/
// 方式二:通过指定(接口)类型获取对象的实例,不需要强转
/*
缺点:如果同一个接口类型下有多个对象实例,会报错的...
UserDao userDao2 = app.getBean(UserDao.class);
userDao2.save();
*/
// 方式三:通过指定id和类型获取对象的实例
UserDao userDao3 = app.getBean("userDao", UserDao.class);
userDao3.save();
}
五.Spring配置文件
(1)Bean标签基本配置:
基本配置:
<!--
bean标签的二个基本属性
id:在ioc容器的唯一标识
class:创建对象实例的全限定名
-->
<bean id="userDao" class="com.wsl.dao.impl.UserDaoImpl"></bean>
作用范围:
<!--
bean标签还有一个属性scope属性:声明此对象的作用范围
singleton:单例对象(默认)
何时创建?
ioc容器初始化时,创建对象
对象运行?
只要ioc容器在,对象就一直活着....
何时销毁?
ioc容器关闭时,销毁对象
prototype:多例对象
何时创建?
在调用getBean()方法时,创建
对象运行?
一直使用就一直活着
何时销毁?
当对象不再使用后,根据JVM GC机制垃圾回收
-->
<bean id="userDao" class="com.wsl.dao.impl.UserDaoImpl" scope="prototype"></bean>
生命周期:
(2)Spring创建对象实例的三种方式:
==无参构造方法实例化:使用最多:
在企业开发中,所有的类必须提供无参构造方法:
==工程静态方法实例化
依赖的jar包中有个A类,A类中有个静态方法m1,m1方法的返回值是一个B对象。如果我们频繁使用B对象,此时我们可以将B对象的创建权交给spring的IOC容器,以后我们在使用B对象时,无需调用A类中的m1方法,直接从IOC容器获得。
/*
工厂静态方法实例化对象
*/
public class StaticFactoryBean {
public static UserDao createUserDao() {
return new UserDaoImpl();
}
// 传统方式,自己通过工厂获取对象...
public static void main(String[] args) {
UserDao userDao = StaticFactoryBean.createUserDao();
}
}
工厂普通方法实例化:
依赖的jar包中有个A类,A类中有个普通方法m1,m1方法的返回值是一个B对象。如果我们频繁使用B对象,此时我们可以将B对象的创建权交给spring的IOC容器,以后我们在使用B对象时,无需调用A类中的m1方法,直接从IOC容器获得。
/*
工厂普通方法实例化对象
*/
public class DynamicFactoryBean {
public UserDao createUserDao() {
return new UserDaoImpl();
}
// 传统方式,自己通过工厂获取对象...
public static void main(String[] args) {
// 1.创建工厂对象
DynamicFactoryBean dynamicFactoryBean = new DynamicFactoryBean();
// 2.创建UserDao对象
UserDao userDao = dynamicFactoryBean.createUserDao();
}
}
(3)Bean依赖注入:
依赖注入(Dependency Injection, DI) 它是 Spring 框架核心 IOC 的具体实现
其实就是给对象中的属性赋值的过程,通过spring完成依赖注入
案例环境搭建:
创建maven的java模块:
创建UserDao,UserService等文件
编写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"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--将userDao交给ioc容器-->
<bean id="userDao" class="cn.wsl.dao.impl.UserDaoImpl"></bean>
<!--将userService交给ioc容器-->
<bean id="userService" class="cn.wsl.service.impl.UserServiceImpl"></bean>
</beans>
测试,报错:
由于service依赖了dao,但是缺少依赖注入环境
public class UserTest {
@Test
public void test01() throws Exception {
// 1.加载spring配置文件
ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
// 2.获取service对象实例
UserService userService = app.getBean(UserService.class);
userService.save();
}
(4)Bean依赖注入方式:
===构造方法:
public class UserServiceImpl implements UserService {
private UserDao userDao;
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void save() {
userDao.save();
}
}
<!--
构造方法注入: <constructor-arg> 子标签
版本一:
name:构造方法参数名称
value:简单数据类型(String、int、double...)
ref:引用数据类型(从ioc容器中获取的对象)
版本二:
index:构造方法参数索引
type:该索引对应的java类型(全限定名)
value:简单数据类型(String、int、double...)
ref:引用数据类型(从ioc容器中获取的对象)
-->
<bean id="userService" class="cn.wsl.service.impl.UserServiceImpl">
<constructor-arg name="userDao" ref="userDao"></constructor-arg>
<!--<constructor-arg index="0" type="cn.wsl.dao.UserDao" ref="userDao"></constructor-arg>-->
</bean>
==set方法:项目中使用
public class UserServiceImpl implements UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void save() {
userDao.save();
}
}
<!--将userDao交给ioc容器-->
<bean id="userDao" class="cn.wsl.dao.impl.UserDaoImpl"></bean>
<!--
set方法注入:<property> 子标签
name:set方法的属性名 setUserDao() -> UserDao -> userDao
value:简单数据类型(String、int、double...)
ref:引用数据类型(从ioc容器中获取的对象)
-->
<bean id="userService" class="cn.wsl.service.impl.UserServiceImpl">
<property name="userDao" ref="userDao"></property>
</bean>
==P 命名空间注入
P命名空间注入底层(本质)使用的也是set方法注入,只是在上着的基础上进行简化
1)导入P命名空间约束
2)使用P命名空间完成注入
(5)Bean依赖注入的数据类型:
简单数据类型
引用数据类型:见上
集合数据类型
单列集合:list,set,array
public class UserDaoImpl implements UserDao {
private List<Object> list;
private Set<Object> set;
private Object[] array;
public void setList(List<Object> list) {
this.list = list;
}
public void setSet(Set<Object> set) {
this.set = set;
}
public void setArray(Object[] array) {
this.array = array;
}
@Override
public void save() {
System.out.println("UserDao保存了....");
System.out.println(list);
System.out.println(set);
System.out.println(Arrays.toString(array));
}
}
<!--
di 注入单列集合类型
需要在 <property>标签中
list集合 使用子标签 <list>
<value> 简单数据类型
<ref> 引用数据类型(对象在ioc容器中)
set集合 使用子标签 <set>
<value> 简单数据类型
<ref> 引用数据类型(对象在ioc容器中)
array数组 使用子标签<array>
<value> 简单数据类型
<ref> 引用数据类型(对象在ioc容器中)
-->
<bean id="userDao" class="cn.wsl.dao.impl.UserDaoImpl">
<property name="list">
<list>
<value>石榴</value>
<value>秋香</value>
<ref bean="user"></ref>
</list>
</property>
<property name="set">
<set>
<value>秋奎</value>
<value>马梅</value>
<ref bean="user"></ref>
</set>
</property>
<property name="array">
<array>
<value>国峰</value>
<value>小羊</value>
<ref bean="user"></ref>
</array>
</property>
</bean>
双列集合
map,properties
public class UserDaoImpl implements UserDao {
private Map<String, Object> map;
private Properties props;
public void setMap(Map<String, Object> map) {
this.map = map;
}
public void setProps(Properties props) {
this.props = props;
}
@Override
public void save() {
System.out.println("UserDao保存了....");
System.out.println(map);
System.out.println(props);
}
}
<!--
di 注入双列集合类型
需要在 <property>标签中
map集合 使用子标签<map>
<entry key="" value="简单数据类型" | value-ref="引用数据类型(对象ioc中)" ></entry>
properties集合 使用子标签 <props>
<prop key="" >value</prop>
-->
<bean id="userDao" class="cn.wsl.dao.impl.UserDaoImpl">
<property name="map">
<map>
<entry key="k1" value="v1"></entry>
<entry key="k2" value="v2"></entry>
<entry key="u1" value-ref="user"></entry>
</map>
</property>
<property name="props">
<props>
<prop key="k1" >v1</prop>
<prop key="k2" >v2</prop>
<prop key="k3" >v3</prop>
</props>
</property>
</bean>
(6)配置文件模块化
实际开发中,Spring的配置内容非常多,这就导致Spring配置很繁杂且体积很大,所以,可以将部分配置拆解到其他配置文件中,也就是所谓的配置文件模块化。
==并列加载:
==主从配置:
注意:
1.在同一个xml中 bean标签的 id不能重复....
2.在多个模块中的xml中,bean标签id重复虽然不会报错,但是后加载的会覆盖先加载的....
在开发中绝不允许出现重名的id
学习资源:源码解读