开发基础

常用类库

基础

  • Hutool:国产后起之秀,Hutool是一个小而全的Java工具类库,通过静态方法封装,降低相关API的学习成本,提高工作效率
  • Google Guava:Guava工程包含了若干被Google的 Java项目广泛依赖 的核心库,例如:集合 [collections] 、缓存 [caching] 、原生类型支持 [primitives support] 、并发库 [concurrency libraries] 、通用注解 [common annotations] 、字符串处理 [string processing] 、I/O 等等
  • Apache Commons:Apache Commons是对JDK的拓展,包含了很多开源的工具,用于解决平时编程经常会遇到的问题,减少重复劳动
  • Spring常用工具类:Spring作为常用的开发框架,在Spring框架应用中,排在ApacheCommon,Guava, Huool等通用库后,第二优先级可以考虑使用Spring-core-xxx.jar中的util包

JSON库

org.json
  • 这是一个非常基础且轻量级的库,包含了一些基本的类来创建、解析和转换JSON数据。尽管功能相比Jackson和Gson较为有限,但对于简单的应用场景而言,它已经足够使用。
  • 特点:轻量、无需依赖额外库、适用于基本需求。
Jackson
  • Jackson是最流行且性能优异的Java JSON库之一。它提供了完整的数据绑定功能,可以将Java对象转换为JSON字符串,反之亦然。Jackson包括几个模块,如jackson-databind, jackson-core, 和jackson-annotations,支持流式API和树模型等多种解析方式。
  • 特点:高性能、全面的功能集、社区活跃。
Gson
  • 由Google开发,Gson提供了简单易用的API来进行JSON与Java对象之间的序列化和反序列化。它的设计目标是尽可能地无痛使用,同时也支持一些高级特性和泛型。
  • 特点:使用简单、代码清晰、支持复杂类型的转换
Fastjson
  • 阿里巴巴开源的高性能JSON处理器,以其快速的解析速度和小巧的体积受到很多开发者的喜爱。Fastjson支持JSON与Java对象的相互转换,同时也提供了丰富的API来处理JSON数据。
  • 特点:快速、轻量、功能丰富,特别适合中国国情,支持一些特定的日期格式处理
  • 有漏洞,谨慎使用

Lombok工具库用来解决什么问题

Lombok 是一个工具,包括 Java 库和开发工具的插件,它提供注解的方式,在编译时自动为Java类生成构造器、getter/setter、equals、hashCode和toString等常用方法,从而极大地减少了编写这些模板代码的工作量

Lombok基本使用

@Data

@Data 最常用的注解之一。注解在类上,提供该类所有属性的 getter/setter 方法,还提供了 equals、canEqual、hashCode、toString 方法

@EqualsAndHashCode

重写 equals、hashCode 方法

@Slf4j

作用于类上,为该类提供一个属性名为 log 的 log4j 日志对象

1
log.debug("{}-{},修改之后的注解值:{}", className.getName(), fieldName, tableField.exist());
@Accessors(chain = true)

提供链式写法,set 操作的返回值就是对象本身

@Builder
  1. Entity 上加上 @Builder 时,会默认为类加上全参构造函数,且提供以建造器模式构造对象的方法
  2. 但因为显示声明了全参构造器,默认的无参构造器就失效了,就不能通过 new Obj () 的方式创建对象
  3. 但是如果显式声明了 @NoArgsConstructor,lombok 就不会生成全参构造函数,而 @Builder 中会用到全参构造函数,所以冲突

解决:

1
2
3
@Builder
@NoArgsConstructor
@AllArgsConstructor

为什么很多公司禁止使用lombok?

  1. 代码可读性与可维护性:Lombok通过注解自动生成代码,这意味着实际执行的代码并不直接展示在源文件中。这降低了未安装Lombok插件的开发者阅读和理解代码的便利性,同时也可能使得代码审查和维护变得更加困难。
  2. 编译依赖与工具兼容性:Lombok作为一个编译时插件,需要特定的IDE配置和支持。新加入的团队成员或使用不同开发环境的开发者可能需要额外配置,有时还会遇到与构建工具、持续集成/持续部署(CI/CD)管道或特定IDE版本的兼容性问题。
  3. 学习曲线与团队一致性:对于不熟悉Lombok的开发者来说,需要时间去学习其注解的用法及其背后的工作原理。团队内部若不是所有人都熟悉或同意使用Lombok,可能会导致代码风格不一致,影响团队协作。
  4. 潜在的bug与问题:虽然Lombok简化了代码,但其自动生成的代码可能在特定场景下不符合预期,例如不恰当使用@Data注解可能导致性能问题或不正确的equals/hashCode实现。此外,过度依赖Lombok可能导致开发者忽视了Java基础,如理解equals和hashCode方法的正确实现。
  5. JDK版本与长期支持:虽然Lombok努力保持与最新Java版本的兼容性,但一些公司可能担心未来Java版本更新带来的不确定性,以及Lombok的长期维护和支持问题。
  6. 审计与合规性:对于金融、医疗等行业,代码的可审计性至关重要。自动生成的代码可能难以满足严格的审计要求,特别是当涉及到数据处理和安全规范时。

MapStruct工具库用来解决什么问题

主要用于解决对象之间的拷贝问题,比如PO/DTO/VO/QueryParam之间的转换问题。区别于BeanUtils这种通过反射,它通过编译器编译生成常规方法,将可以很大程度上提升效率

Lombok和MapStruct工具库的原理?

  • 核心之处就是对于注解的解析上
  • JDK5引入了注解的同时,也提供了两种对注解的解析方式。编译时解析运行时解析

网络协议和工具

什么是754层网络模型?

全局上理解 7层协议,4层,5层的对应关系

img

DNS 解析流程

DNS(Domain Name System,域名系统)解析流程是一个将人类可读的域名转换为计算机可识别的IP地址的过程,这一过程涉及多个步骤,通常遵循以下序列:

  1. 查询浏览器缓存
    当用户尝试访问一个网站时,浏览器首先检查其自身的DNS缓存中是否有该域名对应的IP地址。如果存在且未过期,则直接使用该IP地址。

  2. 查询操作系统缓存
    如果浏览器缓存中没有找到相应的记录,操作系统(如Windows、macOS或Linux)会检查其自身的DNS缓存。

  3. 检查本地HOSTS文件
    接下来,操作系统会查看本地HOSTS文件,这是一个允许手动定义域名到IP地址映射的文件。如果域名在HOSTS文件中有定义,系统将使用这个映射关系。

  4. 查询本地DNS服务器
    如果上述所有缓存中都没有找到所需记录,请求会被转发到用户的本地DNS服务器,这通常是由ISP(互联网服务提供商)提供的,或者是在网络配置中手动设定的。

  5. 递归查询外部 DNS服务器
    本地DNS服务器开始进行递归查询,首先它会向根域名服务器发送请求。根域名服务器不会直接提供目标域名的IP地址,而是告诉本地DNS服务器下一步应该查询哪个顶级域名服务器(如.com、.net、.org等)。

  6. 查询顶级域名服务器
    本地DNS服务器根据根域名服务器的指引,向相应的顶级域名服务器查询。顶级域名服务器再进一步指引到负责该域名的具体权威域名服务器。

  7. 查询权威域名服务器
    最终,权威域名服务器会提供该域名对应的IP地址给本地DNS服务器。这个过程中可能涉及多级子域名的查询,直至找到最终的权威记录。

  8. 返回结果并缓存
    一旦本地DNS服务器获得了IP地址,它会将这个结果返回给用户的设备,并且通常会把这个映射关系在其自身缓存中保存一段时间,以便后续相同请求能更快响应。同时,用户的设备和操作系统也可能缓存这个结果,减少未来的查询时间。

  9. 客户端收到IP并建立连接
    用户的设备接收到IP地址后,就可以用这个IP地址与远程服务器建立网络连接,从而加载网页内容或其他服务。

整个DNS解析流程设计了多级缓存机制,旨在提高效率,减少因DNS查询造成的延迟。

什么是DNS劫持?

DNS劫持是一种网络安全攻击手段,攻击者通过非法手段取得了对DNS(域名系统)服务器的控制权,或者在用户的设备上修改了DNS设置,进而操控域名解析的过程。正常情况下,当你在浏览器输入一个网站域名时,DNS服务器会将这个域名转换成对应的IP地址,让你的设备能够访问正确的网络资源。但在DNS劫持发生时,攻击者会恶意篡改DNS记录,使你的请求被重定向到一个由攻击者控制的错误IP地址上。

具体来说,DNS劫持可以有以下几种形式:

  1. 本地DNS缓存污染:在用户的电脑上植入恶意软件,修改本地DNS缓存
  2. 路由器DNS劫持:某些ISP(互联网服务提供商)或路由器固件中的默认设置可能会实施合法的DNS重定向,用于广告或者其他目的,但这同样可被恶意利用进行非法劫持
  3. 篡改DNS服务器记录:攻击者通过漏洞利用、弱口令或其他安全漏洞,直接修改DNS服务器上的域名解析记录,使其指向恶意服务器的IP地址
  4. BGP(边界网关协议)劫持:这是一种更高级的攻击,攻击者通过篡改互联网路由表,使全球范围内的流量被重定向到恶意服务器,影响范围更广

DNS劫持的后果可能包括:用户被引导至假冒网站,导致个人信息被盗;网站流量被劫持,用于广告收益;或者分布式拒绝服务(DDoS)攻击的放大等。为了防范DNS劫持,用户和组织应采取加强密码安全、使用HTTPS、定期检查DNS设置、使用DNSSEC(域名系统安全扩展)等安全措施。

什么是DNS污染?

伪造错误 DNS 记录

DNS污染,也称为DNS缓存投毒(DNS cache poisoning),是一种网络攻击手段,它通过向DNS解析过程中插入虚假的DNS记录,导致用户在尝试访问特定网站时被重定向到错误的IP地址,从而无法访问正确的网络服务或被引导至恶意网站。DNS污染主要利用了DNS协议的一些特性:

  1. 无连接、无认证的UDP查询:DNS查询默认使用UDP协议,它是无连接的,这意味着查询和响应之间没有固定的会话关联,且缺乏内置的安全验证机制。攻击者可以利用这一点,向用户的DNS查询路径中的任一环节发送虚假的DNS响应,这些响应如果比正确的响应更快到达用户,就可能被误认为是合法的。

  2. 缓存机制:DNS系统广泛使用缓存来加速查询过程,一旦一个DNS服务器接收到一个域名到IP地址的映射,它可能会缓存这个映射一段时间。DNS污染就是通过向DNS服务器的缓存中插入错误的映射记录来实现的。

  3. 攻击实施:攻击者通过监控网络中的DNS查询请求,一旦发现与目标域名相关的查询,便迅速向查询发起方发送一个伪造的DNS响应,这个响应包含了攻击者控制的IP地址而非真实的域名解析结果。由于DNS查询的无连接特性,加上伪造响应的快速发送,使得伪造的响应有机会在真实的响应到达之前被接受,从而“污染”了DNS缓存或误导了直接查询的用户。

DNS污染的影响包括阻止用户访问特定网站、重定向流量至恶意站点以窃取信息或进行欺诈,以及干扰网络服务的正常运行。解决DNS污染的方法通常包括使用安全的DNS解析服务(如通过HTTPS的DNS-over-HTTPS, DoH,或TLS加密的DNS-over-TLS, DoT)、使用信誉良好的公共DNS服务器、或者在客户端层面使用VPNs来绕过本地的DNS解析限制。

输入URL 到页面加载过程?

