跳至主要內容

注解

chanchaw大约 6 分钟javaspring

value

@value 注解的使用案例

@Value("${spring.mail.username}")
private String username;

日期格式

@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss")
private LocalDateTime startTime;

Scheduled

概述

定时任务的注解

案例

// 每5秒执行依次
@Scheduled(cron="0/5 * *  * * ? ")
// 每天 12:47:01 和 23:47:01 执行任务
@Scheduled(cron = "1 47 12,23 * * ?")

各种修饰符

配置文件 cron 表达式

// application.yml 配置文件有下面自定义参数
time:
  cron: */5 * * * * *
  interval: 5

// 下面是 java 代码,分别演示使用上面两个参数
@Scheduled(cron="${time.cron}")
void testPlaceholder1() {
    System.out.println("Execute at " + System.currentTimeMillis());
}

@Scheduled(cron="*/${time.interval} * * * * *")
void testPlaceholder2() {
    System.out.println("Execute at " + System.currentTimeMillis());
}

fixedDelay

@Scheduled(fixedDelay = 5000) //上一次执行完毕后5秒再执行

fixedDelayString

@Scheduled(fixedDelayString = "5000") //上一次执行完毕后5秒再执行
// 可以使用占位符读取配置文件参数,单位毫秒
@Scheduled(fixedDelayString = "${globalv.fixedDelay}")

fixedRate

//上一次开始执行后立即开始计时5秒后再次执行
// 如果执行一次的时间超过5秒,可能造成同时执行两个同样的任务
@Scheduled(fixedRate = 5000) 

fixedRateString

// 类似 fixedDelayString,可以使用占位符读取配置文件参数
// 计时方式同 fixedRate,上次开始执行立即开始计时,不管上次是否执行完毕

Spring Security

EnableWebSecurity

启用 spring security 的 web 功能

EnableGlobalMethodSecurity

启用 springSecurity 基于方法的安全功能,当我们使用 @PreAuthorize 修饰接口方法时,要求有对应权限的用户才能访问。

EnableTransactionManagement

启用 spring 基于注解的事务管理功能,需要和 @Configuration 注解一起使用

Conditional

概述

当满足某条件时 spring 组件或者 bean 才会被容器创建

ConditionalOnBean

当某个 bean 存在时配置生效

ConditionalOnMissingBean

当某个bean不存在时配置生效

ConditionalOnClass

当某个类在 classpath 存在时配置生效

ConditionalOnMissingClass

当某个类在 classpath 不存在时配置生效

ConfigurationProperties

用于批量注入外部配置文件中设定的参数,例如配置文件 application.yml 中定义了 secure.ignored 前缀的参数:

