一、Springboot中有几种定义Bean的方式
1、@Bean注解
在方法上加上@Bean注解
注意:这个注解要在配置类中使用即@Configuration修饰的类,或者启动类也行,因为启动类也是配置类 。
2、使用 @Component
、@Service
、@Controller
、@Repository
注解
这些注解通常用于自动扫描类并将它们注册为 Spring 容器中的 Bean。通过
@Component
或其特定的派生注解(如@Service
、@Controller
、@Repository
)来定义。注意:是类注册成bean,而不是里面的方法
@Component
public class MyComponent {
// 业务逻辑
}
@Service
public class MyService {
// 业务逻辑
}
@Controller
public class MyController {
// 控制器逻辑
}
@Repository
public class MyRepository {
// 数据库访问逻辑
}
3、使用 @ConfigurationProperties
注解
@ConfigurationProperties
用于将配置文件中的属性映射到 Java 类中,通常用于外部化配置,并自动将类定义为 Bean。
比如将成员变量的值写到配置文件中 。
@ConfigurationProperties(prefix = "app.config")
public class AppConfig {
private String name;
private int timeout;
// getters and setters
}
注意:单从功能上看,@value也可以完成赋值的操作,只不过@value只能赋值单个@ConfigurationProperties可以赋值同一前缀的属性
4、使用 @Import
注解
@Import
注解允许将一个或多个配置类导入到 Spring 容器中,从而将这些配置类中定义的 Bean 注册到 Spring 容器中。
比如一个Service类没有加上@Service,但是在配置类中使用@Import导入这个Service类,同样可以将这个类注册成Bean
@Import(AppConfig.class)
public class MainConfig {
// 其他配置
}
5、使用@ControllerAdvice注解
这个注解一般是用于配置全局异常处理器的。可以搭配自定义异常来实现自定义异常处理
这个注解底层是有@Component,修饰的类可能成为一个bean
下图是其他用法,比如我们可以实现ResposeBodyAdvice接口,重写beforeBodyWrite方法,在Controller的结果写入Respose之前执行我们自己的逻辑。
实现
ResponseBodyAdvice
接口的类通常需要被@ControllerAdvice
注解修饰。这是因为@ControllerAdvice
注解用于定义一个全局的控制器增强,使得该类能够影响所有控制器的响应处理,包括对响应体的修改。
注意:底层不是AOP实现的,是执行顺序实现的
- 当控制器方法返回响应体时,Spring MVC 会首先找到适当的
HttpMessageConverter
将对象转换为适合的 HTTP 响应格式。 - 在这个过程中,Spring 会检查所有注册的
ResponseBodyAdvice
实现类,调用其beforeBodyWrite
方法。 - 这使得你可以在响应体被转换并返回给客户端之前,对其进行修改。
6、使用@Configuration
这个注解就很清楚了,不做过多的解释,可以当成配置类来用
7、BeanDefintion
这个方法是比较底层的
8、使用<bean/>
这个就很少用了,我们使用的都是springboot,在spring.xml文件中配置相应类为bean
二、springboot的自动配置原理
这位更是重量级,面试就被问到了
在Spring Boot项目中的引导类上有一个注解@SpringBootApplication,这个注解是对三个注解进行了封装,分别是:
-
@SpringBootConfiguration
-
@EnableAutoConfiguration
-
@ComponentScan
其中@EnableAutoConfiguration
是实现自动化配置的核心注解。
该注解通过@Import
注解导入对应的配置选择器(AutoConfigurationImportSelector)。关键的是内部就是读取了该项目和该项目引用的Jar包的的classpath路径下META-INF/spring.factories文件中的所配置的类的全类名。
在这些配置类中所定义的Bean会根据条件注解所指定的条件来决定是否需要将其导入到Spring容器中。
一般条件判断会有像@ConditionalOnClass
这样的注解,判断是否有对应的class文件,如果有则加载该类,把这个配置类的所有的Bean放入spring容器中使用。
当你引入某个依赖时,Maven(或 Gradle)会自动下载相应的 JAR 包,而 Spring Boot 的自动配置 会根据这些依赖判断是否加载对应的自动配置类。当你在项目中引入了对应的 JAR 包,这些 JAR 包中的 class 文件 就会被加载到项目的 类路径(classpath) 中。
总结:1、@SpringBootApplication >>2、 @EnableAutoConfiguration
>>3、@Import 配置选择器AutoConfigurationImportSelector
>>4、META-INF/spring.factories 里面包含上百个类的全类名
>>5、
@ConditionalOnClass判断是否有对应的class文件,有的话放入容器中
三、@Autowired和@Resource的区别
@Autowired是先按照类型找,再按照名称找
@Resource是先按照名称找,再按照类型找
@Autowired private MyService myService;
比如这里的MyService就是类型,myService就是名称
1、对于@Autowired
错误情况:
1、找到多个类型相同,但是名称没有符合的
2、按照类型找没有符合的
《多个没有,没有就真没有》
如果这样注入
@Autowired
private MyService myService; 会报错
因为是先根据类型找,这个时候找到了两个bean,再根据myService这个名称找,发现没有,会直接报错。
@Bean
public MyService myService1 {
}
@Bean
public MyService myService2 {
}
2、对于@Resource
错误情况:
1、按照名称找没有符合的,再按照类型找多个符合
2、按照名称找多个符合的,但是按照类型找没有符合的
《没有多个,多个没有》
如果这样注入
@Resouce
private MyService myService; 会报错
因为是根据名称找没找到,再根据类型找,没有找到。就会报错
@Bean
public MyService myService1 {
}
@Bean
public MyService myService2 {
}
四、#{}
和 ${}的区别
1. #{}
占位符
#{}
是预编译时的占位符,它的作用是将传入的参数作为参数值,而不是作为 SQL 字符串的一部分。这种占位符使用预编译机制,能够防止 SQL 注入。
- 使用场景:一般用于 MyBatis 中处理 SQL 参数绑定。
- 预编译:
#{}
占位符会在预编译时自动转换为占位符 (?
),并将参数安全地绑定到 SQL 语句中。 - SQL 安全:由于 MyBatis 在执行 SQL 语句时,会使用预编译语句,所以能防止 SQL 注入。
示例:
// MyBatis SQL 查询
SELECT * FROM users WHERE id = #{id}
假设 id
是用户传入的值为 5
,则最终执行的 SQL 语句会是:
SELECT * FROM users WHERE id = ?
并且 MyBatis 会安全地将 id
值 5
绑定到 ?
处。
2、${}
占位符
${}
是字符串替换的占位符,它会将传入的参数直接嵌入到 SQL 语句中,不会进行预编译。由于是直接拼接在 SQL 语句中,因此存在 SQL 注入的风险。
- 使用场景:适用于 MyBatis 或 Spring 的动态拼接场景,但要小心 SQL 注入风险。
- 动态 SQL 拼接:
${}
会在运行时将参数直接替换为对应的值,用于拼接 SQL 语句。
// MyBatis SQL 查询
SELECT * FROM ${tableName}
转化为
SELECT * FROM users
五、mybatisplus相比于mybatis有什么优势
MyBatis-Plus 相比 MyBatis 具有以下几个明显的优势:
1. 简化的 CRUD 操作
- MyBatis:需要手动编写 SQL 语句,定义复杂的 XML 映射文件,手动映射结果集与实体类,工作量较大。
- MyBatis-Plus:提供了内置的基础 CRUD 方法,如
insert
、delete
、update
、select
等操作,减少了大量的重复代码,开发效率更高。只需继承BaseMapper
接口即可实现常见的数据库操作,无需编写 SQL。
2. 内置分页功能
- MyBatis:分页需要自己编写 SQL,或者通过分页插件(如
PageHelper
)来实现,操作步骤较为繁琐。 - MyBatis-Plus:提供了分页插件,只需要简单配置即可使用内置的分页功能,配合
IPage
接口,分页变得更加简单。
3. 代码生成器
- MyBatis:需要手动编写实体类、Mapper 接口、Mapper XML 文件,工作量大,容易出错。
- MyBatis-Plus:提供代码生成器工具,可以根据数据库表自动生成实体类、Mapper、Service 等代码,极大提高开发效率,减少出错的可能。
4. Lambda 表达式支持
- MyBatis:如果查询条件较多,SQL 复杂度增加,维护 SQL 变得困难。
- MyBatis-Plus:支持 Lambda 表达式编写查询条件,避免了直接拼接字符串的风险,使代码更加简洁和安全,尤其适合动态条件查询的场景。
5. 条件构造器
- MyBatis:动态条件查询时需要自己拼接 SQL 或使用脚本标签,如
<if>
、<where>
标签。 - MyBatis-Plus:提供
Wrapper
条件构造器,通过链式调用可以轻松构建复杂查询条件,简化了代码,避免了手动拼接 SQL 出现的各种问题。
6. 乐观锁和逻辑删除
- MyBatis:实现乐观锁和逻辑删除需要手动编写 SQL 和处理逻辑,容易遗漏和出错。
- MyBatis-Plus:内置了乐观锁和逻辑删除的实现,只需在实体类中配置相应的注解,即可自动支持这些功能,减少了额外的开发工作。
7. 多租户支持
- MyBatis:多租户架构下的 SQL 处理需要开发者自行处理,难度较大。
- MyBatis-Plus:内置多租户插件,能够在 SQL 层面支持多租户架构,减少开发者的工作量。
8. 插件扩展机制
- MyBatis:虽然支持插件,但 MyBatis-Plus 的插件机制更为丰富。
- MyBatis-Plus:支持非常灵活的插件机制,官方提供了很多有用的插件,如性能分析插件、SQL 执行分析插件等,开发者也可以自定义插件以满足特殊需求。
9. 更加活跃的社区和文档支持
- MyBatis-Plus 拥有丰富的文档、教程和活跃的社区,相比 MyBatis 来说,开发者在遇到问题时可以更容易地找到帮助和解决方案。
六、ApplicationContext和beanFacrtory有什么区别
ApplicationContext
和 BeanFactory
是 Spring 框架中用于管理和配置 beans 的两种容器,它们的区别主要体现在功能和使用场景上:
1. 功能差异
- BeanFactory:是 Spring 容器的基础接口,只提供最基本的依赖注入功能。它主要负责创建和管理 beans 的实例。
BeanFactory
在加载配置文件时并不会立即实例化所有的 bean,只有在首次使用(延迟加载,Lazy Initialization)时才会创建实例。 - ApplicationContext:是
BeanFactory
的子接口,扩展了它的功能。除了具备BeanFactory
的所有功能外,ApplicationContext
还提供了更多面向企业级应用的特性,例如:- 事件机制:允许发布和监听应用事件(如上下文刷新事件)。
- 国际化支持:提供消息资源处理(国际化,I18N 支持)。
- 统一的资源加载:支持从各种不同的资源(如文件系统、类路径、URL)中加载配置文件。
- 自动 bean 初始化:
ApplicationContext
启动时会立即实例化所有的单例 bean,而不是等到它们第一次被使用时再进行实例化(即预加载)。
2. 加载时机
- BeanFactory:通常采用 Lazy Initialization 的方式加载 bean,只有当应用代码通过
getBean()
方法请求 bean 时,才会创建该 bean 的实例。这种方式节省了内存,但如果某个 bean 在启动时就有问题,直到使用时才会发现。 - ApplicationContext:在容器启动时即加载并实例化所有单例 bean(预加载)。这样做的好处是可以在容器启动时发现配置错误或依赖问题,有利于提早排查问题。
3. 额外特性
- BeanFactory:相对轻量,没有像
ApplicationContext
那样提供事件、国际化、资源文件等扩展特性。如果应用程序只需要基本的 IOC 功能,BeanFactory
足够使用,但它不支持更复杂的企业级功能。 - ApplicationContext:
- 事件机制:支持发布和监听
ApplicationEvent
,如上下文启动、停止、刷新等事件。 - 国际化:支持
MessageSource
接口,方便开发多语言环境下的应用。 - AOP:
ApplicationContext
更好地支持 AOP(面向切面编程),如基于注解的事务管理。
- 事件机制:支持发布和监听
4. 使用场景
- BeanFactory:适用于资源有限的场景,或者简单的应用程序,比如在移动设备上或需要节省内存的情况下。由于它没有预加载单例 bean 的功能,启动开销较小。
- ApplicationContext:在现代 Spring 应用中更为常用,特别是复杂的、企业级的应用。
ApplicationContext
提供了更多功能和易用性,适合大部分开发场景。
5. 常见实现类
- BeanFactory:其常见的实现类是
XmlBeanFactory
,不过它在较新的 Spring 版本中已经被弃用,推荐使用ApplicationContext
。 - ApplicationContext:常用的实现类有:
ClassPathXmlApplicationContext
:从类路径下的 XML 配置文件加载 bean 定义。FileSystemXmlApplicationContext
:从文件系统中的 XML 配置文件加载 bean 定义。AnnotationConfigApplicationContext
:通过注解加载配置类(常用于无 XML 配置的 Java 配置模式)。
七、springboot中配置文件的加载顺序是怎么样的
在 Spring Boot 中,配置文件的加载顺序按照以下优先级进行。较高优先级的配置会覆盖较低优先级的配置:
-
application.properties
或application.yml
文件:/config
子目录下的文件(如:config/application.properties
或config/application.yml
)具有比根目录下的配置文件更高的优先级。- 根目录下的文件(如:
application.properties
或application.yml
)。
-
命令行参数:
- 可以通过命令行传递的配置参数,例如
--server.port=8081
。
- 可以通过命令行传递的配置参数,例如
-
@TestPropertySource
注解:- 如果使用了该注解来为测试类指定额外的属性,则这些属性会覆盖其他源的配置。
-
@PropertySource
注解:- 在类中使用该注解加载的属性文件具有较高的优先级。
-
操作系统环境变量:
- 通过操作系统环境变量提供的配置值,优先级高于大多数配置文件。
-
Java 系统属性:
- 使用 JVM 参数传递的属性(例如:
-Dserver.port=8081
)优先级高于默认配置文件。
- 使用 JVM 参数传递的属性(例如:
-
random.*
属性:- 例如
random.uuid
,Spring Boot 提供了生成随机数、随机UUID等属性的能力,优先级高于配置文件中的默认值。
- 例如
-
application.properties
或application.yml
文件中定义的默认配置。 -
Spring Boot 默认配置:
- 例如默认的端口是
8080
,日志级别是INFO
。
- 例如默认的端口是
配置文件的加载顺序总结:
- 命令行参数 > 测试类配置 > 类级别注解配置 > 环境变量 > Java系统属性 >
/config
子目录的配置文件 > 根目录的配置文件 > Spring Boot 默认配置。
通过这种机制,Spring Boot 允许你根据不同的运行环境和需求灵活地配置和覆盖默认值。
补充:springcloud的几个配置文件执行规则
加载顺序:
bootstrap.yml
/bootstrap.properties
:
- 这个文件首先被加载,通常用于加载与 Spring Cloud 相关的设置,如服务发现、配置中心等。
application.yml
/application.properties
:
- 在
bootstrap
文件加载后,application
文件被加载,主要用于应用的常规配置。- Nacos 配置:
- 从 Nacos 加载的配置会在
application.yml
加载之后进行,此时可以覆盖application.yml
中的同名配置项。后面的覆盖前面
示例
假设有以下配置:
bootstrap.yml
:app.name: BootstrapApp
application.yml
:app.name: ApplicationApp
- Nacos 配置:
app.name: NacosApp
- 命令行参数:
--app.name=CommandLineApp
最终的应用配置将是:
app.name = CommandLineApp