一、什么是 spring framework ?
Spring Framework 是一个开源的基于 Java 平台的应用程序开发框架。它提供了一个综合性的编程和配置模型,用于构建企业级的 Java 应用程序。
Spring Framework 的设计宗旨是通过依赖注入(Dependency Injection)和面向切面编程(Aspect-Oriented Programming)等技术降低企业应用程序的复杂性,并提供了一套全面的解决方案。
Spring Framework 的核心功能包括:
1. 控制反转(Inversion of Control,IoC):通过依赖注入的方式,将对象之间的依赖关系由编码实现转变为配置实现,通过配置文件或注解的方式将对象的依赖关系注入到容器中。
2. 面向切面编程(Aspect-Oriented Programming,AOP):提供了一种非侵入式的编程方式,通过切面对横切关注点(如事务管理、日志记录等)进行模块化和管理。
3. 数据访问与集成(Data Access and Integration):提供了对不同数据访问技术的支持,包括 JDBC、ORM 框架(如 Hibernate、MyBatis)、NoSQL 数据库等。
4. Web 开发支持:提供了对 Servlet、MVC 框架、RESTful Web 服务等的支持,简化了 Web 应用的开发和部署。
5. 安全性:提供了对身份验证、授权等安全方面的支持,保护应用程序的安全性。
Spring Framework 还有很多其他的功能模块,如消息队列、缓存、定时任务等,它们可以灵活地组合和使用,根据应用程序的需求进行定制。
总的来说,Spring Framework 提供了一种简化的开发模式和一整套解决方案,帮助开发者构建可测试、可维护、可扩展的企业级 Java 应用程序。它已经成为 Java 开发领域最受欢迎和广泛应用的框架之一。
二、为什么要学习 spring framework?
1、先看图
企业的使用率达到的 90%,基本上现在的企业级项目都在用 spring 框架进行开发了,如果这还不够体现出 spring 的重要的话,大家可以去招聘网站去看 Java 后端开发的招聘要求,绝对都是需要会 spring 的。有句话说,不会 spring 就等于不会 Java。
1)、提供了一种轻量级的、高效的开发方式:Spring框架可以帮助简化Java应用程序的开发过程,提供了大量的现成解决方案和模块化的特性。通过使用Spring,可以减少开发人员的编码工作量,提高开发效率。
2)、促进了松耦合的开发:Spring框架采用了控制反转(IoC)和面向切面编程(AOP)的设计原则。这使得应用程序的各个组件之间实现松耦合,便于代码的维护和扩展。
3)、提供了丰富的功能模块:Spring框架提供了许多功能模块,如事务管理、安全性、缓存、批处理等。这些模块可以方便地集成到应用程序中,提供了对常见问题的解决方案,并遵循了最佳的企业级开发实践。
4)、支持多种技术整合:Spring框架可以与其他流行的Java技术进行整合,如Hibernate、MyBatis、JPA、RESTful Web服务等。通过整合这些技术,可以构建出更加强大和灵活的应用程序。
5)、社区活跃且资源丰富:Spring框架拥有庞大而活跃的社区,有许多专业的开发者和贡献者。在学习Spring框架时,可以通过社区论坛、博客、教程等途径获取丰富的学习资源和技术支持。
总而言之,学习Spring框架可以帮助开发人员提高开发效率、降低代码复杂度、实现松耦合的设计,并且能够应用于各种Java应用程序的开发中。这使得Spring成为了Java开发领域一个非常重要的框架。
2、学习spring framework
这门课程很重要,这是学习 spring 全家桶的基础,如果要学习其他的 spring 框架必须要学好这门课程。
三、开始学习
1、先来看看我们现在代码中的问题
这是我们以前写代码的方式,从图片中看,我们在业务层需要对 dao 实现类进行业务处理时,我们需要 New 一个 dao 接口的实现类出来。但是,现在 dao 接口有两个实现类,我们 New 一个对象已经不能满足我们了,当 dao 接口有多个实现类时,我们需要去 New 多个对象出来,就需要在业务层中去改动代码,每当 dao 接口有新的实现类,业务层就需要改一次代码,新 New 一个对象出来。
所以现在写代码的问题就是:耦合度太高了。
2、改动
我们这样一改是不是就不会依赖 dao 层具体的实现了,直接就定一个 dao 变量,不就可以调用方法了吗?不就可以运行起来了吗?答案是不可以,虽然说编译没有错误,但是运行之后就会报空指针异常。
因为 userDao 只是一个空的变量,并没有给它赋值,所以运行会报空指针异常。所以现在问题就是,我们现在需要这个对象,但是我们不能在业务层 New 出一个对象,怎么办呢?
这就是本章节要讲的内容 Spring 容器和 bean 的装配。
使用对象的时候,在程序中不再主动使用 New 产生对象,将 New 对象交给外部(spring容器)去做。
3、使用 spring 容器,并装配 bean
1)、 新建一个 Maven 项目,结构如下
2)、导入 spring 依赖
<!-- spring 的核心依赖 -->
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.23</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.4.5</version>
</dependency>
</dependencies>
导入之后会有五个包
Spring Framework是一个基于Java语言的开源应用框架,其核心部分由5个关键的模块组成:
spring-core.jar:Spring的核心容器,包含了IoC和DI的基本实现。
spring-context.jar:该模块建立在spring-core之上,提供Spring的整合上下文功能,包括对JNDI、EJB、邮件服务、调度任务等的支持。
spring-aop.jar:该模块提供了面向切面编程的特性,允许在程序运行期间动态地将代码横向插入到实现中。
spring-web.jar:该模块提供了一种方便的集成方式,使得Spring能够与Web应用程序进行更加紧密的集成。
spring-webmvc.jar:该模块提供了基于MVC模式的Web应用程序开发框架,通过提供一组可重用的MVC组件,使得开发人员可以轻松构建灵活的Web应用程序。
这五个核心模块可以根据需要进行逐一导入,并且它们也会默认导入其他必需的依赖项。当然,Spring Framework提供了非常丰富的扩展模块和第三方库,用户可以根据自己的需求选择性的导入相关的jar包。
3)、新建一个接口 UserService
public interface UserService {
void say();
}
4)、 新建一个包 impl,在包下新建一个类,UserService 的实现类 UserServiceImpl
@Slf4j
public class UserServiceImpl implements UserService {
@Override
public void say() {
log.info("Hello spring...");
}
}
注意:接口本身不能被实例化,因为接口是一个抽象的概念,无法直接创建对象。因此,你不能将接口本身作为bean进行配置和装配。但是,可以将接口的实现类作为bean进行配置和装配。Spring提倡面向接口编程,因此通常我们会定义接口,并在实际的业务类中实现该接口。然后,可以通过配置文件或者注解来将实现类注册为bean,并通过接口类型来引用和使用这些实现类的实例。
5)、在 resources 下新建一个 spring 的 xml 文件 beans.xml,在配置文件中完成 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">
<!-- 装配各种 bean 对象 -->
<!-- id 表示当前装配的 Bean 在容器中的唯一标识
Class 指定 Bean 的完整类名,spring 会使用这个完整类名反射
创建实例纳入容器中管理
-->
<bean id="userService" class="edu.nf.ch01.impl.UserServiceImpl"/>
</beans>
这里要注意了,bean对象不能是接口和抽象类,所以这里的 class 指定的就是 UserServiceImpl 实现类的完整类名。 还有 bean 的 id 在同一个配置文件中是不能重复的。
6)、测试,新建一个 Mai 类
public class Main {
public static void main(String[] args) {
// 创建容器工厂,(在 spring 框架中存在多种不同的容器工厂,每种容器工厂都有自身的特点和功能,例如:
// 当我们需要通过解析 xml 配置文件来初始化一个容器工厂时,可以使用 ClassPathXmlApplicationContext
// 这个容器工厂),而这些工厂最终实现的都是 ApplicationContext 接口
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
// 从容器中根据 bean 的 id 获取 bean 的实例
UserService userService = (UserService) context.getBean("userService");
// getBean 方法第二个参数执行泛型类型,因此不需要强转
UserService userService1 = context.getBean("userService", UserService.class);
// 如果接口的实现类只有一个,可以不需要指定 id,只需要泛型类型即可
UserService bean = context.getBean(UserService.class);
userService.say();
userService1.say();
bean.say();
}
}
这是获取 bean实例的三种方法,需要注意的是第三种,我们现在是只有一个实现类,当我们有两个及以上的实现类时,第三种方法就不可以使用了。
7)运行
运行之后是不是三种方式都可以调用到我们的 say 方法输出, 这三种方法大家自己选择使用。
四、 自定义工厂
1、在 impl 包下,新建一个 PeopleServiceImpl 实现类,实现 UserService 接口
public class PeopleServiceImpl implements UserService {
@Override
public void say() {
System.out.println("Hello people");
}
}
2、 在 edu.nf.ch01 包下,新建一个 factory 包,在包下新建一个 PeopleServiceFactory 类
public class PeopleServiceFactory {
/**
* 简单工厂,用来创建 PeopleServiceImpl 实例
* @return
*/
public UserService create(){
return new PeopleServiceImpl();
}
}
自定义工厂,并交给 spring 容器管理。第一种方法是把实现类交给容器管理,这种方法是创建一个简单工厂,这个工厂就负责创建具体的实现类的实例,然后再把这个工厂交给 spring 容器管理,是不是感觉和第一个一样呢。
3、 在 beans.xml,在配置文件中完成 Bean 的装配
<!-- 装配自定义工厂方式一,spring 会将我们编写的工厂类纳入容器管理 -->
<bean id="peopleFactoryBean" class="edu.nf.ch01.factory.PeopleServiceFactory"/>
<!-- 告诉 sprint 使用我们自定义的工厂来创建实例
factory-bean:引用上面自定义工厂的 id
factory-method:指定工厂中方法名
-->
<bean id="peopleService" factory-bean="peopleFactoryBean" factory-method="create"/>
4、测试
// 创建容器工厂,(在 spring 框架中存在多种不同的容器工厂,每种容器工厂都有自身的特点和功能,例如:
// 当我们需要通过解析 xml 配置文件来初始化一个容器工厂时,可以使用 ClassPathXmlApplicationContext
// 这个容器工厂),而这些工厂最终实现的都是 ApplicationContext 接口
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
// 使用自定义工厂创建实例
UserService peopleService = (UserService) context.getBean("peopleService");
peopleService.say();
运行结果
五、 使用 FactoryBean 接口自定义工厂(推荐)
1、在 impl 包下,新建一个 StudentServiceImpl 实现类,实现 UserService 接口
public class StudentServiceImpl implements UserService {
@Override
public void say() {
System.out.println("Hello student");
}
}
2、在 factory 包,在包下新建一个StudentServiceFactoryBean 类,实现 FactoryBean 接口
public class StudentServiceFactoryBean implements FactoryBean<UserService> {
/**
* 创建具体的 Bean 实例
* @return
* @throws Exception
*/
@Override
public UserService getObject() throws Exception {
return new StudentServiceImpl();
}
/**
* 返回 Bean 实例的 class 类型
* @return
*/
@Override
public Class<?> getObjectType() {
return StudentServiceImpl.class;
}
}
getObject():该方法返回所创建的 Bean 实例,这是工厂模式的核心方法。
getObjectType():该方法返回创建的对象的类型。
isSingleton():该方法返回 Bean 是否为单例。
这里没有实现 isSingleton() 方法,因为默认就是单例的。
3、 在 beans.xml,在配置文件中完成 Bean 的装配
<!-- 自定义工厂方式二,还可以使用 spring 提供的 FactoryBean 接口,创建自定义工厂(推荐)
当我们从容器中获取 studentService 这个 bean 的时候,
spring 会调用 studentServiceFactoryBean 的 getObject
方法返回创建好的对象
-->
<bean id="studentService" class="edu.nf.ch01.factory.StudentServiceFactoryBean"/>
4、测试
// 创建容器工厂,(在 spring 框架中存在多种不同的容器工厂,每种容器工厂都有自身的特点和功能,例如:
// 当我们需要通过解析 xml 配置文件来初始化一个容器工厂时,可以使用 ClassPathXmlApplicationContext
// 这个容器工厂),而这些工厂最终实现的都是 ApplicationContext 接口
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
// 实现 FactoryBean 接口创建 Bean 对象
UserService studentService = (UserService) context.getBean("studentService");
studentService.say();
运行结果
使用实现了 FactoryBean 接口的自定义工厂有以下好处:
1、灵活性:通过自定义工厂,你可以在创建 Bean 实例之前进行一些自定义逻辑,例如初始化、参数设置等。这样可以提供更大的灵活性来满足特定的需求。
2、隔离性:通过自定义工厂,你可以将一些复杂的实例化逻辑封装在工厂中,从而将其与业务逻辑相分离。这样可以提高代码的可维护性和可读性。
3、控制对象的生命周期:通过自定义工厂,你可以控制所创建的对象的生命周期。例如,可以设置为单例或原型模式,根据需要创建多个对象实例或共享单个对象实例。
4、延迟加载:当你的 Bean 对象很重量级或需要完成一些初始化操作时,你可以通过自定义工厂来延迟加载这些对象。只有当首次访问该对象时才会创建,从而提高应用程序的性能。
5、提供更高层次的抽象:通过自定义工厂,你可以提供一种更高层次的抽象,将复杂的创建过程封装在简单的接口后面。这样可以降低代码耦合度,提高代码的可扩展性和可复用性。
综上所述,使用实现了 FactoryBean 接口的自定义工厂可以为你的应用程序提供更大的灵活性、可控性和可维护性。
第一个入门案例就完成啦,大家是不是发现还是存在 New 一个对象出来的操作,代码之间的耦合度还是挺高的,该怎么解决呢?在后面的 DI:依赖注入中解决。