从在浏览器地址栏输入URL到页面加载完成,整个过程大致可以分为以下几个步骤:

  1. 输入URL:用户在浏览器的地址栏输入URL并按下回车键
  2. URL解析与检查缓存
    • 浏览器首先解析URL,提取出协议、域名、路径等信息。
    • 检查浏览器缓存、操作系统缓存、路由器缓存以及ISP的DNS缓存中是否已有该域名的IP地址记录。如果有且未过期,则直接使用缓存的IP地址
  3. DNS解析
    • 如果缓存中没有找到IP地址,浏览器会向本地DNS解析器发起DNS请求
    • 本地DNS解析器向上查询,依次经过本地缓存、路由器、ISP的DNS服务器,直到根域名服务器,逐步缩小查询范围,最终找到负责该域名的权威DNS服务器
    • 权威DNS服务器返回该域名对应的IP地址
  4. TCP连接建立(针对HTTP协议):
    • 浏览器使用获取到的IP地址+URL 中的端口与服务器建立TCP连接,这通常涉及到TCP的三次握手过程,确保数据传输的可靠性
  5. 发送HTTP请求
    • 连接建立后,浏览器向服务器发送HTTP请求,请求中包含URL、浏览器信息、接受的内容类型等
  6. 服务器处理请求
    • 服务器接收到请求后,进行处理,可能包括数据库查询、服务器端脚本执行等
  7. 服务器响应
    • 服务器将处理结果封装成HTTP响应,包括状态码、响应头和响应体(即网页内容)
  8. 浏览器接收响应
    • 浏览器接收到响应后,开始解析HTML文档,构建DOM树
  9. 加载资源
    • 在解析HTML过程中,浏览器遇到CSS、JavaScript、图片等外部资源的引用时,会发起额外的HTTP请求来下载这些资源
    • 这些请求同样会经历DNS解析、TCP连接建立等步骤
  10. 渲染页面
    • 浏览器根据CSS构建渲染树,并计算布局,然后将渲染树的各个节点绘制到屏幕上
    • JavaScript执行,可能会影响DOM结构、CSS样式以及执行异步操作,如AJAX请求
  11. 页面加载完成
    • 当初始HTML文档及其引用的所有资源加载完毕,且所有脚本执行完成(如果有的话),浏览器标记页面为“加载完成”
  12. TCP连接断开(针对HTTP协议):
    • 页面加载完成后,TCP连接通常会通过四次挥手的过程被关闭,尽管对于HTTP/2和HTTP/3,连接可能保持开启以供后续请求复用

这个过程涉及到多个技术层次的交互,包括网络协议、浏览器行为、服务器处理以及页面渲染技术等

开发安全

开发中有哪些常见的Web安全漏洞?

在Web开发中,常见的安全漏洞主要包括但不限于以下几种:

  1. SQL注入:攻击者通过在输入字段中插入恶意SQL代码,以此来操纵数据库查询,获取、修改或删除敏感数据。
    • 输入验证:检查用户输入的合法性,以确保输入的内容为正常的数据。数据检查应当在客户端和服务器端都加上
    • Druid 数据库连接池提供了一个内置的 SQL 防注入模块,名为 WallFilter,用于检测和阻止潜在的 SQL 注入攻击
  2. **跨站脚本攻击(XSS)**:攻击者通过在网页中注入恶意脚本,当其他用户浏览该页面时,脚本被执行,可能窃取用户信息、进行钓鱼攻击等。
  3. **跨站请求伪造(CSRF)**:攻击者利用用户已经登录的身份,在用户不知情的情况下执行操作,如转账、修改密码等。
  4. 不安全的对象直接引用:当应用直接暴露了内部对象如文件、数据库记录的标识符,攻击者可能通过修改这些标识符来访问未授权的数据。
  5. 认证与会话管理缺陷:包括弱口令、会话标识未加密传输、会话固定攻击等,这些都可能导致攻击者冒充合法用户。
  6. 敏感数据泄露:缺乏加密或不当存储敏感信息,如密码、信用卡号等,容易被截取和利用。
  7. 功能级访问控制缺失:未对用户权限进行适当限制,允许低权限用户访问或操作高权限功能。
  8. 安全配置错误:应用或服务器的默认设置未被正确调整,留下易被利用的安全漏洞。
  9. 使用含有已知漏洞的组件:使用未经更新的库、框架或服务,这些可能包含已知的安全漏洞。
  10. 不安全的反序列化:反序列化过程中的漏洞可能被利用来执行任意代码或造成其他安全问题。
  11. 不安全的加密存储:使用弱加密算法或不当的密钥管理,导致数据容易被破解。
  12. **服务器端请求伪造(SSRF)**:攻击者利用应用程序发送的请求来访问内部系统或执行未授权操作。

防范这些漏洞通常需要综合运用多种策略,包括但不限于输入验证、输出编码、使用参数化查询、实施严格的访问控制策略、加密敏感数据、及时更新依赖库、安全配置服务器和应用程序、实施安全编码规范等。同时,定期进行安全审计和渗透测试也是确保Web应用安全的重要手段。

什么是 XSS

XSS,全称为跨站脚本攻击(Cross-Site Scripting),是一种常见的网络安全漏洞,允许攻击者在其他用户的浏览器中注入并执行恶意脚本代码。这种攻击方式利用的是Web应用程序对用户提供的数据处理不当,没有充分验证或过滤,导致恶意脚本能够嵌入到动态网页的内容中。

XSS攻击可以分为三大类:

  1. 存储型XSS(Persistent XSS):攻击者将恶意脚本注入到Web服务器上存储的数据中,如论坛帖子、评论或用户个人资料中。当其他用户访问含有恶意脚本的页面时,脚本将在他们的浏览器中执行。

  2. 反射型XSS(Non-Persistent or Reflected XSS):这种类型的XSS通过带有恶意脚本的URL传播,用户点击特制的链接后,恶意脚本作为请求的一部分发送给服务器,服务器未经恰当处理就将脚本内容“反射”回用户的浏览器执行。

  3. DOM-based XSS:这种XSS发生在浏览器的Document Object Model(DOM)层面,而不是服务器响应。攻击者通过修改网页的DOM结构来插入恶意脚本,当浏览器解析修改后的DOM时,脚本激活。

XSS攻击的目的多样,包括但不限于窃取用户会话信息(如Cookies)、进行身份冒充、传播恶意软件、操纵网页内容或进行钓鱼攻击等。防御XSS攻击的关键在于对用户输入进行严格的验证编码和过滤,以及实施内容安全策略(CSP)等安全措施。

什么是CSRF

CSRF(Cross-Site Request Forgery,跨站请求伪造)是一种网络攻击方式,它利用用户在已登录网站的合法身份,诱使用户在不知情的情况下执行攻击者希望他们执行的操作。这种攻击形式利用了Web浏览器cookie的自动发送机制,即使在不同的域名之间,只要它们属于同一个站点,浏览器也会自动附带该站点的cookie。

具体来说,当用户已经登录了一个信任的网站A,并且在这个网站上有活跃的会话,攻击者就可以在另一个不受信任的网站B上构造一个请求,这个请求是针对网站A的。如果用户此时访问了网站B并触发了这个恶意请求(可能是点击链接、提交表单或者是通过JavaScript自动发送的请求),浏览器会自动带上登录网站A的cookie,使得这个请求看似是从用户自己发起的合法请求,从而绕过了网站A的身份验证机制。

CSRF攻击的目标通常是执行一些敏感操作,如转账、修改密码、添加管理员账号等。由于这些操作是在用户已经认证的上下文中发生的,网站可能不会再次要求用户进行身份验证,从而使得攻击成功。

防止CSRF的方法包括但不限于:

  • 验证HTTP Referer头部,确保请求来源于预期的站点
  • 使用CSRF令牌(token),在每个需要防护的表单中隐藏一个随机生成的token,并在服务器端验证这个token的有效性
  • 在HTTP头中使用SameSite属性限制cookie的发送范围
  • 对于敏感操作实施二次验证,如短信验证码或身份确认对话框

通过这些防护措施,可以大幅度降低CSRF攻击成功的可能性

单元测试

谈谈你对单元测试的理解?

  • 什么是单元测试

单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证

  • 为什么要写单元测试

使用单元测试可以有效地降低程序出错的机率,提供准确的文档,并帮助我们改进设计方案等等

  • 什么时候写单元测试

比较推荐单元测试与具体实现代码同步进行这个方案的。只有对需求有一定的理解后才能知道什么是代码的正确性,才能写出有效的单元测试来验证正确性,而能写出一些功能代码则说明对需求有一定理解了

单元测试要写多细?

  1. 复杂的逻辑复杂,容易出错,考虑可能不全面
  2. 影响比较大的
    1. 公共代码。比如自定义的所有http请求都会经过的拦截器;工具类等
    2. 核心业务代码。一个产品里最核心最有业务价值的代码应该要有较高的单元测试覆盖率

你在开发中使用什么框架来做单元测试

  • JUnit4/5
  • Mockito, mock测试
  • Powermock, 静态util的测试

代码质量

  • 代码规范和检查工具:阿里巴巴的 Java 开发手册、Vue官方规范、Airbnb js、借鉴大厂规范「腾讯、京东」。配合开发工具中插件,checkStyle、p3c、ESLint、Prettier ESLint、Vue

  • 代码审查(Code Reviews):实施代码审查流程,每位开发者提交的代码需经过至少一位同事「组长或同事」的审查。这不仅可以发现并修正错误,还能促进知识分享,提升团队整体水平

  • 单元测试与集成测试:编写单元测试以验证代码模块的每个部分按预期工作,同时执行集成测试以确保各模块间协同无误。自动化测试是提高测试效率和覆盖率的重要手段

  • 持续集成/持续部署(CI/CD):设置自动化构建和测试流程,每次代码提交后自动运行,及时反馈问题,确保代码质量。这通常包括代码质量检查、单元测试、集成测试等步骤

  • 使用版本控制系统:通过Git、SVN等版本控制系统管理代码,合理使用分支策略,确保代码历史清晰,便于回溯和协同工作

  • 日志管理和错误监控:实施有效的错误监控和日志记录策略,帮助快速定位并解决问题,同时也是评估代码质量的一个重要途径

  • 合理的架构设计:采用成熟、合理的架构设计

  • 模块化设计:采用模块化设计提高代码结构的清晰度和可维护性

  • 设计模式:选择合适的设计模式

  • 重构:定期进行代码重构,去除冗余代码,代码复用,优化复杂逻辑,提高代码的可读性和可维护性

  • 性能和安全性考量:在开发过程中考虑代码的性能和安全性,遵循安全编码原则,进行性能基准测试和安全审计

  • 文档:保持文档更新,确保其他开发者能快速理解代码的功能和工作原理。【开发文档、使用文档】

  • 培训:定期组织技术培训和分享会议,提高团队成员的技术水平和对新工具、新技术的掌握能力

开发框架和中间件

Spring

什么是Spring框架

Spring框架是一个流行的、开源的Java平台,它提供了一个全面的基础设施来支持企业级应用程序的开发。Spring的核心特性旨在简化Java开发,特别是针对企业级应用,通过提供分层架构,实现了组件之间的松耦合。以下是Spring框架的一些关键特点和组成部分:

  1. 控制反转(Inversion of Control, IoC):Spring通过IoC容器管理对象的生命周期和依赖关系,从而减少了代码间的耦合。开发者无需直接控制对象的创建和装配,而是通过配置让容器来完成。

  2. 面向切面编程(Aspect-Oriented Programming, AOP):Spring支持AOP,允许将横切关注点(如日志记录、事务管理)从业务逻辑中分离出来,实现代码的模块化和重用。

  3. 数据访问/集成:Spring提供了对JDBC、Hibernate、JPA等持久层框架的集成支持,简化了数据访问层的开发。

  4. 模型-视图-控制器(Model-View-Controller, MVC):Spring MVC是Spring的一个模块,提供了一个分离关注点的、灵活的Web应用程序开发框架。

  5. 事务管理:Spring提供了声明式事务管理,使得事务处理代码与业务逻辑分离,易于维护和测试。

  6. 安全性:Spring Security框架提供了全面的安全服务,包括认证、授权、保护Web应用程序不受常见攻击等。

  7. 测试支持:Spring框架设计时考虑到了测试的便利性,提供了对JUnit等测试框架的集成,便于编写单元测试和集成测试。

