目录
1. 为啥要用 Spring
张三是一个编程小白,他每次在 service 层写代码都要自己 new 一堆 Dao 接口的实现类。
public class ProjectServiceImpl implements ProjectService {
UserDao userDao = new UserDaoImpl();
ProjectSectionDao projectSessionDao = new ProjectSessionDaoImpl();
ProjectDao projectDao = new ProjectDaoImpl();
SupplyDao supplyDao = new SupplyDaoImpl();
.......
}
复制代码
有一天正 new 着对象,张三心想:"我这一个 service 都需要 new 好多 Dao ,那如果有一堆 service ,那我不得花费好长时间?"
"有没有一个工具类或者什么框架能帮我管理这些对象?我只需要配置一下,需要的时候它就能自动帮我 new 个对象出来?"
张三陷入了深深的沉思之中。
张三的室友李四也是一个编程小白。
李四呢想给自己的小项目增加一个功能:记录方法执行的时间。结果他脑子一热竟然给所有的方法都增加了一堆打印方法:
System.out.println("项目开始执行");
// 开始时间
long start = System.currentTimeMillis();
// 业务代码
// 结束时间
long end = System.currentTimeMillis();
// 计算执行时间
System.out.printf("执行时间:%d 毫秒.", (end - start));
复制代码
过了半个小时,李四终于给项目中所有的方法都复制粘贴上了打印语句。他长舒一口气:"我真是个大聪明!"
张三看了一眼李四的代码,连连鼓掌:"妙啊!咱们宿舍的技术大神!"
旁边的王五实在忍不住了,对张三说:"妙个屁!最近的 Spring 框架课你俩是不是都没去?光顾着打游戏了?我都替你俩答了三次到了!"
李四问王五:"这个Spring 框架学了有用吗?"
王五:"不仅能解决张三说的管理对象的问题,还能帮你解决记录日志的问题。配置完 Spring ,你只需要定义一个切面类,根本不需要在一堆类上面复制粘贴一堆代码。"
张三摸摸后脑勺笑着说:"原来 Spring 框架那么好用,我以后再也不逃课了。我这就去翻课本学习 Spring 框架去。"
2. Spring 简介
Spring 是一个轻量级的 Java 开发框架。Spring 的核心是控制反转(IOC)和面向切面编程(AOP)。
Spring 主要有如下优点:
1.解耦
2.支持面向切面编程
3.便于集成其他框架
3. 环境搭建
1.创建 Maven 项目
File -> New -> Project -> Maven
2.引入依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.16.RELEASE</version>
</dependency>
</dependencies>
复制代码
3.创建接口和实现类
UserService
public interface UserService {
void print();
}
复制代码
UserServiceImpl
public class UserServiceImpl implements UserService{
@Override
public void print() {
System.out.println("hello world");
}
}
复制代码
4.创建配置文件
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="userService" class="com.xxl.service.impl.UserServiceImpl"/>
</beans>
复制代码
5.测试
@Test
public void testSpring(){
// 1、获取工厂
ApplicationContext act = new ClassPathXmlApplicationContext("/applicationContext.xml");
// 2、通过工厂类获得对象
UserService userService = (UserService)act.getBean("userService");
// 3.调用方法
userService.print();
}
复制代码
测试结果:
4. IOC
4.1 IOC 简介
IOC,全称 Inversion of Control,意思是控制反转。它是 Spring 框架中的一种思想。
控制反转就是将对象的控制权
从程序中的代码转移
到了 Spring 的工厂,通过 Spring 的工厂完成对象的创建以及赋值。
也就是说之前是我们自己 new 对象、给对象中的成员变量赋值。现在是让 Spring 来帮助我们创建对象、给成员变量赋值。
4.2 Spring 核心内容描述
1.配置文件
Spring 的配置文件可以放到项目中的任意一个地方,也可以随意命名,但是建议使用:applicationContext.xml。
你可以将这个配置文件看成一个装有一堆 bean 标签的容器。
2.bean 标签
Spring 工厂创建的对象,叫做 bean,所以一个 bean 标签代表一个对象。
<bean id="userService" class="com.xxl.service.impl.UserServiceImpl"/>
复制代码
bean 标签中必须要有 class 属性,它的值是一个类的全限定名(包名+类名)。
除了 class 属性,bean 标签还可以设置 id 、name 、scope属性。
id:
id 必须以字母开头,相当于这个 bean 的身份证号,是唯一的。
如果这个 bean 只使用一次,id 可以省略不写。
如果这个 bean 需要被其他 bean 引用,或者这个 bean 要使用很多次,则必须要有 id 属性。
如果只配置 class 属性,Spring 框架会给每一个 bean 配置一个默认的 id:"全限定名#1"。
例如:
com.xxl.service.impl.UserServiceImpl#1
复制代码
name:
name 相当于这个 bean 的别名,它可以配置多个,例如:
<bean id="user" name="aa,bb,cc" class="com.xxl.model.User"/>
复制代码
scope:
scope 属性可以控制简单对象的创建次数,它有两个值:
1.singleton:每次只会创建唯一⼀个简单对象,默认值。
2.prototype:每⼀次都会创建新的对象。
例如:
<bean id="user" class="com.xxl.model.User" scope="singleton"/>
复制代码
3.ApplicationContext
ApplicationContext 是 Spring 的工厂,主要用来创建对象。
Spring 通过读取配置文件创建工厂。
因为 Spring 的工厂会占用大量内存,所以一个程序一般只会创建一个工厂对象。
4.工厂常用方法
1.根据 id 获取对象
UserService userService = (UserService)act.getBean("userService");
复制代码
2.根据 id 和类名获取对象
UserService userService = (UserService)act.getBean("userService",UserService.class);
复制代码
3.只根据类名获取对象
UserService userService = (UserService)act.getBean(UserService.class);
复制代码
4.获取配置文件中所有 bean 标签的 id 值
String[] beanDefinitionNames = act.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
复制代码
结果:
5.判断是否存在指定 id 或者 name 的 bean
act.containsBean("userService")
复制代码
6.判断是否存在指定 id 的 bean,只能用来判断 id
act.containsBeanDefinition("userService")
复制代码
5.创建对象
Spring 是如何创建对象的呢?
工厂和反射。
首先说下反射,我们可以通过一个类的全限定名获取 Class 对象,然后再通过 Class 实例化一个对象:
Class serviceClass = Class.forName("com.xxl.service.impl.UserServiceImpl");
UserService userService = (UserService)serviceClass.newInstance();
复制代码
Spring 配置文件中 bean 标签的 id 和类的全限定名一一对应,所以 Spring 工厂的 getBean 方法其实就是先根据 bean 的 id 获取该类的全限定名,然后再利用反射根据类的全限定名创建对象并返回。
4.3 IOC 优点
解耦。
说起解耦之前先说下耦合:耦合是指代码之间的关联性太强,我如果改了这一段代码,可能会影响到一堆代码。
那创建对象哪里有耦合了?其实就是new
关键字带来的耦合。
如果你发现一个接口的实现类需要修改,你需要手动改动程序中的代码,比如修改 new 关键字后面的实现类,这样可能会影响到其他的代码。
但是使用了 Spring 之后,我们只需要修改配置文件中 bean 标签的 class 属性对应的类的全限定名,不用修改程序中的代码,这样就做到了解耦。
解耦就是解除不同代码之间的关联性、依赖性。
5. DI
DI 全称 Dependency Injection,意思是依赖注入,它是 IOC 的具体实现。
依赖就是说我需要你,比如 Service 层依赖 Dao 层,注入就是赋值。
依赖注入:使用 Spring 的工厂和配置文件为一个类的成员变量赋值。
没有使用 Spring 的依赖注入我们是这样赋值的:
User user = new User();
user.setName("张三");
复制代码
如果设置有误,就需要手动修改代码,代码耦合度较高,而依赖注入的出现就是为了解耦。
Spring 的依赖注入包含两种方式:
5.1 set 注入
set 注入:Spring 调用 Set 方法通过配置文件为成员变量赋值。
1.创建对象,为属性添加 set/get 方法
public class User {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
复制代码
2.修改配置文件
<bean id="user" class="com.xxl.model.User">
<property name="name" value="知否君" />
<property name="age" value="18" />
</bean>
复制代码
3.测试
// 1、获取工厂
ApplicationContext act = new ClassPathXmlApplicationContext("/applicationContext.xml");
// 2、通过工厂类获得对象
User user = (User)act.getBean("user");
System.out.println("姓名:"+user.getName());
System.out.println("性别:"+user.getAge());
复制代码
测试结果:
从上面的例子可以看出 Set 注入就是在 property 标签中为属性赋值。spring 可以为 JDK 内置的数据类型进行赋值,也可以为用户自定义的数据类型进行赋值。
5.1.1 JDK 内置数据类型
1.基本类型
<property name="name" value="知否君" />
<property name="age" value="18" />
复制代码
2.List 集合
<property name="phones">
<list>
<value>15799999918</value>
<value>15788888819</value>
<value>15766666620</value>
</list>
</property>
复制代码
3.Set 集合
<property name="phones">
<set>
<value>15799999918</value>
<value>15788888819</value>
<value>15766666620</value>
</set>
</property>
复制代码
4.Map 集合
<property name="mapInfo">
<map>
<entry>
<key><value>name</value></key>
<value>知否君</value>
</entry>
<entry>
<key><value>age</value></key>
<value>23</value>
</entry>
</map>
</property>
复制代码
5.数组
<property name="phones">
<list>
<value>15799999918</value>
<value>15788888819</value>
<value>15766666620</value>
</list>
</property>
复制代码
6.Properites
<property name="prop">
<props>
<prop key="key1">value1</prop>
<prop key="key2">value2</prop>
</props>
</property>
复制代码
5.1.2 用户自定义数据类型
1.为成员变量添加 set/get 方法
public class UserServiceImpl implements UserService {
private UserDao userDao;
public UserDao getUserDao() {
return userDao;
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void print() {
userDao.print();
}
}
复制代码
2.bean 标签使用 ref 属性
<bean id="userDao" class="com.xxl.dao.impl.UserDaoImpl" />
<bean id="userService" class="com.xxl.service.impl.UserServiceImpl">
<property name="userDao" ref="userDao"/>
</bean>
复制代码
3.测试
@Test
public void testSpring(){
// 1、获取工厂
ApplicationContext act = new ClassPathXmlApplicationContext("/applicationContext.xml");
// 2、通过工厂类获得对象
UserService userService = (UserService)act.getBean("userService");
// 3.调用方法
userService.print();
}