思科 Java 开发人员面试记录 2024(Java、Spring-Boot、Hibernate)
Spring-Boot 中的响应实体是什么?
在 Spring Boot 中,ResponseEntity 是一个用于表示发送回客户端的整个 HTTP 响应的类。它不仅仅是数据本身,还封装了三个关键方面:
- 状态代码:表示请求的结果,如成功(200 OK)、未找到(404)或内部服务器错误(500)。
- 标头:这些是可选的键值对,提供有关响应的附加信息,例如内容类型、缓存控制或身份验证详细信息。
- 正文:这是发送回客户端的实际数据。它可以是 JSON、XML 或纯文本,具体取决于您的 API 设计。
通过使用 ResponseEntity,您可以精细地控制 Spring Boot 如何构建响应。您可以设置适当的状态代码、添加自定义标头,并在正文中包含响应数据。这让您可以构建更具信息性和灵活性的 API。
@RestController
public class ProductController {
@GetMapping( "/products/{id}" )
public ResponseEntity<Product> getProduct( @PathVariable Long id) {
// 模拟产品检索逻辑
Product product = getProductFromDatabase(id);
// 检查产品是否存在
if (product == null ) {
return ResponseEntity.notFound().build(); // 404 Not Found
}
// 返回状态为 OK(200)的产品
return ResponseEntity.ok(product);
}
// 模拟从数据库检索产品(用您的实际逻辑替换)
private Product getProductFromDatabase( Long id) {
// ...(实现细节)
return new Product(id, "Sample Product" , 10.0 );
}
}
如何在spring-boot应用中配置多个数据库?
这是一个非常有趣的问题,在采访中经常被重复。
Spring Boot 提供了一种在应用程序中配置多个数据库的便捷方法。以下是所涉及步骤的细分:
1.定义数据源属性:
- Spring Boot 使用属性来配置数据源。您可以在
application.yml
或application.properties
文件中定义它们。 - 每个数据源都需要一组自己的属性,并以唯一标识符作为前缀。常见属性包括:
url
:数据库连接URL。username
:数据库用户名。password
:数据库密码。driverClassName
:数据库的JDBC驱动类名。
users
以下是两个名为和 的数据库的示例配置orders
:
spring:
datasource:
users:
url: jdbc:mysql://localhost:3306/users
username: user
password: password
driverClassName: com.mysql.cj.jdbc.Driver
orders:
url: jdbc:postgresql://localhost:5432/orders
username: orders_user
password: orders_password
driverClassName: org.postgresql.Driver
2.创建数据源Bean:
- Spring Boot 提供了注释和实用程序来创建 DataSource bean。
- 您可以使用
@ConfigurationProperties
将先前定义的数据源属性映射到 bean。 DataSourceBuilder
下面是为每个数据源创建 bean 的示例配置类:@Configuration public class DataSourceConfig { @Bean @ConfigurationProperties(prefix = "spring.datasource.users") public DataSource usersDataSource() { return DataSourceBuilder.create().build(); } @Bean @ConfigurationProperties(prefix = "spring.datasource.orders") public DataSource ordersDataSource() { return DataSourceBuilder.create().build(); } }
配置实体管理器和事务管理器(可选):
- 如果您使用 Spring Data JPA,则需要为每个数据源配置单独的实体管理器和事务管理器。
- 这些可以以类似于 DataSource bean 的方式创建,指定与每个数据源关联的实体。
4.注入正确的数据源:
- 默认情况下,Spring Boot 自动配置单个数据源。要使用特定数据源:
- 您可以为特定的存储库或服务注入
@Qualifier("usersDataSource")
或。@Qualifier("ordersDataSource")
- JPA 存储库还可以使用
@Entity
带有属性的注释entityManagerFactoryRef
来指定 EntityManager。
请记住根据您的特定数据库调整配置细节(数据库类型、连接细节)。
Springboot 应用中如何声明全局异常?[imp问题]
Spring Boot 提供两种主要方法来声明全局异常:
1.使用@ControllerAdvice:
这是集中异常处理的推荐方法。其工作原理如下:
- 创建一个用 注释的类
@ControllerAdvice
。 - 定义带注释的方法
@ExceptionHandler
来处理特定异常。 - 这些方法可以:
- 返回包含有关异常详细信息的自定义错误响应对象。
- 使用 设置特定的 HTTP 状态代码
ResponseEntity
。 - 记录异常以供进一步分析。
@ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(ResourceNotFoundException.class) public ResponseEntity<ErrorResponse> handleResourceNotFound(ResourceNotFoundException ex) { ErrorResponse errorResponse = new ErrorResponse("Resource not found"); return ResponseEntity.status(HttpStatus.NOT_FOUND).body(errorResponse); } // Define methods for other exceptions you want to handle globally }
什么是 Spring Bean LifeCycle?
什么是IOC容器?
IoC 容器是一个软件框架组件,用于管理应用程序中的对象 (bean)。它负责创建、配置和组装这些对象及其依赖项。
它是如何工作的?
- 对象创建:传统上,您需要在代码中手动创建对象。使用 IoC 容器,您可以使用配置文件(XML 或注释)或 Java 类定义应用程序中所需的对象(bean)。然后容器负责实例化这些对象。
- 依赖注入:对象通常依赖其他对象才能正常运行(依赖项)。您无需手动创建和传递这些依赖项,而是在对象定义中声明它们。IoC 容器会向其创建的对象注入(提供)所需的依赖项。这在对象之间创建了松散的耦合,使您的代码更加模块化且更易于测试。
- 对象生命周期管理: IoC 容器还管理对象的生命周期,包括初始化和销毁。这样你就不用为这些任务编写样板代码了。
什么是依赖注入?
在软件开发中,依赖注入 (DI) 是一种为对象提供其运行所需的对象(依赖项)的技术。以下是关键概念的细分:
什么是依赖关系?
- 依赖项是类或函数依赖的其他对象,以有效地执行其工作。
- 例子:
- 汽车依靠发动机、车轮和其他零件来运行。
- 数据库访问类依赖于数据库连接对象来与数据库交互。
什么是应用程序上下文及其用途?
在 Spring Boot 应用程序中,ApplicationContext 是一个中央接口,在管理整个应用程序中使用的对象(bean)方面起着关键作用。它本质上是一个提供以下功能的容器:
1.Bean管理:
- ApplicationContext 的核心职责是管理组成应用程序的对象(bean)。
- 这些 bean 通常使用注释或 XML 配置文件来定义。
- ApplicationContext 负责根据指定的配置创建、配置和组装这些 Bean。
2.依赖注入:
- Bean 经常需要依赖其他 Bean 才能正常运行。这被称为依赖关系。
- ApplicationContext 通过自动向其创建的 bean 提供所需的依赖项来促进依赖项注入。这消除了手动创建和管理依赖项的需要,从而实现了松散耦合且更易于维护的代码。
3.资源访问:
- ApplicationContext 提供对应用程序可能需要的各种资源的访问,例如属性文件、配置文件和消息包。
- 这简化了资源检索并确保了整个代码的一致访问。
如何启用多个 Eureka 服务器?
Spring Filter 和 Spring Interceptors 有什么区别?(这个问题可以检查你的 Spring MVC 框架相关概念)
HandlerInterceptor
基本上类似于 Servlet Filter
,但与后者不同的是,它只允许自定义预处理(可选择禁止执行处理程序本身)和自定义后处理。过滤器功能更强大,例如,它们允许交换传递到链中的请求和响应对象。请注意,过滤器在应用程序上下文中web.xml
的中进行配置。HandlerInterceptor
作为基本准则,细粒度处理程序相关的预处理任务是HandlerInterceptor
实现的候选,尤其是分解出的通用处理程序代码和授权检查。另一方面,Filter
非常适合请求内容和视图内容处理,如多部分表单和 GZIP 压缩。这通常显示当需要将过滤器映射到某些内容类型(例如图像)或所有请求时。
Interceptor#postHandle()
那么和的区别在哪里Filter#doFilter()
?
postHandle
将在调用处理程序方法之后但在呈现视图之前调用。因此,您可以向视图添加更多模型对象,但无法更改,HttpServletResponse
因为它已经提交。
doFilter
比 更加灵活postHandle
。你可以改变请求或响应,并将其传递给链,甚至阻止请求处理。
此外,在preHandle
和postHandle
方法中,您可以访问HandlerMethod
处理请求的。因此,您可以根据处理程序本身添加预处理/后处理逻辑。例如,您可以为具有某些注释的处理程序方法添加逻辑。
正如文档所述,细粒度处理程序相关的预处理任务是HandlerInterceptor
实现的候选,尤其是分解出的通用处理程序代码和授权检查。另一方面,Filter
非常适合请求内容和视图内容处理,如多部分表单和 GZIP 压缩。这通常显示当需要将过滤器映射到某些内容类型(例如图像)或所有请求时。
为什么要使用@Transactional注解,有什么好处?(在Spring面试中,@Transactional注解总是被问到)
@Transactional 是一个Spring注解,可以应用于方法或类,以指示注释的代码应在事务中执行。当 Spring 遇到@Transactional注解时,它会自动围绕注释的代码创建一个事务并管理事务生命周期。
默认情况下,@Transactional 会创建一个具有默认隔离级别(通常为 READ_COMMITTED)和默认传播行为(REQUIRED)的事务。但是,您可以通过向注释传递参数来自定义这些设置。
以下是在Spring类中使用 @Transactional 的示例:
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Transactional
public void createUser(String name, String email) {
User user = new User(name, email);
userRepository.save(user);
}
}
在这个例子中,createUser()方法用@Transactional注释,这意味着UserRepository的save()方法将在事务中执行。
@Transactional的优点:
@Transactional 注释有几个好处:
- 简化事务管理:通过使用
@Transactional
,您可以避免编写样板代码来手动创建和管理事务。Spring 会为您处理事务管理,因此您可以专注于编写业务逻辑。 - 促进一致性和完整性:事务确保多个数据库操作以原子方式执行,这有助于维护数据的一致性和完整性。
- 提高性能:事务可以通过减少应用程序和数据库之间的往返次数来提高数据库性能。
- 支持声明式编程:使用@Transactional,您可以使用声明式编程来指定事务管理规则。这使您的代码更简洁,更易于阅读。
HashTable 和 ConcurrentHashmap 之间的主要区别是什么?
Java 中 Hashtable 和 ConcurrentHashMap 之间的主要区别:
同步:
- Hashtable:对整个表使用单个锁。这意味着一次只有一个线程可以访问该表,即使是读取也是如此,这在高并发场景中会造成瓶颈。
- ConcurrentHashMap:在 bucket 级别(段)使用细粒度锁定。这允许并发读取和有限的并发写入,从而显著提高多线程环境中的性能。
HashTable 和 Hashmap 的主要区别是什么?
如果你的应用程序有内存泄漏,你该如何找到它?
内存泄漏的症状
- 应用程序长时间连续运行时性能严重下降。
- 应用程序中的 OutOfMemoryError 堆错误。
- 自发且奇怪的应用程序崩溃。
- 应用程序偶尔会耗尽连接对象。
Stringtokenizer 有什么用途?
字符串标记器类允许应用程序将字符串拆分为标记。标记器方法比该类使用的方法简单得多StreamTokenizer
。这些StringTokenizer
方法不区分标识符、数字和带引号的字符串,也不识别和跳过注释。
分隔符集(分隔标记的字符)可以在创建时指定,也可以基于每个标记指定。
的实例StringTokenizer
会以两种方式之一运行,具体取决于它是使用returnDelims
具有值true
或 的标志创建的false
:
- 如果标志为
false
,分隔符用于分隔标记。标记是非分隔符的连续字符的最大序列。 - 如果标志为
true
,分隔符本身将被视为标记。因此,标记要么是一个分隔符,要么是非分隔符的最大连续字符序列。
StringTokenizer 对象在内部维护要标记的字符串中的当前位置。某些操作会将此当前位置推进到已处理的字符之后。
通过获取用于创建 StringTokenizer 对象的字符串的子字符串来返回一个标记。
以下是使用 tokenizer 的一个示例。代码:
StringTokenizer st = new StringTokenizer("this is a test");
while (st.hasMoreTokens()) {
System.out.println(st.nextToken());
}
什么是 Spring Security Context?
SecurityContext — 从 SecurityContextHolder 获取,包含当前已验证用户的 Authentication。Authentication — 可以作为 AuthenticationManager 的输入,以提供用户提供的用于验证的凭证或来自 SecurityContext 的当前用户。
对象级锁定和类级锁定之间有什么区别?
在并发编程中,当多个线程访问共享资源时,同步对于保持数据一致性至关重要。锁定机制通过限制对这些资源的访问来实现同步,确保一次只有一个线程对其进行操作。Java 等面向对象编程语言中有两种主要的锁定方法:对象级锁定和类级锁定。
对象级锁定
- 适用于单个对象:Java 中的每个对象都有一个与之关联的唯一锁。
synchronized
使用关键字与非静态方法或代码块实现。- 确保一次只有一个线程可以对特定对象执行同步方法。
- 尝试访问同一对象上的同一同步方法的其他线程将被阻塞,直到锁被释放。
- 适用于同步对对象的实例变量和方法的访问。
- 保持粒度,允许并发访问同一类的不同对象。
类级别锁定
- 适用于整个类:使用
synchronized
带有静态方法的关键字实现。 - 只有一个线程可以执行类的同步静态方法,无论对象实例如何。
- 所有其他尝试访问同步静态方法的线程都将被阻止。
- 对于同步对类的静态变量和方法的访问很有用。
- 提供更广泛的控制级别,但与对象级锁定相比,会导致更大的性能开销。
这两者之间谁会消耗更多的内存(int 还是 Integer)?
在大多数编程语言中,int
消耗的内存比 少Integer
。原因如下:
int
是一种原始数据类型:它表示基本整数值本身,并将数字直接存储在内存中。大小通常是固定的,在大多数现代系统中通常为 4 个字节(32 位)。Integer
是一个类(或对象):在 Java 等语言中,Integer
是一个封装int
值的类。除了存储数字之外,它还提供其他功能,例如转换方法或高级数学运算。这些额外功能需要消耗内存。
什么是弱HashMap?
WeakHashMap 是 Java 中 Map 接口的一个特殊实现。它与常规 HashMap 的不同之处在于处理键的方式:
- 键存储:在 WeakHashMap 中,键使用WeakReferences存储。这意味着键本身不被视为阻止垃圾回收 (GC) 的强引用。
- 自动移除:当 WeakHashMap 中某个键的唯一引用是该映射本身内的弱引用,并且程序中其他地方没有对该键的其他强引用时,GC 可以回收该键的内存。因此,相应的键值对将自动从 WeakHashMap 中移除。
WeakHashMap 的用例:
- 缓存实现: WeakHashMap 可用于创建缓存,如果条目不再被使用,可以自动删除这些条目。这有助于内存管理,因为未使用的条目会被 GC 丢弃。
- 弱引用:当您需要将数据与对象关联但又不想阻止其垃圾收集时,WeakHashMap 是一个不错的选择。
这些接口 Predicates、Supplier、Consumer 和 Function 之间有什么区别?
我记下了数据库和 Hibernate 中重复的问题,但没有给出答案,因为它们很容易知道。如果你不知道,请评论,我会提供答案。你也可以提供答案。
把这当成家庭作业吧,哈哈:)
Callable 和 Runnable 之间的区别?
Runnable
和Callable
都是 Java 中的接口,用于实例旨在由线程执行的类。但是,它们之间存在一些关键差异:
可运行
- 该
Runnable
接口有一个方法run()
,该方法不接受任何参数并且不返回任何结果。public interface Runnable { void run(); }
- 如果您需要从对象返回结果
Runnable
,或者抛出已检查的异常,则必须使用解决方法,例如修改在对象之外可见的变量Runnable
。
可调用
- 该
Callable
接口有一个名为的方法call()
,它可以返回一个值并可以引发异常。public interface Callable<V> { V call() throws Exception; }
Callable
设计用于ExecutorService
,它可以返回Future
表示计算结果的。
如果你需要在线程中执行计算并且不需要返回结果,则可以使用Runnable
。 如果需要返回结果,或者计算可能引发异常,则应使用Callable
。
主键和唯一键的区别?
主键:
唯一性:主键是表中唯一标识每一行的一列(或一组列)。它必须包含每行的唯一且非空的值。
空值:主键列不能包含空值。表中的每一行都必须有一个主键值。
约束:每个表只能有一个主键。它唯一地标识表中的行,并用作与其他表的关系的参考点。
自动索引:默认情况下,主键列会自动建立索引,这可以提高查询的性能。
唯一键:
唯一性:唯一键约束可确保一列(或一组列)中的值是唯一的。与主键不同,唯一键可以允许空值,但如果将列标记为唯一,则只允许一个空值。
空值:唯一键列可以包含空值,但每个唯一键列只允许一个空值。
约束:每个表可以有多个唯一键约束,每个约束确保其指定列内的唯一性。
不自动索引:唯一键列不会自动索引。但是,手动在唯一键列上创建索引以提高查询性能是一种常见的做法。
数据库中的触发器有什么用途?
数据库中的触发器就像是响应表上的事件(插入、更新、删除)而自动运行的小程序。它们用于:
- 数据验证和完整性:确保数据符合特定规则。
- 自动化任务:根据数据变化触发通知或计算等操作。
- 数据审计:追踪谁、什么以及何时修改了数据。
准备好的语句和语句有什么区别?
SQL 和 NoSQL 数据库的区别?
以下是 SQL 和 NoSQL 数据库之间的核心区别:
SQL 数据库:
- 关系:数据以相互关联的关系的形式存储在表中。
- 用于访问和操作的结构化查询语言(SQL)。
- 预定义模式(数据结构),确保强大的数据完整性。
- 垂直可扩展(添加更强大的硬件)。
- 适合复杂查询和相关数据。
NoSQL 数据库:
- 非关系:数据可以存储为各种格式(文档、键值对、图表)。
- 不太严格的模式,允许灵活的数据结构。
- 水平可扩展(添加更多服务器)。
- 更快地处理大型非结构化数据集。
什么是数据库索引?
数据库索引就像是数据库表的有序归档系统。它是一种特殊的数据结构,允许快速访问特定信息,从而显著加快数据检索速度。
想象一下没有索引的大型图书馆。要找到一本特定的书,就需要逐个搜索每个书架。数据库中的索引就像图书馆的卡片目录一样 - 它直接指向所需数据的位置,而无需扫描整个表格。
数据库中的分片是什么?
数据库中的分片是一种将大型数据库拆分成更小、更易于管理的部分(称为分片)的技术。这些分片随后分布在多个服务器或节点上。下面详细介绍了其工作原理:
- 想象一个巨大的书架:这个书架代表你的整个数据库,里面堆满了书籍(数据)。
- 分片就像划分书架:根据选定的标准(如类型、作者、出版日期)将数据分成更小的部分。每个部分都成为一个分片。
- 分发分片:然后将每个分片放置在单独的服务器上,就像将分类的书籍放置在不同房间的不同书架上一样。
Hibernate 一级缓存和二级缓存的区别?
Hibernate 提供两级缓存,通过减少数据库调用来提高应用程序性能:一级缓存和二级缓存。以下是它们主要区别的细分:
范围
- 一级缓存 (L1 缓存):特定于会话。存在于单个 Hibernate 会话期间。会话中一个查询加载的数据可供同一会话中的后续查询使用,而无需再次访问数据库。
- 二级缓存 (L2 Cache):可选,应用程序范围的缓存。由与同一会话工厂关联的所有 Hibernate 会话共享。一个会话加载的数据可以被其他会话重用,从而大大减少数据库交互。
Hibernate中Get和Load的区别?
数据获取策略:
- get:立即执行数据库查询以获取由 ID 标识的对象。
- 如果该对象存在于数据库中,则将其作为完全填充的对象返回。
- 如果对象不存在,
get
则返回null
。 - load:返回代表已识别实体的代理对象。
- 仅当您访问对象的属性或方法时,才会从数据库中检索实际数据。这种技术称为延迟加载。
- 如果数据库中不存在该对象,则在您尝试访问其属性时
load
抛出一个。ObjectNotFoundException
数据库交互:
- get:总是触发数据库查询,即使对象已经在 Hibernate 缓存(一级缓存)中。
- 加载:如果对象在缓存中,可能不会立即触发数据库查询。只有当您访问对象的数据时才会发生查询。