Spring框架自2003年由Rod Johnson提出以来,已经成为Java EE和Java SE开发中不可或缺的一部分,它通过其模块化设计,让开发者能够根据项目需求选择合适的组件,从而提高开发效率、增强应用程序的可测试性和灵活性。

列举一些重要的Spring模块

  • Spring Core:提供了框架的基础部分,包括IoC(Inverse of Control)容器和DI(Dependency Injection)功能,管理Bean的生命周期和依赖关系。
  • Spring Beans:定义了Spring中的Bean如何被创建、配置和管理。
  • Spring Context:构建于Core之上,提供了框架式的Bean访问方式,以及企业级功能如邮件发送、国际化支持、事件传递等。
  • **Spring Expression Language (SpEL)**:强大的表达式语言,用于在运行时查询和操作对象图。
  • Spring JDBC:简化了使用JDBC进行数据库操作的过程,提供异常层次结构和数据源支持。
  • Spring ORM:对流行的对象关系映射API(如JPA, Hibernate, MyBatis)提供集成层。
  • Spring Transactions:为编程和声明式事务管理提供了一致的抽象。
  • Spring Web:提供了基本的Web集成特性,包括对多部分文件上传、Web应用上下文等的支持。
  • Spring Web MVC:基于Servlet API的全面的MVC实现,用于构建Web应用。
  • Spring AOP:实现了面向切面编程,允许定义方法拦截器和切点来横切关注点(如日志记录和事务管理)。
  • Spring Test:提供了对JUnit和TestNG的集成支持,简化了编写集成测试的过程。
  • Spring Security:提供了一套全面的安全服务,包括认证、授权、安全上下文等。

什么是IOC? 如何实现的?

IOC,即Inversion of Control(控制反转),是一种设计思想,它提倡将原本由应用程序代码负责的对象创建和依赖关系的管理工作交由外部容器来完成。这样一来,对象的创建不再由调用者直接控制,而是由外部容器控制,从而实现了控制权的反转。这种方式降低了组件之间的耦合度,提高了系统的灵活性和可维护性。

如何实现IOC

IOC的实现主要依赖于一个核心容器,这个容器负责管理对象的生命周期和依赖关系。在Spring框架中,这个容器是通过以下步骤实现IOC的:

  1. 资源读取:容器首先读取配置元数据,这个配置可以是XML文件、注解或者Java配置类。配置中定义了Bean(即组件)的定义,包括Bean的类名、属性、依赖关系等。

  2. Bean定义解析:容器解析这些配置元数据,将其转换成内部的数据结构(BeanDefinition),这个过程可能包含XML解析、注解扫描等操作。BeanDefinition包含了创建Bean所需的所有信息。

  3. Bean实例化和装配:当应用程序请求一个Bean时,容器根据BeanDefinition创建Bean实例,并通过反射或构造器注入等方式来设置Bean的属性和依赖。这意味着容器负责创建Bean实例,并处理它们之间的依赖关系,如自动装配(autowiring)。

  4. 依赖注入(DI):这是实现IOC的一种具体方式,容器通过查找并注入依赖项来满足Bean的依赖需求。这可以通过setter方法注入、构造器注入等方式完成。

  5. Bean缓存和管理:容器会缓存已经创建的Bean实例,对于Singleton作用域的Bean,容器只创建一次并重复使用。对于Prototype作用域的Bean,则每次请求都创建新的实例。

实现原理简述
  • XML解析:如果使用XML配置,容器会读取XML文件,解析其中的<bean>标签,生成BeanDefinition。

  • 反射:容器利用Java反射API根据BeanDefinition中的类信息实例化Bean,并通过反射调用方法来注入依赖。

  • 工厂模式:Spring也可以使用工厂模式来创建Bean,容器通过调用用户自定义的工厂方法来获取Bean实例。

  • 注解处理:如使用@Component、@Autowired等注解时,Spring通过扫描特定的包路径,识别出带有这些注解的类和方法,并自动将它们注册为Bean或进行依赖注入。

通过这些机制,Spring框架实现了控制反转,使得开发者可以更加专注于业务逻辑的实现,而非组件的创建和管理。

什么是AOP? 有哪些AOP的概念?

AOP,即Aspect-Oriented Programming(面向切面编程),是一种编程范式,用于解决软件开发中的横切关注点(Cross-cutting Concerns)问题。横切关注点是指那些遍布于整个应用的多个模块中的功能,例如日志记录、事务管理、安全性验证等,这些功能往往与核心业务逻辑交织在一起,增加了代码的复杂性和耦合度。

AOP的核心概念包括:

  1. 切面(Aspect):切面是关注点的模块化,比如日志记录、事务管理就是一个切面。它封装了横切关注点的实现,可以被定义为独立的类或组件。

  2. 连接点(Joinpoint):在程序执行过程中某个特定的点,比如方法的调用或异常的抛出,这些点可以被AOP拦截并加以处理。

  3. 切入点(Pointcut):定义了切面应该在哪些连接点上执行的规则。通常通过表达式来匹配特定的方法或类,以便确定切面应该在哪些连接点应用。

  4. 通知(Advice):在切面的某个特定连接点上执行的动作。通知有多种类型,如前置通知(Before)、后置通知(After)、环绕通知(Around)等,分别对应在连接点之前、之后或前后执行的代码逻辑。

  5. 目标对象(Target Object):被一个或多个切面所通知的对象。它通常是业务逻辑对象,不负责横切关注点的实现。

  6. 代理(Proxy):AOP框架创建的对象,用来实现切面功能。代理对象在目标对象的基础上增加额外的功能,如拦截方法调用,插入切面逻辑等。代理可以是静态的(编译时生成)或动态的(运行时生成)。

  7. 织入(Weaving):将切面代码插入到目标对象中的过程。这个过程可以在编译时、类加载时或运行时进行。

通过AOP,开发者可以将这些横切关注点从核心业务逻辑中分离出来,使得业务逻辑代码更加清晰,易于维护,同时也提高了代码的可重用性。AOP广泛应用于各种框架中,最著名的例子是在Spring框架中对AOP的支持。

AOP 有哪些应用场景?

  • 记录日志(调用方法后记录日志)
  • 监控性能(统计方法运行时间)
  • 权限控制(调用方法前校验是否有权限)
  • 事务管理(调用方法前开启事务,调用方法后提交关闭事务 )
  • 缓存(第一次调用查询数据库,将查询结果放入内存对象, 第二次调用,直接从内存对象返回,不需要查询数据库 )

有哪些AOP Advice通知的类型?

特定 JoinPoint 处的 Aspect 所采取的动作称为 Advice。Spring AOP 使用一个 Advice 作为拦截器,在 JoinPoint “周围”维护一系列的拦截器。

  • 前置通知(Before advice) : 这些类型的 Advice 在 joinpoint 方法之前执行,并使用 @Before 注解标记进行配置。
  • 后置通知(After advice) :这些类型的 Advice 在连接点方法之后执行,无论方法退出是正常还是异常返回,并使用 @After 注解标记进行配置。
  • 返回后通知(After return advice) :这些类型的 Advice 在连接点方法正常执行后执行,并使用@AfterReturning 注解标记进行配置。
  • 环绕通知(Around advice) :这些类型的 Advice 在连接点之前和之后执行,并使用 @Around 注解标记进行配置。
  • 抛出异常后通知(After throwing advice) :仅在 joinpoint 方法通过抛出异常退出并使用 @AfterThrowing 注解标记配置时执行。

AOP 有哪些实现方式?

实现 AOP 的技术,主要分为两大类:

  • 静态代理

    - 指使用 AOP 框架提供的命令进行编译,从而在编译阶段就可生成 AOP 代理类,因此也称为编译时增强

    • 编译时编织(特殊编译器实现)
    • 类加载时编织(特殊的类加载器实现)
  • 动态代理

    - 在运行时在内存中“临时”生成 AOP 动态代理类,因此也被称为运行时增强

    • JDK 动态代理
      • JDK ProxyJava 语言自带的功能,无需通过加载第三方类实现;
      • Java 对 JDK Proxy 提供了稳定的支持,并且会持续的升级和更新,Java 8 版本中的 JDK Proxy 性能相比于之前版本提升了很多;
      • JDK Proxy 是通过拦截器加反射的方式实现的;
      • JDK Proxy 只能代理实现接口的类
      • JDK Proxy 实现和调用起来比较简单;
    • CGLIB
      • CGLib 是第三方提供的工具基于 ASM 实现的,性能比较高
      • CGLib 无需通过接口来实现,它是针对类实现代理,主要是对指定的类生成一个子类,它是通过实现子类的方式来完成调用的「重写方法」

谈谈你对CGLib的理解?

JDK 动态代理机制只能代理实现接口的类,一般没有实现接口的类不能进行代理。使用 CGLib 实现动态代理,完全不受代理类必须实现接口的限制。

CGLib 的原理是对指定目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对 final 修饰的类进行代理。

Spring AOP和AspectJ AOP有什么区别?

  • Spring AOP是属于运行时增强,而AspectJ是编译时增强。Spring AOP基于代理(Proxying),而AspectJ基于字节码操作(Bytecode Manipulation)。

  • Spring AOP已经集成了AspectJ,AspectJ应该算得上是Java生态系统中最完整的AOP框架了。AspectJ相比于Spring AOP功能更加强大,但是Spring AOP相对来说更简单。

  • 如果我们的切面比较少,那么两者性能差异不大。但是,当切面太多的话,最好选择AspectJ,它比SpringAOP快很多。

Spring中的bean的作用域有哪些?

  1. Singleton(单例)
    • 这是Spring默认的作用域。当一个bean定义为singleton时,Spring IoC容器中只会存在一个该bean的实例,每次请求该bean都将返回相同的实例。
    • 单例bean在容器启动时或者第一次被请求时初始化。
    • 适用于无状态的服务,如数据访问对象(DAOs)、业务服务等。
  2. Prototype(原型)
    • 每次从容器中请求prototype作用域的bean时,Spring都会创建一个新的bean实例。
    • 这意味着每次注入或通过ApplicationContext获取该bean时,都会获得一个新的实例。
    • 适合有状态的bean,比如某些控制器、视图对象等。
  3. Request(请求)
    • 只在Web环境中有效,每次HTTP请求都会创建一个新的bean实例,该实例仅在当前请求的上下文中有效。
    • 请求结束时,bean会被销毁。
  4. Session(会话)
    • 同样是Web环境中使用,为每个HTTP会话创建一个bean实例,该实例在会话期间内有效。
    • 当HTTP会话结束时,bean随之销毁。
    • 适用于存储用户会话信息的bean。
  5. GlobalSession(全局会话)
    • 主要用于Portlet环境中,为所有参与会话的portlet共享一个bean实例。
    • 类似于session作用域,但作用范围更广,跨多个portlet应用。
  6. Application/Global(全局作用域)
    • 这个作用域在不同的来源中有不同的名称(如application或globalSession),但基本含义相似。
    • 在一个Web应用的ServletContext生命周期内,该bean只初始化一次,并且所有请求和会话共享同一实例。
    • 适用于需要在Web应用范围内保持唯一且持久状态的bean。
  7. WebSocket(WebSocket作用域)
    • 针对WebSocket应用,为每个WebSocket连接创建一个bean实例。
    • 该bean的生命周期与对应的WebSocket会话相同。

Spring中的单例bean的线程安全问题

