1. @ActiveProfiles
用来声明活动的profile–@ActiveProfiles(“prod”(这个prod定义在配置类中))
@RunWith(SpringRunner.class)
@SpringBootTest
@ActiveProfiles("test")
public class MyApplicationTests {
@Test
public void contextLoads() {
// 你的测试代码
}
}
@ActiveProfiles(“test”) 注解告诉 Spring框架激活名为 test 的配置文件。这意味着 Spring 将会加载与 test 配置文件相关的所有 bean 和配置。如果你有多个配置文件需要激活,可以使用逗号分隔它们,如 @ActiveProfiles(“test,test1”)。
2. @After
后置建言(advice),标记那些在每个测试方法执行之后都会运行的代码,和@Before相反
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class MyTest {
@Before
public void setUp() {
// 在每个测试方法执行之前运行
}
@Test
public void testMethod1() {
// 测试方法1
}
@Test
public void testMethod2() {
// 测试方法2
}
@After
public void tearDown() {
// 在每个测试方法执行之后运行
}
}
这个注解主要用于执行清理工作,比如释放资源、恢复测试前的状态等,以确保测试的独立性和可重复性。
3. @Before
前置建言(advice),在原方法前执行。
4. @Around
环绕建言(advice),Spring AOP(面向切面编程)中的一个重要注解,环绕通知是一种在目标方法执行前后插入额外逻辑的增强处理,它可以在方法执行前进行操作,并在方法执行后进行操作,甚至能够控制目标方法的执行与否、修改返回值或抛出异常。
@Aspect
public class LoggingAspect {
@Around("execution(* com.example.service.*.*(..))")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
// 在目标方法执行之前执行的逻辑
System.out.println("Method " + joinPoint.getSignature().getName() + " starts");
// 执行目标方法
Object proceed = joinPoint.proceed();
// 在目标方法执行之后执行的逻辑
long endTime = System.currentTimeMillis();
System.out.println("Method " + joinPoint.getSignature().getName() + " ends. Execution time: " + (endTime - startTime) + " ms");
return proceed;
}
}
注意事项
- 环绕通知中的 proceed() 方法必须被调用,否则目标方法将不会被执行。
- 环绕通知需要谨慎使用,因为不当的使用可能会导致程序逻辑出错或性能问题。
- 确保环绕通知方法的返回类型与目标方法的返回类型一致,以便能够正确返回结果。
- 在环绕通知中处理异常时,要注意异常的传递和处理,避免程序异常终止。
5. @Aspect
Spring AOP(面向切面编程)中的一个核心注解,用于声明一个类为切面(Aspect)
@Aspect 的主要作用:
- 声明切面:通过
@Aspect
注解,Spring 容器能够识别出哪些类是切面类,进而对它们进行特殊的处理。 - 定义通知:在切面类中,你可以定义各种通知(Advice),如前置通知(Before Advice)、后置通知(After Advice)、环绕通知(Around Advice)等,来指定在目标方法执行的不同阶段需要执行的逻辑。
- 指定切入点:通过切入点表达式(Pointcut Expression),你可以指定哪些类的哪些方法需要被增强(即哪些方法需要执行通知中定义的逻辑)。
使用 @Aspect 的基本步骤:
-
引入依赖:确保你的项目中已经引入了 Spring AOP 和 AspectJ 的相关依赖。
-
定义切面类:使用
@Aspect
注解来声明一个类为切面类。 -
定义通知:在切面类中,使用 Spring AOP 提供的注解(如
@Before
、@After
、@Around
等)来定义通知。 -
指定切入点:在通知注解中,通过
value
或pointcut
属性来指定切入点表达式,以指定哪些方法需要被增强。 -
配置切面:将切面类注册到 Spring 容器中,以便 Spring 能够识别并处理它。这通常通过 Java 配置或 XML 配置来完成。
@Aspect
@Component // 或者使用 @Configuration 和 @EnableAspectJAutoProxy
public class LoggingAspect {
// 定义一个切入点表达式
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceLayerExecution() {}
// 使用切入点表达式和 @Before 注解定义前置通知
@Before("serviceLayerExecution()")
public void logBeforeServiceMethod(JoinPoint joinPoint) {
System.out.println("Before executing " + joinPoint.getSignature().getName());
}
// 使用切入点表达式和 @Around 注解定义前置通知
// @Around("serviceLayerExecution()")
// public void logBeforeServiceMethod(JoinPoint joinPoint) {
// System.out.println("Around executing " + joinPoint.getSignature().getName());
// }
// 类似地,你可以定义其他类型的通知,如 @After、@AfterReturning、@AfterThrowing
注意事项
- 确保你的 Spring 容器已经启用了 AOP 支持,这通常通过 @EnableAspectJAutoProxy 注解在配置类上实现。
- 切入点表达式是 Spring AOP 的强大功能之一,它允许你精确地指定哪些方法需要被增强。
- 在切面类中定义的通知方法不应有返回值(除了环绕通知),并且它们的参数列表应该与 Spring AOP 要求的参数类型相匹配。
- 具体使用按照自己代码的业务逻辑
6. @Autowired
Spring 框架中用于实现依赖注入的一个核心注解。它使得 Spring 容器能够自动地识别和注入标注了 @Autowired 的字段、构造器参数或方法参数所需的 bean。这极大地简化了依赖管理,使得开发者可以更加专注于业务逻辑的实现,而不是如何配置和注入依赖
工作原理
当 Spring 容器启动时,它会扫描被 @Component、@Service、@Repository 或 @Controller 等注解标记的类,并将这些类的实例作为 bean 注册到 Spring 容器中。然后,对于每一个标注了 @Autowired 的字段、构造器或方法,Spring 容器会尝试在容器中查找一个类型匹配的 bean,并将其注入到被注解的位置。
使用场景
- 字段注入:直接在类的字段上使用 @Autowired 注解,Spring 容器会自动将匹配的 bean 注入到这个字段中。
@Autowired
private UserRepository userRepository;
- 构造器注入:在类的构造器上使用 @Autowired 注解,Spring 容器会在创建类的实例时自动注入构造器参数所需的 bean。
@Autowired
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
- 设置方法注入(Setter Injection):在类的 setter 方法上使用 @Autowired 注解,Spring 容器会在调用这个方法时自动注入所需的 bean。
@Autowired
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
Demo
优点
- 减少代码耦合:通过依赖注入,类之间的耦合度降低,更易于维护和测试。
- 易于配置:使用 @Autowired 可以简化配置过程,减少 XML 配置或 Java 配置的复杂性。
- 支持自动装配:Spring 容器能够自动地识别并注入依赖关系,提高了开发效率。
注意事项
- 可选依赖:如果某个依赖是可选的,可以在 @Autowired 注解后添加 required = false 属性,这样即使没有匹配的 bean,Spring 也不会抛出异常。
- 多个候选者:如果 Spring 容器中有多个类型匹配的 bean,并且你需要注入其中一个,那么可能需要使用 @Qualifier 注解来指定具体要注入哪一个 bean。
- 循环依赖:@Autowired 默认支持构造器注入的循环依赖,但如果是通过字段注入的循环依赖,可能需要额外的配置或避免使用。
7. @Bean
Spring 框架中的一个核心注解,它主要用于在配置类中声明方法,并将这些方法的返回值注册为 Spring 容器中的 Bean
作用
- 声明 Bean:@Bean 注解用于告诉 Spring 容器,被注解的方法将返回一个对象,该对象要注册为 Spring 应用上下文中的 Bean。
- 管理 Bean:一旦对象被注册为 Bean,Spring 容器将负责其生命周期的管理,包括创建、装配和销毁等。
使用
- 配置类:@Bean 注解通常用在带有 @Configuration 注解的类中,这些类被称为配置类。
- 方法级别:@Bean 是一个方法级别上的注解,用于标记那些将返回对象作为 Bean 的方法。
@Configuration
public class AppConfig {
@Bean
public UserService userService() {
return new UserService();
}
}
特性
- 依赖注入:@Bean 方法可以依赖其他 Bean,这些依赖将通过方法参数自动注入。
- 生命周期回调:可以使用 @PostConstruct 和 @PreDestroy 注解来定义 Bean 初始化后和销毁前的回调方法。
- 指定名称和别名:可以使用 @Bean 注解的 name 或 value 属性来指定 Bean 的名称和别名。
- 指定作用域:可以使用 @Scope 注解来指定 Bean 的作用域,如 singleton(单例)、prototype(原型)等。
注意事项
- 方法调用:虽然 @Bean 方法可以被调用,但通常不建议直接调用它们来获取 Bean 实例,而应该通过 Spring 容器来获取。
- 唯一性:在同一个配置类中,@Bean 方法定义的 Bean 名称默认是方法名,如果有多个方法返回相同类型的对象但名称不同,则它们会被注册为不同的 Bean。
- 重载方法:虽然 Java 支持方法重载,但在使用 @Bean 注解时要小心,因为 Spring 容器可能无法正确处理重载的 @Bean 方法,导致不可预测的行为。
- @Autowired:用于自动装配 Bean,与 @Bean 配合使用,可以实现依赖的自动注入。
- @Component、@Repository、@Service、@Controller:这些注解也是用于定义 Bean 的,但它们通常用于标注具体的类,而 @Bean 是用于标注配置类中的方法。
- **@Bean **:是 Spring 框架中定义和管理 Bean 的重要工具,通过它,开发者可以灵活地在配置类中声明和管理 Bean,从而实现依赖注入和对象管理的目的。
8. @Component
Spring 框架中的一个核心注解,它主要用于标识一个类作为 Spring 容器中的组件,成为Spring管理的Bean。当使用基于注解的配置和类路径扫描时,这些类被视为自动检测的候选对象。同时@Component还是一个元注解。
作用
- 组件标识:@Component 注解用于告诉 Spring 容器,被注解的类是一个组件,应该被 Spring 管理。
- 自动扫描:当使用基于注解的配置时,Spring 容器会扫描带有 @Component 注解的类,并自动实例化它们作为 Spring 应用上下文中的 Bean。
使用
- 通用组件:当一个类不好归类到特定的注解(如 @Controller、@Service、@Repository)时,可以使用 @Component 进行标注。
- 简化配置:@Component 注解简化了 Spring 应用的配置过程,使得开发人员能够更加专注于业务逻辑的实现。
衍生注解
- @Component 有三个常见的衍生注解:@Controller、@Service 和 @Repository。
@Controller
:用于标识控制器类,处理 Web 请求。@Service
:用于标识服务层类,提供业务逻辑。@Repository
:用于标识数据访问对象(DAO),提供数据访问功能。
这些衍生注解不仅简化了组件的标识,还带来了额外的好处,如异常转换(@Repository)或 MVC 控制功能(@Controller)
依赖注入
- 被 @Component 注解的类可以和其他类一样进行自动装配。例如,可以在另一个组件中使用 @Autowired 注解来自动注入一个被 @Component 注解的组件。
- @Component 也支持指定组件的名称,使用 @Qualifier 注解可以根据名称来装配组件。
注意事项
- 虽然 @Component 注解非常灵活,但在实际开发中,建议根据组件的用途选择合适的衍生注解(如 @Controller、@Service、@Repository),以便更好地组织和管理代码。
- 当使用 @Component 注解时,要确保 Spring 容器能够扫描到被注解的类。这通常通过设置 – -@ComponentScan 注解或相应的 XML 配置来实现。
9. @ComponentScan
用于指定 Spring 容器应该扫描哪些包来查找并注册带有特定注解的类(如 @Component、@Service、@Repository、@Controller 等)作为 Spring Bean
作用
根据定义的扫描路径,把符合扫描规则的类装配到 Spring 容器中。这样,Spring 容器就能够管理这些类的生命周期,并提供依赖注入等功能。
基本属性
@ComponentScan 注解包含多个属性,用于自定义组件扫描的行为:
- basePackages:指定需要扫描的包名,可以是一个字符串数组,也可以使用点号分隔的包路径。
- basePackageClasses:指定包中的某些类,Spring 会扫描这些类所在的包及其子包。
- value:与 basePackages 功能相同,用于指定扫描的包名。
- useDefaultFilters:是否使用默认的过滤器。如果设置为 true,则扫描所有带有 @Component、@Service、@Repository 和 @Controller 注解的类。如果设置为 false,则不会使用默认的过滤器,此时可以通过 includeFilters 和 excludeFilters 属性来自定义过滤规则。
- includeFilters:指定 Spring 应该包含哪些类型的类。可以包含多个过滤器,每个过滤器都包含一个类型和一个类。
- excludeFilters:指定 Spring 不应该包含哪些类型的类。用法与 includeFilters 类似。
Demo
例如包结构如下:
com
└── example
└── myapp
├── AppConfig.java
└── service
├── UserService.java
└── ProductService.java
在 UserService.java 和 ProductService.java 中,我们分别使用 @Service 注解标注了这两个类。然后,在 AppConfig.java 配置类中,我们可以使用 @ComponentScan 注解来指定 Spring 应该扫描的包:
@Configuration
@ComponentScan(basePackages = "com.example.myapp")
public class AppConfig {
// 这里可以定义其他配置 Bean 和设置
}
在这个例子中,@ComponentScan 注解被用于 AppConfig 类上,指定 Spring 应该扫描 com.example.myapp 包及其子包,以将组件注册为 Spring Bean。
自定义过滤器
如果需要更细粒度的控制,可以通过 includeFilters 和 excludeFilters 属性来自定义过滤规则。例如,只扫描带有特定注解的类等
@ComponentScan(
basePackages = "com.example.myapp",
useDefaultFilters = false,
includeFilters = {
@Filter(type = FilterType.ANNOTATION, classes = {MyCustomAnnotation.class})
}
)
10. @Configuration
它用于指示一个类声明了一个或多个 @Bean 方法,并且这些 @Bean 方法将被 Spring 容器管理,用于生成和管理 Spring 应用上下文中的对象(即 beans)。当你使用 @Configuration 注解一个类时,该类就变成了一个配置类(configuration class),Spring 容器会在启动时处理这个类,并识别出其中的 @Bean 方法。
主要特点
- 配置类:@Configuration 注解的类被视为一个配置类,它允许你通过 @Bean 注解的方法来定义和初始化 beans。
- 完全控制:通过配置类,你可以完全控制你的 Spring 应用上下文中的 bean 的创建和配置。
- JavaConfig:@Configuration 是 JavaConfig 的一部分,JavaConfig 是指使用 Java 类和注解来代替传统的 XML 文件来配置 Spring 应用的方式。
- 环境抽象:配置类还可以与 @Profile、@PropertySource 等注解结合使用,以支持更复杂的配置需求,如环境特定的配置和属性文件加载。
Demo
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean
public MyService myService() {
return new MyServiceImpl();
}
}
AppConfig 是一个配置类,它定义了一个名为 myService 的 @Bean 方法。当 Spring 容器启动时,它会调用 myService 方法来创建 MyService 接口的一个实现(MyServiceImpl),并将其注册为 Spring 应用上下文中的一个 bean。这样,你就可以在其他 Spring 管理的组件中通过自动装配(如 @Autowired)来注入这个 MyService bean 了。
注意事项
- 当你在配置类中使用 @Bean 注解的方法时,Spring 容器会确保每个 bean 只有一个实例(除非你在 @Bean 方法上使用了特定的作用域注解,如 @Scope)。
- 配置类本身也是由 Spring 容器管理的,但它不是通过 @Bean 方法定义的。相反,它是通过 Spring 容器在启动时扫描到的带有 @Configuration 注解的类来识别的。
- 配置类中的 @Bean 方法可以互相调用,以构建 bean 之间的依赖关系。但是,要注意避免循环依赖,因为这会导致 Spring 容器在启动时抛出异常。
11. @ConfigurationProperties
它主要用于将配置文件(如 application.properties 或 application.yml)中的属性绑定到 Java 对象上。这个注解极大地简化了配置属性的读取和管理,使得开发者能够将外部配置与应用程序代码解耦,提高代码的可维护性和可扩展性。
主要特点
- 属性绑定:将配置文件中的属性绑定到 Java 对象的字段上,无需手动编写大量的 getter 和 setter 方法来读取配置。
- 类型转换:支持将配置文件中的字符串自动转换为 Java 对象的相应类型(如 int、boolean、List、Map 等)。
- 默认值:当配置文件中没有指定某个属性的值时,可以使用 Java 字段的默认值或通过 @Value 注解指定默认值。
- 验证:支持对配置属性进行有效性验证,确保配置的正确性。
- 松散绑定:支持松散绑定规则,即属性的名称不需要严格匹配,例如 my-property-name 可以绑定到 myPropertyName 字段上。
Demo
-
- 添加注解:在 Java 类上添加 @ConfigurationProperties 注解,并指定需要绑定的配置文件前缀
@Component
@ConfigurationProperties(prefix = "myapp")
public class MyAppProperties {
private String name;
private int version;
// getter 和 setter 方法
}
-
- 配置文件:在 application.properties 或 application.yml 配置文件中添加相应的属性。
application.properties
myapp.name=My App Name
myapp.version=1.0.0
application.yml
myapp:
name: My App Name
version: 1.0.0
-
- 使用配置:通过 Spring 容器获取 MyAppProperties 类的实例,并使用其中的属性值。
@Autowired
private MyAppProperties myAppProperties;
public void doSomething() {
String name = myAppProperties.getName();
int version = myAppProperties.getVersion();
// 使用属性值进行其他操作
}
注意事项
- 前缀:@ConfigurationProperties 注解的 prefix 属性必须小写,否则会报错。
- JavaBean 接口:被 @ConfigurationProperties 注解的类需要遵循 JavaBean 规范,即提供公共的 getter 和 setter 方法。
- 容器组件:只有被 Spring 容器管理的组件(如通过 @Component、@Service 等注解标注的类)才能使用 @ConfigurationProperties 注解的功能。
- 配置文件处理器:可以导入 Spring Boot 的配置文件处理器依赖,以便在编写配置时获得代码提示。
12. @ContextConfiguration
Spring 测试场景中经常使用,它主要用于加载和配置 Spring 上下文(ApplicationContext)。这个注解允许开发者指定要加载的配置文件或配置类的位置,以便在运行时或测试时能够正确地构建和初始化 Spring 上下文。
作用
- 加载配置文件:用于指定 Spring 配置文件的位置,这些配置文件包含了 Spring 应用程序的配置信息,如 bean 的定义、数据源配置等。
- 加载配置类:也可以用来指定一个或多个包含 @Configuration 注解的 Java 配置类的位置。
使用
@ContextConfiguration 注解可以通过两种主要方式使用:
-
- 指定配置文件:
使用 locations 属性来指定 XML 配置文件的位置。可以指定一个或多个配置文件,多个文件之间用逗号或数组语法分隔。
示例:@ContextConfiguration(locations = { “classpath:applicationContext.xml”, “classpath:test-context.xml” })
- 指定配置文件:
-
- 指定配置类:
使用 classes 属性来指定一个或多个配置类的位置。这些类必须包含 @Configuration 注解。
示例:@ContextConfiguration(classes = { TestConfig.class, AnotherTestConfig.class })
- 指定配置类:
原理
- 当使用 @ContextConfiguration 注解时,Spring 容器会根据指定的配置文件或配置类来创建和初始化应用程序上下文。
- 如果指定了 XML 配置文件,Spring 容器会通过相应的 ContextLoader(如 GenericXmlContextLoader)来加载这些 XML 文件。
- 如果指定了配置类,Spring 容器则会使用这些配置类中定义的 @Bean 方法来创建和注册 bean。
13. @Controller
用于声明一个类作为 Spring MVC 控制器(Controller)。当一个类被 @Controller 注解标记时,Spring 容器会检测到这个类,并将其注册为一个 Spring MVC 控制器,从而能够处理通过 HTTP 请求映射的方法。
作用
- 定义控制器:@Controller 注解将一个类标记为 Spring MVC 控制器,这意味着这个类中的方法可以处理来自客户端的 HTTP 请求。
- 请求映射:虽然 @Controller 注解本身不直接处理请求映射,但它常与 @RequestMapping 或其派生注解(如 @GetMapping、@PostMapping 等)一起使用,以将 HTTP 请求映射到控制器中的特定方法上。
- 视图解析:控制器方法通常返回一个视图名称或 ModelAndView 对象,Spring MVC 会根据这个返回值找到相应的视图模板,并将其渲染为 HTML 响应返回给客户端。
- 数据绑定:控制器方法可以接收请求参数、路径变量等,并自动将其绑定到方法参数上,这简化了数据的提取和处理过程。
Demo
- 添加 @Controller 注解:在类定义前添加 @Controller 注解,将类标记为控制器。
@Controller
public class MyController {
}
- 定义请求处理方法:在控制器类中定义方法,并使用 @RequestMapping 或其派生注解来映射 HTTP 请求。
@Controller
public class MyController {
@GetMapping("/hello")
public String hello(Model model) {
model.addAttribute("message", "Hello, Spring MVC!");
return "hello"; // 返回视图名称,Spring MVC 会找到相应的视图模板进行渲染
}
}
- 配置视图解析器:在 Spring MVC 的配置中,需要配置视图解析器(ViewResolver),以便 Spring MVC 能够根据控制器方法返回的视图名称找到相应的视图模板。
@Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
return resolver;
}
注意事项
- 不要与 @RestController 混淆:@RestController 是 @Controller 和 @ResponseBody 的组合注解,它用于创建 RESTful Web 服务。如果你的控制器方法主要返回 JSON 或 XML 等数据而不是视图,则应该使用 @RestController。
- 路径匹配:在 @RequestMapping 注解中定义的路径是相对于应用程序的上下文路径的。
- 方法参数和返回值:控制器方法可以接收各种类型的参数,如 @RequestParam、@PathVariable、Model 等,并可以返回 String(视图名称)、ModelAndView、ResponseEntity、void(配合 HttpServletResponse 使用)等。
- 异常处理:可以通过 @ExceptionHandler 注解来定义异常处理方法,以处理控制器中抛出的异常。
- 拦截器:可以通过实现 HandlerInterceptor 接口来创建拦截器,并在 Spring MVC 配置中注册它们,以便在请求处理流程中的不同阶段执行自定义的代码。
14. @ExceptionHandler
Spring MVC 中的一个注解,用于处理控制器中抛出的异常。当你在控制器中处理请求时,可能会遇到各种异常情况,比如数据验证失败、资源找不到、权限不足等。使用 @ExceptionHandler 注解,你可以定义一个或多个方法来专门处理这些异常,从而避免在控制器方法中直接处理异常逻辑,使代码更加清晰和易于维护。
使用
- 定义异常处理方法:在控制器中,你可以使用 @ExceptionHandler 注解来标记一个或多个方法作为异常处理方法。这些方法需要接收一个异常类型的参数,并可以返回一个视图名称、ModelAndView 对象、ResponseEntity 或其他任何 Spring MVC 支持的响应类型。
- 指定异常类型:@ExceptionHandler 注解可以指定一个或多个异常类型,表示该方法将处理哪些类型的异常。如果未指定异常类型,则该方法将作为默认异常处理器,处理所有未被其他 @ExceptionHandler 方法捕获的异常。
- 返回值:异常处理方法的返回值决定了如何响应客户端。你可以返回一个视图名称来渲染一个错误页面,或者返回一个包含错误信息的 ResponseEntity 对象。
Demo
@Controller
public class MyController {
// 处理特定类型的异常
@ExceptionHandler(value = MyCustomException.class)
public ResponseEntity<String> handleMyCustomException(MyCustomException ex) {
// 处理异常逻辑
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("自定义异常处理");
}
// 处理所有未捕获的异常(可选)
@ExceptionHandler(Exception.class)
public String handleAllExceptions(Exception ex) {
// 记录日志等操作
return "error"; // 返回错误页面的视图名称
}
// 控制器方法
@GetMapping("/somePath")
public String someMethod() {
// 假设这里抛出了 MyCustomException 异常
throw new MyCustomException("这是一个自定义异常");
}
// 自定义异常类
public static class MyCustomException extends RuntimeException {
public MyCustomException(String message) {
super(message);
}
}
}
注意事项
- 全局异常处理:如果你希望在整个应用程序中集中处理异常,而不是在每个控制器中分别处理,你可以创建一个实现了 HandlerExceptionResolver 接口的类,或者更简单地,使用 @ControllerAdvice 注解来定义一个全局异常处理类。
- 异常优先级:如果有多个 @ExceptionHandler 方法可以处理同一个异常,Spring MVC 会根据方法的定义顺序(从上到下)来选择第一个匹配的方法。因此,你应该注意异常处理方法的定义顺序,以确保它们按照你期望的顺序被调用。
- 日志记录:在异常处理方法中,不要忘记记录异常信息到日志中,以便在出现问题时进行调试和追踪。
- 响应客户端:确保你的异常处理方法能够向客户端返回清晰、有用的错误信息,以提高用户体验和系统的可维护性。
15. @ModelAttribute
Spring框架中的一个注解,主要用于将HTTP请求中的数据绑定到模型对象,并在视图中进行渲染。它可以应用于方法参数或方法本身,具有灵活的数据处理能力。
使用
- 方法参数:
当@ModelAttribute注解用于方法参数时,它表示从模型中获取对应的属性值,并将其绑定到方法参数上。这通常用于处理表单提交或URL中的查询参数,自动将请求中的数据填充到对应的JavaBean对象中,并添加到模型中,便于后续方法或视图使用。
@GetMapping("/users/{id}")
public String getUser(@PathVariable("id") int userId, @ModelAttribute("user") User user) {
// 假设这里根据userId从数据库中获取User对象
user = userService.getUserById(userId);
// 方法执行时,user参数已经包含了从数据库获取的User对象
// ...
return "userPage";
}
@ModelAttribute(“user”)注解将请求中或模型中名为”user”的属性绑定到User类型的参数user上。但请注意,这里的实际使用场景可能有所不同,因为通常我们不会将@ModelAttribute用于已经通过@PathVariable等注解获取了数据的参数上。这里的示例主要是为了说明@ModelAttribute在方法参数上的用法。
- 方法级别:
当@ModelAttribute注解用于方法本身时,它表示该方法的返回值应该被添加到模型中。这常用于在多个请求处理方法之前,准备共用的模型数据。
@ModelAttribute
public void addAttributes(Model model) {
model.addAttribute("msg", "Hello, Spring MVC!");
}
@GetMapping("/")
public String home() {
// 这里可以直接使用模型中的"msg"属性
// ...
return "homePage";
}
addAttributes方法使用了@ModelAttribute注解,其返回值(实际上是void,但通过model.addAttribute方法向模型中添加数据)会在每个请求处理方法之前执行,向模型中添加了一个名为”msg”的属性。这样,在后续的请求处理方法(如home方法)中就可以直接使用这个属性了。
特点
- 自动绑定:可以自动将请求参数绑定到模型对象,简化了数据绑定的过程。
- 灵活配置:通过指定属性名称,可以灵活控制模型中的属性名称。
- 优先级:@ModelAttribute注解的方法会优先于控制器中的其他请求处理方法执行,确保模型数据在请求处理之前就已经准备好。
注意事项
- @ModelAttribute注解的方法会在控制器中的每个请求处理方法之前执行(除非有特定的条件阻止其执行),因此在使用时需要谨慎,避免不必要的性能开销。
- 当@ModelAttribute注解用于方法参数时,如果模型中不存在对应的属性,Spring会尝试从请求中查找并绑定数据;如果模型中已存在对应的属性,则直接使用模型中的属性值。
- 在使用@ModelAttribute注解时,可以通过指定属性名称来控制模型中的属性名称,未指定时则默认使用返回类型的首字母小写作为属性名称(对于方法级别的@ModelAttribute)。
16. @Transactional
Spring 框架中用于声明式事务管理的一个注解。它允许开发者通过简单的注解来声明某个方法或类需要事务支持,而无需编写复杂的事务管理代码。Spring 会在运行时自动为这些方法或类创建代理对象,并在这些对象的方法执行时应用事务管理。
使用
- 业务逻辑需要保证数据一致性:当多个数据库操作需要作为一个整体来执行时,使用 @Transactional 可以确保这些操作要么全部成功,要么在遇到错误时全部回滚,从而保持数据的一致性。
- 简化事务管理:通过声明式事务管理,开发者可以将事务管理的逻辑从业务代码中分离出来,专注于业务逻辑的实现,而无需直接处理事务的开启、提交或回滚等繁琐事务。
- 提高代码的可读性和可维护性:使用 @Transactional 注解可以使代码更加简洁明了,易于理解和维护。
使用
@Transactional 可以应用于接口定义、接口中的方法、类定义或类中的方法上。但是,请注意,由于 Spring 代理机制的限制,该注解在接口定义或接口方法上的效果可能不如预期,因此通常建议将其应用于具体的类方法上。
@Service
public class MyService {
@Transactional
public void myMethod() {
// 执行数据库操作
}
}
myMethod 方法被标记为事务性的。当这个方法被调用时,Spring 会自动为这个方法的执行创建一个事务上下文,并在方法执行结束时根据是否出现异常来决定是提交事务还是回滚事务。
属性
@Transactional 注解还提供了多个属性,允许开发者对事务的行为进行更细致的控制,例如:
- propagation:事务的传播行为,定义了当前方法如何与已存在的事务进行交互。
- isolation:事务的隔离级别,定义了事务中的修改对其他事务的可见性。
- timeout:事务的超时时间,超过这个时间限制,事务将被自动回滚。
- readOnly:标记事务是否为只读事务,只读事务用于不需要修改数据的场景,可以提高性能。
- rollbackFor 和 rollbackForClassName:定义了哪些异常会导致事务回滚。
- noRollbackFor 和 noRollbackForClassName:定义了哪些异常不会导致事务回滚。
注意事项
- 方法可见性:@Transactional 注解的方法必须是 public 的,因为 Spring AOP 是通过代理机制来实现的,而代理机制要求被代理的方法必须是 public 的。
- 自调用问题:在同一个类中,一个 @Transactional 注解的方法调用另一个 @Transactional 注解的方法时,事务的传播行为可能不会按预期工作,因为自调用不会通过代理对象,因此事务管理不会被触发。
- 异常处理:默认情况下,运行时异常和错误会导致事务回滚,而受检异常则不会。但是,可以通过 @Transactional 注解的 rollbackFor 和 noRollbackFor 属性来自定义哪些异常会导致事务回滚。
17. @Value
Spring 框架中的一个注解,它用于注入配置文件(如 properties 或 YAML 文件)中的值到 Spring 管理的 bean 的字段中。这个注解通常与 @ConfigurationProperties 或直接在 Spring 的组件(如 @Component、@Service、@Controller 等)中使用,以便从外部配置源(如 application.properties 或 application.yml 文件)中读取值。
使用
- 注入基本类型值:如字符串、整数、布尔值等。
- 注入复杂类型值:如使用 SpEL(Spring Expression Language)表达式来注入更复杂的值或执行一些操作。
- 与 @ConfigurationProperties 一起使用:虽然 @ConfigurationProperties 提供了更强大的方式来绑定一组配置到 Java 对象,但 @Value 对于简单的值注入来说仍然非常有用。
Demo
app.name=MyApp
app.description=This is my application
app.enabled=true
app.number=123
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class MyAppProperties {
@Value("${app.name}")
private String name;
@Value("${app.description}")
private String description;
@Value("${app.enabled}")
private boolean enabled;
@Value("${app.number}")
private int number;
// getters and setters
}
注意事项
- SpEL 表达式:@Value 注解还支持 SpEL 表达式,允许你执行更复杂的操作,如字符串连接、条件判断等。
- 默认值:如果配置文件中没有找到对应的属性,你可以为 @Value 注解提供一个默认值,例如 @Value(“${some.property:defaultValue}”)。
- 类型安全:虽然 @Value 注解提供了方便的值注入方式,但它可能不如 @ConfigurationProperties 那样类型安全,因为 @ConfigurationProperties 允许你通过 JavaBean 验证来验证配置属性的值。
- 环境变量和命令行参数:Spring Boot 还允许你通过环境变量和命令行参数来覆盖 application.properties 或 application.yml 文件中的值。这些值同样可以通过 @Value 注解注入到你的应用中。
18. @WebAppConfiguration
Spring框架中的一个类级别的注解,主要用于Spring MVC的集成测试中,以指定测试类所加载的ApplicationContext应该是一个WebApplicationContext。这个注解在测试过程中模拟了ServletContext,并构建了一个WebApplicationContext,从而为测试环境提供了Web应用程序的环境。
作用
- 集成测试:主要用于Spring MVC应用程序的集成测试中,确保测试能够运行在Web应用程序的上下文中。
- 模拟环境:通过模拟ServletContext,使得测试能够更接近实际的Web运行环境。
特性
- 类级别注解:必须应用于测试类上,而不是测试方法。
- 自动配置:在Spring Boot应用中,当@WebAppConfiguration被应用到一个@Configuration类上时,Spring Boot会自动配置一个Web环境,包括启动Servlet容器(如Tomcat)和处理HTTP请求等。
- 结合使用:通常与@ContextConfiguration注解一起使用,以指定WebApplicationContext的配置位置或配置类。
Demo
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration("classpath:spring/applicationContext.xml")
public class MyWebControllerTests {
private MockMvc mockMvc;
@Autowired
private WebApplicationContext webApplicationContext;
@Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.webApplicationContext).build();
}
@Test
public void testMyController() throws Exception {
// 使用mockMvc来模拟HTTP请求并验证响应
}
}
注意事项
- Spring版本:在Spring 4.0及其之后的版本中,虽然@WebAppConfiguration仍然可用,但Spring提供了更灵活的方式来指定ApplicationContext,因此,在某些情况下,可能不再需要显式地使用@WebAppConfiguration。
- 资源路径:@WebAppConfiguration允许通过其value()属性来覆盖默认的Web资源路径,从而提供更灵活的配置选项。
- 继承性:自Spring Framework 5.3起,@WebAppConfiguration注解可以从包围(enclosing)测试类中自动继承,这简化了注解的使用和管理。
19. @Order
用于定义Spring IOC容器中Bean的执行顺序的优先级(这里的顺序也可以理解为存放到容器中的先后顺序)。它并不直接控制Bean的加载顺序,而是影响Bean在特定场景下的执行顺序,如依赖注入、事件监听、拦截器/过滤器执行等。
使用
- Bean加载顺序:
在配置类中,可以使用@Order注解来指定Spring容器中Bean的创建顺序。这对于依赖于其他Bean初始化顺序的Bean特别有用。需要注意的是,@Order并不严格保证Bean的加载顺序,特别是在并发环境下,但它提供了一个执行的优先级指导。 - 过滤器和拦截器顺序:
在Web应用中,@Order注解可用于指定过滤器和拦截器的执行顺序。通过设置不同的@Order值,可以确保它们按照指定的顺序执行。 - 事件监听器顺序:
在Spring框架中,@Order注解可用于指定事件监听器的执行顺序。这有助于确保事件监听器按照特定的顺序接收和处理事件。 - AOP切面顺序:
在使用Spring AOP进行方法拦截时,@Order注解可用于指定切面的执行顺序。通过设置不同的@Order值,可以控制切面的执行顺序,从而实现对方法的多层次拦截。 - JUnit测试执行顺序:
在JUnit测试中,@Order注解也可用于定义测试方法的执行顺序。这有助于确保测试按照预期的顺序运行,尤其是在测试之间存在依赖关系时。
Demo
@Component
@Order(1)
public class FirstBean {
// ...
}
@Component
@Order(2)
public class SecondBean {
// ...
}
FirstBean将在SecondBean之前被创建(或按优先级执行,具体取决于上下文)。
注意事项
- 顺序值:
@Order注解接收一个整数值作为参数,表示顺序。数值越低,优先级越高,意味着该组件或操作会更早地被执行或创建。 - 默认值:
如果@Order注解没有指定值,它将使用Ordered.LOWEST_PRECEDENCE作为默认值,这通常是Integer.MAX_VALUE,表示最低优先级。 - 并发环境:
在并发环境下,@Order注解并不能保证严格的执行顺序。它更多地是提供一个执行的优先级指导。 - 与@Priority的关系:
@Priority注解是JSR-250的一部分,它在功能上与@Order类似,但有更广泛的应用,包括在CDI(Contexts and Dependency Injection)中。
20. @SpringBootTest
是Spring Boot中一个非常重要的测试注解,它简化了集成测试的配置和执行过程,使得开发者能够更容易地创建接近生产环境的测试环境。
作用
- 创建应用上下文:@SpringBootTest注解的主要用途是在测试过程中方便地创建一个应用上下文(ApplicationContext)。它告诉Spring Boot去寻找一个主配置类(比如带有@SpringBootApplication的类),并使用它来启动Spring应用上下文。
- 集成测试:该注解通常用于测试Spring组件,特别是那些需要与Spring容器交互的组件。它能够模拟出一个完整的Spring Boot应用环境,包括加载所有的Spring配置和Bean,从而使得测试能够在一个真实的应用环境中运行。
使用
- 默认行为:
- 默认情况下,@SpringBootTest会加载application.properties或application.yml文件中的配置,并启动一个Spring应用上下文。
- 如果你的应用有多个配置类,可以通过classes属性指定要加载的配置类。
- 灵活的配置:
- excludeAutoConfiguration:使用excludeAutoConfiguration属性来排除特定的自动配置。
- properties:使用properties属性来指定测试时使用的属性值,以覆盖默认配置。
- webEnvironment:通过webEnvironment属性来指定Web环境,支持的模式包括MOCK(默认,模拟Servlet环境)、RANDOM_PORT(启动内嵌的Web服务器并监听随机端口)、DEFINED_PORT(启动内嵌的Web服务器并监听指定的端口)等。
- 与其他注解结合使用:
- @SpringBootTest可以与其他Spring注解一起使用,如@DataJpaTest、@RestClientTest等,以提供更具体的测试环境。
- 如果需要在测试中使用模拟的Bean,可以结合使用@MockBean注解。
- 当测试Spring MVC控制器时,可以结合使用@SpringBootTest和@WebMvcTest。
- 自动注入功能:
- 在使用@SpringBootTest注解的测试类中,可以通过Spring的@Autowired注解自动注入需要的组件和配置,方便进行集成测试。
Demo
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@SpringBootTest
public class MyApplicationTests {
@Autowired
private MyService myService;
@Test
public void testService() {
// 使用myService进行一些测试操作...
assertNotNull(myService, "myService should not be null");
// 其他测试逻辑...
}
}
@SpringBootTest注解确保了MyApplication的应用上下文被加载,从而使得MyService能够被自动注入到测试类中。这样我们就可以在测试中使用MyService,就像它被Spring管理一样,进行集成测试。
21. @PointCut
AspectJ框架(一个面向切面编程的框架)中的一个核心注解,在Spring AOP(面向切面编程)中也被广泛使用。它用于定义一个切入点(Pointcut),即指定在哪些连接点(Join Point)上应用某个切面(Aspect)的增强逻辑。
定义
- 定义切入点表达式:@Pointcut注解后面跟着的是一个字符串,这个字符串是一个切入点表达式,用于指定哪些方法会被拦截或增强。
- 表达式格式:通常使用execution表达式,格式为
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
。其中,各个部分都是可选的,并且可以用通配符(如*)来表示任意值。
组成
- 修饰符匹配(modifier-pattern?):方法的修饰符,如public、protected等,可选。
- 返回值匹配(ret-type-pattern):方法的返回类型,可以用*表示任意类型,也可以指定具体的类型。
- 类路径匹配(declaring-type-pattern?):方法所在的类路径,可以指定具体的类名或包名,包名后可以用..表示该包及其子包下的所有类。
- 方法名匹配(name-pattern):具体的方法名,也可以用表示任意方法名,或SomeMethod表示以SomeMethod结尾的方法名。
- 参数匹配(param-pattern):方法的参数列表,可以用*表示任意参数,(String)表示一个String类型的参数,(String, int)表示两个参数,第一个是String类型,第二个是int类型。也可以用(..)表示任意数量和类型的参数。
- 异常类型匹配(throws-pattern?):方法抛出的异常类型,可选。
用法
- 匹配所有方法:@Pointcut(“execution(* *(..))”)
- 匹配特定包下的所有公有方法:@Pointcut(“execution(public * com.example.service..(..))”)
- 匹配特定类及其子包下的所有方法:@Pointcut(“execution(* com.example.service..*(..))”)
- 组合表达式:可以使用&&、||、!等逻辑运算符来组合多个切入点表达式。
- 重用切入点表达式:将切入点表达式定义在一个方法中,然后在其他通知(Advice)中通过方法名来引用这个切入点,从而实现代码的复用。
Demo
@Aspect
@Component
@Slf4j
@Order(1)
public class SqlLoggingAspect {
//定义切点
@Pointcut("execution(* org.example.mapper..*(..))")
public void repositoryExecution() {}
// 在切入点之前执行
@Before("repositoryExecution()")
public void logBefore(JoinPoint joinPoint) {
log.debug("Executing method: " + joinPoint.getSignature().getName());
Object[] args = joinPoint.getArgs();
}
}
注意事项
- 可读性和可维护性:将切入点表达式抽取到一个独立的方法中,可以提高代码的可读性和可维护性。
- 性能考虑:虽然重用切入点表达式可以提高代码的可读性和可维护性,但过多的抽象和组合可能会降低性能,因为每次执行通知时都需要解析和匹配切入点表达式。
22. @Service
Spring 框架中的一个注解,它用于标注在服务层(Service Layer)的组件上。Spring 框架的核心思想之一是依赖注入(Dependency Injection, DI),而 @Service 注解正是 Spring 依赖注入功能的一部分。通过使用 @Service 注解,Spring 能够自动地检测到被标注的类,并将其实例化、配置并管理起来,同时将这些实例注入到需要它们的类中(比如控制器层(Controller Layer)的类)。
作用
- 服务层组件标识:@Service 注解的主要用途是告诉 Spring 这是一个服务层组件。服务层通常负责业务逻辑的实现,它位于表现层(如控制器层)和持久层(如数据访问层)之间。
- 自动装配:Spring 容器能够扫描到被 @Service 注解的类,并自动地将其注册为 Spring 应用上下文中的一个 Bean。这样,其他组件(如控制器)就可以通过依赖注入的方式使用这些服务层组件了。
- 事务管理:虽然 @Service 注解本身并不直接提供事务管理功能,但服务层组件经常需要处理事务。Spring 允许你在服务层方法上使用 @Transactional 注解来声明性地管理事务。
Demo
import org.springframework.stereotype.Service;
@Service
public class UserService {
// 这里可以注入其他依赖,比如数据访问层的组件
public User getUserById(Long id) {
// 实现根据用户ID获取用户的业务逻辑
// ...
return new User();
}
// 其他业务方法...
}
UserService 类被 @Service 注解标注,表示它是一个服务层组件。Spring 容器会自动检测到这个类,并将其注册为一个 Bean,然后就可以在需要的地方通过依赖注入的方式使用这个服务了。
注意事项
- 虽然 @Service 注解是 Spring 提供的,但它实际上是一个泛型的注解,可以用在任何层级的组件上。然而,按照 Spring 的最佳实践,我们通常将 @Service 用于服务层组件,而将 @Repository 用于数据访问层组件,将 @Controller 用于控制器层组件等。
- 在使用 @Service 注解时,需要确保你的 Spring 配置(无论是基于 XML 的配置还是基于 Java 的配置)能够扫描到被注解的类所在的包。这通常是通过在配置类上添加 @ComponentScan 注解来实现的。
- 如果你使用的是 Spring Boot,那么通常不需要显式地进行包扫描配置,因为 Spring Boot 会自动扫描启动类所在的包及其子包下的所有组件。
23. @SpingBootApplication
Spring Boot 中的一个核心注解,它主要用于标记 Spring Boot 应用的主配置类。这个注解是一个复合注解,它结合了多个其他 Spring 框架中的注解,以简化 Spring Boot 应用的配置和启动过程。
组成
@SpringBootApplication 注解实际上是以下三个注解的集合:
- @SpringBootConfiguration:这是 @Configuration 的一个特殊形式,用于定义配置类。它表明该类可以使用 Spring Boot 的自动配置功能,并且能够被 Spring 容器管理。
- @EnableAutoConfiguration:这个注解启用了 Spring Boot 的自动配置机制。Spring Boot 会根据项目的类路径和依赖关系自动配置各种 Spring 框架和第三方库的功能,减少手动配置的工作量。
- @ComponentScan:这个注解用于指定 Spring 容器要扫描的组件的基础包路径。默认情况下,它会扫描当前包及其子包中的组件,如使用
@Component
、@Service
、@Repository
和@Controller
等注解的类,并将它们注册为 Spring 应用上下文中的 Bean。
作用
- 简化配置:通过 @SpringBootApplication 注解,开发者可以快速地启动并运行一个 Spring Boot 应用,而无需进行大量的手动配置。
- 自动配置:Spring Boot 会根据项目的依赖和类路径中的 Bean 自动配置应用程序的各个方面,如数据库连接、MVC 配置等,提供开箱即用的功能。
- 组件扫描:自动扫描并注册带有 @Component、@Service、@Repository 和 @Controller 等注解的类作为 Spring 的 Bean,使得开发者可以方便地使用和管理这些组件。
- 启动类标识:将带有 @SpringBootApplication 注解的类标识为 Spring Boot 应用程序的入口点。在运行 Spring Boot 应用程序时,会首先加载并启动被 @SpringBootApplication 注解标记的类,从而启动整个应用程序。
Demo
@SpringBootApplication 注解会被添加到主类上
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MySpringBootApplication {
public static void main(String[] args) {
SpringApplication.run(MySpringBootApplication.class, args);
}
// 这里可以定义其他的方法或组件
}
@SpringBootApplication 注解简化了启动 Spring Boot 应用的过程。当运行 main 方法时,Spring Boot 会自动进行配置并启动应用。
自定义配置
@SpringBootApplication
注解还允许开发者通过其属性进行自定义配置。例如,使用 exclude
属性可以排除特定的自动配置类,以避免不需要的自动配置。
24. @Profile
主要用于定义特定的配置环境(profile),以便在不同的环境中加载不同的配置或Bean
作用
- 环境区分:在软件开发过程中,经常需要区分不同的环境,如开发环境(dev)、测试环境(test)、生产环境(prod)等。每个环境可能有不同的配置需求,如数据库连接、日志级别等。@Profile 注解允许开发者为不同的环境定义不同的配置,从而实现环境间的灵活切换。
- 条件化Bean的创建:通过 @Profile 注解,可以指定某个Bean或配置类只在特定的环境下才会被创建和加载。这有助于减少不必要的资源加载,提高应用的启动速度和运行效率。
- 提高可维护性和可扩展性:通过为不同的环境定义不同的配置,可以使得应用更加模块化,易于维护和扩展。当需要添加新的环境或修改现有环境的配置时,只需修改对应的配置类或Bean即可,无需修改整个应用的代码。
使用
- 修饰类:@Profile 可以直接修饰配置类(即带有 @Configuration 注解的类),表示该配置类及其内部的Bean定义只在指定的环境下有效。
@Configuration
@Profile("dev")
public class DevConfig {
// 定义开发环境特有的Bean
}
- 修饰方法:在配置类中,@Profile 也可以修饰 @Bean 方法,表示该方法定义的Bean只在指定的环境下被创建。
@Configuration
public class DataSourceConfig {
@Bean
@Profile("dev")
public DataSource devDataSource() {
// 返回开发环境的数据源
}
@Bean
@Profile("prod")
public DataSource prodDataSource() {
// 返回生产环境的数据源
}
}
- 组合使用:@Profile 还支持组合使用多个环境名称,用逗号分隔。这表示该配置或Bean在多个环境下都会生效。
@Bean
@Profile("dev,test")
public DataSource devTestDataSource() {
// 返回开发和测试环境共有的数据源
}
注意:使用的时候要激活Profile
在 application.properties 或 application.yml 文件中,通过设置 spring.profiles.active 属性来指定当前激活的Profile。
# application.properties
spring.profiles.active=dev
# application.yml
spring:
profiles:
active: dev
25. @Reponsitory
专门用于数据访问层(DAO层)的组件
作用
- 标识数据访问层:@Repository 注解用于标识数据访问层(DAO层)的类,这些类通常负责与数据库进行交互,执行数据的CRUD(创建、读取、更新、删除)操作。
- 自动检测和管理:通过 @Repository 注解,Spring 容器能够自动检测并管理这些 DAO 组件,将它们作为 Bean 注入到应用的其他部分。
- 异常处理:@Repository 注解还提供了与数据访问相关的异常处理机制,它会自动将数据库相关的异常转换为 Spring 的数据访问异常层次结构(如 DataAccessException),从而简化了异常处理的复杂性。
与其他注解关系
- @Component:@Repository 是 @Component 注解的一个特殊形式,专门用于数据访问层。虽然它们都可以将类标记为 Spring 容器管理的组件,但 @Repository 提供了更明确的语义和额外的数据访问支持。
- @Service 和 @Controller:与 @Repository 类似,@Service 和 @Controller 也是 @Component 注解的扩展,分别用于服务层和控制器层。它们共同构成了 Spring 的分层架构。
使用
- 关系型数据库访问:@Repository 注解可以应用于操作关系型数据库的 DAO 类,通过 JDBC、JPA 等技术实现数据库操作。
- 非关系型数据库访问:同样适用于操作非关系型数据库的 DAO 类,如 MongoDB、Redis 等。
- 消息队列操作:虽然不常见,但理论上 @Repository 注解也可以用于标记操作消息队列的组件,尽管这更多是由 @Service 或其他业务逻辑层组件来处理的。
Demo
@Repository
@Mapper
public interface UserMapper {
List<UserModel> findAll();
UserModel findByName(String username);
String findPswByName(String userName);
void save(UserModel user);
}
@Service
public class UserserviceImpl implements UserService {
@Autowired
UserMapper userMapper;
// 登录操作
public String login(UserModel user) {
try {
UserModel userExistN = userMapper.findByName(user.getUsername());
if (userExistN != null) {
String userExistP = userMapper.findPswByName(user.getUsername());
if (userExistP.equals(user.getPassword())) {
return user.getUsername()+"登录成功,欢迎您!";
}else{
return "登录失败,密码错误!";
}
}else {
return "登录失败,用户不存在!";
}
}catch (Exception e) {
e.printStackTrace();
return e.getMessage();
}
}
}
注意事项
- 异常处理:虽然 @Repository 注解提供了自动的异常转换机制,但在实际应用中,仍然需要根据业务需求进行适当的异常处理。
- 依赖注入:在使用 @Repository 注解的类中,通常会通过 @Autowired 或其他依赖注入方式注入其他必要的组件或服务。
- 配置:确保 Spring 配置类或启动类上启用了相应的自动扫描机制(如 @ComponentScan),以便 Spring 能够扫描到并管理这些 DAO 组件。
26. @RequestBody
Spring MVC 和 Spring Boot 中常用的一个注解,它用于处理 HTTP 请求的 body 部分。当客户端发送一个请求到服务器时,请求体(body)通常包含了要发送到服务器的数据。@RequestBody 注解告诉 Spring 的 DispatcherServlet,应该将请求体中的 JSON 或 XML 数据绑定到控制器(Controller)方法的参数上。
使用
- POST 和 PUT 请求:在大多数情况下,@RequestBody 用于处理 POST 和 PUT 请求,因为这两种请求类型通常用于提交数据到服务器。
- 接收 JSON 或 XML 数据:当客户端发送 JSON 或 XML 格式的数据时,@RequestBody 可以帮助将这些数据自动绑定到 Java 对象上。
原理
当控制器方法使用 @RequestBody 注解时,Spring 会使用 HttpMessageConverters 将请求体中的数据转换为 Java 对象。HttpMessageConverters 是一组用于转换 HTTP 请求和响应的类,它们负责将请求体中的数据转换为 Java 对象,以及将 Java 对象转换回响应体数据。
默认情况下,Spring Boot 提供了对 JSON 和 XML 的支持,因此你可以直接接收和发送 JSON 或 XML 数据。但是,你也可以通过添加其他库来支持其他格式的数据。
Demo
// User.java
public class User {
private Long id;
private String name;
// 省略getter和setter方法
}
// UserController.java
@RestController
@RequestMapping("/users")
public class UserController {
@PostMapping("/add")
public ResponseEntity<String> addUser(@RequestBody User user) {
// 这里可以处理user对象,例如保存到数据库
return ResponseEntity.ok("User added successfully!");
}
}
当客户端发送一个 POST 请求到 /users/add,并且请求体中包含了一个 JSON 格式的 User 对象时,Spring 会自动将这个 JSON 数据转换成 User 类的实例,并将其作为 addUser 方法的参数传入。
注意事项
- 确保你的请求头(Content-Type)正确设置为了 application/json 或其他相应的 MIME 类型,以便 Spring 知道如何解析请求体。
- 如果请求体中的数据无法正确转换成 Java 对象(例如,JSON 格式错误或字段不匹配),Spring 会抛出一个异常。你可以通过全局异常处理来捕获这些异常,并返回友好的错误消息给客户端。
- 默认情况下,Spring 使用 Jackson 库来解析 JSON 数据。如果你需要处理 XML 数据,可能需要添加 JAXB 或其他相关依赖。
27. @RequestMapping
Spring MVC 中一个非常核心的注解,它用于将 HTTP 请求映射到特定的处理器(比如控制器中的一个方法)上。这个注解可以声明在类或方法上,用于定义请求的 URL、HTTP 方法(如 GET、POST)、请求参数、请求头等信息,以及它们如何被映射到特定的处理函数上。
主要属性
value / path
:指定请求的 URL 路径。可以是具体的路径,也可以是包含变量(如 {id})的路径模板。method
:指定请求的类型(如 GET、POST)。这个属性是 RequestMethod 枚举的一个值或它们的组合。params
:指定请求的必须包含或不能包含的参数。headers
:指定请求的必须包含或不能包含的 HTTP 头信息。consumes
:指定处理请求的提交内容类型(Content-Type),如 application/json。produces
:指定返回的内容类型,仅当请求头中的(Accept)类型包含该指定类型才返回。
使用
- 类级别上的 @RequestMapping
@Controller
@RequestMapping("/users")
public class UserController {
@GetMapping("/{id}")
public String getUserById(@PathVariable("id") Long id, Model model) {
// 根据 id 获取用户信息,并填充到 model 中
return "userDetail"; // 返回视图名
}
@PostMapping
public String createUser(@RequestParam String name, Model model) {
// 创建用户
return "userCreated";
}
}
@RequestMapping(“/users”) 应用于 UserController 类上,这意味着这个类中的所有请求 URL 都会以 /users 开头。然后,@GetMapping(“/{id}”) 和 @PostMapping 分别用于映射具体的 HTTP GET 和 POST 请求到不同的处理方法上。
2. 方法级别上的 @RequestMapping
@Controller
public class MyController {
@RequestMapping(value = "/hello", method = RequestMethod.GET)
public String sayHello() {
return "hello"; // 返回视图名
}
@RequestMapping(value = "/goodbye", method = RequestMethod.GET, params = "name")
public String sayGoodbye(@RequestParam String name) {
// 仅在请求中包含 'name' 参数时调用
return "goodbye";
}
}
@RequestMapping 直接用于方法上,指定了请求的 URL 路径、HTTP 方法以及请求参数。params = “name” 表示这个请求必须包含名为 name 的参数。
28. @ResponseBody
它用于将控制器的返回值绑定到 web 响应体(Response Body)上。当处理器方法被 @ResponseBody 注解时,Spring 会自动将方法的返回值写入到 HTTP 响应(HttpServletResponse)中。这个过程通常涉及到使用消息转换器(Message Converters)将返回值转换为适当的格式(如 JSON、XML 等),然后写入到响应体中。
作用
- RESTful Web 服务:在构建 RESTful Web 服务时,@ResponseBody 非常有用,因为它允许你直接将对象序列化为 JSON 或 XML,并作为 HTTP 响应发送给客户端。
- Ajax 请求:在 Ajax 请求中,服务器通常需要返回 JSON 或 XML 格式的数据,@ResponseBody 可以轻松实现这一点。
Demo
有一个用户对象 User 和一个控制器方法,你想要将 User 对象以 JSON 格式返回给客户端。
@RestController // 这是一个方便的注解,相当于在每个方法上都加了 @Controller 和 @ResponseBody
public class UserController {
@GetMapping("/user/{id}")
public User getUserById(@PathVariable Long id) {
// 假设这里有一个服务或DAO层调用,根据id返回User对象
User user = new User();
user.setId(id);
user.setName("John Doe");
// 由于这里使用了 @RestController,或者如果只有这个方法使用了 @ResponseBody,
// Spring 将自动将 User 对象序列化为 JSON 并写入响应体
return user;
}
}
// 如果你没有使用 @RestController,但想要对单个方法应用 @ResponseBody,可以这样做:
@Controller
public class UserController {
@GetMapping("/user/{id}")
@ResponseBody // 告诉 Spring MVC 返回的内容应该绑定到响应体上
public User getUserById(@PathVariable Long id) {
// ... 与上面的示例相同
}
}
注意事项
- 当你使用 @ResponseBody 时,Spring 会查找合适的 HttpMessageConverter 来将你的返回值转换为响应体所需的格式。
- 如果你正在使用 Spring Boot,并且已经添加了 Spring Web Starter 依赖,那么 Spring Boot 会自动配置一些常用的 HttpMessageConverter,如用于 JSON 的 Jackson 和用于 XML 的 JAXB。
- @RestController 是 @Controller 和 @ResponseBody 的组合注解,用于构建 RESTful Web 服务。如果你发现你的控制器中的所有方法都需要 @ResponseBody,那么使用 @RestController 会更简洁。
29. @RestController
Spring 4.0 引入的一个注解,它是 @Controller 和 @ResponseBody 的组合注解。当你在一个类上使用 @RestController 注解时,意味着这个类中的所有方法都会默认应用 @ResponseBody 注解的效果,即方法的返回值会自动地绑定到 Web 响应体(Response Body)上,并且通常会被转换为 JSON 或 XML 等格式(这取决于配置的消息转换器)。
作用
- RESTful Web 服务:@RestController 是构建 RESTful Web 服务的理想选择,因为它简化了将对象序列化为 JSON 或 XML 并发送到客户端的过程。
- 简化配置:如果你发现你的控制器类中的大多数或所有方法都需要 @ResponseBody 注解,那么使用 @RestController 可以减少重复的代码,并使你的控制器类更加简洁。
Demo
@RestController
public class UserController {
@GetMapping("/user/{id}")
public User getUserById(@PathVariable Long id) {
// 假设这里有一个服务或DAO层调用,根据id返回User对象
User user = new User();
user.setId(id);
user.setName("John Doe");
// 由于使用了 @RestController,Spring 会自动将 User 对象序列化为 JSON 并写入响应体
return user;
}
@PostMapping("/user")
public ResponseEntity<String> createUser(@RequestBody User user) {
// 假设这里有一个服务层调用,用于创建用户
// 尽管这里返回的是 ResponseEntity,但 @RestController 仍然适用,因为它只关心返回值是否应该被写入响应体
return ResponseEntity.ok("User created successfully");
}
}
getUserById 方法返回一个 User 对象,而 createUser 方法返回一个 ResponseEntity 对象。由于 UserController 类被 @RestController 注解,Spring 会自动处理这两个方法的返回值,将它们转换为 JSON(或其他配置的格式)并写入 HTTP 响应体中。
注意事项
- 当你在一个类上使用 @RestController 时,该类中的所有处理方法都会被视为 @ResponseBody 方法,除非你在方法级别上明确地使用 @ResponseBody(false) 来覆盖这个行为(但通常这不是必要的)。
- @RestController 使得构建 RESTful Web 服务变得更加简单和直接,因为它减少了需要编写的样板代码量。
- 如果你需要在一个控制器类中混合使用 @ResponseBody 和非 @ResponseBody 方法(例如,某些方法返回视图名称而不是数据),那么你应该使用 @Controller 而不是 @RestController,并在需要的地方显式地添加 @ResponseBody 注解。
30. @Async
用于声明一个异步方法。当你在方法上使用 @Async 注解时,Spring 会在调用该方法时,在一个单独的线程中异步地执行它。这意味着调用者线程不需要等待被 @Async 注解的方法执行完成,而是可以继续执行其他任务。
作用
- 提高应用程序性能:通过异步处理,可以避免在执行长时间运行的任务时阻塞主线程,从而提高应用程序的响应性和吞吐量。
- 解耦:异步处理可以帮助你解耦方法的调用和执行,使得方法的调用者不需要关心方法的执行细节和执行时间。
使用
为了使用 @Async 注解,你需要满足以下条件:
- 配置异步支持:在你的 Spring 配置中启用异步支持。这可以通过在配置类上添加 @EnableAsync 注解来实现。
- 将 @Async 注解应用于适当的方法:确保 @Async 注解被应用于公共的、非静态的、非void返回类型(或在Java 8+中为CompletableFuture/Future等)的、且没有被 final 修饰的方法上。
- 返回类型:通常,@Async 方法会返回一个 Future 或 CompletableFuture 对象,以便调用者可以检查异步方法的执行状态或获取结果。但是,也可以返回 void 或其他类型,但这样调用者就无法直接知道异步方法的执行结果或状态。
Demo
@Configuration
@EnableAsync
public class AsyncConfig {
// 这里通常不需要实现任何bean,只需要@EnableAsync注解来启用异步支持
}
@Service
public class AsyncService {
@Async
public Future<String> executeAsyncTask(int number) throws InterruptedException {
// 模拟长时间运行的任务
Thread.sleep(1000);
return new AsyncResult<>("AsyncTask completed with value " + number);
}
// 也可以返回void,但这样调用者就无法获取到执行结果
@Async
public void executeVoidAsyncTask(int number) {
// 同样的长时间运行任务
System.out.println("Executing void async task with number " + number);
}
}
@RestController
public class AsyncController {
@Autowired
private AsyncService asyncService;
@GetMapping("/async/{number}")
public ResponseEntity<String> asyncEndpoint(@PathVariable int number) throws ExecutionException, InterruptedException {
Future<String> future = asyncService.executeAsyncTask(number);
// 这里可以执行其他任务,而不需要等待异步方法完成
String result = future.get(); // 获取异步方法的执行结果,这将阻塞直到异步方法完成
return ResponseEntity.ok(result);
}
}
虽然 Future.get() 方法可以用来获取异步方法的执行结果,但它会阻塞调用线程直到异步方法完成。如果你想要避免阻塞,可以使用 CompletableFuture 并利用它的非阻塞API来处理异步结果。
注意事项
- 当你在使用 @Async 注解时,确保不要在同一个类中调用异步方法,因为 Spring 的代理机制(通常是基于JDK动态代理或CGLIB)只会对通过Spring容器调用的方法进行代理。如果你在同一个类的方法中调用另一个带有 @Async 注解的方法,那么这个调用将不会被异步处理。
- 异步方法执行时的异常处理需要特别注意。如果你使用 Future.get() 来获取结果,那么异步方法抛出的任何未捕获的异常都将被封装为 ExecutionException 抛出。如果你使用的是 CompletableFuture,则可以更灵活地处理异常,例如通过 exceptionally 方法来指定异常处理逻辑。
31. @AutoConfigureAfter
它主要用于指示某个自动配置类(Configuration Class)应该在指定的其他自动配置类之后进行自动配置。这个注解提供了一种机制来控制 Spring Boot 自动配置的顺序,确保依赖关系的正确性,从而保证应用的正确性和可维护性。
作用
- 控制自动配置顺序:在 Spring Boot 中,有很多自动配置的类,它们会根据系统的环境、条件等自动进行配置。然而,这些配置类有时会依赖于其他的配置类。通过使用 @AutoConfigureAfter 注解,可以确保依赖的配置类先被加载和配置。
- 保证依赖关系的正确性:当一个自动配置类需要使用另一个自动配置类中的 Bean 或配置时,使用 @AutoConfigureAfter 可以避免由于加载顺序错误导致的依赖问题。
使用
- 注解位置:@AutoConfigureAfter 注解只能用于配置类上,即被 @Configuration 注解修饰的类上。
- 参数指定:在 @AutoConfigureAfter 注解的 value 属性中,可以指定一个或多个类,这些类必须在指定的自动配置类之后进行自动配置。
Demo
@Configuration
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class MyAutoConfiguration {
// ... 配置类的实现 ...
}
MyAutoConfiguration 类将在 DataSourceAutoConfiguration 类之后进行自动配置。
注意事项
- 指定的类必须已存在:@AutoConfigureAfter 注解中指定的类必须已经被 Spring Boot 自动配置或显式定义,否则将抛出 AutoConfigurationOrderFailedException 异常。
- 多个配置类:如果有多个自动配置类都使用了 @AutoConfigureAfter 注解,那么这些类将按照注解中指定的顺序进行加载。
- 与 @AutoConfigureBefore 的关系:@AutoConfigureBefore 是与 @AutoConfigureAfter 相对应的注解,用于指示某个自动配置类应该在指定的其他自动配置类之前进行自动配置。
32. @Cacheable
主要用于声明性缓存,即将方法的返回值缓存起来,以便在后续相同方法的调用中可以直接从缓存中获取结果,而无需再次执行方法的实际逻辑。这样做可以显著提高系统的性能和响应速度,特别是在处理读取操作频繁但数据更新不频繁的场景时。
作用
- 减少数据库访问:对于需要从数据库读取数据的操作,使用 @Cacheable 可以避免重复的数据库查询,减轻数据库压力。
- 提升性能:通过减少方法的执行次数,降低计算资源的消耗,从而提升系统的整体性能。
使用
在 Spring Boot 项目中,使用 @Cacheable 注解通常需要以下几个步骤:
- 添加依赖:在项目的 pom.xml 文件中添加 Spring Boot 的缓存启动器依赖,如 spring-boot-starter-cache。
- 开启缓存:在 Spring Boot 的启动类上添加 @EnableCaching 注解,以开启缓存支持。
- 配置缓存:根据需要配置缓存的类型(如 Redis、EhCache 等)和相关属性。
- 使用注解:在需要缓存的方法上添加 @Cacheable 注解,并指定缓存的名称、键的生成策略等参数。
参数
- value/cacheNames:指定缓存的名称,可以是一个字符串或字符串数组,表示方法的结果可以被缓存到哪些缓存中。
- key:指定缓存的键,可以是一个 SpEL(Spring Expression Language)表达式,用于根据方法参数动态生成缓存的键。如果不指定,则默认使用方法的参数作为键。
- condition:指定缓存的条件,是一个 SpEL 表达式,用于决定在何种情况下将方法的返回值缓存起来。如果不指定,则默认缓存所有结果。
- unless:指定缓存的排除条件,也是一个 SpEL 表达式,用于决定在何种情况下不将方法的返回值缓存起来。如果不指定,则默认不排除任何结果。
Demo
@Service
public class MyService {
@Cacheable(value = "myCache", key = "#id")
public String getData(int id) {
// 模拟耗时操作
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 实际的数据获取逻辑
return "Data for id: " + id;
}
}
getData 方法的结果会被缓存到名为 myCache 的缓存中,缓存的键是方法的参数 id。当再次调用 getData 方法并传入相同的 id 时,如果缓存中存在对应的结果,则直接返回缓存中的值,而不会执行方法的实际逻辑。
注意事项
- 缓存一致性:在使用 @Cacheable 时,需要关注缓存数据的一致性问题。如果缓存的数据在外部被修改,而系统没有感知到这种变化,就可能导致数据不一致的问题。
- 缓存穿透:当查询一个不存在的数据时,缓存中不会存储该数据,导致每次查询都会穿透到数据库。可以通过布隆过滤器等机制来避免缓存穿透。
- 缓存雪崩:当大量缓存同时失效时,所有请求都会直接访问数据库,导致数据库压力骤增。可以通过设置缓存的过期时间时加入随机因子、使用多级缓存等方式来避免缓存雪崩。
33. @Conditional
用于在基于条件的情况下控制配置类的注册或Bean的创建。这个注解是 Spring 4.0 引入的,作为 Spring 的条件化配置的一部分,它提供了一种灵活的方式来控制哪些配置或Bean应该被包含在Spring应用上下文中。
使用
@Conditional 注解通常与自定义条件类一起使用,这些条件类实现了 Condition 接口。通过实现 matches(ConditionContext, AnnotatedTypeMetadata) 方法,你可以定义何时应该包含特定的配置类或Bean。这种方法非常适合于根据运行时环境(如操作系统类型、JVM版本、特定的库是否可用等)来条件化地包含配置。
工作原理
- 定义条件:首先,你需要定义一个或多个实现了 Condition 接口的类。在这个类中,你将实现 matches 方法,该方法根据给定的条件返回 true 或 false。
- 应用条件:然后,你可以将 @Conditional 注解应用于配置类或Bean方法上,并通过其 value 属性指定你的条件类。如果条件类的 matches 方法返回 true,则配置类或Bean将被包含在Spring应用上下文中;如果返回 false,则将被忽略。
Demo
// 自定义条件类
public class OnWindowsCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 检查操作系统是否为Windows
return System.getProperty("os.name").toLowerCase().contains("win");
}
}
// 使用@Conditional注解
@Configuration
public class AppConfig {
@Bean
@Conditional(OnWindowsCondition.class)
public MyWindowsSpecificBean myWindowsSpecificBean() {
return new MyWindowsSpecificBean();
}
}
MyWindowsSpecificBean 只有在操作系统为Windows时才会被创建并注册到Spring应用上下文中。
注意事项
- @Conditional 可以与 @Bean、@Configuration、@Component 等注解一起使用。
- Spring提供了多个内置的条件注解,如 @ConditionalOnClass、@ConditionalOnMissingBean、@ConditionalOnProperty 等,这些注解可以覆盖大多数常见的条件化配置需求。
- 使用条件化配置时,应注意避免创建复杂的条件逻辑,以保持配置的清晰和可维护性。
34. @ConditionalOnBean
条件注解,用于在容器中存在特定 Bean 的情况下才创建当前 Bean。这个注解通常用在基于 Spring Boot 的应用中,与自动配置(Auto-configuration)功能结合使用,以根据应用的配置和已存在的 Bean 来条件化地注册新的 Bean。
使用
@ConditionalOnBean
注解可以应用于配置类中的 @Bean 方法上。当 Spring 容器中存在指定的 Bean 时,才会执行该方法并创建相应的 Bean。如果不存在指定的 Bean,则该方法会被忽略,不会创建任何 Bean。
属性
有一个 RedisTemplate 的 Bean,并且你只想在 RedisTemplate 存在时才创建 RedisOperBean
@ConditionalOnBean
注解包含几个属性,用于指定条件细节:
value
:Class<?>[] 类型,指定需要存在的 Bean 的类型。type
:String[] 类型,与 value 属性类似,但允许使用 Bean 的名称(而不是类型)。annotation
:Class<? extends Annotation>[] 类型,指定需要存在的 Bean 上必须拥有的注解类型。name
:String[] 类型,直接指定需要存在的 Bean 的名称。search
:SearchStrategy 类型,用于指定搜索 Bean 的策略,默认是 SearchStrategy.ALL,表示在所有的 Bean 定义中搜索。
Demo
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate() {
// 配置 RedisTemplate ...
return redisTemplate;
}
@Bean
@ConditionalOnBean(name = "redisTemplate")
public RedisOperBean redisOperBean(RedisTemplate<String, Object> redisTemplate) {
// 假设 RedisOperBean 需要 RedisTemplate 作为依赖
return new RedisOperBean(redisTemplate);
}
}
redisOperBean 方法上的 @ConditionalOnBean(name = “redisTemplate”) 注解确保了只有当 redisTemplate Bean 存在时,redisOperBean 方法才会被执行,从而创建 RedisOperBean。
注意事项
- @ConditionalOnBean 与其他条件注解(如 @ConditionalOnMissingBean、@ConditionalOnClass 等)一起构成了 Spring Boot 的条件化配置功能,提供了灵活的 Bean 加载控制。
- 在使用 @ConditionalOnBean 时,需要注意 Bean 的加载顺序。如果当前 Bean 依赖于其他尚未加载的 Bean,可能会导致意外的行为。
- 尽量避免在应用中创建复杂的条件逻辑,以保持配置的清晰和可维护性。
没有回复内容