Spring高级注解与接口

这里开始记录一些学到的强力的注解和接口

CommandLineRunner

CommandLineRunner是Spring Boot框架中的一个接口,用于定义在应用程序启动后执行的任务。它包含一个run方法,该方法在应用程序启动后被调用,并接收命令行参数作为参数。
具体来说,CommandLineRunner接口的作用是允许开发人员在应用程序启动后执行一些初始化或后续操作的逻辑。通过实现CommandLineRunner接口并重写run方法,你可以在应用程序启动后执行自定义的逻辑代码。
当应用程序启动时,Spring Boot会自动扫描并检测实现了CommandLineRunner接口的Bean,并在启动后调用它们的run方法。这样,你可以在应用程序启动后执行一些必要的初始化任务,例如加载数据、启动定时任务、建立连接等。
在CommandLineRunner接口的run方法中,你可以编写任何你希望在应用程序启动后执行的代码。该方法可以抛出Exception异常,因此你可以在其中处理任何可能发生的异常情况。
总结起来,CommandLineRunner接口允许你在应用程序启动后执行自定义的逻辑代码。通过实现该接口并重写run方法,你可以在应用程序启动后执行一些初始化或后续操作的任务。

1
2
3
4
@Override
public void run(String... strings) throws Exception {
log.info("ConfigProperties properties:{}", properties);
}

@PostConstruct

@PostConstruct是Java标准库中的一个注解,它用于标记一个方法,在对象创建后立即执行。在Spring框架中,@PostConstruct注解被用于定义在Bean初始化之后执行的方法。

具体来说,当一个Bean被实例化并完成依赖注入后,Spring容器会检测该Bean中是否存在被@PostConstruct注解标记的方法。如果存在,Spring容器会在Bean初始化完成后立即调用该方法。

@PostConstruct注解的作用是允许开发人员在Bean初始化之后执行一些自定义的初始化逻辑。这些逻辑可以包括数据加载、资源初始化、建立连接等操作。通过在方法上添加@PostConstruct注解,你可以确保在Bean完全初始化之后执行这些操作。

需要注意的是,@PostConstruct注解只能用于非静态方法,并且只能标记一个方法。如果在一个类中存在多个被@PostConstruct注解标记的方法,它们的执行顺序是不确定的。

总结起来,@PostConstruct注解用于标记一个方法,在Bean初始化之后立即执行。它允许开发人员在Bean初始化完成后执行一些自定义的初始化逻辑。

1
2
3
4
5
6
7
8
@PostConstruct
public void init(){
String targetService = properties.getTargetService();
if (StringUtils.isBlank(targetService)){
targetService = System.getenv("TService");
}
CompensateContextHolder.setTargetService(targetService);
}

@ConditionalOnProperty

@ConditionalOnProperty是Spring Boot框架中的一个条件注解,用于根据配置属性的值来决定是否启用或禁用特定的组件或配置。
具体来说,@ConditionalOnProperty注解用于在特定的配置属性满足条件时才加载或启用某个组件。它接收一个或多个属性参数,用于指定要检查的配置属性的名称和期望的值。
比如:@ConditionalOnProperty注解的value参数设置为”compensate.enabled”,表示要检查名为”compensate.enabled”的配置属性。而havingValue参数设置为”true”,表示期望该配置属性的值为”true”。
因此,当配置属性”compensate.enabled”的值为”true”时,被注解的组件或配置将被加载或启用。如果配置属性的值不是”true”,则被注解的组件或配置将被禁用或忽略。
这个注解通常用于根据配置属性的值来决定是否启用某些特定的功能或组件。通过使用@ConditionalOnProperty注解,你可以根据配置属性的值来动态地控制应用程序的行为,使其更加灵活和可配置。

1
2
3
4
5
@Bean
@ConditionalOnProperty(value = "compensate.enabled", havingValue = "true")
public CompensateManager compensateManager(DataSource dataSource, CompensateExecutionChain chain) {
return new CompensateManager(properties, dataSource, chain);
}

@NestedConfigurationProperty