Spring中的单例bean线程安全问题是一个重要考量点,特别是在多线程环境下。以下是对这一问题的详细理解:

  1. 默认单例行为:Spring框架默认配置下,大多数bean是以单例(Singleton)模式创建的,意味着在整个应用上下文中,对于给定的bean定义,Spring只会实例化一个该bean的实例,并且这个实例会被所有请求和服务共享。

  2. 线程安全取决于使用方式:单例bean是否线程安全,从根本上讲,取决于该bean的实现细节和使用场景。

    • 无状态Bean:如果一个单例bean是无状态的,即它的操作不依赖于实例变量的状态,或者它不修改任何实例变量,那么这个bean通常是线程安全的。服务类(Service)和数据访问对象(DAO)往往设计为无状态,因此在很多情况下是线程安全的。
    • 有状态Bean:如果单例bean内部维护了可变状态(即有状态Bean),并且这些状态在多个线程间共享并被修改,那么就存在线程安全问题。在这种情况下,如果没有适当的并发控制措施,可能会导致数据不一致或其他并发问题。
  3. 解决线程安全问题的方法

    • 避免共享可变状态:最简单的做法是避免在单例bean中使用可变状态。如果必须使用,可以考虑将其设计为不可变对象或者使用不可变的数据结构。
    • ThreadLocal:使用ThreadLocal可以为每个线程提供一个单独的实例副本,从而避免了线程间的共享冲突。
    • 同步机制:对于需要修改共享状态的情况,可以使用synchronized关键字、Lock接口或者java.util.concurrent包下的原子类(如AtomicInteger)等机制来确保线程安全。
    • 设计模式:在某些情况下,可以采用设计模式如享元模式(Flyweight Pattern)来减少对象创建,同时确保线程安全。

综上所述,虽然Spring框架本身并不直接处理单例bean的线程安全问题,但作为开发者,应该根据bean的具体用途和内部实现来判断并采取相应的措施来保证线程安全。在设计和实现bean时,考虑其生命周期和并发访问模式是非常关键的。

Spring中的bean生命周期?

Spring中的bean生命周期指的是从bean被创建到最终被销毁的整个过程。这个过程可以分为几个主要阶段,对于 singleton作用域的bean尤其明显,因为这些bean由Spring容器管理其完整的生命周期。以下是典型的bean生命周期阶段:

  1. 实例化

    • Spring容器通过反射调用bean类的构造函数来实例化bean。
  2. 属性注入

    • 容器使用依赖注入(DI)来填充bean定义中声明的属性。这包括设置bean引用和其他配置值。
  3. Aware接口回调

    • 如果bean实现了Spring的任何Aware接口(如BeanNameAware、ApplicationContextAware等),Spring容器会调用这些接口的方法,让bean能够知道它所在的环境信息。
  4. 初始化前处理

    • 如果有BeanPostProcessor的后置处理器实现了postProcessBeforeInitialization方法,Spring会在bean初始化方法调用之前执行这些方法,允许对bean进行自定义处理。
  5. 初始化

    • 调用bean定义中指定的初始化方法,这可能是通过@PostConstruct注解标记的方法,或者是通过init-method属性指定的方法。
  6. 初始化后处理

    • BeanPostProcessor的postProcessAfterInitialization方法在此阶段执行,允许进一步自定义bean实例。
  7. 使用阶段

    • Bean已准备好使用,可以响应客户端请求或被其他bean使用。
  8. 销毁前处理

    • 当容器关闭时,如果有BeanPostProcessor实现了销毁前的处理逻辑,将会被调用。
  9. 销毁

    • 如果bean实现了DisposableBean接口或使用了@PreDestroy注解标记的方法,Spring会在bean销毁之前调用这些方法,执行清理工作。
    • 对于singleton作用域的bean,这通常发生在容器关闭时。非singleton作用域的bean(如prototype)通常不由Spring容器管理其销毁过程。
  10. 垃圾回收

    • 最终,bean实例失去所有引用后,将由Java垃圾收集器回收。

注意,不是所有的bean都会经历上述所有阶段,特别是那些非singleton作用域的bean,它们的生命周期可能不会包括由Spring管理的销毁阶段。

说说自己对于Spring MVC的了解

  • Spring MVC是Spring框架的一个模块,它提供了一个基于Java的、高度可配置的、面向MVC(Model-View-Controller)设计模式的Web应用程序框架。

  • Spring MVC下我们一般把后端项目分为Service层(处理业务)、Dao层(数据库操作)、Entity层(实体类)、Controller层(控制层,返回数据给前台页面)

  1. MVC架构:Spring MVC遵循经典的MVC设计模式,将应用程序划分为三个主要部分:

    • Model(模型):代表业务数据和逻辑。
    • View(视图):负责展示数据给用户。
    • Controller(控制器):接收用户的请求,处理业务逻辑,并选择合适的视图返回给用户。
  2. 核心组件:Spring MVC的核心组件包括DispatcherServlet(前端控制器)、HandlerMapping(处理器映射器)、HandlerAdapter(处理器适配器)、ViewResolver(视图解析器)等,它们共同协作处理HTTP请求并返回响应。

  3. DispatcherServlet:作为中央调度器,接收所有HTTP请求,并分发给合适的控制器处理。它是整个流程的入口点。

  4. HandlerMapping:负责将接收到的请求映射到具体的控制器和处理器方法上。

  5. HandlerAdapter:将请求传递给控制器,并将控制器的返回值转换为视图或模型。

  6. ViewResolver:根据控制器返回的逻辑视图名找到实际的视图实现,并渲染视图。

  7. 灵活的配置:Spring MVC支持XML配置、Java配置和基于注解的配置,提供了高度的灵活性和可定制性。

  8. 支持RESTful风格:Spring MVC很好地支持了REST风格的URL和HTTP方法,便于构建RESTful API。

  9. 数据绑定与验证:自动将请求参数绑定到方法参数上,支持数据验证,并能处理验证错误。

  10. 国际化和本地化:提供了简单的方式来实现Web应用的国际化,支持多种语言和区域设置。

  11. 拦截器:Spring MVC允许定义拦截器,可以在请求处理前后执行额外的逻辑,如权限检查、日志记录等。

  12. 与其他Spring组件集成:Spring MVC与Spring框架的其他部分(如数据访问、安全、事务管理)紧密集成,提供了统一的编程模型。

总之,Spring MVC以其强大的灵活性、可扩展性和与Spring生态系统的良好集成,成为构建现代企业级Web应用的首选框架之一。

Spring MVC的工作原理

Spring MVC的工作原理可以概括为以下几个步骤,这些步骤详细描述了从用户发起请求到响应返回的整个处理流程:

  1. 请求到达DispatcherServlet
    用户通过浏览器发送HTTP请求到服务器,请求首先被Spring MVC的前端控制器DispatcherServlet捕获。DispatcherServlet是处理所有请求的中央入口点。

  2. 查找HandlerMapping
    DispatcherServlet查询一个或多个HandlerMapping来确定哪个控制器(Controller)应该处理这个请求。HandlerMapping根据请求的URL、HTTP方法等信息,找到与之匹配的处理器(Handler)。

  3. 执行Controller
    找到合适的Controller后,DispatcherServlet将请求传递给对应的Controller方法(也称为处理器方法或HandlerMethod)。这个方法通常会执行业务逻辑,处理请求数据,并可能准备模型数据(Model)。

  4. 准备ModelAndView
    处理器方法完成后,通常会返回一个ModelAndView对象给DispatcherServlet。ModelAndView对象包含了视图的逻辑名称以及要展示给用户的数据模型。

  5. 视图解析
    DispatcherServlet接下来将ModelAndView对象传递给ViewResolver,ViewResolver根据逻辑视图名找到实际的视图实现(如JSP页面、Thymeleaf模板等)。

  6. 渲染视图
    视图(如 JSP)负责呈现数据给用户。在这个阶段,视图会从Model中获取数据,并生成HTML、JSON或其他格式的响应内容。

  7. 响应给客户端
    最后,DispatcherServlet将视图渲染后的响应内容通过HTTP响应发送回客户端浏览器,完成整个请求响应过程。

除此之外,Spring MVC还支持诸如数据验证、类型转换、拦截器(Interceptor)等高级功能,这些功能可以在请求处理的不同阶段插入自定义的行为,例如在进入Controller之前进行安全检查,或在返回视图之前记录日志等。整个过程高度可配置和灵活,以适应不同应用的需求。

Spring框架中用到了哪些设计模式

Spring框架广泛运用了多种设计模式,以下是它所使用的一些核心设计模式:

  1. 工厂模式(Factory Pattern):

    • BeanFactory 是工厂模式的一个实现,它负责管理和创建bean对象,根据传入的bean标识来实例化、配置和管理bean。
  2. 单例模式(Singleton Pattern):

    • Spring默认使用单例模式管理bean,确保每个bean定义在每个Spring IoC容器中只有一个实例。
  3. 代理模式(Proxy Pattern):

    • Spring AOP(面向切面编程)功能基于JDK动态代理或CGLIB代理实现,用来在不修改原有业务逻辑的情况下,增加横切关注点,如日志记录、事务管理等。
  4. 模板方法模式(Template Method Pattern):

    • 类似 JdbcTemplateHibernateTemplate 等,这些类提供了重复性代码的骨架,并定义了执行数据库操作的步骤,子类可以覆盖某些步骤来改变其行为。
  5. 观察者模式(Observer Pattern):

    • Spring事件驱动模型中使用了观察者模式,允许应用中的各个组件对特定事件感兴趣并注册监听器。
  6. 适配器模式(Adapter Pattern):

    • 用于将一个接口转换成客户希望的另一个接口,使得原本由于接口不兼容而不能一起工作的类可以一起工作。Spring的AOP代理也使用适配器模式来支持不同类型的Advice(通知)。
  7. 装饰器模式(Decorator Pattern):

    • Spring的HandlerInterceptor(处理器拦截器)和WebRequestInterceptor(WebRequest拦截器)可以看作装饰器模式的应用,它们可以在请求处理前后添加额外的功能。
  8. 策略模式(Strategy Pattern):

    • Spring利用策略模式来支持可插拔的事务管理策略,例如通过PlatformTransactionManager接口的不同实现来选择不同的事务管理策略。
  9. 简单工厂模式(Simple Factory Pattern):

    • 虽然简单工厂模式并非GoF设计模式之一,但Spring的BeanFactory有时被描述为简单工厂模式的实现,因为它根据提供的bean名称创建和返回bean实例。

BeanFactory和 FactoryBean 区别

BeanFactory和FactoryBean是Spring框架中的两个不同概念的接口,它们的主要区别如下:

  1. BeanFactory:

    • BeanFactory是Spring用来管理Bean的容器,它是IoC(Inverse of Control,控制反转)容器的核心接口。
    • 它负责管理和实例化应用中的Bean,包括Bean的创建、配置、装配以及管理Bean之间的依赖关系。
    • BeanFactory提供了一系列的方法用于获取Bean实例(如getBean())、查询容器中是否包含某个Bean(如containsBean())、判断Bean的作用域(如isSingleton()isPrototype())等。
    • BeanFactory作为一个容器,是Spring如何管理Bean的抽象表示,它是访问和管理Bean的入口点。
  2. FactoryBean:

    • FactoryBean是一个特殊类型的Spring Bean,它不仅仅是一个普通的Java对象,而是一个能够创建其他Bean实例的工厂Bean
    • 实现了FactoryBean接口的类可以控制Bean实例化的过程,允许开发者自定义实例化逻辑、初始化以及配置Bean。
    • 当你从BeanFactory中通过ID获取一个Bean,如果这个Bean是FactoryBean的实例,那么默认情况下,Spring会返回FactoryBean.getObject()方法产生的对象,而不是FactoryBean本身。
    • FactoryBean常用于复杂对象的创建,如代理对象的创建、配置文件的解析等,提供了更多的灵活性和扩展性。
    • 当你需要自定义Bean的创建逻辑,可以考虑实现FactoryBean接口

