“一个人最好的状态:梦想藏在心里,行动落于腿脚。”
目录
1、简介
1.1 什么是Spring框架
-
Spring是一个开源的重量级的应用开发框架,其目的是用于简化企业级应用程序开发,降低开发者的开发难度
-
Spring提供的IOC和AOP应用,可以将组建的耦合度降到最低(即解耦),便于系统日后的维护和升级
-
Spring为系统提供了一个整体的解决方案,开发者可以利用它自身提供的功能外,也可以与第三方框架和技术整合应用,可以自由选择利用哪种技术进行开发
1.2 为什么要学习Spring框架
-
方便解耦,简化开发
通过Spring提供的IOC容器,可以将对象之间的依赖关系交给Spring控制,避免硬代码所造成的的过渡程序耦合
-
AOP编程的支持
通过Spring提供的AOP功能,方便进行面向对象的编程,如性能检测、事务管理、日志记录等
-
声明式事物的支持
-
方便集成各种优秀框架
-
降低开发难度,例如对JDBC,远程调用等提供了简便封装
2、Spring架构
组成Spring框架的每个模块(或组件)都可以单独存在,或者与其中一个或多个模块联合实现,每个模块的功能如下:
模块 | 说明 | |
---|---|---|
1 | Spring Core | 核心IOC容器,解决对象创建及依赖关系 |
2 | Spring Context | Spring上下文,是一个配置文件,由Spring框架提供上下文信息 |
3 | Spring AOP | 面向切面的编程 |
4 | Spring DAO | 对传统的JDBC进行了抽象 |
5 | Spring ORM | 关系映射模块,封装和管理了若干个ORM框架 |
6 | Spring WEB | web模块建立与应用上下文之间,提供了web开发的基础集成特性 |
7 | Spring MVC | 是一个全功能的构建Web应用程序的MVC实现 |
Spring 框架的功能可以用在任何J2EE服务器中,大多数功能也适用于不受管理的环境。Spring 的核心要点是:支持不绑定到特定J2EE服务的可重用业务和数据访问对象。毫无疑问,这样的对象可以在不同J2EE环境(Web或EJB)、独立应用程序、测试环境之间重用。
3、Spring IOC 控制反转
3.1 什么是控制反转
IOC(Inversion of Control)控制反转
所谓的控制反转,就是指对象的创建,存储,管理(依赖查找、依赖注入)交给了Spring容器(核心IOC容器)
回顾
之前我们需要对象的时候,往往都是new关键字创建一个对象
//创建一个User对象
User user = new User();
//调用对象的say()方法
user.say();
由于是new的对象,会提高代码之间的耦合性
所谓的耦合指的是在软件开发中,在层与层之间产生了某种紧密的关系。这种关系可能会导致,在我们修改或者是替换某一层时会影响其他的层,像这种情况我们就称之为层与层之间产生了耦合。
由于耦合(层与层之间的紧密关系)可能会导致我们在修改某一层时影响到其他的层,而这严重违反了我们对软件进行分层的最初设想 -- 软件各层之间应该相互独立、互不干扰,在修改或者是替换某一层时,应该做到不影响其他层。
而使用Spring框架,对象的管理和操作就可以交给Spring来管理
//从Spring容器中获取对象(而不是自己new)
User user = (User) context.getBean("user");
//调用对象的say()方法
user.say();
我们只需要提前把类配置到Spring配置文件中,就可以将对象的创建交给Spring容器,当需要对象时,不需要自己创建,而是直接从Spring中获取,省去了new对象,降低了代码的耦合度、
3.2 IOC入门
创建maven项目,导入所需要的JAR包
-
点击File,新建Project项目
- 选中Maven,直接点击Next即可
-
依次填写groupid和artifactid后点击next即可
- 输入项目名称Spring,点击Next即可
- 这样就创建好了一个名称为Spring的maven项目,结构如下图:
- 在pom.xml文件中导入所需要的的jar包
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.5.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.9.RELEASE</version>
<scope>compile</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/aopalliance/aopalliance -->
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjrt -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.2</version>
<scope>runtime</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-tx -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
</dependency>
</dependencies>
添加applicationContext.xml文件(核心配置文件)
-
可以使用Idea集成开发工具自带的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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
-
为了方便我们以后后使用注解,AOP等开发使用,将默认配置文件修改如下:
<?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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
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
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
</beans>
3.3 获取Spring容器对象
Spring容器不单单只有一个,通常分为两种类型
-
Bean工厂,BeanFactory(功能简单)
/**
* 通过resource来获取BeanFactory
* 1.加载Spring的核心配置文件
* 2.通过XmlBeanFactory+配置⽂件来创建IOC容器
*/
ClassPathResource resource = new ClassPathResource("applicationContext.xml");
BeanFactory factory = new XmlBeanFactory(resource);
System.out.println(factory);
-
应用上下文,ApplicationContext(功能强大,一般使用这个)
/**
* XML获取ApplicationContext
* 1.直接通过ClassPathXmlApplicationContext获取
*/
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
System.out.println(context);
- 创建User实体类
@Data
public class User {
private String id;
private String username;
public User() {
System.out.println("我是User,我被创建了...");
}
public User(String id, String username) {
this.id = id;
this.username = username;
}
}
在Spring框架中总体上有四种方式来配置JavaBean对象
-
XML文件配置
-
注解配置
-
JavaConfig配置
-
droovy脚本 DSL
以上四种,我们主要介绍前三种常见的方式
3.4 三种JavaBean配置对象
第一种:使用xml文件配置
-
无参构造函数创建Bean
<!--
使用bean节点来创建对象
id属性标识着对象
class属性代表着类的路径
-->
<bean id="user" class="com.cn.pojo.User"></bean>
测试
//直接用之前获取到IOC容器得到User对象
User user = (User) context.getBean("user");
System.out.println(user);
输出结果
-
有参构造函数创建Bean
<bean id="user" class="com.cn.pojo.User">
<!--通过constructor-arg来表示参数是第几个,名称,值,类型-->
<constructor-arg index="0" name="id" value="1" type="java.lang.String"></constructor-arg>
<constructor-arg index="1" name="username" value="xu" type="java.lang.String"></constructor-arg>
</bean>
-
测试
//直接用之前获取到IOC容器得到User对象
User user = (User) context.getBean("user");
System.out.println(user);
- 输出结果
constructor-arg标签中name的属性必须和构造函数中的参数的名字相同
普通属性直接用value注入
对象属性需要用ref注入
@Data
public class User {
private String id;
private String username;
//User对象上维护了一个Person对象的值
private Person person;
public User() {
System.out.println("我是User,我被创建了...");
}
public User(String id, String username, Person person) {
this.id = id;
this.username = username;
this.person = person;
}
配置文件中代码如下:
<bean id="person" class="com.cn.pojo.Person"></bean>
<bean id="user" class="com.cn.pojo.User">
<!--通过constructor-arg来表示参数是第几个,名称,值,类型-->
<constructor-arg index="0" name="id" value="1" type="java.lang.String"></constructor-arg>
<constructor-arg index="1" name="username" value="xu" type="java.lang.String"></constructor-arg>
<constructor-arg index="2" name="person" ref="person" type="com.cn.pojo.Person"></constructor-arg>
</bean>
测试结果输出:
-
工厂静态方法创建Bean
- 使用一个工厂的静态方法返回一个对象
//使用工厂静态方法创建对象
public class Factory {
public static User getBean(){
return new User();
}
}
- 配置文件中使用工厂的静态方法返回对象
<!--直接使用class指向静态类,指定静态即可-->
<bean id="user" class="com.cn.pojo.Factory" factory-method="getBean"></bean>
- 测试结果输出:
-
工厂非静态方法创建Bean
//使用工厂非静态方法创建对象
public class Factory {
public User getBean(){
return new User();
}
}
配置文件中使用工厂的非静态方法返回对象
<!--首先创建工厂对象-->
<bean id="factory" class="com.cn.pojo.Factory"></bean>
<!--指定工厂对象和工厂方法-->
<bean id="user" class="com.cn.pojo.User" factory-bean="factory" factory-method="getBean"></bean>
测试结果输出
总结:基于XML方式创建Bean的三种方式就已经介绍完毕,分别是
-
无参函数创建Bean
-
有参构造函数创建Bean
-
工厂方法创建Bean
-
工厂静态方法创建Bean
-
工厂非静态方法创建Bean
-
第二种:使用注解配置
通过注解来配置信息就是为了简化IOC容器的配置步骤,Spring的注解可以把对象添加到IOC容器中、处理对象依赖关系等。
-
引入Context名称空间
在applicationContext.xml文件开头添加
xmlns:context="http://www.springframework.org/schema/context"
之前在我们创建项目的时候已经添加好,可以参考上面步骤。
-
开启注解扫描器
<!--base-package的值根据自己包的结果自行设置-->
<context:component-scan base-package="com.cn"></context:component-scan>
-
常用的相关注解
注解 | 说明 |
---|---|
@ComponentScan | 扫描器 |
@Configuration | 表明该类是一个配置类 |
@Component | 把一个对象加入到IOC容器 |
@Repository | 作用同@Component ,在持久层使用 |
@Service | 作用同@Component ,在业务使用 |
@Controller | 作用同@Component ,在控制层使用 |
@Resource | 依赖关系; 不指定值,就根据类型去找; 指定值,就根据名称去找; |
-
UserDao层代码
//把对象添加到容器中,首字母会小写
@Repository
public class UserDao implements IUser{
public void save(){
System.out.println("保存用户");
}
}
-
UserService代码
//把对象添加到容器中,首字母会小写
@Service
public class UserService {
@Resource(name = "userDao") //不指定值根据类型来找,指定值根据名称来找
private UserDao userDao;
public void save(){
userDao.save();
}
}
-
UserController代码
-
//把对象添加到容器中,首字母会小写 @Controller public class UserController { @Resource(name = "userService") private UserService userService; public void save(){ userService.save(); }
-
测试代码:
public class test1 {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml")
//注解配置方式
UserController userController = (UserController)context.getBean("userController");
userController.save();
}
}
-
输出结果
第三种:JavaConfig配置
-
编写配置类,使用配置类创建Bean
@org.springframework.context.annotation.Configuration
public class Configuration {
@Bean
public UserDao userDao(){
UserDao userDao = new UserDao();
System.out.println("我是在configuration中的" + userDao);
return userDao;
}
}
-
测试代码:
//测试代码中要使用@ContextConfiguration注解加载配置类的信息
@ContextConfiguration(classes = Configuration.class)
public class test1 {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml")
//javaconfig配置方式
UserDao userDao = (UserDao) context.getBean("userDao");
System.out.println(userDao);
}
}
-
输出结果:
以上三种配置Bean方法,在公司的项目中,一般是XML+注解的方式配置Bean的方式较多,所有重点掌握前两种方式。
Bean对象创建细节
-
scope="singleton"
指定创建对象的时候是单例
<!--测试bean对象创建细节-->
<bean id="user" class="com.cn.pojo.User" scope="singleton"></bean>
测试类
public class test2 {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
User user1 = (User) context.getBean("user");
User user2 = (User) context.getBean("user");
if (user1 ==user2){
//默认对象在容器之前创建
System.out.println("单例");
}else {
//对象在使用的时候创建
System.out.println("多例");
}
}
结果输出
当scope="singleton"时,两次创建的对象都不一样,并且输出结果也可以看出单例对象是在ioc容器之前创建的。
单例模式下我们可以控制对象创建的时间
-
lazy-init="true"
延迟加载属性
<!--测试bean对象创建细节-->
<bean id="user" class="com.cn.pojo.User" scope="singleton" lazy-init="true"></bean>
测试代码不变,结果输出
-
scope="prototype"
指定创建对象的时候是多例
<!--测试bean对象创建细节-->
<bean id="user" class="com.cn.pojo.User" scope="prototype" lazy-init="true"></bean>
测试代码不变,结果输出
当scope="prototype"时,两次创建的对象一样,并且输出结果也可以看出单例对象是在ioc容器之后才创建的。
3.5 DI 依赖注入
什么是依赖注入?
依赖注入(Dependency Injection),即组件之间的依赖关系由容器在应用系统运行期来决定,也就是容器动态的将某种关系的目标对象实例注入到应用系统选中的各个关联的组件之中
简单的说,所谓的依赖注入就是,在创建对象的同时,如何给对象的属性赋值。
回顾
如果对象是我们在自己创建, 那一切就很简单
//set方法
User user = new User();
user.setId("01");
user.setUsername("桔子");
//构造g函数
User user = new User("01","桔子");
如果对象由Spring创建,Spring框架为我们提供了以下几种方法:
-
-
通过Set方法给属性注入值
-
通过构造函数
-
内部Bean
-
p名称控件
-
自动装配(了解)
-
注解
-
4、搭建测试环境
UserService中使用userDao变量来维护与Dao层之间的依赖关系,UserController中使用userService变量来维护与Service层之间的依赖关系
代码如下:
public class UserDao{
public void save(){
System.out.println("保存用户");
}
}
public class UserService {
@Resource(name = "userDao") //不指定值根据类型来找,指定值根据名称来找
private UserDao userDao;
public void save(){
userDao.save();
}
}
public class UserController {
@Resource(name = "userService")
private UserService userService;
public void save(){
userService.save();
}
}
4.1 第一种:通过Set方法依赖注入
测试UserService和UserDao之间的依赖关系即可,也就是说在Servcie层通过set方法把UserDao注入到UserService去
-
UserServcie代码如下
//第二种:set方法注入
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
//测试是否成功注入
System.out.println(userDao);
}
-
applicationContext配置文件中修改
<!--创建UserDao对象-->
<bean id="userDao" class="com.cn.controller.UserDao"></bean>
<!--创建UserService对象
通过<property/>节点给对象注入依赖
基本类型使用value
引用类型使用ref
-->
<bean id="userService" class="com.cn.controller.UserService">
<property name="userDao" ref="userDao"></property>
</bean>
-
测试
//直接用之前获取到IOC容器得到UserService对象
UserService userService = (UserService) context.getBean("userService");
-
输出结果
4.2 第二种:通过构造方法依赖注入
其实我们再之前的有参构造函数创建Bean已经讲解过了,现在我们一起来回顾一下
-
UserServcie代码如下:
public UserService(UserDao userDao) {
this.userDao = userDao;
//测试是否成功注入
System.out.println(userDao);
}
-
applicationContext配置文件中修改
<!--创建UserDao对象-->
<bean id="userDao" class="com.cn.controller.UserDao"></bean>
<!--创建UserService对象
通过<constructor-arg/>节点给对象注入依赖
使用ref属性引入UserDao对象
-->
<bean id="userService" class="com.cn.controller.UserService">
<constructor-arg ref="userDao"/>
</bean>
-
测试
//直接用之前获取到IOC容器得到UserService对象
UserService userService = (UserService) context.getBean("userService");
-
输出结果
4.3 第三种:通过内部Bean依赖注入
第一种通过set函数依赖注入方法中,我们是先创建UserDao对象后,再UserServcie中对UserDao对象进行引用。
现在我们换一种思维,先创建UserServcie,然后发现UserServcie中需要UserDao属性,最后再创建UserDao
具体步骤如下:
-
UserServcie代码不变,同第一种方法
-
applicationContext配置文件中修改
<!--
创建UserService对象
在<property/>标签下内置一个bean
创建UserDao对象
-->
<bean id="userService" class="com.cn.controller.UserService">
<property name="userDao">
<bean id="userDao" class="com.cn.controller.UserDao"></bean>
</property>
</bean>
-
测试
//直接用之前获取到IOC容器得到UserService对象
UserService userService = (UserService) context.getBean("userService");
-
输出结果
4.4 第四种:p 名称控件依赖注入
p名称控件这种方式其实就是set方法的一种优化,优化了配置而已
注意:这种方式需要再Spring3.0版本以上才能使用
具体步骤如下:
-
UserServcie代码不变,同第一种方法
-
applicationContext配置文件中修改
<!--创建UserDao对象-->
<bean id="userDao" class="com.cn.controller.UserDao"></bean>
<!--
创建UserService对象
不使用<property/>标签注入属性
直接使用p名称控件
-->
<bean id="userService" class="com.cn.controller.UserService" p:userDao-ref="userDao">
</bean>
-
测试
//直接用之前获取到IOC容器得到UserService对象
UserService userService = (UserService) context.getBean("userService");
-
输出结果
4.5 第五种:自动装配依赖注入
Spring框架的自动装配默认是不打开的,可分为以下两种:
1:根据名称来装配
2:根据类型来分配
1:Xml配置根据名称装配
-
applicationContext配置文件中修改
<!-- 1:XML配置根据名称 -->
<bean id="userDao" class="com.cn.controller.UserDao"></bean>
<bean id="userService" class="com.cn.controller.UserService" autowire="byName"></bean>
-
测试
//直接用之前获取到IOC容器得到UserService对象
UserService userService = (UserService) context.getBean("userService");
-
输出结果
2:Xml配置根据类型装配
<!-- 1:XML配置根据名称 -->
<bean id="userDao" class="com.cn.controller.UserDao"></bean>
<bean id="userService" class="com.cn.controller.UserService" autowire="byType"></bean>
-
applicationContext配置文件中修改
<!-- 1:XML配置根据名称 -->
<bean id="userDao" class="com.cn.controller.UserDao"></bean>
<bean id="userService" class="com.cn.controller.UserService" autowire="byType"></bean>
-
测试输出结果
4.6 第六种:通过注解依赖注入
开发最常用的一种方法,@Autowired直接来实现依赖注入
具体步骤如下:
-
UserServcie代码
@Service
public class UserService {
private UserDao userDao;
@Autowired
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
-
测试输出结果
这篇文章主要介绍了Spring这个大家族的IOC控制反转和DI依赖注入,学习新的框架是为了有助于程序员去更高效率的开发,企业中Spring框架就变的尤其重要
希望我们可以循序渐进,脚踏实地,修炼内功,厚积薄发,成为更好的自己!
下一篇我们继续开始学习Spring框架的AOP切面编程和事务的相关内容,加油!