@NestedConfigurationProperty是Spring框架中的一个注解,用于指示一个属性应该被视为嵌套的配置属性。它通常用于嵌套类中的属性,以便在配置文件中以嵌套的方式组织属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
@Data
public class Employee {
private String name;
private int age;
private Address address;

@Data
public static class Address {
private String street;
private String city;
private String country;
}
}

在上面的例子中,Employee类包含一个嵌套类Address,它表示员工的地址信息。Address类具有三个属性:street、city和country。
通过这种方式,可以在Employee类中组织员工的信息,并将地址信息作为嵌套类的属性。这样,可以更清晰地表示员工对象的结构,并在配置文件中以嵌套的方式配置地址信息。
在配置文件中,可以使用以下方式配置Employee对象的属性和Address对象的属性:

1
2
3
4
5
employee.name=John Doe
employee.age=30
employee.address.street=123 Main St
employee.address.city=New York
employee.address.country=USA

这样,就可以通过嵌套类的方式组织和配置复杂的对象结构。

InstantiationAwareBeanPostProcessorAdapter

InstantiationAwareBeanPostProcessorAdapter是Spring框架中的一个接口,它是InstantiationAwareBeanPostProcessor接口的适配器类。这个接口在Spring的Bean生命周期中扮演着重要的角色。
InstantiationAwareBeanPostProcessor接口定义了一组回调方法,用于在Spring容器实例化Bean之前和之后进行自定义处理。而InstantiationAwareBeanPostProcessorAdapter是一个适配器类,它提供了默认的空实现,使得我们可以只关注我们感兴趣的回调方法,而不需要实现接口中的所有方法。
这个接口的作用是允许开发者在Bean实例化的不同阶段进行自定义的处理。它提供了以下几个重要的回调方法:
postProcessBeforeInstantiation(): 在实例化Bean之前调用,允许开发者返回一个自定义的Bean实例,或者返回null来使用默认的实例化机制。
postProcessAfterInstantiation(): 在实例化Bean之后调用,允许开发者对实例进行自定义的处理,例如修改属性值或执行其他初始化操作。
postProcessPropertyValues(): 在Bean的属性注入之前调用,允许开发者对属性值进行自定义的处理,例如修改属性值或验证属性的合法性。
通过实现InstantiationAwareBeanPostProcessor接口或使用InstantiationAwareBeanPostProcessorAdapter适配器类,开发者可以在Bean实例化的不同阶段进行自定义的处理。这样,我们可以在Spring容器实例化Bean时,对Bean进行额外的操作或修改,以满足特定的需求。
以下是一个简单的示例,展示了如何使用InstantiationAwareBeanPostProcessorAdapter接口来自定义处理Bean实例化的过程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;

public class CustomBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter {

@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
if (beanClass == MyBean.class) {
// 在实例化MyBean之前,返回一个自定义的Bean实例
return new MyBean("Custom Instance");
}
return null; // 返回null,使用默认的实例化机制
}

@Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
if (bean instanceof MyBean) {
// 在实例化MyBean之后,对其进行自定义的处理
MyBean myBean = (MyBean) bean;
myBean.setSomeProperty("Custom Property Value");
}
return true;
}
}

在上述示例中,我们创建了一个名为CustomBeanPostProcessor的类,它继承自InstantiationAwareBeanPostProcessorAdapter适配器类。我们重写了postProcessBeforeInstantiation()和postProcessAfterInstantiation()方法来实现自定义的处理逻辑。
在postProcessBeforeInstantiation()方法中,我们检查要实例化的Bean是否是MyBean类。如果是,我们返回一个自定义的MyBean实例,否则返回null,使用默认的实例化机制。
在postProcessAfterInstantiation()方法中,我们检查实例化后的Bean是否是MyBean类的实例。如果是,我们对其进行自定义的处理,例如设置属性值。
通过使用CustomBeanPostProcessor类,我们可以在Bean实例化的过程中对特定的Bean进行自定义处理。这样,我们可以根据需要修改Bean的实例或属性,以满足特定的业务逻辑或需求。

ApplicationContextAware