@Component和@Bean的区别是什么

@Component@Bean都是Spring框架中用于定义和管理Bean的注解,但它们在使用场景、目的和方式上有明显的区别:

  1. 作用目标不同

    • @Component注解主要用于类,是Spring组件扫描机制的一部分。当Spring启动并扫描到带有此注解的类时,会自动将这些类作为Bean纳入Spring容器进行管理。它适用于自定义组件,如服务类、数据访问对象等,并且通常与@Autowired注解配合使用,以实现依赖注入。

    • @Bean注解则应用于方法上,它用来显式地指示一个方法的返回值应该作为Bean注册到Spring容器中。这允许开发者通过Java配置类来手动定义和配置Bean,而不是依赖于类路径扫描。@Bean方法通常包含创建Bean实例的实际逻辑,例如初始化参数或执行其他配置设置。

  2. 用途不同

    • @Component注解促进了自动化的Bean发现和配置,适用于应用内部的类,简化了配置,使得开发人员无需为每个类编写显式的Bean定义。

    • @Bean注解提供了更多的灵活性和控制力,特别是对于那些无法标记为@Component的类,比如第三方库的类或需要特殊配置的Bean。它允许在配置类中通过代码来设定Bean的属性或其他配置细节。

  3. 应用场景不同

    • @Component更适合应用在项目的内部组件上,通过@ComponentScan注解来自动发现和装配这些组件,减少了XML配置或Java配置的负担。

    • @Bean则常用于配置类中,当需要对Bean的创建过程进行精细控制,或者需要将非Spring管理的类(如第三方库的类)转化为Spring管理的Bean时,@Bean就显得尤为有用。

综上所述,**@Component强调自动发现和装配,而@Bean则侧重于手动定义和配置Bean,两者共同丰富了Spring的依赖注入和配置机制。**

将一个类声明为Spring的bean的注解有哪些

在Spring框架中,可以使用多种注解来声明一个类作为Spring管理的Bean。以下是一些常见的注解:

  1. @Component:这是一个通用注解,可以用来声明任何类型的组件。Spring会自动检测带有此注解的类并将其注册为Bean。通常,此注解用于没有明确归入Service、Repository或Controller分类的类。

  2. @Service:用于标记业务层(Service层)的类,功能上等同于@Component,但语义上更明确,表明这是一个业务服务类。

  3. @Repository:用于数据访问层(DAO层),通常用于数据库操作相关的类。它也是@Component的一个特化,强调这个类是用于数据存取的。

  4. @Controller:用于Spring MVC的控制器类,处理HTTP请求并返回响应。在Spring Boot或更现代的Web开发中,可能会更多地使用@RestController,它结合了@Controller和@ResponseBody,直接返回JSON或XML等响应体内容。

  5. @Configuration:用于定义配置类,其中可以包含@Bean注解的方法来显式地定义Bean。这样的类通常包含应用的配置信息。

  6. @Bean:这个注解放在方法上,而不是类上,用来指示该方法的返回值应该被注册为一个Spring Bean。通常在@Configuration类中使用。

  7. @RestController:是@Controller和@ResponseBody的组合,用于创建RESTful Web服务的控制器,其中方法直接返回响应体内容,通常用于JSON或XML数据。

除了上述注解,还有一些其他的注解可能用于特定场景下的Bean声明,但上述六个是最为常见和基础的。此外,使用这些注解时,通常需要配合@ComponentScan注解来启用自动扫描,让Spring自动发现并注册这些Bean。在Spring Boot应用中,这些配置往往被自动配置,无需手动添加@ComponentScan

Spring事务管理的方式有几种

编程式事务管理
  • 这种方式需要在代码中手动进行事务的开始、提交或回滚操作。Spring为此提供了PlatformTransactionManager接口以及TransactionTemplateTransactionCallback等工具类来辅助完成事务的管理。虽然提供了细粒度的控制,但在实际开发中较少使用,因为它使得事务管理代码与业务逻辑代码耦合在一起,降低了代码的可读性和可维护性。
声明式事务管理
  • 这是Spring推荐的事务管理方式,它通过配置的方式将事务管理与业务代码分离,使得事务管理变得更加透明。声明式事务管理又可以进一步分为两种形式:
    • 基于XML的配置:在Spring的配置文件中,通过<tx:advice>元素定义事务属性,并使用<aop:config><aop:advisor>元素来指定哪些方法或类需要进行事务管理。
    • 基于注解的配置:在需要进行事务管理的方法或类上使用@Transactional注解,这是最简洁的方式。Spring会自动为标注了此注解的方法开启事务管理,开发者无需在代码中显式地管理事务的生命周期。

Spring事务中的隔离级别有哪几种

Spring事务支持以下五种隔离级别,这些隔离级别是基于SQL标准定义的,并且可以在Spring的事务配置中使用:

  1. ISOLATION_DEFAULT
    这是Spring事务的默认设置,它实际上并没有定义具体的隔离级别,而是使用底层数据库的默认隔离级别。例如,MySQL的默认隔离级别是REPEATABLE_READ,而Oracle的默认隔离级别是READ_COMMITTED

  2. ISOLATION_READ_UNCOMMITTED
    最低的隔离级别,允许读取未提交的数据变更,可能会导致脏读、不可重复读和幻读等问题。

  3. ISOLATION_READ_COMMITTED
    保证一个事务读取到的数据都是已经提交的,可以防止脏读,但不可重复读和幻读仍有可能发生。

  4. ISOLATION_REPEATABLE_READ
    防止脏读和不可重复读,确保同一事务中多次读取同一数据的结果是一致的,但幻读仍有可能发生。

  5. ISOLATION_SERIALIZABLE
    最高的隔离级别,通过强制事务串行执行,可以防止脏读、不可重复读和幻读。这是最安全但性能开销最大的隔离级别。

选择合适的隔离级别需要在数据一致性和系统性能之间做出权衡。通常,更高的隔离级别会带来更强的数据一致性保证,但同时可能增加锁的竞争,影响系统的并发性能。在具体应用中,应根据业务需求和数据库的具体情况来确定最合适的事务隔离级别。

Spring事务中有哪几种事务传播行为

Spring事务定义了七种事务传播行为,这些行为控制着当一个方法调用一个事务方法时,事务应该如何进行。以下是这七种事务传播行为:

  1. PROPAGATION_REQUIRED(默认):
    如果当前没有事务,就新建一个事务;如果已经存在一个事务中,加入到这个事务中。这是最常用的选项。

  2. PROPAGATION_SUPPORTS
    如果当前存在事务,就加入到这个事务中;如果不存在事务,则以非事务的方式执行。

  3. PROPAGATION_MANDATORY
    必须在一个已存在的事务中运行,否则抛出异常。

  4. PROPAGATION_REQUIRES_NEW
    总是新建一个事务,如果当前存在事务,则将当前事务挂起。

  5. PROPAGATION_NOT_SUPPORTED
    总是以非事务方式执行,如果当前存在事务,则将当前事务挂起。

  6. PROPAGATION_NEVER
    不应该在一个事务中运行,如果当前存在事务,则抛出异常。

  7. PROPAGATION_NESTED
    如果当前存在事务,则在嵌套事务内执行。嵌套事务可以独立于当前事务进行回滚,而不会对外部事务产生影响。如果当前没有事务,则其行为类似于PROPAGATION_REQUIRED

这些传播行为允许开发者精确控制事务应该如何在复杂的调用链中传播,以满足不同的业务需求和保持数据的一致性。

ApplicationContext 是什么

ApplicationContext是Spring框架中的一个核心接口,它是BeanFactory的子接口,提供了更加丰富的功能集,旨在支持实际的企业级应用开发。ApplicationContext不仅仅是简单地管理Bean的生命周期,还额外提供了以下关键特性:

  1. 国际化的支持:可以帮助应用程序提供多语言的用户界面,通过资源文件加载不同地区的语言信息。

  2. 资源访问:简化了对各种外部资源(如文件、URL)的访问,提供了统一的资源访问接口。

  3. 事件传递:允许在Bean之间传播事件,支持应用的松耦合设计,通过ApplicationEventApplicationListener接口实现。

  4. 载入多个配置文件:能够从多个配置源(如XML、注解或Java配置类)加载和合并Bean定义。

  5. 自动装配Bean:基于注解(如@Autowired)或按名称自动装配Bean,减少显式配置。

  6. AOP支持:内建对面向切面编程的支持,可以方便地实现诸如日志记录、事务管理等横切关注点。

  7. 消息资源处理:集成消息解析器,用于处理文本消息,支持国际化和本地化。

  8. 环境抽象:通过Environment接口提供对操作系统环境变量和JVM系统属性的访问,支持配置的环境特定性。

  9. 启动和关闭回调:允许Bean在容器启动和关闭时执行特定的操作。

  10. 模板类:提供了如JdbcTemplateJmsTemplate等,简化了对各种技术的访问。

ApplicationContext的主要实现类包括ClassPathXmlApplicationContextFileSystemXmlApplicationContext用于基于XML配置的上下文,以及AnnotationConfigApplicationContext用于基于Java配置的上下文。在Web环境中,WebApplicationContext是专门为Web应用设计的,继承自ApplicationContext,提供了与Web应用服务器集成的功能。

总之,ApplicationContext是Spring应用的基础,它不仅仅是一个Bean容器,还是一个全面的解决方案,帮助开发者管理应用的配置和依赖,促进松耦合和模块化设计。

Spring Boot

什么是SpringBoot?

SpringBoot是Spring框架家族中的一个成员,它是一种快速开发框架,旨在简化和加速Spring应用程序的开发、配置、部署和运行过程。SpringBoot遵循“约定优于配置”的原则,通过提供一系列的默认配置和约定,大幅度减少了传统Spring应用所需的XML配置,并简化了依赖管理,使开发者能够更加聚焦于业务逻辑的实现,而不是配置细节。

SpringBoot的核心特点包括:

  1. **自动配置(Auto-Configuration)**:SpringBoot会根据项目中添加的依赖自动配置Spring框架的组件,比如数据源、Web服务器等,无需手动编写大量的配置代码。

  2. **简化依赖(Starter Dependencies)**:SpringBoot通过起步依赖(starter POMs)来管理项目的依赖关系,一个起步依赖本质上是一个Maven或Gradle项目的依赖描述符,它包含了一组常用库的依赖集合,使得添加新的功能变得简单快捷。

  3. 内嵌式服务器:SpringBoot应用可以内嵌如Tomcat、Jetty或Undertow这样的Web服务器,这意味着应用可以被打包成一个可执行的JAR文件并直接运行,无需部署到外部应用服务器上。

  4. Actuator:这是一个可选模块,提供了生产环境下对应用进行监控和管理的端点,如健康检查、度量信息、审计日志等。

  5. 模板引擎和RESTful支持:对于Web应用,SpringBoot提供了对ThymeleafFreeMarker等模板引擎的集成,同时也简化了构建RESTful API的过程

通过上述特性,SpringBoot极大地提高了开发效率,降低了Spring应用的入门门槛,是现代微服务架构和云原生应用开发的优选框架之一。

为什么使用SpringBoot

