Spring(一)
一、引言
1.1 原生web开发中存在哪些问题?
- 传统Web开发存在硬编码所造成的过度程序耦合(例如:Service中作为属性Dao对象)。
- 部分Java EE API较为复杂,使用效率低(例如:JDBC开发步骤)。
- 侵入性强,移植性差(例如:DAO实现的更换,从Connection到SqlSession)。
二、Spring框架
2.1 概念
- Spring是一个项目管理框架,同时也是一套Java EE解决方案。
- Spring是众多优秀设计模式的组合(工厂、单例、代理、适配器、包装器、观察者、模板、策略)。
- Spring并未替代现有框架产品,而是将众多框架进行有机整合,简化企业级开发,俗称"胶水框架"。
2.2 访问与下载
官方网站:https://spring.io/
下载地址:http://repo.spring.io/release/org/springframework/spring/
三、Spring架构组成
Spring架构由诸多模块组成,可分类为
-
核心技术:依赖注入,事件,资源,i18n,验证,数据绑定,类型转换,SpEL,AOP。
-
测试:模拟对象,TestContext框架,Spring MVC测试,WebTestClient。
-
数据访问:事务,DAO支持,JDBC,ORM,封送XML。
-
Spring MVC和 Spring WebFlux Web框架。
-
集成:远程处理,JMS,JCA,JMX,电子邮件,任务,调度,缓存。
-
语言:Kotlin,Groovy,动态语言。
Spring架构组成
四、自定义工厂
4.1 配置文件
userDAO=com.qf.dao.UserDAOImpl
userService=com.qf.service.UserServiceImpl
4.2 工厂类
/**
* 自定义工厂
*/
public class MyFactory {
private Properties properties = new Properties();
public MyFactory(){}
public MyFactory(String config) throws IOException {
// 加载配置文件
properties.load(MyFactory.class.getResourceAsStream(config));
}
// 获取对象
public Object getBean(String beanName) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
// 获得类路径
String classPath = properties.getProperty(beanName);
if(classPath!=null){
Class claz = null;
// 反射:加载类对象
claz = Class.forName(classPath);
// 反射:获得对象
return claz.newInstance();
}
return null;
}
}
五、Spring环境搭建
5.1 pom.xml中引入Spring常用依赖
<?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.qf</groupId>
<artifactId>hello-spring</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<!-- Spring常用依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
</dependencies>
</project>
5.2 创建Spring配置文件
命名无限制,约定俗成命名有:spring-context.xml、applicationContext.xml、beans.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">
</beans>
六、Spring工厂编码
定义目标Bean类型
public class MyClass{
public void show(){
System.out.println("HelloWorld");
}
}
spring-context.xml中的< beans >内部配置bean标签
<!-- 配置实例(id:“唯一标识” class="需要被创建的目标对象全限定名") -->
<bean id="mc" class="com.qf.spring.part1.factory.MyClass" />
调用Spring工厂API(ApplicationContext接口)
public class TestFactory{
/**
* 程序中的对象都交由Spring的ApplicationContext工厂进行创建。
*/
public static void main(String[] args){
//1. 读取配置文件中所需创建的bean对象,并获得工厂对象
ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-context.xml");
//2. 通过id获取bean对象
MyClass mc = (MyClass) ctx.getBean("mc");
//3. 使用对象
mc.show();
}
}
七、依赖与配置文件详解
Spring框架包含多个模块,每个模块各司其职,可结合需求引入相关依赖Jar包实现功能。
7.1 Spring依赖关系
Spring常用功能的Jar包依赖关系
- 注意:Jar包彼此存在依赖,只需引入最外层Jar即可由Maven自动将相关依赖Jar引入到项目中。
7.2 schema
配置文件中的顶级标签中包含了语义化标签的相关信息
- xmlns:语义化标签所在的命名空间。
- xmlns:xsi:XMLSchema-instance 标签遵循Schema标签标准。
- xsi:schemaLocation:xsd文件位置,用以描述标签语义、属性、取值范围等。
八、IoC(Inversion of Control )控制反转【重点】
Inverse Of Controll:控制反转
反转了依赖关系的满足方式,由之前的自己创建依赖对象,变为由工厂推送。(变主动为被动,即反转)
解决了具有依赖关系的组件之间的强耦合,使得项目形态更加稳健
8.1 项目中强耦合问题
public class UserDAOImpl implements UserDAO{....}
public class UserServiceImpl implements UserService {
// !!!强耦合了UserDAOImpl!!!,使得UserServiceImpl变得不稳健!!
private UserDAO userDAO= new UserDAOImpl();
@Override
public User queryUser() {
return userDAO.queryUser();
}
....
}
8.2 解决方案
// 不引用任何一个具体的组件(实现类),在需要其他组件的位置预留存取值入口(set/get)
public class UserServiceImpl implements UserService {
// !!!不再耦合任何DAO实现!!!,消除不稳健因素!!
private UserDAO userDAO;
// 为userDAO定义set/get,允许userDAO属性接收spring赋值
//Getters And Setters
@Override
public User queryUser() {
return userDAO.queryUser();
}
....
}
<bean id="userDAO" class="com.qf.spring.part1.injection.UserDaoImpl"></bean>
<!-- UserServiceImpl组件 -->
<bean id="userService" class="com.qf.spring.part1.injection.UserServiceImpl">
<!-- 由spring为userDAO属性赋值,值为id="userDAO"的bean -->
<property name="userDAO" ref="userDAO"/>
</bean>
此时,如果需要更换其他UserDAO实现类,则UserServiceImpl不用任何改动!
则此时的UserServiceImpl组件变得更加稳健!
九、DI(Dependency Injection)依赖注入【重点】
9.1 概念
在Spring创建对象的同时,为其属性赋值,称之为依赖注入。
9.2 Set注入
创建对象时,Spring工厂会通过Set方法为对象的属性赋值。
9.2.1 定义目标Bean类型
public class User {
private Integer id;
private String password;
private String sex;
private Integer age;
private Date bornDate;
private String[] hobbys;
private Set<String> phones;
private List<String> names;
private Map<String,String> countries;
private Properties files;
//Getters And Setters
}
9.2.2 基本类型 + 字符串类型 + 日期类型
<bean id="u1" class="com.qf.spring.part1.injection.User">
<!--base field-->
<property name="id" value="1001" />
<property name="password" value="123456" />
<property name="sex" value="male" />
<property name="age" value="20" />
<property name="bornDate" value="1990/1/1" /><!--注意格式"/"-->
</bean>
9.2.3 容器类型
<bean id="u1" class="com.qf.spring.part1.injection.User">
<!--Array-->
<property name="hobbys">
<array>
<value>Run</value>
<value>Swim</value>
<value>Climb</value>
</array>
</property>
<!--Set-->
<property name="phones">
<set>
<value>13777777777</value>
<value>13888888888</value>
<value>13999999999</value>
</set>
</property>
<!--List-->
<property name="names">
<list>
<value>tom</value>
<value>jack</value>
<value>marry</value>
</list>
</property>
<!--Map-->
<property name="countries">
<map>
<entry key="CN" value="China" />
<entry key="US" value="America" />
<entry key="KR" value="Korea" />
</map>
</property>
<!--Properties-->
<property name="files">
<props>
<prop key="first">One</prop>
<prop key="second">Two</prop>
<prop key="third">Three</prop>
</props>
</property>
</bean>
9.2.4 自建类型
<!--次要bean,被作为属性-->
<bean id="addr" class="com.qf.spring.part1.injection.Address">
<property name="position" value="北京市海淀区" />
<property name="zipCode" value="100001" />
</bean>
<!--主要bean,操作的主体-->
<bean id="u2" class="com.qf.spring.part1.injection.User">
<property name="address" ref="addr" /><!--address属性引用addr对象-->
</bean>
<!--次要bean,被作为属性-->
<bean id="userDao" class="com.qf.spring.part1.injection.UserDaoImpl" />
<!--主要bean,操作的主体-->
<bean id="userService" class="com.qf.spring.part1.injection.UserServiceImpl">
<property name="ud" ref="userDao" /><!--ud属性引用userDao对象-->
</bean>
9.3 构造注入【了解】
创建对象时,Spring工厂会通过构造方法为对象的属性赋值。
9.3.1 定义目标Bean类型
public class Student {
private Integer id;
private String name;
private String sex;
private Integer age;
//Constructors
public Student(Integer id , String name , String sex , Integer age){
this.id = id;
this.name = name;
this.sex = sex;
this.age = age;
}
//Getters And Setters
}
9.3.2 注入
<!--构造注入-->
<bean id="u3" class="com.qf.zcg.spring.day1.t2.ioc.Student">
<constructor-arg name="id" value="1234" /> <!-- 除标签名称由变化,其他均和Set注入一致 -->
<constructor-arg name="name" value="tom" />
<constructor-arg name="age" value="20" />
<constructor-arg name="sex" value="male" />
</bean>
9.4 自动注入【了解】
不用在配置中 指定为哪个属性赋值,及赋什么值.
由spring自动根据某个 “原则” ,在工厂中查找一个bean,为属性注入属性值
public class UserServiceImpl implements UserService {
private UserDAO userDAO;
//Getters And Setters
....
}
<bean id="userDao" class="com.qf.spring.part1.injection.UserDaoImpl" />
<!-- 为UserServiceImpl中的属性基于类型自动注入值 -->
<bean id="userService" class="com.qf.spring.part1.injection.UserServiceImpl" autowire="byType"></bean>
<bean id="userDao" class="com.qf.spring.part1.injection.UserDaoImpl" />
<!-- 为UserServiceImpl中的属性基于类型自动注入值 -->
<bean id="userService" class="com.qf.spring.part1.injection.UserServiceImpl" autowire="byName"></bean>
十、Bean细节
10.1 控制简单对象的单例、多例模式
配置< bean scope=“singleton | prototype” />
<!--
singleton(默认):每次调用工厂,得到的都是同一个对象。
prototype:每次调用工厂,都会创建新的对象。
-->
<bean id="mc" class="com.qf.zcg.spring.day1.t1.basic.MyClass" scope="singleton" />
- 注意:需要根据场景决定对象的单例、多例模式。
- 可以共用:Service、DAO、SqlSessionFactory(或者是所有的工厂)。
- 不可共用:Connection、SqlSession、Action、ShoppingCart。
10.2 FactoryBean创建复杂对象【了解】
作用:让Spring可以创建复杂对象、或者无法直接通过反射创建的对象。
FactoryBean解决复杂对象创建
10.2.1 实现FactoryBean接口
接口方法描述
- 注意:isSingleton方法的返回值,需根据所创建对象的特点决定返回true/false。
- 例如:Connection 不应该被多个用户共享,返回false。
- 例如:SqlSessionFactory 重量级资源,不该过多创建,返回true。
10.2.2 配置spring-context.xml
配置与获取方式
10.2.3 特例
获取FactoryBean接口的实现类对象,而非getObject()所生产的对象。
十一、Spring工厂特性
11.1饿汉式创建优势
工厂创建之后,会将Spring配置文件中的所有对象都创建完成(饿汉式)。
提高程序运行效率。避免多次IO,减少对象创建时间。(概念接近连接池,一次性创建好,使用时直接获取)
11.2 生命周期方法
- 自定义初始化方法:添加“init-method”属性,Spring则会在创建对象之后,调用此方法。
- 自定义销毁方法:添加“destroy-method”属性,Spring则会在销毁对象之前,调用此方法。
- 销毁:工厂的close()方法被调用之后,Spring会毁掉所有已创建的单例对象。
- 分类:Singleton对象由Spring容器销毁、Prototype对象由JVM销毁。
11.3 生命周期注解
初始化注解、销毁注解
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@PostConstructor //初始化
public void init(){
System.out.println("init method executed");
}
@PreDestroy //销毁
public void destroy(){
System.out.println("destroy method executed");
}
11.4 生命周期阶段
单例bean:singleton
随工厂启动创建 ==》 构造方法 ==》 set方法(注入值) ==》 init(初始化) ==》 构建完成 ==》随工厂关闭销毁
多例bean:prototype
被使用时创建 ==》 构造方法 ==》 set方法(注入值) ==》 init(初始化) ==》 构建完成 ==》JVM垃圾回收销毁