spring基础学习
SpringFrameWork
控制反转(IOC)和面向切面编程(AOP)是SpringFrameWork中最重要的两个概念
IOC容器
简介
IOC将对象的创建,组装和管理的控制权从应用程序代码中反转到spring的IOC容器中,由IOC容器的ApplicationContext,
实现对象的自动装配和依赖注入。
- 控制反转(IOC,Inversion of Control)控制指的是控制对象的创建过程,反转是反转创建对象的主体由程序员转化为容器, 在spring中容器是一个单例工厂,里面的每一个对象称之为bean,我们只需要在外部创建一个bean的构建过程,真正的创建由容器负责,在启动时期bean便完成了实例化
Bean的装配流程
对bean的管理都是依靠BeanFactory进行,可以通过BeanFactory对Bean进行生产和管理
1 | public static void main(String[] args) { |
BeanDefinition是Spring框架中的一个核心概念,它描述了Bean实例的属性和行为,并提供了创建和管理Bean实例的基础信息。BeanDefinition接口定义了一个Bean的元数据,包含了用于创建Bean对象实例的所有必要信息,如Bean的类名、作用域(如singleton或prototype等)、初始化方法、销毁方法、依赖项、属性值等。
BeanDefinition有多种配置方式
- 配置文件配置
1 |
|
- 注解配置
使用注解则更为方便,通过@Service,@Controller等便可声明为bean对象,在配置类中加入@componentScan就可以将其加入到IOC容器中进行管理,配置类中@Bean注解的方法是用来实例化并注册Bean到Spring应用上下文中的,Bean的本质还是@Service等注解的这些类。
Application
1 | public static void main(String[] args) { |
ApplicationContext时IOC容器中的核心接口,是BeanFactory的子接口,相较于BeanFactory有更丰富的功能和更好的性能,负责实例化,配置和管理Bean对象,以及建立这些对象间的依赖关系,当启动Spring项目时,他会创建一个ApplicationContext的实例,然后由他负责初始化和配置应用中的所有对象
现在,我们就已经知道,Bean实际上是一开始通过BeanDefinitionReader进行扫描,然后将所有Bean以BeanDefinition对象的形式注册到对应的BeanFactory中进行集中管理,而我们使用的ApplicationContext实际上内部就有一个BeanFactory在进行Bean管理,这样容器才拥有了最基本的Bean管理功能。
使用IOC容器管理的好处
降低代码耦合度
如果不使用IOC容器管理,则两个相互依赖的类则需要手动创建出实例,如果其中一个类需要改变,则需要改变的大量代码,使用IOC容器后,则不再创建实例,而是声明需要的依赖,由容器负责在运行时注入正确的实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27// 耦合度低的UserService示例(使用接口)
public class UserService {
private UserRepository userRepository; // 声明依赖,但不创建实例
// 通过构造函数注入依赖
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
// ... 其他业务方法
}
// UserRepository接口
public interface UserRepository {
// ... 定义方法
}
// UserRepository的具体实现
public class DatabaseUserRepository implements UserRepository {
// ... 实现方法
}
// 另一个UserRepository的实现(比如API调用)
public class ApiUserRepository implements UserRepository {
// ... 实现方法
}
在这个例子中,UserService
只依赖于UserRepository
接口,而不是具体的实现类。这意味着我们可以轻松地替换UserRepository
的实现,而无需修改UserService
的代码。只需在配置文件中修改相关的代码
安全性
在高并发情况下,如果每一步都需要new一个对象,则会增大对内存的压力,而IOC容器为单例模式,每次只需从缓存中获取,这样内存抖动不会太严重,保证程序的可靠性稳定性。
AOP
简介
AOP(Aspect-Oriented Programming,面向切面编程)是一种编程范式,旨在通过预编译方式和运行期间间动态代理实现程序功能的统一维护的一种技术。AOP 是对 OOP(Object-Oriented Programming,面向对象编程)的补充,用于处理系统中分布于各个模块的横切关注点(cross-cutting concerns),如日志、事务管理、安全等。这些横切关注点通常与业务逻辑无关,但会散落到各个模块中,导致代码难以维护。
核心概念
- 切面(Aspect):切面是一个横切关注点的模块化,这个关注点可能会横切多个对象。切面用来封装横切关注点(cross-cutting concern)的代码,该代码直接影响了业务类代码的执行。
- 连接点(Joinpoint):是程序执行过程中某个特定的位置,例如方法执行前,方法执行后等,可以理解为终须中可能插入前面的地方
- 通知(Advice):在特定的连接点,AOP框架执行的动作。例如,一个方法调用前打印日志,就是一个通知。
- 切点(Pointcut):切点用于定义哪些连接点应该被拦截。
通过切入点来匹配程序中的特定连接点,在这些连接点上执行通知,这种通知是在连接点前后执行,也可以将连接点包围起来。
连接点和切点的差异:
- 定义范围:连接点是程序执行过程中的一个特定位置,是一个具体的点;而切点是一组连接点的集合,是一个更广泛的概念。
- 作用对象:连接点是AOP可以插入额外代码或逻辑的地方,是AOP框架操作的对象;而切点则是用于指定哪些连接点需要被拦截并应用通知,是定义AOP操作范围的工具。
- 使用方式:在AOP中,连接点通常不需要显式定义,而是由AOP框架在运行时自动识别和定位;而切点则需要显式定义,通过规则或表达式来指定需要拦截的连接点。
总结来说,连接点和切点在AOP中各自扮演着不同的角色。连接点是AOP框架操作的具体位置,而切点则是用于定义AOP操作范围的工具,通过指定需要拦截的连接点来实现对程序的增强或修改。
SpringBoot
优势
在原始的springFramWork中,建立一个项目需要经过繁杂的依赖导入以及配置,依赖导入还经常会出现版本不匹配的问题,而SpringBoot通过其起步依赖和自动配置的特性就完美的解决了这两个问题,降低了学习成本,实现了开箱即用。
启动流程(简化版)
- 创建IOC容器 createApplicationContext(..)
- 加载源配置类 loadSourceClass(..) 源配置类通常是main方法所在的类,而且会被注解@SpringBootApplication所修饰,我们又称为主类。
- 加载并处理所有的配置类 processConfigurationClasses(..) SpringBoot会自动找到所有配置类,然后加载处理他们。“自动配置”就属于其中的一环。
- 实例化所有的单例bean instantiateSingletonBeans(..) 实例化所有的单例Bean。“依赖注入”和“自动装配”就属于其中的环节
- 启动web服务器
起步依赖
优势
- 简化配置:Spring Boot的起步依赖通过预定义和整合了常用的依赖项,极大地简化了项目的配置工作。例如,当你添加了
spring-boot-starter-web
依赖后,Spring Boot会自动配置内嵌的Web服务器、Spring MVC和其他相关组件,而无需你手动配置这些组件。 - 快速启动:由于减少了手动配置的需要,开发者可以更快地启动和交付高质量的应用。你只需选择适合项目需求的起步依赖,Spring Boot就会自动为你处理底层的配置和依赖管理。
- 一致性和可维护性:通过使用预定义的起步依赖,可以确保项目在不同环境和开发者之间具有一致性和可维护性。这些起步依赖都经过Spring Boot团队的精心选择和测试,以确保它们之间的兼容性和稳定性。
- 减少错误:由于起步依赖已经处理了底层框架和库的配置和管理,因此可以减少因手动配置错误而导致的运行时问题。这有助于降低项目的风险和成本。
- 易于扩展:虽然起步依赖为开发者提供了很多便利,但它们仍然是可扩展的。如果你需要添加或修改某个特定的依赖项,你可以很容易地在项目中添加或排除它。
自动配置
简介
SpringBoot自动配置就是Spring容器启动后,一些配置类,bean对象就自动存入了IOC容器中,不需要我们手动去声明, 从而简化了开发,省去了繁杂的配置操作。
配置类
- 广义的配置类:被注解@Component直接或间接修饰的某个类,即我们常说的Spring组件,其中包括了@Configuration类
- 狭义的配置类:特指被注解@Configuration所修饰的某个类,又称@configuration类。
如果没有特殊说明,我们说的配置类一般指广义的配置类,狭义的配置类我们一般称@Configuration类。
原理
从源配置类开始通过注解@ComponentScan和注解@Implort不断地递归遍历新的配置类,直到没有新的配置类被发现位置,得到的一个配置类集合,将配置类本身注册到IOC容器中,处理配置类中的@Bean方法,将其返回类型注册到IOC容器中,处理通过@Import导入的ImportBeanDefinitionRegistrar
我们的启动类上有@SpringBootApplication
,@SpringBootApplication
是@SpringBootConfiguration
,@ComponentScan
,@EnableAutoConfiguration
这三个注解组合而成
@SpringBootConfiguration
注解表明启动类是一个特殊的配置类,它间接地由@Configuration
注解修饰,表示该类是一个Spring配置类,可以定义Bean。@ComponentScan
注解用于告诉Spring Boot在哪些包中查找Spring组件、配置和服务。默认情况下,它会扫描启动类所在的包及其子包。@EnableAutoConfiguration
注解是Spring Boot的核心特性之一,它告诉Spring Boot根据添加的jar依赖项、类路径中的其他项以及各种属性设置来自动配置Spring应用。这个注解通过@Import
注解引入了AutoConfigurationImportSelector
,它实现了ImportSelector
接口,从而可以决定在运行时哪些自动配置类应当被添加到应用中。AutoConfigurationImportSelector
在其selectImports
方法中,通过加载META-INF/spring.factories
文件(在Spring Boot 2.7及之前版本,如今读取的是META-INF
目录下的.imports
文件)来确定哪些自动配置类需要被加载。这个文件包含了多个自动配置类的全类名列表,这些类包含了各种Bean的定义和配置。@Condition
注解及其衍生注解(如@ConditionalOnClass
、@ConditionalOnProperty
等)用于在自动配置过程中添加条件。如果条件满足,相关的Bean就会被加载到Spring容器中。否则,这些Bean会被忽略。读取到的自动配置类会创建相应的
BeanDefinition
。BeanDefinition
是Spring框架中用于描述Bean的元数据对象,包含了Bean的各种属性(如名称、作用域、初始化方法等)和配置信息。在Spring容器启动过程中,会根据这些BeanDefinition
来创建Bean的实例。最后,Spring容器会按照这些
BeanDefinition
的定义来实例化、配置和组装Bean,从而构建出整个应用的对象图。
注解
@Import
@Import
注解是一个非常重要的注解,它允许你导入其他配置类、组件或配置,以便在当前的Spring上下文中使用。这个注解主要用于扩展Spring的配置和功能。导入普通类:你可以使用@Import注解来导入一个普通类使之成为Bean对象
1
2
3
4
5
public class MainConfig{
//...
}导入配置类:你可以使用
@Import
注解来导入其他配置类,这样你就可以在一个配置类中组合多个配置。1
2
3
4
5
public class MainConfig {
// ...
}导入ImportSelector的实现:
ImportSelector
是一个接口,它允许你动态地选择并返回要导入的配置类。这对于根据条件或外部配置来加载不同的配置类特别有用。1
2
3
4
5
6
7
8
9
10
11
12
public class MainConfig {
// ...
}
public class MyImportSelector implements ImportSelector {
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"com.example.AnotherConfig"};
}
}导入ImportBeanDefinitionRegistrar的实现:
ImportBeanDefinitionRegistrar
接口允许你在运行时动态地注册bean定义。这给了你更多的灵活性,可以基于复杂的条件或外部输入来注册bean。1
2
3
4
5
6
7
8
9
10
11
12
13
public class MainConfig {
// ...
}
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(AnotherBean.class);
registry.registerBeanDefinition("anotherBean", rootBeanDefinition);
}
}
自定义starter
需求
自定义mybatis的starter
步骤
创建
dmatis-spring-boot-autoconfigure
模块,提供自动配置功能,并自定义配置文件META-INF/spring/xxx.imports
创建
dmatis-spring-boot-starter
模块,在stater
模块中引入自动配置模块
dmatis-spring-boot-autoconfigure
模块
在pom文件中导入依赖
1 | <dependency> |
编写配置类
1 | //表示这是一个自动配置类 |
编写配置文件META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
1 | com.example.config.MybatisAutoConfig #配置类的全类名 |
至此dmatis-spring-boot-autoconfigure
已具备自动配置的功能了
dmatis-spring-boot-starter
模块
在pom文件中导入依赖
1 | <dependency> |
至此dmatis-spring-boot-starter
已具备依赖管理的功能了,在项目中只需要引入该starter
这个工程就可以直接整合mybatis了
SSM框架(Spring+Spring MVC+Mybatis)
优势
- 轻量级与高效性:SSM框架是一种轻量级的开发框架,它部署简单、启动速度快,并且具有高效的性能表现,能够快速响应用户请求。这种轻量级和高效性的特点使得SSM框架成为开发企业级Java Web应用程序的理想选择。
- 模块化设计:SSM框架采用了模块化的设计,将业务逻辑分成多个模块。这种设计使得开发人员可以更加专注于单个模块的开发和维护,提高了开发效率。同时,模块之间的松耦合也使得整个应用程序更加易于扩展和维护。
- 灵活性:SSM框架采用了依赖注入(DI)和面向切面编程(AOP)的思想,这使得开发人员可以更加灵活地组织和管理代码。依赖注入可以帮助开发人员实现代码之间的解耦,而AOP则允许开发人员在不修改业务代码的情况下,为整个应用程序添加或修改横切关注点。
- 可扩展性:SSM框架具有出色的可扩展性。Spring框架提供了丰富的扩展点和插件机制,可以方便地扩展和定制框架的功能。这使得开发人员可以根据具体的业务需求,灵活地添加或修改框架的功能,以满足不同的需求。
- 易于集成:SSM框架与其他第三方库和工具的集成比较容易。例如,它可以轻松地与数据库、缓存、消息队列等进行集成,从而提高了整个应用程序的可用性和可维护性。
- 清晰的分层结构:SSM框架的分层结构非常清晰,包括表示层(由Spring MVC负责)、业务逻辑层(由Spring负责)和数据访问层(由MyBatis负责)。这种清晰的分层结构使得开发人员能够快速地定位和解决问题,提高了开发效率和维护性。
- 易于维护:由于SSM框架的模块化设计和清晰的分层结构,使得代码结构清晰、易于维护。当某个模块出现问题时,开发人员可以快速地定位并修复问题,而不需要对整个应用程序进行大规模的修改。
SSM框架在Java Web应用程序开发中具有许多显著的优点,这些优点使得它成为开发企业级Java Web应用程序的首选框架之一。
Spring
在SSM框架中,Spring框架作为基础框架,为整个应用程序提供了强大的支持和保障。它使得开发者可以更加专注于业务逻辑的实现,而无需关心底层框架的细节和复杂性。
功能
- 轻量级开源框架:Spring是一个轻量级的Java EE框架,它使得企业级应用程序的开发变得更为简单和高效。
- 分层结构:Spring为不同的层都提供了企业级解决方案,包括web层(通过Spring MVC)、service层(通过Spring的各种服务组件)和dao层(通过JDBCTemplate等)。
- 核心思想:Spring的核心思想是IOC(控制反转)和AOP(面向切面编程)。通过IOC,Spring可以自动管理对象的生命周期和依赖关系,从而降低代码之间的耦合度。而AOP则允许开发者在不修改已有代码的情况下,增加新的功能,如日志、事务管理等。
- 容器功能:Spring是一个对象容器框架,它负责创建和管理应用程序中的对象。开发者只需要定义好对象的配置信息(如依赖关系、生命周期等),然后Spring就会在运行时自动创建和管理这些对象。
- 事务管理:Spring提供了强大的事务管理功能,可以支持声明式事务和编程式事务。这使得开发者可以更容易地实现复杂的事务逻辑,并保证数据的完整性和一致性。
- 集成性:Spring可以很容易地与其他框架和库进行集成,如Hibernate、MyBatis等。这使得开发者可以根据项目的需求选择合适的技术栈,并快速地构建出高效的应用程序。
Spring MVC
简介
Spring MVC 是一个基于 Java 的实现了 Web MVC 设计模式的请求驱动类型的轻量级 Web 框架,它可以帮助开发者简化 Web 应用程序的开发。
MVC(Model View Controller)
Model(模型层)
在项目中,模型层通常指的是业务逻辑和数据处理的部分。它包含了与数据交互的业务对象(Service)和数据访问对象(DAO),以及用于传输数据的 JavaBean(DTO)或实体类(Entity)。
例如,在一个电商项目中,模型层可能包含:
- UserService:负责处理与用户相关的业务逻辑,如用户注册、登录、查询等。
- ProductDAO:负责与数据库交互,执行产品数据的增删改查操作。
- UserDTO:用于在前后端之间传输用户数据的简单 JavaBean,包含用户的ID、用户名、密码等信息。
View(视图层)
在前后端分离的模式下,视图层主要由前端应用负责实现,如 Web 页面、移动应用界面等。后端通过 API 接口向前端提供数据,前端则负责展示这些数据并与用户进行交互。
Controller(控制层)
在 Spring MVC 中,控制层负责接收前端发送的请求,调用模型层处理业务逻辑和数据,然后将处理结果返回给前端。在前后端分离的项目中,控制层通常负责处理 HTTP 请求,并将数据以 JSON、XML 等格式返回给前端。
例如,在电商项目中,控制层可能包含:
- UserController:负责处理与用户相关的 HTTP 请求,如用户注册、登录等。它会调用 UserService 来处理业务逻辑,并将处理结果转换为 JSON 格式返回给前端。
- ProductController:负责处理与产品相关的 HTTP 请求,如查询产品列表、获取产品详情等。它会调用 ProductDAO 来从数据库中获取产品数据,并将数据转换为 JSON 格式返回给前端。
DispatcherServlet(前端控制器)
核心工作是请求的分发和响应结果的处理
- 接收请求:
DispatcherServlet
接收所有来自Web客户端(如浏览器)的HTTP请求。 - 解析请求:解析请求中的信息,包括请求参数、HTTP方法(GET、POST等)、请求的URI等。
- 确定处理程序:根据请求的URI和其他相关信息,通过HandlerMapping(处理器映射)确定要处理这个请求的处理器(通常是Controller中的一个方法)。
- 调用处理程序:调用确定的处理器来处理请求。这通常涉及调用一个或多个服务层组件来处理业务逻辑。
- 解析并渲染视图:处理器返回一个ModelAndView对象,其中包含要渲染的视图名称和模型数据。
DispatcherServlet
使用ViewResolver(视图解析器)来确定要渲染的视图类型(如JSP、Thymeleaf等),并将模型数据传递给视图进行渲染。 - 返回响应:渲染后的视图作为HTTP响应返回给Web客户端。
Mybatis
简介
MyBatis是一个优秀的持久层框架,它支持自定义SQL、存储过程以及高级映射。MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。MyBatis可以使用简单的XML或注解来配置和映射原始类型、接口和Java POJO(Plain Old Java Objects,普通的Java对象)为数据库中的记录。
作用
在SSM框架中,MyBatis的主要职责是与数据库进行交互,完成数据的增删改查操作。它封装了底层的JDBC API,使得开发者可以更加专注于SQL语句的编写和数据库操作,而无需关心JDBC连接、语句创建、参数设置和结果集处理等繁琐的细节。
SpringSecurity(安全校验)
简介
Spring Security 是 Spring 家族中的一个安全管理框架。相比与另外一个安全框架Shiro,它提供了更丰富的功能,社区资源也比Shiro丰富。
一般来说中大型的项目都是使用SpringSecurity 来做安全框架。小项目有Shiro的比较多,因为相比与SpringSecurity,Shiro的上手更加的简单。
一般Web应用的需要进行认证和授权,而认证和授权也是SpringSecurity作为安全框架的核心功能。
认证:验证当前访问系统的是不是本系统的用户,并且要确认具体是哪个用户
授权:经过认证后判断当前用户是否有权限进行某个操作
大致流程
SpringSecurity的原理是一个过滤器链,内部包含了提供各种功能的过滤器
一个web请求会经过一条过滤器链,在经过过滤器链的过程中会完成认证与授权,如果中间发现这条请求未认证或者未授权,会根据被保护API的权限去抛出异常,然后由异常处理器去处理这些异常。
核心过滤器
UsernamePasswordAuthenticationFilter(认证)
:负责处理我们在登录页面填写了用户名密码后的登录请求ExceptionTranslationFilter
:处理过滤器链中抛出的任何AccessDeniedException
和AuthenticationException
FiletrSecurityInterceptor(授权)
:负责权限校验的过滤器
认证流程
Authentication
接口: 它的实现类,表示当前访问系统的用户,封装了用户相关信息;AuthenticationManager
接口:定义了认证Authentication
的方法;UserDetailsService
接口:加载用户特定数据的核心接口。里面定义了一个根据用户名查询用户信息的方法;UserDetails
接口:提供核心用户信息。通过UserDetailsService
根据用户名获取处理的用户信息要封装成UserDetails
对象返回,然后将这些信息封装到Authentication
对象中。这里我们只需要将他的
Filter
换为我们的登录接口,并且有我们实现UserService这个接口,从而实现从数据库中读取数据,就可以实现下图的效果
重要概念
SecurityContext:上下文对象,Authentication
对象会放在里面。
SecurityContextHolder:用于拿到上下文对象的静态工具类。
Authentication:认证接口,定义了认证对象的数据形式。
AuthenticationManager:用于校验Authentication
,返回一个认证完成后的Authentication
对象。
SecurityContext
一个上下文对象,用于存取认证成功后,用户的信息权限等
1 | public interface SecurityContext extends Serializable { |
SecurityContextHolder
一个用来获取SecurityContext
的工具类
1 | public class SecurityContextHolder { |
SecurityContextHolder
默认使用 ThreadLocalSecurityContextHolderStrategy
作为其策略,该策略使用 ThreadLocal
来存储 SecurityContext
。这意味着 SecurityContext
是与当前线程绑定的,每个线程都可以有自己的 SecurityContext
实例。在web环境下,Spring Security
在用户登录时自动将认证信息绑定到当前线程,在用户退出时自动清除当前线程的认证信息。
Authentication
1 | public interface Authentication extends Principal, Serializable { |
getAuthorities
:返回主体拥有的权限集合。这些权限通常用来决定主体是否有权访问某个资源或执行某个操作。返回值是一个权限的集合,在实际中由于一般使用角色与权限相对应,所有通常返回的也就是角色的信息getCredentials
:获取证明用户认证的信息,通常情况下获取到的是密码等信息。getDetails
:返回与当前认证主体相关的附加信息。getPrincipal
:获取用户身份信息,在未认证的情况下获取到的是用户名,在已认证的情况下获取到的是UserDetails
。isAuthenticated
:获取当前的Authentication
是否已经认证setAuthenticated
:设置当前Authentication
是否被认证
AuthenticationManager
1 | public interface AuthenticationManager { |
它可以接收未认证的Authentication
,并返回一个已认证的Authentication
认证流程
通过一个接口,获取到用户的相关信息,封装成Authentication
,通常情况下是UsernamePasswordAuthenticationToken
这个实现类,传入AuthenticationManager
进行认证,再经过SecurityContextHolder
获取到SecurityContext
,将认证信息存入其中