使用SpringBoot的原因众多,它在现代软件开发中特别受欢迎,主要归因于以下几个关键优势:

  1. 快速启动与开发:SpringBoot通过其“约定优于配置”的原则,极大地减少了应用程序的初始设置和配置工作。开发者几乎可以“开箱即用”,快速启动项目并专注于编写业务逻辑,而不是配置各种XML文件或解决依赖问题。

  2. 自动配置:SpringBoot自动配置了Spring框架及第三方库的大多数常见用例,这意味着开发者通常不需要手动配置Bean或担心如何装配组件。这大大简化了开发流程,并降低了出错的可能性。

  3. 简化依赖:SpringBoot通过起步依赖(Starter Dependencies)简化了依赖管理。每个起步依赖都是针对特定功能(如Web开发、安全、数据访问等)的一组预定义依赖集合,开发者只需包含相应的起步依赖,即可获得所需的所有库和配置。

  4. 内嵌式容器:SpringBoot应用可以内嵌Servlet容器(如Tomcat、Jetty等),这使得部署应用变得极为简单,不再需要外部部署服务器。直接通过命令行就可以运行应用,非常适合微服务架构。

  5. 生产就绪特性:SpringBoot应用天然支持Actuator端点,这些端点提供了对应用进行监控、健康检查等功能,有助于在生产环境中更好地管理和维护应用。

  6. 易于测试:SpringBoot应用支持Spring框架的测试特性,同时也鼓励采用最佳实践进行单元测试和集成测试,确保高质量的代码。

  7. 与云原生友好:SpringBoot应用很容易与云平台集成,支持Docker容器化和Kubernetes编排,适配现代DevOps流程和持续交付实践。

  8. 广泛生态支持:作为Spring框架的一部分,SpringBoot受益于庞大的Spring生态系统,可以轻松集成各种数据库消息队列安全框架等,满足不同应用场景的需求。

综上,SpringBoot以其简化复杂性、提高开发效率、强化生产环境准备度等特性,成为现代企业级应用开发的首选框架之一。

Spring、Spring MVC和SpringBoot有什么区别?

Spring
  • Spring是基础框架
  • 提供了核心功能 IoC(控制反转)和AOP(面向切面编程)
Spring MVC
  • 是Spring框架中的一个模块,用于构建Web应用程序
  • Spring MVC处理HTTP请求,将请求映射到控制器,执行业务逻辑后返回视图或数据给客户端
  • 提供了一种分离式的方法来开发Web应用。通过运用像DispatcherServelet,ModelAndView 和 ViewResolver 等一些简单的概念,开发 Web 应用将会变的非常简单
SpringBoot
  • Spring Boot 则是建立在Spring之上的一个快速开发框架,它旨在简化Spring应用的初始搭建以及开发过程
  • Spring Boot通过提供默认配置起步依赖内嵌式服务器等特性,极大地减少了过去配置Spring应用所需的工作量
  • 它遵循“约定优于配置”的原则,使得开发者能够快速启动和运行一个完整的、生产级别的Spring应用,非常适合微服务架构

SpringBoot自动配置的原理

在 SpringBoot 启动类上注解**@SpringBootApplication** 中通过 @EnableAutoConfiguration开启自动配置,SpringBoot会自动读取每个starter中的spring.factories文件,该文件里配置了所有需要被创建的Spring容器中的bean

Spring Boot的核心注解是哪些?

Spring Boot的核心注解是@SpringBootApplication,这个注解是启动Spring Boot应用的关键,它实际上是由三个主要的注解组成的:

  1. @SpringBootConfiguration: 这个注解进一步扩展了@Configuration注解,表明该类提供了Spring应用程序上下文的配置。@Configuration注解的类会作为Spring IoC容器的配置类,里面可以包含@Bean注解的方法来定义Bean。

  2. @EnableAutoConfiguration: 此注解启动了Spring Boot的自动配置机制。它会根据你的类路径(classpath)上的jar依赖自动配置Spring应用,比如,如果你的项目依赖了Spring Data JPA和一个数据库驱动,那么Spring Boot就会自动配置JPA的数据源和事务管理,而无需你手动配置这些细节。

  3. @ComponentScan: 这个注解启用了Spring组件扫描功能,允许Spring自动发现应用中带有@Component@Service@Repository@Controller等注解的类,并将它们注册为Spring容器中的Bean,从而实现依赖注入。

总结来说,@SpringBootApplication注解结合了配置、自动配置和组件扫描这三个核心功能,极大简化了Spring应用的初始搭建和开发过程,使得开发者能够快速启动和运行一个项目。

SpringBoot的核心配置文件有哪几个?他们的区别是什么?

SpringBoot的核心配置文件主要有两个:application.propertiesapplication.yml,以及在某些特定场景下会用到的bootstrap.propertiesbootstrap.yml。这些配置文件主要用于设置应用程序的属性和环境。

  1. application.properties/application.yml:

    • 用途: 这是Spring Boot应用的主要配置文件,用于存放大部分的应用配置,比如数据库连接信息、服务器端口、日志级别等。
    • 格式: application.properties采用键值对的形式,而application.yml采用了YAML(YAML Ain’t Markup Language)格式,YAML是一种更为易读和简洁的数据序列化格式,支持层次结构,便于表达配置项之间的关系。
    • 位置: 通常位于项目的src/main/resources目录下,也可以放在src/main/resources/config目录下以支持多环境配置。
    • 加载时机: 在应用启动时,Spring Boot会自动加载这些配置文件,用于初始化和配置Bean。
  2. bootstrap.properties/bootstrap.yml:

    • 用途: 主要用于应用程序的引导阶段,特别是用于Spring Cloud Config Server的配置中心客户端时,用于加载外部配置中心的配置信息。它也会加载一些固定且不可被覆盖的属性,以及处理加密解密的场景。
    • 格式: 同样支持.properties.yml两种格式。
    • 加载顺序和优先级: bootstrap配置文件会在application配置文件之前加载,且bootstrap中的属性不能被application中的同名属性覆盖。这意味着bootstrap中的配置被视为更底层的、更全局的配置。

区别总结:

  • 加载顺序: bootstrap先于application加载。
  • 用途侧重: bootstrap更适合用于配置中心的配置加载和其他启动前就需要的配置,而application用于大部分常规应用配置。
  • 覆盖规则: bootstrap中的配置项不能被application中的配置覆盖。
  • 格式选择: application.yml相比application.properties提供了更好的可读性和结构化配置能力,但选择哪种格式主要取决于个人或团队偏好及具体需求。

什么是Spring Boot Starter?有哪些常用的?

Spring Boot Starter的目的也是简化配置,而Spring Boot Starter解决的是依赖管理配置复杂的问题

常用的Spring Boot Starters包括但不限于:

  1. spring-boot-starter-web:为构建Web应用程序提供基础,包括Spring MVC和内嵌的Tomcat服务器。适用于开发RESTful API和传统的Web应用。
  2. spring-boot-starter-data-jpa:简化了关系型数据库的访问,内置了Spring Data JPA和Hibernate,提供了ORM功能和数据库操作的CRUD模板。
  3. spring-boot-starter-security:为应用添加安全功能,基于Spring Security框架,支持身份验证、授权和安全配置。
  4. spring-boot-starter-test:包含单元测试和集成测试所需的依赖,如JUnit、Mockito和Spring Test等,方便进行测试驱动开发。
  5. spring-boot-starter-thymeleaf:用于集成Thymeleaf模板引擎,支持HTML5,适用于现代Web应用的视图渲染。
  6. spring-boot-starter-data-redis:集成Redis键值存储数据库,通过Spring Data Redis提供操作接口。
  7. spring-boot-starter-logging:默认的日志配置,支持多种日志框架如Logback、Log4j2等。
  8. spring-boot-starter-amqp:提供AMQP(Advanced Message Queuing Protocol)支持,通常与RabbitMQ一起使用,实现消息队列和异步通信。
  9. spring-boot-starter-validation:包含Java Bean Validation API的支持,简化数据验证逻辑。

这些Starter极大地简化了Spring应用的搭建过程,开发者只需要关注业务逻辑,而无需深入每个依赖的细节配置。Spring Boot社区还提供了许多其他的Starter,覆盖了从数据库访问到UI框架,从消息队列到安全认证的各个方面,几乎涵盖了开发Web应用所需的所有基础组件。

spring-boot-starter-parent有什么作用?

spring-boot-starter-parent是Spring Boot项目中一个至关重要的Maven父项目(Parent POM),它在Spring Boot应用的开发中扮演着简化配置和标准化构建流程的角色。其主要作用包括但不限于:

  • 定义了Java编译版本,编码格式(UTF-8)
  • 继承自spring-boot-dependencies,这里面定义了依赖的版本,也正是因为继承了这个依赖,所以我们在写依赖时才不需要写版本号
  • 插件管理-pluginManagement,如用于打包可执行jar的spring-boot-maven-plugin,这使得Spring Boot应用可以直接通过java -jar命令运行

综上所述,spring-boot-starter-parent通过提供一系列预设的构建和依赖管理配置,加速了Spring Boot应用的开发进程,减少了配置错误,提高了开发效率。

如何自定义Spring Boot Starter?

  • 实现功能
  • 添加Properties
1
2
3
4
5
6
@Data
@ConfigurationProperties(prefix = "com.pdai")
public class DemoProperties {
private String version;
private String name;
}
  • 添加AutoConfiguration
1
2
3
4
5
6
7
8
9
10
11
12
13
@Configuration
@EnableConfigurationProperties(DemoProperties.class)
public class DemoAutoConfiguration {

@Bean
public com.pdai.demo.module.DemoModule demoModule(DemoProperties properties){
com.pdai.demo.module.DemoModule demoModule = new com.pdai.demo.module.DemoModule();
demoModule.setName(properties.getName());
demoModule.setVersion(properties.getVersion());
return demoModule;

}
}
  • 添加spring.factories

在META-INF下创建spring.factories文件

1
2
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.pdai.demospringbootstarter.DemoAutoConfiguration
  • install

为什么需要spring-boot-maven-plugin?

spring-boot-maven-plugin提供了一些像jar一样打包或者运行应用程序的命令。

  1. spring-boot:run 运行SpringBoot应用程序;
  2. spring-boot:repackage 重新打包你的jar包或者是war包使其可执行
  3. spring-boot:start和spring-boot:stop管理Spring Boot应用程序的生命周期
  4. spring-boot:build-info生成执行器可以使用的构造信息

SpringBoot 打成jar和普通的jar有什么区别?

Spring Boot 打包成的 JAR 文件(通常称为可执行 JAR 或 fat JAR)与普通的 JAR 文件有以下几方面的区别:

  1. 可执行性

    • Spring Boot JAR:可以直接通过 java -jar your-app.jar 命令执行,因为它包含了运行应用程序所需的所有依赖(包括类库、资源文件等)以及一个内置的启动器(Spring Boot Loader),使得应用成为一个独立的、无需外部配置即可运行的程序。
    • 普通 JAR:通常只包含项目的编译后的类和资源文件,不包括依赖库,因此不能直接通过 java -jar 命令运行,需要配合 CLASSPATH 等配置或者构建工具才能运行。
  2. 目录结构

    • Spring Boot JAR:内部结构有所不同,解压后你会发现有一个 \BOOT-INF\ 目录,其中 \BOOT-INF\classes\ 存放你的应用程序代码和资源,\BOOT-INF\lib\ 则存放所有依赖的 JAR 包。
    • 普通 JAR:解压后直接就是包结构,没有额外的目录层次,包内就是编译后的类文件。
  3. 依赖管理

    • Spring Boot JAR:是自包含的,所有依赖都被打包进一个 JAR 中,使得分发和部署变得简单,但这也意味着它的体积通常比普通 JAR 大得多。
    • 普通 JAR:不包含依赖,需要外部项目或构建工具(如 Maven 或 Gradle)管理依赖关系,部署时需确保所有依赖都已正确安装或包含在类路径中。
  4. 依赖引用

    • Spring Boot JAR:由于其特殊的结构,其他项目直接引用 Spring Boot 打包的 JAR 会有困难,因为类路径结构不符合常规预期。
    • 普通 JAR:可以轻易地被其他项目作为依赖库引用和使用。
  5. 用途

    • Spring Boot JAR:设计用于快速部署和运行微服务或独立应用程序,强调快速启动和最少配置。
    • 普通 JAR:通常用于库或模块化开发,需要与其他项目组件一起构建或作为项目的一部分进行编译。

