Spring是轻量级Java EE应用开源框架(官网: http://spring.io/ ),它由Rod Johnson创为了解决企业级编程开发的复杂性而创建
IOC
解决传统Web开发中硬编码所造成的程序耦合
AOP
实现在运行期间不修改源代码对程序进行增强
粘合剂
Spring是一个超级粘合平台,除了自己提供功能外,还提供整合其他技术和框架的能力
Spring 框架根据功能的不同大体可分为 Data Access/Integration(数据访问与集成)
、Web
、AOP、Aspects、Instrumentation(检测)、Messaging(消息处理)
、Core Container(核心容器)
和 Test
。
1997年IBM提出了EJB的思想
1998年,SUN制定开发标准规范EJB1.0
1999年,EJB1.1发布
2001年,EJB2.0发布
2003年,EJB2.1发布
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雏形)
2006年,EJB3.0发布
2017年9月发布了Spring的最新版本Spring5.0通用版x
耦合:耦合指的就是对象之间的依赖关系。对象之间的耦合越高,维护成本越高。
案例:没有引入IOC容器时系统的Web层、业务层、持久层存在耦合
/**
* 持久层实现类
*/
public class UserDaoImpl implements UserDao {
@Override
public void addUser(){
System.out.println("insert into tb_user......");
}
}
/**
* 业务层实现类
*/
public class UserServiceImpl implements UserService {
//硬编码:此处有依赖关系
private UserDao userDao = new UserDaoImpl();
public void addUser(){
userDao.addUser();
}
}
/**
* 模拟表现层
*/
public class Client {
public static void main(String[] args) {
//硬编码:此处有依赖关系
UserService userService = new UserServiceImpl();
userService.addUser();
}
}
问题分析:
上边的代码service层在依赖dao层的实现类,此时如果更改dao了层的实现类或此时没有dao层实现类,编译将不能通过。
IOC(工厂模式)解耦:
IOC (Inverse of Control)即控制反转:由ioc容器来创建依赖对象,程序只需要从IOC容器获取创建好的对象。
原来:
? 我们在获取对象时,都是采用new的方式。是主动的。
现在:
? 我们获取对象时,同时跟工厂要,有工厂为我们查找或者创建对象。是被动的。
这种被动接收的方式获取对象的思想就是控制反转,它是spring框架的核心之一。
案例一
/**
* bean工厂
*/
public class BeanFactory_v1 {
/**
* 获得UserServiceImpl对象
* @return
*/
public static UserService getUserService(){
return new UserServiceImpl();
}
/**
* 获得UserDaoImpl对象
* @return
*/
public static UserDao getUserDao(){
return new UserDaoImpl();
}
}
问题:我们在开发中会有很多个service和dao,此时工厂类就要添加无数个方法。
案例二
#1、配置要使用的dao和service
UserDao=com.by.dao.UserDaoImpl
UserService=com.by.service.UserServiceImpl
`
``
```csharp
/**
* bean工厂
*/
public class BeanFactory_v2 {
private static Properties prop = new Properties();
/**
* 根据全类名获取bean对象
* @param beanName
* @return
* @throws ClassNotFoundException
*/
public static Object getBean(String beanName) {
try {
//不能使用:web工程发布后没有src目录
//InputStream is = new FileInputStream("src/bean.properties");
InputStream is =
BeanFactory_v2.class.getClassLoader()
.getResourceAsStream("bean.properties");
prop.load(is);
return Class.forName(prop.getProperty(beanName)).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) {
System.out.println(prop.get("UserService"));
System.out.println(getBean("UserService"));
}
}
/**
* 业务层实现类
*/
public class UserServiceImpl implements UserService {
private UserDao userDao = (UserDao) BeanFactory.getBean("UserDao");
public void addUser(){
userDao.addUser();
}
}
测试:
/**
* 模拟表现层
*/
public class Client {
public static void main(String[] args) {
//直接引用接口实现类
for (int i = 0; i < 5; i++) {
UserService userService =
(UserService)BeanFactory.getBean("UserService");
System.out.println(userService);
}
}
}
问题:
package com.by.factory;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
/**
* bean工厂
*/
public class BeanFactory_v3 {
//定义一个容器,用于存放对象
private static Map<String, Object> beans = new HashMap<>();
/**
* 加载配置文件
*/
static {
try {
//2、读取配置文件
//不能使用:web工程发布后没有src目录
//InputStream is = new FileInputStream("src/bean.properties");
InputStream is =
BeanFactory_v3.class.getClassLoader()
.getResourceAsStream("bean.properties");
//3、通过反射创建对象,把对象存到容器中
Properties prop = new Properties();
prop.load(is);
Set<Map.Entry<Object, Object>> entrySet = prop.entrySet();
for (Map.Entry<Object, Object> entry : entrySet) {
String key = entry.getKey().toString();
String beanName = entry.getValue().toString();
Object value = Class.forName(beanName).newInstance();
beans.put(key, value);
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 4、在使用的时候,直接从工厂拿
* @param beanName
* @return
*/
public static Object getBean(String beanName) {
try {
return beans.get(beanName);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) {
System.out.println(getBean("UserService"));
}
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.by</groupId>
<artifactId>Spring_IOC_Xml</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<!-- 项目源码及编译输出的编码 -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!-- 项目编译JDK版本 -->
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!-- Spring常用依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.8.RELEASE</version>
</dependency>
</dependencies>
</project>
注意:Jar包彼此存在依赖,只需引入最外层Jar即可由Maven自动将相关依赖Jar引入到项目中。
? 核心容器由 beans、core、context 和 expression(Spring Expression Language,SpEL)4个模块组成。
/**
* 持久层实现类
*/
public class UserDaoImpl implements UserDao {
@Override
public void addUser(){
System.out.println("insert into tb_user......");
}
}
/**
* 业务层实现类
*/
public class UserServiceImpl implements UserService {
//此处有依赖关系
private UserDao userDao = new UserDaoImpl();
public void addUser(){
userDao.addUser();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!--1、注意:要导入schema约束-->
<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">
<!--
2、把对象交给spring来创建
id:给对象在容器中提供一个唯一标识。用于获取对象
class:指定类的全限定类名。用于反射创建对象。默认情况下调用无参构造函数
-->
<bean id="userDao" class="com.by.dao.UserDaoImpl"></bean>
<bean id="userService" class="com.by.service.UserServiceImpl"></bean>
</beans>
注意:命名无限制,约定俗成命名有:spring-context.xml、applicationContext.xml、beans.xml
/**
* 模拟表现层
*/
public class Client {
public static void main(String[] args) {
//1.使用ApplicationContext接口,就是在获取spring容器
ApplicationContext ac = new
ClassPathXmlApplicationContext("applicationContext.xml");
//2.根据bean的id获取对象
UserDao userDao = (UserDao) ac.getBean("userDao");
System.out.println(userDao);
UserService userService = (UserService) ac.getBean("userService");
System.out.println(userService);
userService.addUser();
}
}
问题:service层仍然耦合
概述:DI(Dependency Injection)依赖注入,在Spring创建对象的同时,为其属性赋值,称之为依赖注入。
顾名思义,就是使用类中的构造函数,给成员变量赋值。注意,赋值的操作不是我们自己做的,而是通过配置的方式,让spring框架来为我们注入。具体代码如下:
/**
* 业务层实现类
*/
public class UserServiceImpl implements UserService {
private UserDao userDao;
private String name;
private Integer age;
public UserServiceImpl(UserDao userDao, String name, Integer age) {
this.userDao = userDao;
this.name = name;
this.age = age;
}
public void addUser(){
System.out.println(name+","+age);
userDao.addUser();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!--1、注意:要导入schema约束-->
<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">
<!--2、把对象交给spring来创建-->
<bean id="userDao" class="com.by.dao.UserDaoImpl"></bean>
<bean id="userService" class="com.by.service.UserServiceImpl">
<!--
要求:类中需要提供一个对应参数列表的构造函数。
标签:constructor-arg
==给谁赋值:==
index:指定参数在构造函数参数列表的索引位置
name:指定参数在构造函数中的名称
==赋什么值:==
value:它能赋的值是基本数据类型和String类型
ref:它能赋的值是其他bean类型,也就是说,必须得是在配置文件中配置过的bean
-->
<constructor-arg name="userDao" ref="userDao"></constructor-arg>
<constructor-arg name="name" value="张三"></constructor-arg>
<constructor-arg name="age" value="18"></constructor-arg>
</bean>
</beans>
顾名思义,就是在类中提供需要注入成员的set方法。具体代码如下:
/**
* 业务层实现类
*/
public class UserServiceImpl implements UserService {
private UserDao userDao;
private String name;
private Integer age;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void setName(String name) {
this.name = name;
}
public void setAge(Integer age) {
this.age = age;
}
public void addUser(){
System.out.println(name+","+age);
userDao.addUser();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!--1、注意:要导入schema约束-->
<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">
<!--2、把对象交给spring来创建-->
<bean id="userDao" class="com.by.dao.UserDaoImpl"></bean>
<bean id="userService" class="com.by.service.UserServiceImpl">
<!--
要求:property
标签:constructor-arg
==给谁赋值:==
name:找的是类中set方法后面的部分
==赋什么值:==
value:它能赋的值是基本数据类型和String类型
ref:它能赋的值是其他bean类型,也就是说,必须得是在配置文件中配置过的bean
-->
<property name="userDao" ref="userDao"></property>
<property name="name" value="张三"></property>
<property name="age" value="18"></property>
</bean>
</beans>
不用在配置中 指定为哪个属性赋值,由spring自动根据某个 “原则” ,在工厂中查找一个bean并为属性注入值。具体代码如下:
/**
* 业务层实现类
*/
public class UserServiceImpl implements UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void addUser(){
userDao.addUser();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!--1、注意:要导入schema约束-->
<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">
<!--2、把对象交给spring来创建-->
<bean id="userDao" class="com.by.dao.UserDaoImpl"></bean>
<!--autowire="byType":按照类型自动注入值-->
<bean id="userService" class="com.by.service.UserServiceImpl" autowire="byType">
</bean>
</beans>
<?xml version="1.0" encoding="UTF-8"?>
<!--1、注意:要导入schema约束-->
<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">
<!--2、把对象交给spring来创建-->
<bean id="userDao" class="com.by.dao.UserDaoImpl"></bean>
<!--autowire="byType":按照类型自动注入值-->
<bean id="userService" class="com.by.service.UserServiceImpl" autowire="byName">
</bean>
</beans>
顾名思义,就是给类中的集合成员传值,它用的也是set方法注入的方式,只不过变量的数据类型都是集合。我们这里介绍注入数组,List,Set,Map。具体代码如下:
/**
* 业务层实现类
*/
public class UserServiceImpl implements UserService {
private UserDao userDao;
private String[] myStrs;
private List<String> myList;
private Set<String> mySet;
private Map<String,String> myMap;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
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 addUser(){
System.out.println(Arrays.toString(myStrs));
System.out.println(myList);
System.out.println(mySet);
System.out.println(myMap);
userDao.addUser();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!--1、注意:要导入schema约束-->
<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">
<!--2、把对象交给spring来创建-->
<bean id="userDao" class="com.by.dao.UserDaoImpl"></bean>
<bean id="userService" class="com.by.service.UserServiceImpl">
<!--
要求:property
标签:constructor-arg
==给谁赋值:==
name:找的是类中set方法后面的部分
==赋什么值:==
value:它能赋的值是基本数据类型和String类型
ref:它能赋的值是其他bean类型,也就是说,必须得是在配置文件中配置过的bean
-->
<property name="userDao" ref="userDao"></property>
<!-- 给mySet集合注入数据 -->
<property name="mySet">
<set>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</set>
</property>
<!-- 注入array数组数据 -->
<property name="myArray">
<array>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</array>
</property>
<!-- 注入list集合数据 -->
<property name="myList">
<list>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</list>
</property>
<!-- 注入Map数据 -->
<property name="myMap">
<map>
<entry key="testA" value="aaa"></entry>
<entry key="testB" value="bbb"></entry>
</map>
</property>
</bean>
</beans>
ApplicationContext的实现类,如下图:
spring中工厂的类结构图
区别:
/**
* 业务层实现类
*/
public class UserServiceImpl implements UserService {
private UserDao userDao;
public UserServiceImpl() {
System.out.println("UserServiceImpl对象创建了...");
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void addUser(){
userDao.addUser();
}
}
/**
* 模拟表现层
*/
public class Client {
public static void main(String[] args) {
new ClassPathXmlApplicationContext("applicationContext.xml");
System.out.println("Spring IOC容器创建好了");
}
}
BeanFactory:是在 getBean 的时候才会创建对象。
/**
* 业务层实现类
*/
public class UserServiceImpl implements UserService {
private UserDao userDao;
public UserServiceImpl() {
System.out.println("UserServiceImpl对象创建了...");
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void addUser(){
userDao.addUser();
}
}
/**
* 模拟表现层
*/
public class Client {
public static void main(String[] args) {
new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
System.out.println("Spring IOC容器创建好了");
}
}
在Spring中,bean作用域用于确定bean实例应该从哪种类型的Spring容器中返回给调用者。
目前Spring Bean的作用域或者说范围主要有五种:
作用域 | 说明 |
---|---|
singleton | 默认值,Bean以单例方式存在spring IoC容器 |
prototype | 每次从容器中调用Bean时都返回一个新的实例,相当于执行newInstance() |
request | WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 request 域中 |
session | WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 session 域中 |
application | WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 ServletContext 域中 |
可以通过 <bean>
标签的scope
属性控制bean的作用范围,其配置方式如下所示:
<bean id="..." class="..." scope="singleton"/>
需要根据场景决定对象的单例、多例模式
单例:Service、DAO、SqlSessionFactory(或者是所有的工厂)
多例:Connection、SqlSession
案例
<bean id="userService" class="com.by.service.UserServiceImpl"
scope="singleton" init-method="init" destroy-method="destroy">
/**
* 业务层实现类
*/
public class UserServiceImpl implements UserService {
private UserDao userDao;
public UserServiceImpl() {
System.out.println("调用构造方法创建bean...");
}
public void setUserDao(UserDao userDao) {
System.out.println("调用set方法注入值...");
this.userDao = userDao;
}
public void init(){
System.out.println("调用init方法初始化bean...");
}
public void destroy(){
System.out.println("调用destroy方法销毁bean...");
}
public void addUser(){
userDao.addUser();
}
}
/** * 模拟表现层 */public class Client { public static void main(String[] args) { ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml"); //关闭容器 ac.close(); }}
生命周期:
[容器启动]—>构造方法(实例化)—>set方法(注入)—>init方法(初始化)—>[容器关闭]—>destroy方法(销毁)
案例
<bean id="userService" class="com.by.service.UserServiceImpl"
scope="prototype" init-method="init" destroy-method="destroy">
/**
* 业务层实现类
*/
public class UserServiceImpl implements UserService {
private UserDao userDao;
public UserServiceImpl() {
System.out.println("调用构造方法创建bean...");
}
public void setUserDao(UserDao userDao) {
System.out.println("调用set方法注入值...");
this.userDao = userDao;
}
public void init(){
System.out.println("调用init方法初始化bean...");
}
public void destroy(){
System.out.println("调用destroy方法销毁bean...");
}
public void addUser(){
userDao.addUser();
}
}
/**
* 模拟表现层
*/
public class Client {
public static void main(String[] args) {
ClassPathXmlApplicationContext ac =
new ClassPathXmlApplicationContext("applicationContext.xml");
//使用对象
ac.getBean("userService");
}
}
生命周期:
[使用对象]---->构造方法(实例化)—>set方法(注入)—>init方法(初始化)—>[JVM垃圾回收]—>destroy方法(销毁)