在Spring的历史中,我们曾经使用XML文件进行各种文件的装载与注入,但是由于XML需要额外的文件去维护,导致了代码的可读性较低、程序员编写代码较慢的问题。注解的诞生解决了很多问题。
@Compoment
介绍
此注解作用于一个类上,表明了此类将作为一个组件类,Spring需要为此类创建一个bean。
目前@Controller @Service @Repository都内含@Compoment注解,在功能上没有任何差异,在语义上能够起到辅助说明的作用,期待未来Spring会带来更多区别化更新。
使用
我们先引入一个简单的UserController
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserService userService;
@GetMapping
public User getUser(@RequestParam("userName") String userName) {
return userService.getUserByUserName(userName);
}
}
我们可以看到在controller层中,使用了@Autowired动态注入了UserService,后面的方法体内就可以直接使用这个service的实现类中的一系列方法了,这是为什么呢?我们来看看Service这边的代码:
@Service
public class UserService {
public UserCredential getUserByUserName(String userName) {
LOGGER.info("getUserByUserName() userName = {}", userName);
return userRepo.findByUserName(userName);
}
}
可以看到,在Service层中,UserService类被@Service修饰,表明我们已经告诉了Spring framework需要去创建一个UserService的bean供其他类去调用。如果没有这个注解会如何?
直接在项目启动的时候就会报错说找不到对应的类,并且还很贴心的告诉我们:考虑在配置里定义一个UserService吧!
综上,@Compoment就是为了让Spring建立一个Bean而生的注解,这样就能让其他地方使用啦。
@Bean
请看这个注解的源码,发现木有,这是一个方法级注解,和上面提到的注解完全不是同一个级别嘛。但是!但是!!!功能却是相同的,甚至更加的强大:告诉Spring去创建一个bean供整个项目使用。
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean {
@AliasFor("name")
String[] value() default {};
@AliasFor("value")
String[] name() default {};
/** @deprecated */
@Deprecated
Autowire autowire() default Autowire.NO;
boolean autowireCandidate() default true;
String initMethod() default "";
String destroyMethod() default "(inferred)";
}
通常,@Bean与@Configuration成对出现,类被@Configuration所声明表明当前类为一个配置类,Spring需要扫描去这个类,@Bean在配置类的里面修饰方法,返回一些类的实体对象作为bean供项目中其他地方使用。
例如刚刚我们把@Service注解从UserService中去掉了,但是我们可以构建一个配置类同样能够生成bean给@Autowired拿去使用:
@Configuration
public class UserConfig {
@Bean
public UserService initUserService() {
return new UserService();
}
}
读者们不禁要想:好好地@Compoment注解不用,为啥还要绕这么一大圈子就为了生成一个bean?那是因为我们现在的例子中的UserService还在我们的代码中维护,可以随时加一个@Service注解,但是我们想用外部的libaray引进的类咋办?@Compoment直接就躺了,还好有@Bean能够顶上。
空说无凭,我们来点常见的例子看看:
package org.springframework.security.crypto.password;
public interface PasswordEncoder {
String encode(CharSequence var1);
boolean matches(CharSequence var1, String var2);
default boolean upgradeEncoding(String encodedPassword) {
return false;
}
}
熟悉不?你在做密码匹配的时候用过的吧!BCryptPasswordEncoder就是继承自这个接口。然鹅,很可惜,BCryptPasswordEncoder类没有@Compoment,一 个 注 解 都 没 有 哟 !
咋办咋办?!
@Configuration
public class SecurityConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
就问你舒不舒服,愉不愉快,其他地方轻松使用这种方式调用咯!
@Autowired
private PasswordEncoder passwordEncoder;
总结
@Bean与@Compoment相似度很高,都是为了让Spring framework生成bean供其他地方使用,@Compoment更加简洁,@Bean功能更加强大。