综上所述,Spring Boot 打包的 JAR 主要是为了简化部署和运行过程,而普通 JAR 更多用于模块化开发和作为依赖使用。

如何使用Spring Boot实现异常处理

全局异常处理:你可以创建一个带有@ControllerAdvice@RestControllerAdvice注解的类,该类中的@ExceptionHandler方法可以捕获整个应用中控制器抛出的特定类型的异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@RestControllerAdvice
@Slf4j
public class JeecgBootExceptionHandler {
/**
* 处理自定义异常
*/
@ExceptionHandler(JeecgBootException.class)
public Result<?> handleJeecgBootException(JeecgBootException e){
log.error(e.getMessage(), e);
DynamicDataSourceContextHolder.push("master");
log.info("数据源切换回 master");
return Result.error(e.getMessage());
}
}

SpringBoot 实现热部署有哪几种方式

spring-boot-devtools

这是最常用的热部署方式。在项目的pom.xml文件中添加spring-boot-devtools依赖,Spring Boot会自动处理资源和类文件的改变,实现近乎即时的重启。这种方式下,Spring Boot会监听类路径下的变化,一旦有变化就会触发重启。

使用IDEA等集成开发环境的设置
  • 自动编译设置:确保IDE配置为自动编译项目,例如,在IntelliJ IDEA中,可以通过【Build, Execution, Deployment】→【Compiler】设置面板勾选【Build project automatically】。
  • 运行时编译设置:在IDEA中,使用快捷键Ctrl+Shift+Alt+/(Windows/Linux)或Command+Shift+Option+/(Mac)打开Maintenance对话框,进入Registry设置界面,找到并勾选compiler.automake.allow.when.app.running,允许应用运行时自动编译。
Spring Loaded

Spring Loaded是Spring官方提供的一个类加载器工具,可以在不重启整个应用的情况下,动态加载修改过的类。尽管Spring Loaded在过去曾是热部署的一种选择,但随着spring-boot-devtools的出现,它已不是首选方案

JRebel

JRebel是一款商业的热部署工具,能够实现几乎即时的代码更改效果,支持广泛的框架和技术栈,包括但不限于Spring Boot。它通过替换类加载机制,允许在运行时修改任何类和资源,而不需要重启应用。JRebel是收费软件,但提供了免费试用期。

Spring Boot中的监视器是什么?

Spring Boot中的监视器主要是指Actuator模块,它是一组强大的工具和端点,用于监控和管理Spring Boot应用程序的运行时状态。Actuator提供了对应用内部多个方面的洞察,包括但不限于性能指标、运行状况检查、环境信息配置详情数据源状态内存使用情况、线程信息、日志配置、垃圾回收信息等。

以下是Actuator的一些核心特性:

  1. 端点(Endpoints):Actuator暴露了一系列HTTP端点,每个端点都提供了一类特定的信息或操作。例如,/health端点用于检查应用的健康状况,/info用于展示应用的元数据信息,/metrics提供各种应用性能指标。

  2. 自动配置:Actuator自动配置了许多默认的端点,但同时也允许用户自定义端点来满足特定监控需求。

  3. 安全考虑:默认情况下,某些敏感端点(如关闭应用的端点)是禁用的,且所有端点都受到Spring Security的保护,确保只有授权用户可以访问。

  4. 可定制性:开发人员可以选择开启或关闭特定的端点,也可以通过配置来改变端点的访问路径。

  5. 与外部监控系统集成:Actuator可以轻松地与Prometheus、Grafana、Graphite等外部监控和可视化工具集成,使得监控数据的收集和分析更为便捷。

  6. 审计、日志和跟踪:除了性能和健康检查外,Actuator还能提供审计事件HTTP跟踪和详细的日志记录,有助于故障排查和审计合规性。

总之,Spring Boot的Actuator模块是提升应用可观测性和运维便利性的关键组件,它使得开发者和运维人员能更有效地监控和管理Spring Boot应用。

Spring Boot 可以兼容老 Spring 项目吗?

可以兼容,使用 @ImportResource 注解导入老 Spring 项目配置文件。

Spring Security

什么是Spring Security?核心功能

Spring Security是一个功能强大且高度可定制的身份验证和访问控制框架,专为基于Spring的应用程序设计。它是保护Spring应用的实际标准,提供了一整套安全相关的解决方案,旨在简化企业级应用的安全管理,如认证(Authentication)和授权(Authorization)等核心功能。

核心功能包括:

  1. 认证(Authentication):这是确认用户身份的过程,即验证用户是谁。Spring Security支持多种认证机制,如基本的用户名/密码验证、LDAP、JWT(JSON Web Tokens)、OAuth2等。它还可以集成数据库、外部认证服务或自定义认证逻辑。

  2. 授权(Authorization):在用户身份验证之后,确定用户是否有权限执行特定操作或访问特定资源。Spring Security支持基于角色的访问控制(Role-Based Access Control, RBAC)、访问决策管理以及更细粒度的访问控制列表(ACLs)。

除此之外,Spring Security还提供了许多其他安全相关的功能,包括但不限于:

  • 安全上下文管理:维护用户的认证状态,提供安全相关的会话管理。
  • 安全拦截器:通过过滤器链(Filter Chain)模型,可以在HTTP请求的不同阶段插入安全检查,如CSRF保护、XSS防御、安全头设置等。
  • 密码编码与策略:提供密码编码服务和策略管理,支持强密码存储和过期策略。
  • 会话管理:控制会话生命周期,防止会话固定攻击等安全漏洞。
  • Remember-Me 功能:允许用户在一段时间内免登录访问。
  • Spring框架集成:利用Spring IoC、DI(依赖注入)和AOP(面向切面编程)能力,无缝融入Spring生态系统,易于配置和扩展。

通过Spring Security,开发者可以根据应用需求灵活地配置和定制安全策略,无需从零开始编写大量的安全代码,大大提高了开发效率和应用的安全性。

Spring Security的原理

Spring Security是一个Java安全框架,专注于应用程序的认证和授权。它的工作原理基于以下核心点:

  1. 过滤器链:构建一系列过滤器自动处理HTTP请求的安全性,包括认证、授权等。
  2. 认证管理:验证用户身份(如用户名密码),创建表示用户身份的Authentication对象。
  3. 授权判断:决定已认证用户是否有权限访问特定资源。
  4. 灵活配置:支持多种认证方式(表单、OAuth2、JWT等)和自定义扩展,易于与Spring框架集成。
  5. 会话与上下文:管理用户会话状态,并通过SecurityContext存储当前线程的安全信息。

简而言之,Spring Security通过一系列精心设计的过滤器,为应用提供了一套全面且可定制的安全解决方案,保障了应用的认证与授权过程,同时保持了与Spring生态的高度整合性

Spring Security基于用户名和密码的认证模式流程

Spring Security基于用户名和密码的认证模式流程大致遵循以下步骤:

  1. 用户登录请求:用户通过前端界面(如登录表单)输入用户名和密码,然后前端将这些凭据发送到后端服务器。

  2. 数据封装:Spring Security中的UsernamePasswordAuthenticationFilter过滤器截获此登录请求,从请求中提取用户名和密码,并将它们封装成一个UsernamePasswordAuthenticationToken对象。这个对象实现了Authentication接口,代表着用户的认证请求。

  3. 认证处理:接下来,AuthenticationManager(认证管理器)接收到这个AuthenticationToken,它负责协调认证过程。AuthenticationManager内部可能使用多个认证提供者(如DaoAuthenticationProvider)尝试认证。

  4. 凭证验证:认证提供者使用用户信息(如从数据库中查询)来验证提供的用户名和密码是否匹配。如果验证成功,它会填充Authentication对象,包含完整的用户权限信息等。

  5. 认证成功处理

    • 成功时,AuthenticationManager返回完全填充的Authentication对象,表示用户已成功认证。
    • 此时,系统可能会创建一个会话(如果使用session-based认证),或者生成一个JWT(如果采用无状态认证)。
    • 生成的JWT可以存储在Cookie或者直接在响应体中返回给客户端,用于后续请求的认证。
  6. 安全上下文设置:认证成功后,SecurityContextHolder会被设置为当前线程的上下文,存储认证信息,以便于后续请求中快速识别用户身份。

  7. 权限控制:对于后续的每个请求,Spring Security的FilterSecurityInterceptor会根据配置的访问规则检查用户是否有权限访问请求的资源。

  8. 错误处理:如果认证失败,AuthenticationEntryPoint会被调用来处理异常情况,通常引导用户重新进行认证。

整个流程是高度可配置的,开发者可以根据具体需求调整认证数据源(如内存、数据库、LDAP等)、认证逻辑和错误处理策略。

MyBatis

MyBatis-Plus

  • 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
  • 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
  • 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
  • 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
  • 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
  • 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
  • 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
  • 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
  • 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
  • 分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
  • 内置性能分析插件:可输出 SQL 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
  • 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作

日志框架

什么是日志系统和日志门面?分别有哪些框架?

日志系统是具体的日志框架,日志门面是不提供日志的具体实现,而是在运行时动态的绑定日志实现组件来工作,是一种外观模式。

  • 日志系统
    • java.util.logging (JUL),JDK1.4 开始,通过 java.util.logging 提供日志功能。虽然是官方自带的log lib,JUL的使用确不广泛。
    • Log4j,Log4j 是 apache 的一个开源项目,创始人 Ceki Gulcu。Log4j 应该说是 Java 领域资格最老,应用最广的日志工具。Log4j 是高度可配置的,并可通过在运行时的外部文件配置。它根据记录的优先级别,并提供机制,以指示记录信息到许多的目的地,诸如:数据库,文件,控制台,UNIX 系统日志等。Log4j 的短板在于性能,在Logback 和 Log4j2 出来之后,Log4j的使用也减少了。
    • Logback,Logback 是由 log4j 创始人 Ceki Gulcu 设计的又一个开源日志组件,是作为 Log4j 的继承者来开发的,提供了性能更好的实现,异步 logger,Filter等更多的特性。
    • Log4j2,维护 Log4j 的人为了性能又搞出了 Log4j2。Log4j2 和 Log4j1.x 并不兼容,设计上很大程度上模仿了 SLF4J/Logback,性能上也获得了很大的提升。Log4j2 也做了 Facade/Implementation 分离的设计,分成了 log4j-api 和 log4j-core。
  • 日志门面
    • common-logging,common-logging 是 apache 的一个开源项目。也称Jakarta Commons Logging,缩写 JCL。
    • slf4j, 全称为 Simple Logging Facade for Java,即 java 简单日志门面。作者又是 Ceki Gulcu!这位大神写了 Log4j、Logback 和 slf4j。类似于 Common-Logging,slf4j 是对不同日志框架提供的一个 API 封装,可以在部署的时候不修改任何配置即可接入一种日志实现方案。但是,slf4j 在编译时静态绑定真正的 Log 库。使用 SLF4J 时,如果你需要使用某一种日志实现,那么你必须选择正确的 SLF4J 的 jar 包的集合(各种桥接包)。

在日志配置时会考虑哪些点?

