一、Spring概述
1.1 web项目开发中的耦合度问题
在Servlet中需要调用service中的方法,则需要在Servlet类中通过new关键字创建Service的实例
public interface ProductService{
public List<Product> listProducts();
}
public class ProductServiceImpl1 implements ProductService{
public List<Product> listProducts(){
//查询热销商品
}
}
public class ProductServiceImpl2 implements ProductService{
public List<Product> listProducts(){
//查询好评商品
}
}
public class ProductListServlet extends HttpServlet{
//在servlet中使用new关键字创建ProductServiceImpl1对象,增加了servlet和service的耦合度
private ProductService productService = new ProductServiceImpl1();
protected void doGet(HttpServletRequest request,HttpServletResponse response){
doPost(request,response);
}
protected void doPost(HttpServletRequest request,HttpServletResponse response){
productService.listProducts();
}
}
在service实现类中需要调用DAO中的方法,也需要在servcie实现类通过new关键字创建DAO实现类对象
如果使用new关键字创建对象:
失去了面向接口编程的灵活性
代码的侵入性增强(增加了耦合度)、降低了代码的灵活性
1.2 面向接口编程
解决方案:在Servlet中定义Service接口的对象变量,不使用new关键字创建实现类对象,在servlet的实例化的时候,通过反射动态的给Service对象变量赋值。
如何实现:Spring可以做到!!!
1.3 Spring介绍
Spring是一个`轻量级的控制反转和面向切面的容器`框架,用来解决企业项目开发的复杂度问题—解耦
- 轻量级:体积小,对代码没有侵入性
- 控制反转:IoC(Inverse of Control),把创建对象的工作交由Spring完成,Spring在创建对象的时候同时可以完成对象属性赋值(DI)
- 面向切面:AOP(Aspect Oriented Programming)面向切面编程,可以在不改变原有业务逻辑的情况下实现对业务的增强
- 容器:实例的容器,管理创建的对象
1.4 Spring架构
官网 https://spring.io/
Spring架构图
1.4.1 Core Container
Spring容器组件,用于完成实例的创建和管理
- core
- beans 实例管理
- context 容器上下文
1.4.2 AOP、Aspects
Spring AOP组件,实现面向切面编程
- aop
- aspects
1.4.3 web
Spring web组件实际指的是SpringMVC框架,实现web项目的MVC控制
- web (Spring对web项目的支持)
- webmvc (SpringMVC组件)
1.4.4 Data Access
Spring数据访问组件,也是一个基于JDBC封装的持久层框架(即使没有mybatis,Spring也可以完成持久化操作)
1.4.5 Test
Spring的单元测试组件,提供了Spring环境下的单元测试支持
二、Spring IoC — 基于XML
Spring IoC 容器组件,可以完成对象的创建、对象属性赋值、对象管理
2.1 Spring框架部署(IoC)
- core
- beans
- aop
- expression
- context
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.13.RELEASE</version>
</dependency>
2.1.3 创建Spring配置文件
通过配置文件"告诉"Spring容器创建什么对象,给对象属性赋什么值
在resources目录下创建名为`appicationContext.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">
<!-- 对于一个xml文件如果作为框架的配置文件,需要遵守框架的配置规则 -->
<!-- 通常一个框架为了让开发者能够正确的配置,都会提供xml的规范文件(dtd\xsd) -->
</beans>
2.2 SpringIoC使用
使用 SpringIoC组件创建并管理对象
2.2.1 创建一个实体类
public class Student {
private String stuNum;
private String stuName;
private String stuGender;
private int stuAge;
private Date enterenceTime; //入学日期
}
2.2.2 在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">
<!--通过bean将实体类配置给Spring进行管理,id表示实体类的唯一表示-->
<bean id="stu" class="com.qfedu.ioc.bean.Student">
<property name="stuNum" value="10002"/>
<property name="stuName" value="李斯"/>
<property name="stuGender" value="女"/>
<property name="stuAge" value="20"/>
</bean>
</beans>
2.2.3 初始化Spring对象工厂,获取对象
ClassPathXMLApplicationContext
//1.初始化Spring容器,加载Spring配置文件
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//2.通过Spring容器获取Student对象
Student student2 = (Student) context.getBean("stu");
2.3 IoC和DI
- IoC (Inverse of Control) 控制反转,通过Spring对象工厂完成对象的创建
- DI (Dependency Injection)依赖注入,在Spring完成对象创建的同时依赖Spring容器完成对象属性的赋值
2.3.1 IoC
当我们需要通过Spring对象工厂创建某个类的对象时候,需要将这个交给Spring管理——通过bean标签配置
<!--通过bean将实体类配置给Spring进行管理,id表示实体类的唯一表示-->
<bean id="stu" class="com.qfedu.ioc.bean.Student"></bean>
<bean id="book" class="com.qfedu.ioc.bean.Book"></bean>
2.3.2 DI
通过Spring容器给创建的对象属性赋值
<bean id="clazz" class="com.qfedu.ioc.bean.Clazz"></bean>
<!--通过bean将实体类配置给Spring进行管理,id表示实体类的唯一表示-->
<bean id="stu" class="com.qfedu.ioc.bean.Student" autowire="byName">
<property name="stuNum" value="10001"/>
</bean>
2.4 DI依赖注入
2.4.1 依赖注入三种方式
Spring容器加载配置文件之后,通过`反射`创建类的对象,并给属性赋值;
Spring容器通过反射实现属性注入有三种方式:
set方法注入
构造器注入
接口注入(不常用)
2.4.2 set方法注入
在bean标签中通过配置property标签给属性属性赋值,实际上就是通过反射调用set方法完成属性的注入
简单类型及字符串
直接通过property标签的value属性赋值
<!--通过bean将实体类配置给Spring进行管理,id表示实体类的唯一表示-->
<bean id="stu" class="com.qfedu.ioc.bean.Student" autowire="byName">
<!-- 简单类型 -->
<property name="stuNum" value="10001"/>
<property name="stuAge" value="12"/>
<!-- 字符串类型-->
<property name="weight" value="62.3"/>
</bean>
日期类型
方式1:在property标签中通过ref引用Spring容器中的一个对
<bean id="date" class="java.util.Date"></bean>
<bean id="stu" class="com.qfedu.ioc.bean.Student" >
<!-- 日期类型-->
<property name="enterenceTime" ref="date"/>
</bean>
方式2:在property标签中添加子标签bean来指定对象
<bean id="stu" class="com.qfedu.ioc.bean.Student" >
<!-- 日期类型-->
<property name="enterenceTime">
<bean class="java.util.Date"/>
</property>
</bean>
自定义类对象属性
方式1:
<bean id="cla" class="com.qfedu.ioc.bean.Clazz">
<property name="classId" value="2010"/>
<property name="className" value="Java2010"/>
</bean>
<bean id="stu" class="com.qfedu.ioc.bean.Student">
<!-- 自定义对象类型-->
<property name="clazz" ref="cla"/>
</bean>
方式2
<bean id="stu" class="com.qfedu.ioc.bean.Student">
<!-- 自定义对象类型-->
<property name="clazz">
<bean class="com.qfedu.ioc.bean.Clazz">
<property name="classId" value="2010"/>
<property name="className" value="Java2010"/>
</bean>
</property>
</bean>
集合类型
List
List<String> List中的元素是字符串或者简单类型的封装类
<property name="hobbies" value="旅游,电影"/>
或
<property name="hobbies" >
<list>
<value>旅游</value>
<value>电影</value>
<value>Java</value>
</list>
</property>
List<Object> List中的元素是对象类型
<property name="hobbies" >
<list>
<bean class="com.qfedu.ioc.bean.Book"/>
<bean class="com.qfedu.ioc.bean.Book"/>
<bean class="com.qfedu.ioc.bean.Book"/>
<bean class="com.qfedu.ioc.bean.Book"/>
</list>
</property>
<property name="hobbies" >
<list>
<ref bean="book"></ref> <!--引用容器中的bean-->
<ref bean="book"></ref>
</list>
</property>
Set
<property name="sets">
<set>
<!--和list元素注入方式相同-->
<bean class="com.qfedu.ioc.bean.Book"/>
<bean class="com.qfedu.ioc.bean.Book"/>
<bean class="com.qfedu.ioc.bean.Book"/>
<bean class="com.qfedu.ioc.bean.Book"/>
</set>
</property>
Map
<property name="maps">
<map>
<entry>
<key>
<value>k1</value>
</key>
<value>123</value>
</entry>
<entry>
<key>
<value>k2</value>
</key>
<value>456</value>
</entry>
</map>
</property>
Properties
<property name="properties">
<props>
<prop key="k1">aaa</prop>
<prop key="k2">bbb</prop>
</props>
</property>
2.4.3 构造器注入
简单类型、字符串、对象
public class Student {
private String stuNum;
private String stuName;
private String stuGender;
private int stuAge;
private double weight;
private Date enterenceTime; //入学日期
private Clazz clazz;
public Student(String stuNum, String stuName, String stuGender, int stuAge, double weight, Date enterenceTime, Clazz clazz) {
this.stuNum = stuNum;
this.stuName = stuName;
this.stuGender = stuGender;
this.stuAge = stuAge;
this.weight = weight;
this.enterenceTime = enterenceTime;
this.clazz = clazz;
}
}
<bean id="date" class="java.util.Date"></bean>
<bean id="stu" class="com.qfedu.ioc.bean.Student">
<constructor-arg index="0" value="10001"/> <!--字符串类型-->
<constructor-arg index="2" value="女"/>
<constructor-arg index="1" value="张三"/>
<constructor-arg index="3" value="21"/> <!--简单类型-->
<constructor-arg index="4" value="62.5"/>
<constructor-arg index="5" ref="date"/> <!--对象类型-->
<constructor-arg index="6"> <!--对象类型-->
<bean class="com.qfedu.ioc.bean.Clazz"></bean>
</constructor-arg>
</bean>
集合类型属性
public class Student{
private List<String> hobbies;
private Set<String> sets;
private Map<String,Object> maps;
private Properties properties;
public Student(List<String> hobbies, Set<String> sets, Map<String, Object> maps, Properties properties) {
this.hobbies = hobbies;
this.sets = sets;
this.maps = maps;
this.properties = properties;
}
}
<bean id="stu1" class="com.qfedu.ioc.bean.Student">
<constructor-arg index="0">
<list>
<value>11</value>
<value>22</value>
<value>33</value>
</list>
</constructor-arg>
<constructor-arg index="1">
<set>
<value>aa</value>
<value>bb</value>
<value>cc</value>
</set>
</constructor-arg>
<constructor-arg index="2">
<map>
<entry>
<key><value>key1</value></key>
<value>value1</value>
</entry>
<entry>
<key><value>key2</value></key>
<value>value2</value>
</entry>
</map>
</constructor-arg>
<constructor-arg index="3">
<props>
<prop key="k1">v1</prop>
<prop key="k2">v2</prop>
</props>
</constructor-arg>
</bean>
2.5 Bean的作用域
在bean标签可以通过scope属性指定对象的的作用域
scope="singleton" 表示当前bean是单例模式(默认饿汉模式,Spring容器初始化阶段就会完成此对象的创建;当在bean标签中设置 lazy-init="true"变为懒汉模式)
scope="prototype" 表示当前bean为非单例模式,每次通过Spring容器获取此bean的对象时都会创建一个新的对象
单例
<bean id="book" class="com.qfedu.ioc.bean.Book" scope="singleton" lazy-init="true"></bean>
多例
<bean id="book" class="com.qfedu.ioc.bean.Book" scope="prototype"></bean>
2.6 Bean的声明周期方法
在bean标签中通过init-method属性指定当前bean的初始化方法,初始化方法在构造器执行之后执行,通过destroy-method属性指定当前bean的销毁方法,销毁方法在对象销毁之前执行
Bean类
public class Book {
private int bookId;
private String bookName;
//初始化方法:在创建当前类对象时调用的方法,进行一些资源准备工作
public void init(){
System.out.println("-------init");
}
//销毁方法:在Spring容器销毁对象时调用此方法,进行一些资源回收性的操作
public void destory(){
System.out.println("-------destory");
}
}
Spring配置文件
<bean id="book" class="com.qfedu.ioc.bean.Book" scope="prototype"init-method="init" destroy-method="destory" ></bean>
2.7 自动装配
自动装配:Spring在实例化当前bean的时候从Spring容器中找到匹配的实例赋值给当前bean的属性
自动装配策略有两种:
byName 根据当前Bean的属性名在Spring容器中寻找匹配的对象 ,如果根据name找打了bean但是类型不匹配则抛出异常
byType 根据当前Bean的属性类型在Spring容器中寻找匹配的对象,如果根据类型找到了多个bean也会抛出异常
byName
<bean id="clazz" class="com.qfedu.ioc.bean.Clazz"></bean>
<bean id="stu2" class="com.qfedu.ioc.bean.Student" autowire="byName"></bean>
byType
<bean id="clazz2" class="com.qfedu.ioc.bean.Clazz"></bean>
<bean id="stu2" class="com.qfedu.ioc.bean.Student" autowire="byType"></bean>
2.8 SpringIoC 工作原理
三、Spring IoC — 基于注解
SpringIoc的使用,需要我们通过XML将类声明给Spring容器进行管理,从而通过Spring工厂完成对象的创建及属性值的注入;
Spring除了提供基于XML的配置方式,同时提供了基于注解的配置:直接在实体类中添加注解声明给Spring容器管理,以简化开发步骤。
3.1 Spring框架部署
3.1.1 创建Maven项目
略
3.2.2 添加SpringIoC依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.13.RELEASE</version>
</dependency>
3.2.3 创建Spring配置文件
因为Spring容器初始化时,只会加载applicationContext.xml文件,那么我们在实体类中添加的注解就不会被Spring扫描,所以我们需要`在applicationContext.xml声明Spring的扫描范围`,以达到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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 声明使用注解配置 -->
<context:annotation-config/>
<!-- 声明Spring工厂注解的扫描范围 -->
<context:component-scan base-package="com.qfedu.beans"/>
</beans>
3.2 IoC常用注解
3.2.1 @Component
类注解,声明此类被Spring容器进行管理,相当于bean标签的作用
@Component(value="stu") value属性用于指定当前bean的id,相当于bean标签的id属性;value属性也可以省略,如果省略当前类的id默认为类名首字母改小写
除了@Component之外 @Service、@Controller、@Repository这三个注解也可以将类声明给Spring管理,他们主要是语义上的区别
@Controller 注解主要声明将控制器类配置给Spring管理,例如Servlet
@Service 注解主要声明业务处理类配置Spring管理,Service接口的实现类
@Repository 直接主要声明持久化类配置给Spring管理,DAO接口
@Component 除了控制器、servcie和DAO之外的类一律使用此注解声明
3.2.2 @Scope
类注解,用于声明当前类单例模式还是 非单例模式,相当于bean标签的scope属性
@Scope("prototype") 表示声明当前类为非单例模式(默认单例模式)
3.2.3 @Lazy
类注解,用于声明一个单例模式的Bean是否为懒汉模式
@Lazy(true) 表示声明为懒汉模式,默认为饿汉模式
3.2.4 @PostConstruct
方法注解,声明一个方法为当前类的初始化方法(在构造器之后执行),相当于bean标签的init-method属性
3.2.5 @PreDestroy
方法注解,声明一个方法为当前类的销毁方法(在对象从容器中释放之前执行),相当于bean标签的destory-method属性
3.2.6 @Autowired
属性注解、方法注解(set方法),声明当前属性自动装配,默认byType
@Autowired(required = false) 通过requried属性设置当前自动装配是否为必须(默认必须——如果没有找到类型与属性类型匹配的bean则抛出异常)
byType
ref引用
@Autowired
public void setClazz(@Qualifier("c2") Clazz clazz) {
this.clazz = clazz;
}
3.2.7 @Resource
属性注解,也用于声明属性自动装配
默认装配方式为byName,如果根据byName没有找到对应的bean,则继续根据byType寻找对应的bean,根据byType如果依然没有找到Bean或者找到不止一个类型匹配的bean,则抛出异常。