目录
一、几个概念
XML : 用来传输数据,因为解析数据太过繁琐,后来就使用 JSON 来替代。那么现在大家都是使用 XML 来做配置文件。
IOC : 控制反转(外层):把你的权限交给 Spring 去管理
(小时候吃饭,吃饭的权限交给老妈管理)
DI : 依赖注入(内层):Spring 会把你想要的东西直接给你用。
(小时候打针,护士小姐姐会根据你哪里不舒服,就把对应的药,打到哪里)
AOP : 面向切面:就是用来对某个功能进行迭代增强的。
二、Spring 简介
1.Spring 是一个开源的设计层面框架,他解决的是业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用。
也就是 Spring 建议我们在编程的时候,使用面向接口的方式。
我们一般情况,Service 和 Dao 都是需要执行面向接口编程的。
接口类+实现类(多个)。
因为我们可以定义一个接口,把规范放出来,调用者只需要了解规范即可不需要了解我们的实现细节。
一个接口还可以有多个实现,这样可以很好的进行扩展。
在我们后期,如果想要增加或者改变功能,只需要制定对应的实现类即可,不需要关心前面的人怎么去调用。
就是需要交个 Spring 管理。
2.Spring 也是一个 JavaEE(JakartaEE)轻量级企业应用的一个开源框架,主要是为了更好的解耦合。
- 轻量级:指耦合度比较低的应用。
- 重量级:指耦合度比较高的应用。
容器 = 上下文:
相当于,我们画画的时候,需要在画板上才能画,画板外是不能画的。
就是一个我们操作的范围,在某个指定的范围内,我们能够去操作对应的数据。
3.Spring 的特点
1)方便解耦,简化开发
2)AOP 面向切面变成的支持
3)声明式事务的支持:
事务都需要在 Service 层实现,开启事务、提交事务、回滚数据。
使用 Spring 之后,就 so easy , 所有的事务操作, Spring 会自动完成。
①开启事务注解
②在 Service 层添加事务注解
4)方便程序的测试:整合 Junit 测试框架
5)方便继承各种优秀框架:Hibernate、MyBatis、Dubbo、Zookeeper、Struts2、...
6)降低 Java EE API 的使用难度
7)Java 源码是经典学习范例:里面包含了大量优秀的设计模式,代码确实很优雅。
Spring 是一个超级大的家族,几乎包含了你开发中每个层面的技术框架。
三、搭建 Spring 环境
我们需要搭建 Spring 开发环境的话,这里直接采用导入 jar 包到工程中,进行开发。
1.获取 Spring 的开发资源
官网:http://spring.io/ 不能下载 jar 文件,只能用 maven 或者 gradle 来构建。
下载 jar 包:http://repo.springsource.org/libs-release-local/org/springframework/spring/
点进对应的版本,即可看到对应的资源。
下载可以得到一个压缩包,解压之后的目录如下:
需要用到的 jar 包一共有 21 个。
API:就是人家写好的代码,放了一个接口出来,你如果想要用的话,直接调用的这个东西就是 API。就好比,你做一个插座,人家拿一个插头过来怼进来就可以有电用了。
2.新建 Web 动态工程 - Spring01,并添加相关的目录结构。
在创建目录结构的时候,没有特别要求的话,一般使用公司域名反写。
action:主要存放表现层的代码,不如 struts2 和 springMVC 的代码
bean:主要存放 JavaBean 代码,主要是用来做数据映射和数据封装的。
commons:一些常用的类
dao:主要存放持久层代码,包括接口类和实现类。
其中实现类,需要额外放到 impl 包中。
比如 Hibernate 和 Mybatis 的代码
service:主要是存放业务逻辑层代码,包括接口类和实现类。其中实现类,需要额外放到 impl 包中。
test:可以放一些测试用的代码。
utils:可以放功能封装好的工具类,比如 JSON 的解析、数据库的连接、验证码生成等。
另外,新建一个名为 config 的source folder 文件夹,用来存储一些配置文件。
❤ 其中注意: source folder 是虚拟文件夹,不在磁盘中显示的,主要文件是在类路径下的。folder 是物理文件夹,可以在磁盘中查找出来的。如果访问 folder 中的类,需要给定全路径。
3.导入 Spring 的 21 个 jar 包到项目的 /WebContent/WEB-INF/lib 包中,不需要手动 build path ,它会自动添加到开发资源库环境中。
4.手动在 /WebContent 下,添加一个 index.html 文件,因为 web.xml 文件中指定了一个欢迎页面,所以需要有一个对应的文件,否则会报 404 找不到资源异常。
四、使用 Spring 开发
1.回顾以前调用某类中的某方法
1)在 service 包中,创建 UserService 接口类和 UserServiceImpl 类。
先在接口类中,声明一个方法规范。
package com.dong.service;
public interface UserService {
public void sayHello() throws Exception;
}
然后在实现类中,进行重写实现。
package com.dong.service.impl;
import com.dong.service.UserService;
public class UserServiceImpl implements UserService{
@Override
public void sayHello() throws Exception {
// TODO Auto-generated method stub
System.out.println("我是 UserServiceImpl 类...");
}
}
2)新建一个 Demo 的测试类,直接通过使用 new 的方式来创建对象实例,并调用方法。
public class DemoTest {
@Test
public void fun1() throws Exception {
// 在没有使用 Spring 之前,我们需要使用 new 关键创建对象实例
UserService us = new UserServiceImpl();
us.sayHello();
}
}
❤ 使用 @Test 注解进行单元测试时,需要导入Junit4 关联包,就可以直接选中方法名,右键执行。
2.使用 Spring 方式来创建
1)配置 Spring 的配置文件,可以参考开发者文档中的模板。
在 config 文件夹中,新建一个 spring-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>
2)在 spring-beans.xml 配置文件中,将 UserServiceImpl 类交给Spring 管理。
<!-- 将 UserServiceImpl 类,交给 Spring 管理 -->
<bean id="userService" class="com.dong.service.impl.UserServiceImpl"></bean>
3)回到 DemoTest 类中,通过三个步骤,读取文件,获取对象实例,执行方法。
@Test
public void fun2() throws Exception {
// 1. 读取 beans.xml 配置文件,获取 Spring 的上下文
ApplicationContext context = new ClassPathXmlApplicationContext("spring-beans.xml");
// 2. 获取 UserServiceImpl 实例
UserService us = (UserServiceImpl) context.getBean("userService");
// 3. 调用方法
us.sayHello();
}
五、Spring 中的工厂
1.ClassPathXmlApplicationContext 类,通过源码观察,到最后其实它为了去获取某个资源,通过加载资源,最终得到一个 Spring 的上下文对象。
在 org.springframework.core 包的 ApplicationContext 接口中,可以看到包含 ClassPathXmlApplicationContext 和 FileSystemXmlApplicationContext 两个实现类。
ClassPathXmlApplicationContext : 主要是去类路径中,获取对应的资源文件,加载得到上下文对象。
FileSystemXmlApplicationContext : 主要是在本地磁盘中,获取对应的资源文件,加载得到上下文对象。
注意,以后不管写页面,或者是代码,如果需要获取某些资源,比如图片、配置文件等。一定要动态去获取“类资源路径”,也叫 classpath,也叫项目的根路径。可以通过一些专门使用反射原理的类来获取,只需要给名字即可,不需要硬性指定文件的路径。
另外,在页面的时候,直接通过 ${pageContext.request.contextPath}/请求名字或者页面名字。
2.ApplicationContext 其实是 BeanFactory 的子接口
BeanFactory 是一个工厂,通过名字,就可以看出,这个工厂主要是用来生产 Bean。
BeanFactory 底层使用的是延迟加载方式,第一次调用 getBean 方法时,才会初始化 指定的 Bean。
ApplicationContext 会在项目启动之后,当 beans.xml 文件被加载后,就可以直接使用指定的 bean了,省了每次都需要调用 getBean() 方法,才能获取到实例。
六、配置文件详解
在 spring-beans.xml 文件中,我们主要通过 <bean> 标签来管理对应的 Bean。
<bean id="userService" class="com.dong.service.impl.UserServiceImpl"></bean>
1. id 属性:给 bean 起一个名字,这个名字代表的就是对应的 Bean ,如果其他地方需要引用的话,直接把名字指定过去即可的。
2. name 属性:也是用来给 bean 起名字的,但没有唯一性,所以我们推荐大家使用 id 来配置。
3. class 属性:用来设置需要给 Spring 管理的 Bean 对应的路径。
4. scope 属性:用来指定范围的。
a)singleton 单例(默认值)在开发中,尽量少用单例,因为不安全,容易受其他地方修改,导致数据的不完整性和数据不安全。
b)prototype 多例,以后我们 Spring + Struts2 整合使用的时候,Action 类都需要设置为多例。
c)crequest 请求,每次 http 请求都会创建一个新的 Bean。
d)session 会话,同一个 session 会话共享一个 Bean。
e)gloableSession 全局会话
5. Bean 对象创建和销毁的时候自动执行对应的方法。
a)init-method:主要是在 Bean 创建的时候,自动执行,一般情况下,我们用于初始化某些数据,然后就可以直接使用。类似于程序中的一个静态块。
b)destory-method:主要是在 Bean 销毁的时候,比如服务器再正常关闭情况下就会销毁。一般情况下,我们会在销毁方法中释放掉某些资源,比如数据库连接、输入输出流等。
七、IOC 控制反转(值注入和类型注入)
控制反转主要有两种说法:IOC 控制反转、DI 依赖注入。
这两个概念是两个不同的人提出来的,其实质讲的还是用一个内容。
有些人会分开来说,是不同的东西,有些人,又说是同一样的东西。都不要理,如果确实要分开来讲的话,可以认为它们是同一个东西中的不同部分。
控制反转,还是一句话,将 bean 原有的创建、管理等权限交付给 Spring。
由 Spring 来控制它何时创建,给谁用。
1.值注入
需要满足三个条件:
1)添加字段,比如 name
2)添加 set 方法,比如 setName
3)在 spring-beans.xml 配置文件中,通过 <property> 属性来注入值
// 1. 添加字段
private String name;
// 2. 添加 set 方法
public void setName(String name) {
this.name = name;
}
// 3. 在 spring-beans.xml 文件中,添加 <property> 配置信息。
<bean id="userService" class="com.neuedu.service.impl.UserServiceImpl">
<!-- 给类中属性,设置值,也叫注入值:也就是哪里想要值,
spring 就可以往哪里设置值 -->
<property name="name" value="翠花"></property>
</bean>
2.类型注入
// 1. 在 dao 包中,创建 UserDao 接口类,然后添加 save() 方法
public void save() throws Exception;{
// 2. 在 dao.imp 实现类包中,创建 UserDaoImpl 实现类,重写 save() 方法。
@Override
public void save() throws Exception {
System.out.println("我是 UserDao 中的 save() 方法");
}
// 3. 回到 UserServiceImpl 实现类中,添加 userDao字段
private UserDao userDao;
// 4. 接着添加 set 方法
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
// 5. 将 UserDaoImpl 交付给 Spring 去管理
<bean id="userDao" class="com.neuedu.dao.impl.UserDaoImpl"></bean>
// 6. 需要将 userDao 引入到 userService 中。
<bean id="userService" class="com.neuedu.service.impl.UserServiceImpl">
…
<!-- 如果要配置类型注入的话,需要使用 ref属性来指定,
代表 reference 关联哪个指定的类 -->
<property name="userDao" ref="userDao"></property>
</bean>
八、设计模式 -- 桥接模式
如果一个组件存在 2 个或者 2 个以上维度(比如,选择)的变化。就应该将组件分离成 2 类或者 2 类以上的组件。
每类组件专门负责一个维度的变化。
最后才将这 2 类或者 2 类以上的组件组合起来得到一个最终的组件。比如,鸡肉 + 面 + 微辣 + 葱花 = 葱花微辣鸡肉面。
在开发中,我们的中间层组件,同样存在 service 业务层和 dao 持久层技术的两个维度的变化,应该将该组件分离成 2 类的组件:service 组件和 dao 组件。
其中,service 组件负责业务规则的改变,dao 组件负责持久层技术的改变。
当业务规则发生改变的时候,项目只需要去修改 service 组件即可。
当持久层技术发生改变的时候,项目只需要去修改 dao 组件即可。
九、构造器注入
构造器:主要用来在类启动的时候,进行实例化,可以获取到实例。
有参构造器和无参构造器的区别:就是你妈生你的时候和哪吒他妈生他的时候。
如果是直接将 car 交给 Spring 管理,这个时候,底层驱动 Spring 去执行的是当前类的无参构造器,然后去构建类的实例。
<!-- 将 Car 类,交给 Spring 管理-->
<bean id="car" class="com.neuedu.bean.Car"></bean>
步骤:
1) 创建 Car 类。
public class Car implements Serializable {
private String name;
private double money;
// 添加有参构造器
public Car(String name, double money) {
super();
this.name = name;
this.money = money;
}
// toString() 是为了打印对象实例的时候,方便查看数据
@Override
public String toString() {
return "Car [name=" + name + ", money=" + money + "]";
}
}
2) 把 Car 类交给 Spring 去管理,然后通过使用 <constructor-arg> 标签给有参构造器中的参数进行传值。有多少个参数,就需要有多少个 <constructor-arg> 标签。
<!-- 将 Car 类,交给 Spring 管理-->
<bean id="car" class="com.neuedu.bean.Car">
<!-- 给有参构造器中的参数进行赋值 -->
<constructor-arg name="name" value="宝马X6"/>
<constructor-arg name="money" value="120"/>
</bean>
3) 测试
@Test
public void fun4() throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-beans.xml");
Car car = (Car)context.getBean("car");
System.out.println(car);
}
如果说,这个车是属于某个人的呢?
步骤:
1) 创建 User 类,然后引入 Car 类,并创建有参构造器。
public class User implements Serializable {
private String name;
// 如果说,某个人拥有一辆车
private Car car;
// 使用构造器的方式来注入
public User(String name, Car car) {
super();
this.name = name;
this.car = car;
}
@Override
public String toString() {
return "User [name=" + name + ", car=" + car + "]";
}
}
2) 在 beans.xml 文件中,将 User 交付给 Spring 管理。
<!-- 将 User 类,交给 Spring 管理-->
<bean id="user" class="com.neuedu.bean.User">
<constructor-arg name="name" value="翠花"/>
<constructor-arg name="car" ref="car"/>
</bean>
3) 测试
@Test
public void fun5() throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-beans.xml");
User user = (User)context.getBean("user");
System.out.println(user);
}
十、集合注入
常见的集合:array、list、set、map、properties
注意:此处的 array 和 list 是一样的。
1. array
<bean id="user" class="com.neuedu.bean.User">
<property name="strs">
<array>
<value>翠花</value>
<value>春花</value>
<value>牵牛花</value>
<value>龙眼花</value>
</array>
</property>
</bean>
2. list :跟上面使用的 array 是一样的效果。
3. set:
1)先在 User 类中,添加字段和 set 方法
// 特点:没有顺序,不重复
private Set sets;
public void setSets(Set sets) {
this.sets = sets;
}
2)在 beans.xml 文件中设置。
<bean id="user" class="com.neuedu.bean.User">
<property name="sets">
<set>
<value>翠花</value>
<value>春花</value>
<value>牵牛花</value>
<value>龙眼花</value>
</set>
</property>
</bean>
4. map:主要是以 key-value 形势来显示。以后学习的 JSON 也是 key:value 形势。
1) 先在 User 类中,添加 map 字段
// map
private Map<String, String> map;
public void setMap(Map<String, String> map) {
this.map = map;
}
2) 在 beans.xml 文件中配置
<!-- map -->
<bean id="user" class="com.neuedu.bean.User">
<property name="map">
<map>
<entry key="8" value="翠花"></entry>
<entry key="9" value="春花"></entry>
<entry key="7" value="小翠"></entry>
</map>
</property>
</bean>
5. Properties key = value key-value key:value
1)在 User 中添加字段
2)在 spring-beans.xml 文件中配置