在进行日志配置时,需要综合考虑多个因素以确保日志的有效性、效率和安全性。以下是一些关键点:

  1. 日志级别设置:确定不同场景下应记录的日志级别,如DEBUG、INFO、WARN、ERROR、FATAL。合理的级别设置可以帮助区分日志的重要程度,便于问题定位和日常监控。

  2. 日志格式定义:定义日志的输出格式,包括时间戳、日志级别、进程ID、线程名、类名、方法名、行号、请求信息(如HTTP请求中的URL、客户端IP、User-Agent等)及自定义信息。格式应便于阅读和分析。

  3. 日志滚动策略:为了避免日志文件无限增长导致磁盘空间耗尽,需要设置日志滚动策略,比如按时间(每日、每周)、按大小(每达到一定MB)滚动,并且保留一定数量的旧日志文件。

  4. 异步日志记录:考虑使用异步日志记录以减少日志输出对主程序性能的影响,尤其是在高并发场景下。

  5. 日志分类与分离:将不同类型的日志(如访问日志、应用日志、审计日志)分别记录到不同的文件,便于管理和分析。

  6. 日志加密与敏感信息处理:确保敏感信息(如密码、个人隐私数据)不在日志中明文记录,必要时对日志进行加密处理。

  7. 日志存储位置与备份:确定日志文件的存储位置,确保有足够的磁盘空间。定期备份日志文件,以防数据丢失。

  8. 日志收集与集中管理:在分布式系统中,考虑使用日志收集工具(如Logstash、Fluentd)将日志集中到日志服务器(如ELK Stack、Splunk)进行统一管理、分析和检索。

  9. 日志监控与报警:配置日志监控系统,设定关键指标的阈值,一旦触发即自动发送报警通知,帮助及时发现并解决问题。

  10. 日志配置的版本控制:将日志配置文件纳入版本控制系统,确保配置的变更可追溯。

  11. 兼容性和标准化:确保所选日志框架与应用的技术栈兼容,并尽可能遵循行业标准或最佳实践,以便于维护和迁移。

  12. 性能考量:优化日志输出的性能,避免因频繁的IO操作影响应用性能,特别是在生产环境中。

对Java日志组件选型的建议

推荐使用SLF4J作为日志门面,结合LogbackLog4j 2作为实现

Tomcat

Tomcat 整体架构的设计?

Apache Tomcat是一个开源的轻量级Web服务器和Servlet容器,主要为Java Web应用程序提供运行环境。Tomcat的整体架构设计围绕着Servlet规范展开,旨在提供高性能、稳定和可扩展的服务平台。以下是Tomcat架构的关键组成部分和设计原则:

  1. 多层架构:

    • 连接器(Connectors): 负责接收来自客户端的HTTP请求,并将响应返回给客户端。Tomcat支持多种连接器,如HTTP/1.1的BIO(阻塞I/O)、NIO(非阻塞I/O)、APR(Apache Portable Runtime,使用本地库提高性能)和最新的AJP(Apache JServ Protocol,用于与其他Web服务器如Apache HTTP Server集成)。

    • 容器(Containers): 负责管理Servlet的生命周期和处理请求。容器分为四层结构:

      • Host Container: 管理多个虚拟主机,每个虚拟主机可以有不同的web应用。
      • Context Container: 对应一个Web应用程序,管理该应用内的所有组件。
      • Servlet Container: 管理Servlet实例,负责Servlet的加载、初始化、服务请求和销毁。
      • Wrapper Container: 包装单个Servlet实例,处理特定Servlet的调用细节。
  2. Servlet生命周期管理:
    Tomcat根据Servlet规范管理Servlet的加载、初始化、服务请求和销毁过程,确保每个Servlet在正确的时间点执行相应的操作。

  3. 配置与管理:

    • 使用server.xmlweb.xml等配置文件进行初始化配置,允许管理员定义连接器、容器配置、虚拟主机、上下文路径等。
    • 提供管理界面(如Tomcat Manager和Host Manager Web应用)和命令行工具(如catalina.sh脚本)进行服务器管理和应用部署。
  4. 类加载器体系:
    Tomcat实现了自己的类加载器层次结构,包括Common ClassLoader、Catalina ClassLoader、Shared ClassLoader和Webapp ClassLoader,确保了不同Web应用之间的类隔离和共享库的管理。

  5. 线程池管理:
    Tomcat通过线程池来管理处理请求的线程,可以配置线程池的大小、超时等参数,以优化资源使用和提高响应速度。

  6. 可扩展性和模块化:
    Tomcat设计为高度模块化,核心服务(如Servlet容器、JSP引擎)和附加功能(如WebSocket、SSL支持)可以通过不同的模块加载,方便根据需要定制服务。

  7. 安全特性:
    支持SSL/TLS加密通信,提供身份验证、授权机制,以及各种安全配置选项来保护Web应用和服务器。

整体来说,Tomcat的设计兼顾了灵活性、性能和易用性,适合作为Java Web应用的部署平台,特别是在中低负载到中等规模的应用场景中表现出色。

Tomcat 一个请求的处理流程?

当一个HTTP请求到达Tomcat服务器时,它会经历一系列处理步骤,直至响应返回给客户端。下面是Tomcat处理一个请求的大致流程:

  1. 连接器(Connector)接收请求:

    • 客户端发起HTTP请求,连接到Tomcat的指定端口。
    • 相应的连接器(如BIO、NIO、APR)接收此请求,进行初步处理,如解码、解析请求头等。
  2. 线程分配:

    • 连接器将请求交给线程池(Executor),从线程池中分配一个工作线程来处理该请求。线程池管理有助于控制并发和资源使用。
  3. 请求映射:

    • 工作线程使用连接器和容器的信息来确定哪个Web应用应该处理这个请求。这涉及到解析域名、上下文路径等,以找到正确的虚拟主机和Web应用。
  4. 请求转发至容器:

    • 请求被转发到相应的容器层级开始处理。首先到达Host Container,然后是Context Container,接着是Servlet Container。
  5. Servlet的调用:

    • 在Servlet Container中,根据URL映射找到对应的Servlet Wrapper,然后初始化或复用Servlet实例。
    • Servlet实例的service()方法被调用,根据HTTP方法(GET、POST等)进一步分派到具体的doGet()、doPost()等方法。
  6. 处理请求和生成响应:

    • Servlet处理业务逻辑,可能涉及数据库操作、调用其他服务、处理请求参数等。
    • 处理完成后,Servlet构造响应对象(HttpServletResponse),包含状态码、响应头和响应体。
  7. 过滤器链(Filter Chain):

    • 在Servlet响应之前和之后,相关过滤器(如果有配置)按照配置顺序执行,可以对请求和响应进行预处理或后处理。
  8. 响应返回:

    • 响应经由容器结构反向传递,最终回到连接器。
    • 连接器将响应序列化为HTTP响应报文,通过网络发送回客户端。
  9. 资源清理和线程回收:

    • 工作线程完成任务后,释放资源,线程返回线程池等待新的任务。

这个流程体现了Tomcat处理请求的高效和模块化设计,确保了请求能够准确、安全地被处理,并且响应能够及时返回给客户端。

Tomcat 中Executor?

在Apache Tomcat服务器中,Executor 是一个关键组件,负责管理线程池,用于处理传入的客户端请求。它代表了一个可配置的线程池执行策略,允许更细粒度地控制Tomcat处理请求时所使用的线程资源。以下是关于Tomcat中Executor的一些核心概念和功能:

  • 资源管理:通过重用线程减少创建和销毁线程的开销,提高了服务器的响应速度和整体性能。
  • 并发控制:限制同时运行的线程数量,防止在高负载情况下由于过多线程导致的资源耗尽(如内存溢出)。
  • 负载均衡:合理分配任务给线程,帮助平滑系统负载,特别是在面对突发流量时。

Tomcat允许用户自定义Executor并在Connector中引用它,而不是使用每个Connector自己的默认线程池。这样可以实现多个Connector共享同一个线程池,提高资源利用率和管理效率。

配置通常在server.xml文件中进行,示例如下:

1
2
3
4
5
6
7
8
9
10
11
<Executor name="tomcatThreadPool"
namePrefix="catalina-exec-"
maxThreads="500"
minSpareThreads="50"
maxIdleTime="60000"/>

<Connector executor="tomcatThreadPool"
port="8080"
protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"/>

在这个例子中,Executor名为tomcatThreadPool,配置了最大线程数maxThreads为500,最小空闲线程数minSpareThreads为50,以及线程最大空闲时间maxIdleTime为60秒。

  • name: Executor的唯一标识。
  • namePrefix: 每个创建的线程名称前缀。
  • maxThreads: 线程池能创建的最大线程数。
  • minSpareThreads: 线程池维护的最小空闲线程数。
  • maxIdleTime: 空闲线程在终止前等待新任务的最长时间(毫秒)。

通过使用Executor,Tomcat提供了高度灵活的线程管理机制,使得管理员可以根据应用的实际需求和服务器的硬件资源情况,优化配置以达到最佳性能和稳定性。正确配置Executor是提升Tomcat服务器处理能力的关键步骤之一。

Tomcat 中的设计模式

  1. 门面模式(Facade Pattern)

    • 应用:Tomcat使用门面模式对外提供简洁的API接口,隐藏了内部复杂性。例如,在RequestResponse对象的封装、Standard WrapperServletConfig的封装、ApplicationContextServletContext的封装中,都使用了门面模式来简化外部调用。
  2. 责任链模式(Chain of Responsibility Pattern)

    • 应用:在容器间的调用中,Tomcat通过责任链模式处理请求。当请求进入Tomcat时,会依次经过Engine、Host、Context、Wrapper等容器,每个容器都可以有机会处理请求或者将其传递给链中的下一个容器,直到找到合适的Servlet来处理请求。此外,过滤器链(FilterChain)也是责任链模式的一个典型应用,它允许过滤器按序处理请求和响应。
  3. 观察者模式(Observer Pattern)

    • 应用:用于事件监听和通知机制。例如,当ServletContext初始化或销毁时,会触发一系列监听器(ServletContextListener),这些监听器作为观察者被通知并执行相应的操作。
  4. 策略模式(Strategy Pattern)

    • 应用:Tomcat中某些组件的行为可以通过策略模式动态改变。例如,不同的连接器(Connectors)和协议处理器(Protocol Handlers)可以视为不同的策略,根据配置选择合适的策略来处理网络通信。
  5. 工厂模式(Factory Pattern)

    • 应用:在创建对象时使用,以提供统一的接口来创建不同类型的对象。Tomcat中的连接器、协议处理器等组件的创建就利用了工厂模式,以增加代码的灵活性和可维护性。
  6. 单例模式(Singleton Pattern)

    • 应用:确保某些核心组件(如全局配置管理器)在整个应用中只存在一个实例,以减少资源消耗和状态同步问题。
  7. 组合模式(Composite Pattern)

    • 应用:Tomcat中的容器模型(如Engine、Host、Context、Wrapper)形成了一个树状结构,符合组合模式,使得容器的管理和请求的路由变得简单且统一。

开发工具

Git

微服务

Spring Cloud

什么是微服务?谈谈你对微服务的理解?

微服务(Microservices)是一种软件架构风格,它将一个大型的应用程序拆分成一组小型、自治的服务。每个服务都围绕着特定的业务功能进行构建,能够独立部署、运行和扩展,且服务之间通过API(通常是RESTful API或gRPC)进行轻量级通信。微服务架构的核心理念在于“小”与“独”,强调服务的高内聚、低耦合,以实现敏捷开发、持续交付和轻松维护的目标。

什么是Spring Cloud?

Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册配置中心智能路由消息总线负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。

Spring Cloud并没有重复制造轮子,它只是将各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包

  • SpringCloud的优点
  1. 耦合度比较低。不会影响其他模块的开发。
  2. 减轻团队的成本,可以并行开发,不用关注其他人怎么开发,先关注自己的开发。
  3. 配置比较简单,基本用注解就能实现,不用使用过多的配置文件。
  4. 微服务跨平台的,可以用任何一种语言开发。
  5. 每个微服务可以有自己的独立的数据库也有用公共的数据库。
  6. 直接写后端的代码,不用关注前端怎么开发,直接写自己的后端代码即可,然后暴露接口,通过组件进行服务通信。
  • SpringCloud的缺点
  1. 部署比较麻烦,给运维工程师带来一定的麻烦。
  2. 针对数据的管理比麻烦,因为微服务可以每个微服务使用一个数据库。
  3. 系统集成测试比较麻烦
  4. 性能的监控比较麻烦。