ApplicationContextAware接口是Spring框架中的一个接口,用于实现对应用程序上下文(ApplicationContext)的感知。
该接口继承自Aware接口,通过实现ApplicationContextAware接口,可以让Bean获取对应用程序上下文的引用,并在需要时进行操作。
具体来说,ApplicationContextAware接口定义了一个方法setApplicationContext(ApplicationContext var1),当Bean被实例化并注入到Spring容器中时,Spring容器会自动调用该方法,并将当前的应用程序上下文作为参数传递进来。
通过实现setApplicationContext()方法,我们可以在Bean中获取到应用程序上下文的引用,并利用它来进行一些操作,例如获取其他Bean、发布事件、访问资源等。
一般来说,这么整就行了!

1
2
3
4
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}

总结起来,ApplicationContextAware接口的作用是让Bean获取对应用程序上下文的引用,以便在需要时进行操作。通过实现该接口并实现setApplicationContext()方法,我们可以在Bean中获取到应用程序上下文,并利用它进行各种操作。

@Aspect

@Aspect注解是Spring框架中的一个注解,用于声明一个切面(Aspect)。切面是一种用于横切关注点(Cross-cutting Concerns)的模块化方式,它可以定义一组通用的横切逻辑,并将其应用到多个目标对象中。
具体来说,@Aspect注解用于标记一个类为切面类,该类中定义了一些通用的横切逻辑,例如日志记录、性能监控、事务管理等。切面类通常包含一些切点(Pointcut)和通知(Advice)的定义。

  • 切点(Pointcut):切点定义了在哪些连接点(Join Point)上应用通知。连接点是程序执行过程中可以插入切面逻辑的点,例如方法调用、方法执行、异常抛出等。切点通过表达式或注解来定义,用于匹配符合条件的连接点。
  • 通知(Advice):通知定义了在切点上执行的具体逻辑。通知可以在切点之前、之后、抛出异常时等不同的时机执行。常见的通知类型包括前置通知(Before)、后置通知(After)、返回通知(AfterReturning)和异常通知(AfterThrowing)等。

通过使用@Aspect注解,Spring框架可以识别并将切面类纳入到AOP(面向切面编程)的管理中。在运行时,Spring会根据切点和通知的定义,将切面逻辑织入到目标对象的相应连接点上,从而实现横切关注点的功能。
要使用切面,需要完成以下步骤:

  1. 在Spring配置文件中启用AOP的自动代理功能。可以使用aop:aspectj-autoproxy标签或@EnableAspectJAutoProxy注解来实现。这将告诉Spring框架在运行时自动创建代理对象,并将切面逻辑织入到目标对象的连接点上。
  2. 创建目标对象。目标对象是需要应用切面逻辑的对象。可以是任何Spring管理的Bean,例如服务类、控制器等。
  3. 创建切面类。切面类是包含切点和通知的类。使用@Aspect注解标记切面类,并在其中定义切点和通知的方法。
  4. 在切面类中定义切点。切点定义了在哪些连接点上应用通知。可以使用表达式或注解来定义切点,以匹配符合条件的连接点。
  5. 在切面类中定义通知。通知定义了在切点上执行的具体逻辑。可以使用@Before、@After、@AfterReturning、@AfterThrowing等注解来定义不同类型的通知。
  6. 运行应用程序。当目标对象的方法被调用时,切面逻辑会自动织入到连接点上,根据切点和通知的定义执行相应的逻辑。

以下是一个简单的示例,演示如何使用切面类来实现日志记录的横切逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MainApp {

public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

MyService myService = context.getBean(MyService.class);
myService.doSomething();

context.close();
}
}
1
2
3
4
5
6
7
8
9
import org.springframework.stereotype.Service;

