1.Spring概述
1.1.Spring介绍
Spring是轻量级Java EE应用开源框架(官网: http://spring.io/ ),它由Rod Johnson创为了解决企业级编程开发的复杂性而创建
1、Spring是什么?
Spring是轻量级的JAVAEE应用开源框架
2、Spring的好处?
IOC:控制反转
AOP:面向切面
粘合剂:整合其他技术和框架
1.2.简化应用开发体现在哪些方面?
-
IOC
解决传统Web开发中硬编码所造成的程序耦合
-
AOP
实现在运行期间不修改源代码对程序进行增强
-
粘合剂
Spring是一个超级粘合平台,除了自己提供功能外,还提供整合其他技术和框架的能力
1.3.Spring的体系结构
Spring 框架根据功能的不同大体可分为 Data Access/Integration(数据访问与集成)
、Web
、AOP、Aspects、Instrumentation(检测)、Messaging(消息处理)
、Core Container(核心容器)
和 Test
。
- Core Container: 框架的最基础部分,提供控制反转和依赖注入特性
- AOP :提供了面向切面的编程的实现
- Data Access/Integration:简化了持久层的操作
- Web:提供了Spring MVC Web 框架实现以及与Servlet、WebSocket的集成
- Test:方便程序的测试
2.Spring IOC
2.1.程序的耦合
创建工程:
- 耦合:耦合指的就是对象之间的依赖关系。对象之间的耦合越高,维护成本越高。
- 产生耦合的原因:硬编码
- 案例:没有引入IOC容器时系统的Web层、业务层、持久层存在耦合
持久层
package com.by.dao;
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层的实现类,编译将不能通过
- 2、IOC
控制:控制对象的创建
IOC (Inverse of Control)即控制反转:正传是自己创建对象;反转是由工厂创建依赖对象 - 3、工厂模式的IOC解决程序耦合
1.把dao和service配置到beans.properties
2.读取beans.properties
3.通过反射创建对象,并装到容器中
4.在使用时,直接从容器中获得
2.2 IOC解决程序耦合
2.2.1IOC(工厂模式)解耦
案例一
package com.by.factory;
import com.by.dao.UserDao;
import com.by.dao.UserDaoImpl;
import com.by.service.UserService;
import com.by.service.UserServiceImpl;
public class BeanFactory_v1 {
public static UserDao getUserDao(){
return new UserDaoImpl();
}
public static UserService getUserService(){
return new UserServiceImpl();
}
}
- 业务实现层
package com.by.service;
import com.by.dao.UserDao;
import com.by.dao.UserDaoImpl;
import com.by.factory.BeanFactory_v1;
import com.by.factory.BeanFactory_v2;
import com.by.factory.BeanFactory_v3;
public class UserServiceImpl implements UserService{
//高耦合:对象之间的依赖关系
private UserDao userDao = new UserDaoImpl();
private UserDao userDao = BeanFactory_v1.getUserDao();
@Override
public void addUser() {
userDao.addUser();
}
}
问题:我们在开发中会有很多个service和dao,此时工厂类就要添加无数个方法。
案例二
- 配置文件beans.properties
#1、配置要使用的dao和service
UserDao=com.by.dao.UserDaoImpl
UserService=com.by.service.UserServiceImpl
- 工厂 BeanFactory_v2
package com.by.factory;
import com.by.dao.UserDao;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
/*
问题:
1.每次都会创建新的对象
2.获得对象时才读取配置文件
*/
public class BeanFactory_v2 {
public static Object getBean(String propertiesKey) {
try {
//不能使用:项目发布后没有是src目录
//InputStream inputStream = new FileInputStream("src\\main\\resources\\Bean.properties");
//getClassLoader 负责加载classes下的资源
InputStream inputStream = BeanFactory_v2.class.getClassLoader().getResourceAsStream("Bean.properties");
//Properties:继承了Hashtable,作用是读取 *.properties文件
Properties properties = new Properties();
properties.load(inputStream);
/*************** 根据配置文件的values值创建反射对象********************/
//通过反射 创建对象
return Class.forName(properties.getProperty(propertiesKey)).newInstance();
} catch (IOException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException {
UserDao userDao = (UserDao) BeanFactory_v2.getBean("userDao");
userDao.addUser();
}
}
- 业务实现层
package com.by.service;
import com.by.dao.UserDao;
import com.by.dao.UserDaoImpl;
import com.by.factory.BeanFactory_v1;
import com.by.factory.BeanFactory_v2;
import com.by.factory.BeanFactory_v3;
public class UserServiceImpl implements UserService{
//高耦合:对象之间的依赖关系
//private UserDao userDao = (UserDao) BeanFactory_v2.getBean("userDao");
@Override
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);
}
}
}
问题:
- 1.每次都会创建对象
- 2.程序运行时才会创建对象
案例三
- 工厂
package com.by.factory;
import com.by.dao.UserDao;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
/*
问题:
1.每次都会创建新的对象
2.获得对象时才读取配置文件
*/
public class BeanFactory_v3 {
private static Map<String,Object> iocMap = new HashMap<>();
static {
try {
//解决 2.获得对象时才读取配置文件
//读取配置文件
InputStream inputStream = BeanFactory_v3.class.getClassLoader().getResourceAsStream("Bean.properties");
Properties properties = new Properties();
properties.load(inputStream);
//解决 1.每次都会创建新的对象 在静态代码块中创建对象并装到Map中,
// 每次调用getBean()都直接从Map中获取对象而无需在重新创建
Set<Object> keySet = properties.keySet();
for (Object key : keySet) {
String propertyValue = properties.getProperty((String) key);
iocMap.put((String) key,Class.forName(propertyValue).newInstance());
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static Object getBean(String propertiesKey) {
return iocMap.get(propertiesKey);
}
public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException {
UserDao userDao = (UserDao) BeanFactory_v3.getBean("userDao");
userDao.addUser();
}
}
- 业务层
package com.by.service;
import com.by.dao.UserDao;
import com.by.dao.UserDaoImpl;
import com.by.factory.BeanFactory_v1;
import com.by.factory.BeanFactory_v2;
import com.by.factory.BeanFactory_v3;
public class UserServiceImpl implements UserService{
//高耦合:对象之间的依赖关系
private UserDao userDao = (UserDao) BeanFactory_v3.getBean("userDao");
@Override
public void addUser() {
userDao.addUser();
}
}
- 模拟表现层
package com.by.web;
import com.by.factory.BeanFactory_v1;
import com.by.factory.BeanFactory_v2;
import com.by.factory.BeanFactory_v3;
import com.by.service.UserService;
import com.by.service.UserServiceImpl;
public class Client {
public static void main(String[] args) {
//高耦合:硬编码
UserService userService = (UserService) BeanFactory_v3.getBean("userService");
userService.addUser();
for (int i = 0; i < 5; i++) {
UserService userService1 = (UserService) BeanFactory_v3.getBean("userService");
System.out.println(userService1);
}
}
}
解决了每次都会创建对象以及获取对象时才读取配置文件的问题
2.2.2.spring的IOC解决程序耦合
创建工程
- 1.pom.xml
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.30</version>
</dependency>
</dependencies>
- 2.log4j.properties
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
- 3.applicationContext.xml
<?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来创建-->
<!--
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
- 测试
package com.by.web;
import com.by.dao.UserDao;
import com.by.service.UserService;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;
public class Client {
public static void main(String[] args) {
//使用ApplicationContext 接口就是在Spring中获取容器
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
//根据bean的id获取对象
UserDao userDao = ac.getBean("userDao", UserDao.class);
userDao.addUser();
UserService userService = ac.getBean("userService", UserService.class);
userService.addUser();
}
}
2.2.3DI
- 概述:DI(Dependency Injection)依赖注入,在Spring创建对象的同时,为其属性赋值,称之为依赖注入。
2.2.3.1构造器注入
- 顾名思义,就是使用类中的构造函数,给成员变量赋值。注意,赋值的操作不是我们自己做的,而是通过配置的方式,让spring框架来为我们注入。具体代码如下:
package com.by.service;
import com.by.dao.UserDao;
import com.by.service.UserService;
import com.by.dao.UserDaoImpl;
/**
* 业务层实现类
*/
public class UserServiceImpl implements UserService {
private UserDao userDao;
private String name;
public UserServiceImpl() {
System.out.println("UserServiceImpl对象创建了...");
}
public UserServiceImpl(UserDao userDao, String name) {
this.userDao = userDao;
this.name = name;
}
public void addUser(){
System.out.println(name+","+age);
userDao.addUser();
}
}
- applicationContext.xml
<?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来创建-->
<!--
2、把对象交给spring来创建
id:给对象在容器中提供一个唯一标识。用于获取对象
class:指定类的全限定类名。用于反射创建对象。默认情况下调用无参构造函数
-->
<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="666666~"></constructor-arg>
</bean>
</beans>
2.2.3.2set注入
- 顾名思义,就是在类中提供需要注入成员的set方法。具体代码如下:
package com.by.service;
import com.by.dao.UserDao;
import com.by.service.UserService;
import com.by.dao.UserDaoImpl;
/**
* 业务层实现类
*/
public class UserServiceImpl implements UserService {
private UserDao userDao;
private String name;
public UserServiceImpl() {
System.out.println("UserServiceImpl对象创建了...");
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void setName(String name) {
this.name = name;
}
public void addUser(){
System.out.println(userDao+"------------"+name);
userDao.addUser();
}
}
- applicationContext.xml
<?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来创建-->
<!--
2、把对象交给spring来创建
id:给对象在容器中提供一个唯一标识。用于获取对象
class:指定类的全限定类名。用于反射创建对象。默认情况下调用无参构造函数
-->
<bean id="userDao" class="com.by.dao.UserDaoImpl"></bean>
<bean id="userService" class="com.by.service.UserServiceImpl">
<!-- 构造器注入-->
<!--
要求:类中需要提供一个对应参数列表的set方法。
标签:property
==给谁赋值:==
index:指定参数在构造函数参数列表的索引位置
name:指定参数在构造函数中的名称
==赋什么值:==
value:它能赋的值是基本数据类型和String类型
ref:它能赋的值是其他bean类型,也就是说,必须得是在配置文件中配置过的bean
-->
<!-- SET注入-->
<property name="userDao" ref="userDao"></property>
<property name="name" value="浇给~~"></property>
</bean>
</beans>
2.2.3.3自动注入
- 不用在配置中 指定为哪个属性赋值,由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="byName":名字类型自动注入值-->
<bean id="userService" class="com.by.service.UserServiceImpl" autowire="byName">
</bean>
</beans>
2.3Spring中的工厂类
2.3.1ApplicationContext
- ApplicationContext的实现类,如下图:
- ClassPathXmlApplicationContext:加载类路径下 Spring 的配置文件
- FileSystemXmlApplicationContext:加载本地磁盘下 Spring 的配置文件
2.3.2BeanFactory
-
spring中工厂的类结构图
-
区别
-
ApplicationContext: 子接口 只要一读取配置就会创建对象
-
BeanFactory:顶级接口 是在 getBean 的时候才会创建对象。即使用对象的时候
-
ApplicationContext
/**
* 业务层实现类
*/
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
/**
* 业务层实现类
*/
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) {
BeanFactory factory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
factory.getBean("userService", UserService.class);
System.out.println("Spring IOC容器创建好了");
}
}
2.4bean的作用范围
2.4.1.概述
- 在Spring中,bean作用域用于确定bean实例应该从哪种类型的Spring容器中返回给调用者。
2.4.2.五种作用域
-
目前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
2.5bean的生命周期
2.5.1.单例bean
- 案例
<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方法(销毁)
2.5.2多例bean
<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方法(销毁)