学习目标
一、简介
1. 什么是Spring
- Spring 是分层的Java SE/EE应用 full-stack(服务端的全栈)轻量级(跟EJB比)开源框架,以IoC(Inversion of Control控制反转,目的是解耦)和AOP(面向切面编程,本质是动态代理,目的是增强)为内核
- Spring家族有很多的框架,涉及到所有层(web |service |dao)
- 今天学的Spring仅仅是Spring家族里面的其中一个框架 Spring Framework (IOC + AOP)
- 提供了:
- 表现层(web层): Spring MVC
- 业务层(service层) : Spring
- 持久层(Dao层):Spring JDBCTemplate, Spring Data JPA
- 能整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的Java EE企业应用开源框架。
2. Spring的发展历程
- 1997年,IBM提出了EJB的思想
- 1998年,SUN制定开发标准规范EJB1.0
- 1999年,EJB1.1发布
2001年,EJB2.0发布
2003年,EJB2.1发布
2006年,EJB3.0发布 - 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雏形)
- Expert One-to-One J2EE Design and Development(2002)
- 2017年9月份发布了spring的最新版本spring 5.0通用版(GA)
3. Spring的优势
-
方便解耦,简化开发(IoC思想,第1、2天)
通过Spring提供的IoC容器,可以将对象间的依赖关系交由Spring进行控制,避免硬编码所造成的过度程序耦合。用户也不必再为单例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。 创建对象更简单!
-
AOP编程的支持(第3天)
通过Spring的AOP功能,方便进行面向切面的编程,许多不容易用传统OOP实现的功能可以通过AOP轻松应付。
比如:要求面面项目里,每个方法被调用时,都输出日志到控制台“2020-03-20 11:20:31执行了xxx.xx方法”
-
声明式事务的支持(第3天) 在xml里面配置事务,在方法上或者类上 打一个注解即可。
可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务的管理,提高开发效率和质量。@Transactional
-
方便程序的测试(第1天)
可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可做的事情。
例如:Spring整合了Junit
-
方便集成各种优秀框架(SSM整合,SpringMVC第2天)
Spring可以降低各种框架的使用难度,提供了对各种优秀框架(Struts、Hibernate、Hessian、Quartz等)的直接支持。
-
降低JavaEE API的使用难度(第3天)
Spring对JavaEE API(如JDBC、JavaMail、远程调用等)进行了薄薄的封装层,使这些API的使用难度大为降低。
-
Java源码是经典学习范例
Spring的源代码设计精妙、结构清晰、匠心独用,处处体现着大师对Java设计模式灵活运用以及对Java技术的高深造诣。它的源代码无疑是Java技术的最佳实践的范例。
4. Spring的体系结构
二、工厂模式解耦(理解)
1. 耦合性问题
-
耦合性:程序之间(代码间)的依赖性。代码与代码之间的联系。
- 编译期依赖:**编译时必须提供依赖的类,否则编译不通过。 **
- UserService us = new UserService();
- UserService us02 = new UserServiceImpl();
- 运行期依赖:运行时必须提供依赖的类,否则不能运行。
-
接口和实现的写法:
UserService us02 = Class.forName("com.itheima.service.impl.UserServiceImpl").newInstance();
-
- 应当减少编译期依赖,使用运行期依赖
- 编译期依赖:**编译时必须提供依赖的类,否则编译不通过。 **
-
耦合性越强,维护成本(时间成本&精力成本)就越高
-
开发时要求:高内聚,低耦合
-
低耦合 : 耦合度很低,代码与代码之间耦合度很低。
-
高内聚: 把具有一样功能的代码,尽可能靠拢起来。 一个业务有很多的方法,这些方法要尽可能靠在一块。方便管理,维护。
类与类之间的内聚 模块化servlet
- 注册 ---- RegisterServlet
- 登录 --- LoginServlet
- 更新用户 --- UpdateUserServlet
- 用户 ----- UserServlet
方法与方法之间的内聚
每个方法里面都有乱码解决..两句话 ---- 过滤器。。
req.setCharacterEncoding();
resp.setContentType();
-
1.1 耦合性问题现象
- 在web开发中,服务端通常分为三层:web层、service层、dao层
- web层调用service层完成功能:需要new一个Service对象
- 以前的写法,直接new对象
- UserService userService = new UserService();
- 真正开发的时候是面向接口编程。
- UserService userService = new UserServiceImpl();
- 以前的写法,直接new对象
- service层调用dao层操作数据库:需要new一个dao对象
- 以前的写法, 直接new对象
- UserDao userDao = new UserDao();
- 真正开发的时候是面向接口编程。
- UerDao userDao = new UserDaoImpl();
- 以前的写法, 直接new对象
- web层调用service层完成功能:需要new一个Service对象
- 三层之间的耦合性比较强:存在编译期依赖
- service里写死了创建某一个dao对象:一旦dao对象换了,就需要修改service的源码
- web里写死了创建某一个service对象:一旦service对象换了,就需要修改web的源码
1.2 解耦的思路
-
可以使用反射技术,代替
new
创建对象,避免编译期依赖bject obj = Class.forName("全限定类名").newInstance();
-
再把全限定类名提取到配置文件中(xml或properties),读取配置文件信息来反射创建对象
//读取配置文件,得到要创建对象的全限定类名;再通过反射技术创建对象 String className = ...; Class clazz = Class.forName(className); Object obj = clazz.newInstance();
2. 使用工厂模式解耦
需求描述
- UserService调用UserDao,在每一层的方法论里面都打印一句日志即可
- 使用工厂模式+配置文件的方式,降低它们之间的耦合性
需求分析
需求实现
-
创建项目,导入依赖
<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies>
-
dao层代码
- 接口
package com.itheima.dao; public interface UserDao { void add(); }
- 实现类
package com.itheima.dao.impl; import com.itheima.dao.UserDao; public class UserDaoImpl implements UserDao { public void add() { System.out.println("调用了UserDaoImpl的add方法~!"); } }
-
service层代码
- 接口
package com.itheima.service; public interface UserService { void add() throws Exception; }
- 实现类
package com.itheima.service.impl; import com.itheima.dao.UserDao; import com.itheima.dao.impl.UserDaoImpl; import com.itheima.factory.BeanFactory; import com.itheima.service.UserService; public class UserServiceImpl implements UserService { public void add() throws Exception { System.out.println("调用了UserServiceImpl的add方法~!"); //以前的写法: /*UserDao userDao = new UserDao(); UserDao userDao = new UserDaoImpl(); userDao.add();*/ //现在的写法: UserDao userDao = (UserDao) BeanFactory.getBean("ud"); userDao.add(); } }
-
配置文件
beans.properties
在resource下面创建 beans.properties
us=com.itheima.service.impl.UserServiceImpl
ud=com.itheima.dao.impl.UserDaoImpl
- 工厂类
BeanFactory
package com.itheima.factory;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.ResourceBundle;
/*
这是专门用来创建对象的工厂类
1. 使用静态代码块来读取beans.properties
2. 提供一个静态方法,供外面的人调用获取对象
*/
public class BeanFactory {
static Map<String , String> map = new HashMap<String , String>();
//1. 在这里读取beans.properties文件
static{
//1. 读取外部的properties文件,只要写名字即可
ResourceBundle resourceBundle = ResourceBundle.getBundle("beans");
//2. 读取里面的内容
Enumeration<String> keys = resourceBundle.getKeys();
//3. 遍历每一个key value
while(keys.hasMoreElements()){
//4. 取出每一个key
String key = keys.nextElement();
//5. 得到每一个value
String value = resourceBundle.getString(key);
//6. 把key和value保存到map集合
map.put(key , value );
}
}
/**
* 提供对象
* @param name 名称
* @return
*/
public static Object getBean(String name) throws Exception {
//1. 从map集合里面获取全路径
String className = map.get(name);
//2. 判定
if(className != null){
return Class.forName(className).newInstance();
}
return null;
}
}
- 测试
package com.itheima.test;
import com.itheima.factory.BeanFactory;
import com.itheima.service.UserService;
import org.junit.Test;
public class TestUserServiceImpl {
@Test
public void testAdd() throws Exception {
//1. 问工厂要对象
UserService us = (UserService) BeanFactory.getBean("us");
us.add();
}
}
小结
- 首先得有接口和实现类 : UserDao 和 UserDaoImpl , UserService 和 UserServiceImpl
- 使用properties配置文件来记录 ,别名和实现类的全路径
- 定义一个工厂类
1. 在静态代码块里面读取配置文件,使用map集合来存映射关系
2. 定义一个静态方法,只要有人来获取实例,那么就从map集合里面取出来全路径
3. 使用反射技术来构建实例返回。
三、控制反转IOC【重点】
- 什么是IOC inversion of control
控制反转,把对象的创建工作交给框架(工厂 Spring),我们不需要自己去new这个对象,只管问工厂要。由原来的主动创建对象,变成自己被动接收 框架创建的对象。
- IOC的作用
IOC是Spring的核心之一,作用就是为了解耦,降低程序,代码间的耦合度。
1. 快速入门【重点】
需求描述
- 有
UserDao
接口和UserDaoImpl
实现类 - 通过Spring容器(工厂)得到
UserDaoImpl
的实例对象(IoC方式)
开发步骤
- 创建Maven项目,导入依赖坐标:Spring的依赖坐标
- 编写dao接口
UserDao
及实现UserDaoImpl
- 创建Spring核心配置文件,并配置
UserDaoImpl
(作用类似bean.properties) - 测试:使用Spring的API,获取Bean实例对象
需求实现
1. 创建Maven项目,导入依赖坐标
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
2. 编写dao接口UserDao
及实现UserDaoImpl
- 接口
UserDao
package com.itheima.dao;
public interface UserDao {
void add();
}
- 实现类
UserDaoImpl
package com.itheima.dao.impl;
import com.itheima.dao.UserDao;
public class UserDaoImpl implements UserDao {
public void add() {
System.out.println("调用了UserDaoImpl的add方法~!~");
}
}
3. 创建Spring核心配置文件,并配置UserDaoImpl
这个步骤的作用就是告诉spring,要创建哪个类的对象,并且给这个类起一个别名,方便以后我们问spring要对象。它的作用等于我们前面写的
beans.properties
- 配置文件名称,通常叫
applicationContext.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">
<!--
在这里告诉spring要创建哪个类的对象,并且给这个对象起一个别名
bean标签:
作用: 用来托管(类)对象
属性:
id: 唯一标识,不能出现重复!
class: 托管类的全路径
-->
<bean id="ud" class="com.itheima.dao.impl.UserDaoImpl" />
</beans>
4. 使用Spring的API,获取Bean实例对象
- 编写测试类
package com.itheima.test;
import com.itheima.dao.UserDao;
import com.itheima.dao.impl.UserDaoImpl;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestUserDao {
@Test
public void testAdd(){
//1. 创建工厂
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//2. 问工厂要对象
UserDao userDao = (UserDao) context.getBean("ud");
//3. 调用方法
userDao.add();
}
}
小结
- 首先编写UserDao 和 UserDaoImpl
- 在pom.xml里面添加依赖
- 在resources下面,创建一个xml文件,名字随意。不要手动创建文件的方式。要选择xml配置文件的方式
- 在xml文件里面登记|注册|托管实现类
- 问工厂要实例
2. 配置文件详解【了解】
1. bean
标签的基本配置
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"></bean>
-
介绍
-
用于配置:把对象交给Spring进行控制 , spring会帮助我们创建对象。
-
默认情况下,Spring是调用类的无参构造来创建对象的;如果没有无参构造,则不能创建成功
-
-
基本属性
id
:唯一标识class
:bean的全限定类名
了解:bean的id和name的区别
- 一个bean只能有一个id;一个bean可以有多个name
- bean的name值:多个name之间以
,
;
空格
隔开,第1个name作为id,其它作为别名
2. bean
标签的作用范围配置
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl" scope="singleton"></bean>
- scope属性取值如下:
取值 | 说明 |
---|---|
singleton |
默认,表示单例的,一个Spring容器里,只有一个该bean对象 |
prototype |
多例的,一个Spring容器里,有多个该bean对象 |
request |
web项目里,Spring创建的bean对象将放到request 域中:一次请求期间有效 |
session |
web项目里,Spring创建的bean对象将放到session 域中:一次会话期间有效 |
globalSession |
web项目里,应用在Portlet环境/集群环境;如果没有Portlet/集群环境,那么globalSession相当于session(新版本中已删除) |
-
不同scope的bean,生命周期:
-
singleton:bean的生命周期和Spring容器的生命周期相同
- 整个Spring容器中,只有一个bean对象
- 何时创建:加载Spring配置文件,初始化Spring容器时,bean对象创建
- 何时销毁:Spring容器销毁时,bean对象销毁
-
prototype:bean的生命周期和Spring容器无关。Spring创建bean对象之后,交给JVM管理了
- 整个Spring容器中,会创建多个bean对象,创建之后由JVM管理
- 何时创建:调用
getBean
方法获取bean对象时,bean对象创建 - 何时销毁:对象长时间不用时,垃圾回收
-
3. bean
生命周期相关方法的配置【了解】
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"
init-method="" destroy-method=""></bean>
-
init-method
:指定类中初始化方法名称,该方法将在bean对象被创建时执行 -
destroy-method
:指定类中销毁方法名称,该方法将在bean对象被销毁时执行注意:
- prototype类型的bean:Spring容器销毁时,也不会执行销毁方法,因为Spring不负责它的销毁
- singleton类型的bean:在Spring容器显式关闭时,会执行destroy-method指定的方法
-
dao
package com.itheima.dao.impl;
import com.itheima.dao.UserDao;
public class UserDaoImpl implements UserDao {
public UserDaoImpl(){
System.out.println("创建对象了额~!!~");
}
public void add() {
System.out.println("调用了UserDaoImpl的add方法~!");
}
//创建好对象的时候调用这个方法
public void init(){
System.out.println("调用了UserDaoImpl的init方法~!");
}
//销毁对象的时候调用这个方法
public void destroy(){
System.out.println("调用了UserDaoImpl的destroy方法~!");
}
}
- 单元测试
package com.itheima.test;
import com.itheima.dao.UserDao;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestUserDaoImpl {
//测试单例多例
@Test
public void testAdd2(){
//1. 创建工厂
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//2. 问工厂要对象
UserDao userDao = (UserDao) context.getBean("ud");
//3. 关闭工厂!
context.close();
}
}
- xml配置
<!--
在这里告诉spring要创建哪个类的对象,并且给这个对象起一个别名
bean标签:
作用: 用来托管(类)对象
属性:
id: 唯一标识,不能出现重复!
class: 托管类的全路径
name : 用于给对象起别名,标识以后可以用这个别名来找到对象
可以写多个值,使用 分号 , 逗号, 空格 ,tab 来间隔!【一般不用它!】
scope: 用来设置创建的对象是单例的还是多例
singleton: 单例,默认是单例! 【大多数情况下,使用的都是单例对象】
何时创建:创建工厂的时候创建了对象
何时销毁:销毁工厂的时候,销毁对象
prototype: 多例
何时创建:问工厂要对象的时候,才创建对象
何时销毁:长时间不用之后,就由GC回收对象!
init-method:
当对象初始化的时候,调用这个方法
destroy-method:
当对象销毁的时候,调用这个方法
spring创建类的对象,默认会执行该类的无参构造方法!
-->
<bean id="ud" class="com.itheima.dao.impl.UserDaoImpl" scope="prototype" init-method="init" destroy-method="destroy"/>
4. bean
实例化的三种方式【了解】
我们通常都是问Spring要对象,那么Spring怎么整出来对象的呢?有三种方式。算起来就只有两种办法创建对象:
- 由Spring来创建对象
- 由我们自己来创建对象,然后spring来拿我们的对象给需要的人。
- 无参构造方法实例化,默认的:让Spring调用bean的无参构造,生成bean实例对象给我们 【由Spring创建】
- 工厂静态方法实例化:让Spring调用一个我们自己写好的工厂类的静态方法,得到一个bean实例对象 【由咱们自己创建】
- 工厂非静态方法实例化(实例化方法):让Spring调用一个工厂对象的非静态方法,得到一个bean实例对象 【由咱们自己创建】
1. 无参构造方法实例化 【spring创建对象】
UserDaoImpl 是由Spring创建的。
<bean id="ud" class="com.itheima.dao.impl.UserDaoImpl"></bean>
2. 工厂静态方法实例化
UserDaoImpl的由我们写好的StaticFactory的类来创建 , Spring工厂没干活,只是问我们的工厂要对象而已。
- 工厂类如下:
com.itheima.factory.StaticFactory
package com.itheima.factory;
import com.itheima.dao.UserDao;
import com.itheima.dao.impl.UserDaoImpl;
/*
使用工厂的静态方法来创建对象
*/
public class StaticFactory {
/**
* 创建UserDaoImpl的对象
* @return
*/
public static UserDao getBean(){
System.out.println("来问StaticFactory要对象了~");
return new UserDaoImpl();
}
}
- 配置如下:
<!--
使用工厂的静态方法来创建对象
1. spring工厂并不会去创建UserDaoImpl的对象。
2. 当有方法拿着ud02来问spring的工厂要对象的时候,spring的工厂会
找StaticFactory的getBean方法得到对象
3. 然后把这个对象返回给我们的方法
-->
<bean id="ud02" class="com.itheima.factory.StaticFactory" factory-method="getBean"/>
3. 工厂非静态方法实例化
UserDaoImpl的由我们写好的InstanceFactory的类来创建 ,, Spring工厂没干活,只是问我们的工厂要对象而已。
- 工厂类如下:
com.itheima.factory.InstanceFactory
package com.itheima.factory;
import com.itheima.dao.UserDao;
import com.itheima.dao.impl.UserDaoImpl;
public class InstanceFactory {
/**
* 创建UserDaoImpl的对象
* @return
*/
public UserDao getBean(){
System.out.println("来问InstanceFactory要对象了~");
return new UserDaoImpl();
}
}
- 配置如下:
<!--
使用工厂的非静态方法来创建对象:
1. spring的工厂并不会去创建UserDaoImpl的对象。
2. 它创建了我们工厂的对象
3. 当有方法拿着ud03来问spring的工厂要对象的时候,spring会拿着instanceFactory去找到工厂对象
(这个工厂对象是由spring创建出来的),然后调用getBean方法,返回我们创建好的UserDaoImpl对象
-->
<bean id="instanceFactory" class="com.itheima.factory.InstanceFactory"/>
<bean id="ud03" factory-bean="instanceFactory" factory-method="getBean"/>
- 小结
-
Spring工厂创建实例有三种方式: 默认的无参构造方式 | 静态工厂方式 | 实例工厂方式
-
只有无参构造的那种方式是spring创建对象,其他两种都是由我们自己来创建对象
-
我们使用spring的IOC ,目的就是为了把对象的创建工作交给Spring,后面这种工厂的方式,反而是我们来创建对象,所以一般不用这两种。
-
既然如此,都不怎么用后面得的两种方式了,为什么spring还要提供这两种入口呢?
这个其实就是为了兼容, 就是为了兼容以前的旧项目,有的旧项目50年前的旧项目,那个没有spring,但是那个时候已经使用了工厂来创建实例了。
3. IOC小结
- IOC是什么? 控制反转,把对象的创建工作交给spring的工厂完成,只管问spring要对象即可
- 在applicationContext.xml里面注册。
- 默认创建的实例是单例,如果想要多例,需要配合scope属性,设置成prototype
四、依赖注入DI【重点】
- 什么是依赖注入呢?
托管类里面有什么属性需要完成赋值工作,把这个赋值的工作交给spring来做。由spring把属性需要用到的值赋值(注入)进来就称之为依赖注入。
我们通过Ioc把bean对象交给了Spring容器进行管理,降低了耦合性。
但是耦合性不能彻底消除,bean之间还是有一些依赖关系。比如:业务层userService要依赖于持久层userDao。
这样的依赖关系,可以交给Spring帮我们进行依赖的注入,而不用我们自己注入依赖
1. 快速入门 【重点】
需求描述
- 有dao层:
UserDao
和UserDaoImpl
- 有service层:
UserService
和UserServiceImpl
UserServiceImpl
中的方法依赖于UserDaoImpl
- 使用Spring,把
UserDaoImpl
注入给UserServiceImpl
开发步骤
- 创建Maven项目,导入依赖坐标
- 编写dao层
UserDao
及UserDaoImpl
、service层UserService
和UserServiceImpl
- 创建Spring核心配置文件,并配置bean和依赖注入
- 使用Spring的API,测试
需求实现
1. 创建Maven项目,导入依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
2. 编写dao层和service层代码
- dao层接口
UserDao
package com.itheima.dao;
public interface UserDao {
void add();
}
- dao层实现类
UserDaoImpl
package com.itheima.dao.impl;
import com.itheima.dao.UserDao;
public class UserDaoImpl implements UserDao {
public void add() {
System.out.println("调用了UserDaoImpl的add方法~!");
}
}
- service层接口
UserService
package com.itheima.service;
public interface UserService {
void add();
}
- service层实现类
UserServiceImpl
package com.itheima.service.impl;
import com.itheima.dao.UserDao;
import com.itheima.service.UserService;
/*
需求: service里面的add方法要调用dao的add方法
分析:
1. 要想调用dao的add方法,必须持有UserDaoImpl的对象。
2. 要想拥有UserDaoImpl的对象有两种办法:
2.1 自己创建 ,自己new 【以前的做法】
UserDao userDao = new UserDaoImpl();
userDao.add();
2.2 让spring把userDaoImpl的对象给注入进来,注入给UserServiceImpl!【采用这种方式】
步骤:
1. 在UserServiceImpl里面定义属性 : private UserDao userDao;
2. 提供这个属性的set方法!
3. 把UserServiceImpl和UserDaoImpl这两个类都交给spring托管。
4. 在xml里面配置,告诉spring要把UserDao的对象注入到UserServiceImpl里面的这个属性 userDao身上!
*/
public class UserServiceImpl implements UserService {
//1. 定义属性
private UserDao userDao;
//2. 提供set方法
public void setUserDao(UserDao userDao) {
System.out.println("来调用Set方法了·~");
this.userDao = userDao;
}
public void add() {
System.out.println("调用了UserServiceImpl的add方法~!");
userDao.add();
}
}
3. 创建Spring核心配置文件,并配置bean和依赖注入
<?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">
<!--1. 把UserDaoImpl交给spring管理-->
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
<!--2. 把UserServiceImpl交给spring管理-->
<bean id="us" class="com.itheima.service.impl.UserServiceImpl">
<!--告诉spring,把id名字叫做ud的对象,赋值给UserServiceImpl里面的userDao属性-->
<property name="userDao" ref="userDao"/>
</bean>
</beans>
4. 使用Spring的API,测试
package com.itheima.test;
import com.itheima.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestUserServiceImpl {
@Test
public void testAdd(){
//1. 创建工厂
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//2. 问工厂要对象
UserService us = (UserService) context.getBean("us");
//3. 调用方法
us.add();
}
}
小结
-
有接口,有实现类
- 给属性提供set方法
- 在托管|登记类的时候,要添加property标签。如果注入的是对象,那么要使用ref属性,如果注入的是普通的数据,那么要使用value属性。
2. 三种常见注入方式
1. set方法注入(最常用)
1) 介绍
在类中提供需要注入的成员(依赖项)的set方法,在配置文件中注入属性的值
<bean id="" class="">
<property name="属性名" value="属性值"></property>
<property name="属性名" ref="bean的id"></property>
</bean>
property
标签:用在bean标签内部,表示要给某一属性注入数据name
:属性名称value
:要注入的属性值,注入简单类型值ref
:要注入的属性值,注入其它bean对象
2) 示例
- service
package com.itheima.service.impl;
import com.itheima.dao.UserDao;
import com.itheima.service.UserService;
/*
三种注入方式之一: set方法
要求:一定要提供属性的set方法!
*/
public class UserServiceImpl01 implements UserService {
private String address;
private UserDao userDao;
public void setAddress(String address) {
this.address = address;
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void add() {
System.out.println("调用了UserServiceImpl01的add方法~!"+address);
userDao.add();
}
}
- 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">
<!--1. 把UserDaoImpl交给spring管理-->
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
<!--
2. 把UserServiceImpl01交给spring管理
2.1 采用set方法完成属性的注入工作。
property : 用来告诉spring,有哪些属性要赋值,代码里面一定要提供set方法
name :属性名
ref : 注入值,针对的是对象类型属性
value : 注入值,针对的是普通的属性(基本数据类型 & 字符串)
-->
<bean id="us" class="com.itheima.service.impl.UserServiceImpl01">
<property name="userDao" ref="userDao"/>
<property name="address" value="深圳"/>
</bean>
</beans>
2. 构造方法注入
1) 介绍
在类中提供构造方法,构造方法的每个参数就是一个依赖项,通过构造方法给依赖项注入值。
<bean id="" class="">
<constructor-arg name="构造参数名称" value="构造参数的值"></constructor-arg>
<constructor-arg name="构造参数名称" ref="bean的id"></constructor-arg>
</bean>
name
:构造参数的名称type
:构造参数的类型index
:构造参数的索引value
:要注入的值,注入简单类型值ref
:要注入的值,注入其它bean对象
2) 示例
- service
package com.itheima.service.impl;
import com.itheima.dao.UserDao;
import com.itheima.service.UserService;
/*
三种注入方式之一: 有参构造
要求:一定要提供有参构造方法!
*/
public class UserServiceImpl02 implements UserService {
private String address;
private UserDao userDao;
public UserServiceImpl02(String address, UserDao userDao) {
this.address = address;
this.userDao = userDao;
}
public void add() {
System.out.println("调用了UserServiceImpl02的add方法~!"+address);
userDao.add();
}
}
- 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">
<!--1. 把UserDaoImpl交给spring管理-->
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
<!--
2. 把UserServiceImpl02交给spring管理
constructor-arg : 用于匹配有参构造函数,
name: 参数名
value : 给参数赋值,针对的是普通的参数(基本类型 & 字符串)
ref : 给参数赋值, 针对的是对象的参数
-->
<bean id="us" class="com.itheima.service.impl.UserServiceImpl02">
<constructor-arg name="address" value="深圳" />
<constructor-arg name="userDao" ref="userDao"/>
</bean>
</beans>
3. p名称空间注入
1) 介绍
p名称空间注入,本质仍然是set方法注入
在xml中引入p名称空间的约束
然后通过p:属性名称=""
来注入简单数据、使用p:属性名称-ref=""
注入其它bean对象,它的本质仍然是set方法注入
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="" class="" p:属性名="简单值" p:属性名-ref="bean的id"></bean>
</beans>
2) 示例
- service
package com.itheima.service.impl;
import com.itheima.dao.UserDao;
import com.itheima.service.UserService;
/*
三种注入方式之一: p名称空间
要求:一定要提供属性的set方法!
*/
public class UserServiceImpl03 implements UserService {
private String address;
private UserDao userDao;
public void setAddress(String address) {
this.address = address;
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void add() {
System.out.println("调用了UserServiceImpl03的add方法~!"+address);
userDao.add();
}
}
- 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"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--1. 把UserDaoImpl交给spring管理-->
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
<!--
2. 把UserServiceImpl03交给spring管理
-->
<bean id="us" class="com.itheima.service.impl.UserServiceImpl03" p:address="中粮商务公园" p:userDao-ref="userDao"/>
</beans>
小结
- 注入方式有三种,set方法 ,构造方法注入,p名称空间
- 最常用的是set方法。
- 以后如果使用注解了,方法也不需要写了。
3. 注入集合数据
介绍
- 前边我们介绍了如何注入简单数据类型和bean对象,但是在实际开发中,可能会需要给集合属性注入数据,比如:给数组、List、Set、Map等注入数据
示例
代码
package com.itheima.service.impl;
import com.itheima.dao.UserDao;
import com.itheima.service.UserService;
import java.util.*;
/*
注入集合数据: 数组、list、set、map、properties
*/
public class UserServiceImpl04 implements UserService {
private String [] array;
private List<String> list;
private Set<String> set;
private Map<String , String> map;
private Properties properties;
public void setArray(String[] array) {
this.array = array;
}
public void setList(List<String> list) {
this.list = list;
}
public void setSet(Set<String> set) {
this.set = set;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
public void add() {
System.out.println("调用了UserServiceImpl04的add方法~!");
System.out.println("array="+Arrays.toString(array));
System.out.println("list = " + list);
System.out.println("set = " + set);
System.out.println("map = " + map);
System.out.println("properties = " + properties);
}
}
配置注入数据
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--1. 把UserDaoImpl交给spring管理-->
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
<!--
2. 把UserServiceImpl04交给spring管理
-->
<bean id="userviceImpl" class="com.itheima.service.impl.UserServiceImpl"/>
<bean id="us" class="com.itheima.service.impl.UserServiceImpl04">
<!--1. 数组-->
<property name="array">
<array>
<value>array01</value>
<value>array02</value>
<value>array03</value>
</array>
</property>
<!--2. list-->
<property name="list">
<list>
<value>list01</value>
<value>list02</value>
<value>list03</value>
</list>
</property>
<!--3. set-->
<property name="set">
<set>
<value>set01</value>
<value>set02</value>
<value>set03</value>
</set>
</property>
<!--4. map-->
<property name="map">
<map>
<entry key="key1" value="value1"/>
<entry key="key2" value="value2"/>
<entry key="key3" value="value3"/>
</map>
</property>
<!--5. properties-->
<property name="properties">
<props>
<prop key="username">张三</prop>
<prop key="password">123456</prop>
</props>
</property>
</bean>
</beans>
所有单列结构的数据集合,标签可以互换使用。例如:List、Set、数组等
所有键值对结构的数据集合,标签可以互换使用。例如:Map、Properties等
小结
- 所有的DI数据类型里面,最常用的是对象数据。
- 最常用的方式 set方法。
- 数组 、 list 、set写法基本一样, map 和 properties基本一样。
五、相关API介绍(了解)
1. ApplicationContext
的继承体系
ApplicationContext
:接口,代表应用上下文,可以通过其实例对象获取Spring容器中的bean对象
2. ApplicationContext
2.1 XmlBeanFactory 和 ApplicationContext
的区别
-
ApplicationContext 是现在使用的工厂
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
-
XmlBeanFactory 是老版本使用的工厂,目前已经被废弃【了解】
BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
-
两者的区别:
-
ApplicationContext加载方式是框架启动时就开始创建所有单例的bean,存到了容器里面
-
XmlBeanFactory加载方式是用到bean时再加载(目前已经被废弃)
-
2.2 ApplicationContext
的实现类
2.2.1 ClassPathXmlApplicationContext
- 是从类加载路径里,加载xml配置文件的
- 什么是类加载路径:代码编译之后的那个classes文件夹,
- 开发中可以认为Maven项目的:Java文件夹、resources文件夹,都是类加载路径
2.2.2 FileSystemXmlApplicationContext
- 从磁盘路径里,加载xml配置文件的
2.2.3 AnnotationConfigApplicationContext
- 用注解配置Spring时,通过此类加载配置类创建Spring容器,它用于读取类上的注解配置
2.3 getBean()
方法
- ApplicationContext提供了多种getBean方法的重载,常用的如下:
方法 | 参数 | 返回值 |
---|---|---|
getBean(String beanId) |
bean的id | Object ,bean对象 |
getBean(String beanId,Class beanType) |
bean的Class类型 | bean对象 |
getBean(Class beanType) |
bean对象 | |
getBeanDefinitionNames | String[] 获取工厂管理的对象的名字 |
package com.itheima.test;
import com.itheima.service.UserService;
import com.itheima.service.impl.UserServiceImpl04;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestApplicationContext {
@Test
public void testGetBean(){
//1. 创建工厂
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext04.xml");
//2. 问工厂要对象=getBean()
/**/ //1. 按照id来找对象,没有问题的!
UserService us = (UserService)context.getBean("us");
us.add();
//2. 按照真实的自己的类型来找对象!
UserService us2 = context.getBean(UserServiceImpl04.class);
us2.add();
//3. 按照接口的类型来找对象!
UserService us3 = context.getBean(UserService.class);
us3.add();
}
// 得到所有对象的id值
@Test
public void testGetBeanNames(){
//1. 创建工厂
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext04.xml");
//2. 获取spring工厂里面的所有对象的id值。
String[] names = context.getBeanDefinitionNames();
for (String name : names) {
System.out.println("name = " + name);
}
}
}
六、CURD练习【了解】
需求描述
- 完成帐户信息的增、删、改、查操作,要求使用Spring对service层和dao层解耦
- 把service和dao交给spring管理
- 如果有依赖的话,需要让spring去注入内容
需求分析
- 准备工作:
- 创建Maven的Java项目,配置坐标,引入依赖
- 创建数据库
- 创建JavaBean
- 编写代码:
- 创建service和dao的接口和实现类,并添加上:查询全部、添加帐号、修改帐号、删除帐号的功能
- 配置文件:
- 创建Spring核心配置文件,配置所有的bean
- 测试
- 创建单元测试类,测试功能是否正常
需求实现
3.1 准备工作
- 准备数据库
需要先手动创建一个数据库: day41_spring,然后再运行下面的这段sql语句
create table t_account (id int primary key auto_increment , name varchar(25) , money int);
insert into t_account values(null , 'zs' , 1000);
insert into t_account values(null , 'ls' , 2000);
insert into t_account values(null , 'ww' , 3000);
- 创建Maven的Java项目,项目坐标自定,然后引入依赖如下:
<dependencies>
<!-- 数据库驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!-- c3p0连接池(也可以用其它连接池) -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
<!-- DBUtils工具包 -->
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.7</version>
</dependency>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.2.RELEASE</version>
</dependency>
<!-- 单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
</dependency>
</dependencies>
-
创建JavaBean:Account类如下:
package com.itheima.bean; import lombok.Data; @Data public class Account { private int id; private String name; private int money; }
3.2 编写代码
1) Service层代码如下:
-
Service层接口:
AccountService
package com.itheima.service; import com.itheima.bean.Account; import java.sql.SQLException; import java.util.List; public interface AccountService { int add(Account account) throws SQLException; int delete(int id) throws SQLException; int update(Account account) throws SQLException; Account findById(int id) throws SQLException; List<Account> findAll() throws SQLException; }
-
Service实现类:
AccountServiceImpl
package com.itheima.serivce.impl; import com.itheima.bean.Account; import com.itheima.dao.AccountDao; import com.itheima.serivce.AccountService; import java.sql.SQLException; import java.util.List; /* 1. 把AccountServiceImpl 交给spring管理 2. 让spring把AccountDaoImpl的对象给注入进来 2.1 定义一个属性 2.2 提供这个属性的set方法~ */ public class AccountServiceImpl implements AccountService { private AccountDao dao; public void setDao(AccountDao dao) { this.dao = dao; } public int add(Account account) throws SQLException { return dao.add(account); } public int delete(int id) throws SQLException { return dao.delete(id); } public int update(Account account) throws SQLException { return dao.update(account); } public Account findById(int id) throws SQLException { return dao.findById(id); } public List<Account> findAll() throws SQLException { return dao.findAll(); } }
2) dao层代码如下:
-
dao层接口:
AccountDao
package com.itheima.dao; import com.itheima.bean.Account; import java.sql.SQLException; import java.util.List; public interface AccountDao { int add(Account account) throws SQLException; int delete(int id) throws SQLException; int update(Account account) throws SQLException; Account findById(int id) throws SQLException; List<Account> findAll() throws SQLException; }
-
dao实现类:
AccountDaoImpl
package com.itheima.dao.impl; import com.itheima.bean.Account; import com.itheima.dao.AccountDao; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.handlers.BeanHandler; import org.apache.commons.dbutils.handlers.BeanListHandler; import java.sql.SQLException; import java.util.List; /* 1. 把这个AccountDaoImpl这个类交给spring管理 2. 这里也不想手动创建QueryRunner对象了,而是让spring来创建它的对象, 但是spring创建对象,这个AccountDaoImpl想要用这个QueryRunner对象,咋办呢? 让spring注入进来这个QueryRunner对象即可 2.1 定义一个属性 2.2 定义属性的set方法 */ public class AccountDaoImpl implements AccountDao { private QueryRunner runner; public void setRunner(QueryRunner runner) { this.runner = runner; } public int add(Account account) throws SQLException { //QueryRunner runner = new QueryRunner(C3P0Utils.getDataSource()); String sql = "insert into t_account values (null , ? , ?)"; return runner.update(sql , account.getName() , account.getMoney()); } public int delete(int id) throws SQLException { String sql = "delete from t_account where id = ?"; return runner.update(sql , id); } public int update(Account account) throws SQLException { String sql = "update t_account set name = ? , money = ? where id = ?"; return runner.update(sql , account.getName() , account.getMoney() , account.getId()); } public Account findById(int id) throws SQLException { String sql = "select * from t_account where id = ?"; return runner.query(sql , new BeanHandler<Account>(Account.class), id); } public List<Account> findAll() throws SQLException { String sql = "select * from t_account "; return runner.query(sql , new BeanListHandler<Account>(Account.class)); } }
3.3 提供配置
-
创建Spring的核心配置文件:
applicationContext.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" 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"> <!--1. 把AccountServiceImpl交给spring管理--> <bean id="as" class="com.itheima.serivce.impl.AccountServiceImpl"> <property name="dao" ref="ad"/> </bean> <!--2. 把AccountDaoImpl交给spring管理--> <bean id="ad" class="com.itheima.dao.impl.AccountDaoImpl"> <property name="runner" ref="runner"/> </bean> <!--3. 把QueryRunner交给spring管理--> <bean id="runner" class="org.apache.commons.dbutils.QueryRunner"> <constructor-arg name="ds" ref="dataSource"/> </bean> <!--4. 把DataSource(其实就是连接池)交给spring管理--> <!--4.1 导入外部的properties文件--> <context:property-placeholder location="db.properties"/> <!--4.2 在下面使用properties的内容--> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${db.driverClass}"/> <property name="jdbcUrl" value="${db.jdbcUrl}"/> <property name="user" value="${db.username}"/> <property name="password" value="${db.password}"/> </bean> <!--<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="com.mysql.jdbc.Driver"/> <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/day41_spring"/> <property name="user" value="root"/> <property name="password" value="root"/> </bean>--> </beans>
3.4 功能测试
-
编写单元测试类
AccountTest
如下:package com.itheima.test; import com.itheima.bean.Account; import com.itheima.serivce.AccountService; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import java.sql.SQLException; public class TestAccountServiceImpl { @Test public void testAdd() throws SQLException { //1. 创建工厂 ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); //2. 问工厂要对象 AccountService as = (AccountService) context.getBean("as"); //3. 调用方法 Account a = new Account(); a.setName("张三"); a.setMoney(100); as.add(a); } @Test public void testDelete() throws SQLException { //1. 创建工厂 ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); //2. 问工厂要对象 AccountService as = (AccountService) context.getBean("as"); //3. 调用方法 as.delete(4); } @Test public void testUpdate() throws SQLException { //1. 创建工厂 ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); //2. 问工厂要对象 AccountService as = (AccountService) context.getBean("as"); //3. 调用方法 //3.1 先查询 Account a = as.findById(3); a.setMoney(9999); //3.2 再修改 as.update(a); } @Test public void testFindAll() throws SQLException { //1. 创建工厂 ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); //2. 问工厂要对象 AccountService as = (AccountService) context.getBean("as"); //3. 调用方法 System.out.println(as.findAll()); } }
补充
引入properties
文件
如果需要在applicationContext.xml
中引入properties文件:
- 准备一个properties文件放在resources里:
db.properties
driverClass=com.mysql.jdbc.Driver
jdbcUrl=jdbc:mysql://localhost:3306/day42_spring
user=root
password=root
- 在
applicationContext.xml
中引入并使用db.properties
- Spring的名称空间(建议使用idea自动生成的,如果idea抽风了,就自己手写)
<beans
xmlns:名称空间="http://www.springframework.org/schema/名称空间"
xsi:scehmaLocation="
http://www.springframework.org/schema/名称空间
http://www.springframework.org/schema/名称空间/spring-名称空间.xsd">
</beans>
- 使用context名称空间提供的标签,引入外部的properties文件
context的标签,硬着头皮写出来就可以了,不要害怕!
<!--导入进来db.properties-->
<context:property-placeholder location="db.properties"/>
<!--在下面使用 ${}来取值-->
<bean id="ds" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${driverClass}"/>
<property name="jdbcUrl" value="${jdbcUrl}"/>
<property name="user" value="${user}"/>
<property name="password" value="${password}"/>
</bean>
分模块提供配置文件
在大型项目开发中,如果把所有的配置都写在一个配置文件applicationContext.xml
中,会导致:
- 配置文件过于臃肿
- 不利于分模块开发,不利于模块之间的解耦
Spring提供了分模块配置的方式,即:每个模块|层提供一个配置文件,在核心配置文件中引入模块配置:
- dao模块有一个配置文件:
applicationContext-dao.xml
只配置dao相关的对象 - service模块有一个配置文件:
applicationContext-service.xml
只配置service相关的对象 - 有一个总的核心配置文件:
applicationContext-all.xml
如下
<import resource="classpath:applicationContext-service.xml"/>
<import resource="classpath:applicationContext-dao.xml"/>
小结
-
把dao和service都交给spring托管
- 在service里面注入进来dao
- dao里面要用到QueryRunner , QueryRunner也可以让Spring注入进来。
- 但凡是我们需要自己new对象,都交给spring来完成,然后注入进来即可。
七、Spring整合Junit【掌握】
在上边的CURD中,单元测试类里还需要我们自己去创建ApplicationContext
,并自己去获取bean对象。Spring提供了整合Junit的方法,让单元测试更简洁方便。
注解简介
注解 | 说明 |
---|---|
@RunWith |
用在测试类上,用于声明不再使用Junit,而是使用Spring提供的运行环境 |
@ContextConfiguration |
用在测试类上,用于指定Spring配置类、或者Spring的配置文件 |
Spring提供了单元测试的运行环境:SpringJunit4ClassRunner,配置到
@RunWith
注解上:
@RunWith(SpringJunit4ClassRunner.class)
- 要使用以上注解,需要导入jar包依赖:
spring-test
和junit
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
使用示例
步骤
-
在pom.xml文件中增加依赖:
spring-test
和junit
-
修改单元测试类
-
在单元测试类上增加注解:
@RunWith(SpringJunit4ClassRunner.class)
目的:使用Spring的单元测试运行器,替换Junit原生的运行器
-
在单元测试类上增加注解:
@ContextConfiguration()
目的:指定配置文件或配置类
-
在测试类里的依赖项上,直接使用
@Autowired
注入依赖
-
实现
-
在pom.xml文件中增加依赖:
spring-test
和junit
<dependencies> <!-- Spring --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.1.2.RELEASE</version> </dependency> <!--spring单元测试的依赖--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.1.2.RELEASE</version> </dependency> <!--Junit的依赖--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies>
-
UserService接口
package com.itheima.service;
public interface UserService {
void add();
}
- UserServiceImpl实现类
package com.itheima.service.impl;
import com.itheima.service.UserService;
public class UserServiceImpl implements UserService {
public void add() {
System.out.println("调用了UserServiceImpl的add方法!~");
}
}
- applicationContext.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">
<bean id="us" class="com.itheima.service.impl.UserServiceImpl"/>
</beans>
- 修改单元测试类
package com.itheima.test;
import com.itheima.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/*
spring整合Junit
1. 在类身上打注解 @RunWith 和 @ContextConfiguration
2. @RunWith(SpringJUnit4ClassRunner.class)
作用:
2.1. 表示我们单元测试,使用的是spring提供的测试环境
2.2. 这个测试环境会帮助我们自动的去创建spring的工厂
3.@ContextConfiguration("classpath:applicationContext.xml")
作用:
3.1 告诉spring的单元测试环境,配置文件在哪里!
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestUserServiceImpl02 {
//让spring把UserServiceImpl的对象给注入进来
//声明属性
@Autowired
private UserService us ;
@Test
public void testAdd(){
//3. 调用方法
us.add();
}
}
小结
-
导入依赖
- 在测试类上打注解
- 直接在测试类里面使用@Autowired注入对象。
总结
IOC:
1. IOC是什么?
控制反转,让spring帮助我们创建对象,我们只要问spring要对象即可
2. IOC怎么用?
2.1 导入spring的依赖
2.2 创建接口和实现类
2.3 在resource下创建一个xml文件
<bean id="us" class="类的全路径"/>
2.4 单元测试里面
a. 创建工厂
b. 问工厂要对象
c. 调用方法即可!
3. 细节:
3.1 默认情况下,spring创建的对象是单例的!如果想做成多例的对象,需要搭配
属性scope="prototype"
3.2 默认情况下,spring创建类的对象,会执行该类的无参构造方法!
3.3 spring创建单例对象是在工厂创建的时候,就创建对象了。但是如果是多例对象
是需要对象的时候,才会创建。
DI:
1. 什么是DI?
DI 就是依赖注入,让spring给托管类的属性,赋值!
2. 怎么用?
2.1 定义属性
2.2 提供属性的set方法
2.3 在xml里面配置<property>标签
<bean id="us" class="com.itheima.service.impl.UserServiceImpl">
<property name="userDao" ref="ud"/>
<property name="address" value="深圳"/>
</bean>
3. 常用的方式:
set方法
有参构造
p名称空间
4. 注入数据类型:
简单数据类型
对象数据类型
集合数据类型
整合Junit:
@RunWith(SpringJunit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")