@Service
public class MyService {

public void doSomething() {
System.out.println("Doing something...");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LoggingAspect {

@Before("execution(* com.example.service.*.*(..))")
public void beforeMethodExecution() {
System.out.println("Before method execution: Logging...");
}
}

在上述示例中,我们创建了一个MyService类作为目标对象,其中包含一个doSomething()方法。我们还创建了一个LoggingAspect类作为切面类,其中定义了一个前置通知,用于在目标对象的方法执行之前记录日志。
在MainApp类中,我们使用AnnotationConfigApplicationContext来加载Spring配置,并获取MyService的实例。当调用myService.doSomething()方法时,切面类中的前置通知会被触发,输出日志信息:”Before method execution: Logging…”。
通过以上步骤,我们成功地将切面逻辑应用到目标对象的连接点上,实现了日志记录的横切逻辑
总结起来,@Aspect注解用于声明一个切面类,该类中定义了切点和通知,用于实现横切关注点的功能。通过使用@Aspect注解,Spring框架可以识别并管理切面类,将切面逻辑织入到目标对象的连接点上。

@Around

@Around是一个用于定义环绕通知的注解。在Spring AOP中,环绕通知是一种切面通知类型,它可以在目标方法执行前后进行拦截,并且可以完全控制目标方法的执行。
使用@Around注解可以将一个方法标记为环绕通知方法。这个方法会在目标方法执行之前和之后执行,可以在这个方法中编写自定义的逻辑来控制目标方法的执行。
环绕通知方法需要满足一些要求:

  • 方法的返回类型可以是任意类型,通常是Object。
  • 方法的参数可以是ProceedingJoinPoint类型,用于执行目标方法,也可以是其他参数,根据需要进行定义。

在环绕通知方法中,可以通过调用ProceedingJoinPoint对象的proceed()方法来执行目标方法。在调用proceed()方法之前,可以在目标方法执行前进行一些前置操作,而在调用proceed()方法之后,可以在目标方法执行后进行一些后置操作。通过使用@Around注解,我们可以更灵活地控制目标方法的执行过程,例如在执行前后进行日志记录、性能监控、事务管理等操作。
比如说:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Aspect
@Component
public class LoggingAspect {

@Around("execution(* com.example.service.MyService.doSomething(..))")
public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("Before method execution: Logging...");

// 执行目标方法
Object result = joinPoint.proceed();

System.out.println("After method execution: Logging...");

return result;
}
}

在这个示例中,我们定义了一个LoggingAspect切面类,并在其中使用@Around注解标记了一个环绕通知方法aroundAdvice()。该方法接受一个ProceedingJoinPoint参数,用于执行目标方法。
在aroundAdvice()方法中,我们首先输出了一条日志信息,表示在目标方法执行之前的前置操作。然后,通过调用joinPoint.proceed()方法,执行了目标方法。最后,我们又输出了一条日志信息,表示在目标方法执行之后的后置操作。
通过这个示例,我们可以在目标方法执行前后添加自定义的逻辑,例如记录日志、计算方法执行时间等。这样,每次调用MyService的doSomething()方法时,都会触发切面的环绕通知方法。

@Aspect和@Around

@Aspect和@Around是Spring AOP框架中的两个不同的注解,它们在AOP的实现中扮演不同的角色。

  • @Aspect注解用于标记一个类为切面类。切面类是包含切面逻辑的类,它定义了在哪些连接点上应用切面逻辑,并且可以包含多个通知类型(如前置通知、后置通知、环绕通知等)。通过使用@Aspect注解,我们可以将一个普通的类声明为切面类,以便在Spring AOP中使用。
  • @Around注解是一个通知类型的注解,用于定义环绕通知。环绕通知是一种切面通知类型,它可以在目标方法执行前后进行拦截,并且可以完全控制目标方法的执行。通过使用@Around注解,我们可以将一个方法标记为环绕通知方法,以便在切面中定义自定义的逻辑。

因此,@Aspect注解用于标记切面类,而@Around注解用于标记环绕通知方法。切面类可以包含多个通知类型的方法,而环绕通知方法是其中的一种。切面类通过@Aspect注解告诉Spring AOP框架它是一个切面类,而环绕通知方法通过@Around注解告诉框架它是一个环绕通知方法。

综上所述,@Aspect和@Around是Spring AOP框架中的两个不同的注解,用于不同的目的。@Aspect用于标记切面类,而@Around用于标记环绕通知方法。