secure:
 ignored:
  urls: #安全路径白名单
   - /swagger-ui/
   - /swagger-resources/**
   - /**/v2/api-docs
   - /**/*.html
   - /**/*.js
   - /**/*.css
   - /**/*.png
   - /**/*.map
   - /favicon.ico
   - /actuator/**
   - /druid/**

在 java 源码中通过 List 集合属性批量接受参数

@Getter
@Setter
@Configuration
@ConfigurationProperties(prefix = "secure.ignored")
public class IgnoreUrlsConfig {
    private List<String> urls = new ArrayList<>();

}

AutowiredAnnotationBeanPostProcessor

概述

AutowiredAnnotationBeanPostProcessor 是 bean 后置处理器,其作用是处理注解: @Autowired@Value@Inject 注解 @Autowired@Valuespring 提供的,相对的 @Resource@WebServiceRef@EJB 都是 Java 提供的 从该类的构造方法可以看出其初始要解析的注解类型:

@SuppressWarnings("unchecked")
public AutowiredAnnotationBeanPostProcessor() {
    this.autowiredAnnotationTypes.add(Autowired.class);
    this.autowiredAnnotationTypes.add(Value.class);
    try {
        this.autowiredAnnotationTypes.add((Class<? extends Annotation>)
                ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
        logger.trace("JSR-330 'javax.inject.Inject' annotation found and supported for autowiring");
    }
    catch (ClassNotFoundException ex) {
        // JSR-330 API not available - simply skip.
    }
}

功能点

静态成员变量/方法不被依赖注入

类 AutowiredAnnotationBeanPostProcessor # buildAutowiringMetadata 方法在遍历目标类(开发人员定义的实体类)中使用了注解 @Autowired 的属性时会检测如果是 static 属性则日志打印提示: Autowired annotation is not supported on static fields: xxx 表示不会为静态属性(静态成员变量)进行依赖注入 同样的,如果方法被使用了 static 也不会被执行

postProcessProperties - 依赖注入

实际执行依赖注入的方法是 postProcessProperties

Lazy

可用于类、属性、方法参数上。进行依赖注入时发现有该注解标明的属性时会产生一个代理对象,并不会到容器中查找对应类型的 bean 进行填充,单纯返回一个代理对象。当该属性被使用时(属性是引用类型,调用该引用类型的方法)才会给代理对象内部的真实对象注入 value。这个过程体现了名称上的延迟。

Lombok 协助自动注入

spring 官方不推荐使用注解 Autowired 标识成员变量为自动注入,推荐使用构造方法来注入,如下面代码

private RestTemplate restTemplate;
public CartServiceImpl(RestTemplate restTemplate){
    this.restTemplate = restTemplate;
}

当需要注入的成员变量过多时,这个构造方法的传入参数就会太多。这里可以通过 lombok 的注解 RequiredArgsConstructor 来实现自动通过构造方法注入,例如下面代码,使用注解 RequiredArgsContructor ,同时使用 final 修饰在构造方法中自动注入的成员变量(常量是必备的参数,会被纳入构造方法的参数列表中),类似成员变量 name 非必须的则不会在构造方法中自动注入(这里不可为类使用注解 AllArgsConstructor ,这样会把成员变量 name 也纳入构造方法的自动注入列表中)

@Service
@RequiredArgsConstructor
public class CartServiceImpl extends ServiceImpl<CartMapper, Cart> implements ICartService {
    private final RestTemplate restTemplate;
    private String name = "";
    ...
}

Autowired

概述

  • 自动注入,给 bean 的属性注入 value
  • 按照类型在 spring 容器中查找 bean 如果找到多个 bean 则查看其中是否有 bean 使用了注解 primary ,有则首先使用该 bean,第二顺位逻辑是按照 priority 先后,第三顺位逻辑是按照名称相同 (被注解 Autowired 表示的属性名称和 spring 容器中的 bean 名称相同)
  • 注解 priority 只能用于类上,不可用于方法上。所以这种情况一般是多个类实现了同一个接口 属性注入时发现是接口类型,同时两个实现类各自有一个 bean,此时使用 priority 用于两个实现类的头上 用于区分优先度。
  • 如果找到的多个 bean 中没有使用注解 primary ,按照 bean 的名称也没有找到匹配的,则会报错

使用

  • 直接在属性头上使用
  • 在多个构造方法中的一个上使用,明确使用该方法生成实例
  • 在 set 方法上使用,此时属性头上可以不用该注解
spring接口Autowired
spring接口Autowired

例如上图,spring 在实例化后进行依赖注入,即属性头上使用了 @Autowired 的情况下,spring 会到容器中查找该类型的 bean 后进行填充,之后再处理使用了该注解的 set 方法。spring 依据传入形参的类型到容器中查找 bean ,set 方法名称去掉 set 的剩余部分作为属性名称,进行赋值。

案例

自动注入中执行自定义逻辑

不在属性上头上使用该注解,转而在 set 方法上使用该注解,只是要注意 set 方法要严格遵守小驼峰的规范。方法内就可以写多出来的逻辑了。

泛型类型属性

类似下面代码使用泛型时 spring 会报错。spring 在按照类型到容器中查找 bean 时获取的类型是 null 自然就找不到对应类型的 bean ,而下面的写法相当于 @Autowired(required=true)即要求必须填充,就导致 spring 报错。

@Autowired
private List<T> orderService;

如果 list 中的泛型修改为 object 则该列表会被填充所有 bean - spring 容器中的所有 bean

Lookup

概述

spring接口Lookup
spring接口Lookup

使用场景

在多线程环境下为保证线程安全要求每个线程使用自己独有的实例对象,此时就要求单例 beanA 中的属性 beanB 可以是多例的。

特点与必要条件

使用了注解 Lookup 的方法的必要条件:

  • 方法必须是 public ,不可以是 private 和 final
  • 使用了该注解的方法所属的类,在容器中实际是代理类的 bean
  • 该方法被代理类重写,所以不会执行方法内的逻辑,自然其中返回任何对象都没有效果
  • 方法所在类不可使用注解 @Bean 来创建 bean 而要使用 @Component