Spring 5.x 那些常见的注解
说起spring5中那些常见的注解,你可肯定会说@Controller、@Service、@Repository.....这些常用的,但是当我们查看springboot源码的时候,我们会发现,原来spring给我们提供了这么多注解啊。
明白这些注解很有必要,不管你现在用不用得上。
下面我们一起来看看这些注解的的基本功能,后面将依次展开来说说。
注解 | 功能 |
---|---|
@Bean | 容器中注册组件,代替原来的<bean标签 |
@Configuration | 声明这是一个配置类,替换以前配置xml文件 |
@ComponentScan | 包扫描,扫描@Controller、@Service、@Repository、@Component注入spring容器中 |
@Conditional | 按条件注入 |
@Primary | 同类组件如果有多个,标注主组件 |
@Lazy | 组件懒加载(最后使用的时候才创建) |
@Scope | 声明组件的作用范围(SCOPE_PROTOTYPE,SCOPE_SINGLETON) |
@DependsOn | 组件之间声明依赖关系 |
@Component | @Controller、@Service、@Repository |
@Indexed | 加速注解,所有标注了 @Indexed 的组件,直接会启动快速加载 |
@Order | 数字越小优先级越高,越先工作 |
@Import | 导入第三方jar包中的组件,或定制批量导入组件逻辑 |
@ImportResource | 导入以前的xml配置文件,让其生效 |
@Profile | 基于多环境激活 |
@PropertySource | 外部properties配置文件和JavaBean进行绑定.结合ConfigurationProperties |
@PropertySources | @PropertySource组合注解 |
@Autowired | 自动装配 |
@Qualifier | 精确指定 |
@Value | 取值、计算机环境变量、JVM系统。@Value(“$”) |
@Lookup | 单例组件依赖非单例组件,非单例组件获取需要使用方法 |
注意:@Indexed 需要引入依赖
org.springframework spring-context-indexer true
部分注解详解
@Configuration && @Bean
我们都知道,在早期spring的项目中,我们都需要建立一个springContext.xml来作为配置文件,然后通过bean来进行注入。
@configuration就相当于xml的配置文件,充当配置文件的配置类。而@Bean就相当于原来的bean标签。
<!--springContext.xml-->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user" class="com.bearjun.bean.User"></bean>
</beans>
class User {
private Integer userId;
private String username;
private String password;
// 省略get、set、toString
}
@Configuration
public class SpringConfig {
@Bean("user1")
User user1() {
return new User(1, "bear", "12212");
}
}
public class SpringTest {
@Test
public void test1() {
// 通过配置文件的方式获取所有的bean
ApplicationContext context = new ClassPathXmlApplicationContext("springContext.xml");
String[] beanDefinitionNames = context.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println("xml=>" + beanDefinitionName);
}
// 最后打印:xml=>user
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
String[] beanDefinitionName = annotationConfigApplicationContext.getBeanDefinitionNames();
for (String name : beanDefinitionName) {
System.out.println("annotation = > " + name);
}
// 最后打印去处默认配置后:
// annotation = > springConfig
// annotation = > user1
}
}
注意:@Bean配置的时候,方法名为默认的id类型,当然也可以@Bean("id")这样来指定id。另外,@Bean注入的对象默认都是单例的。
@Lazy && @Scope
前面我们知道,通过@Bean可以直接把对象注册到容器中,那注册进去的对象怎么设置成原型呢?可以懒加载嘛?
// SpringConfig.class
/**
* @Scope可选内容为:
* ConfigurableBeanFactory#SCOPE_PROTOTYPE 原型模式
* ConfigurableBeanFactory#SCOPE_SINGLETON 单例模式
* org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST request模式,同一次强求一个对象
* org.springframework.web.context.WebApplicationContext#SCOPE_SESSION session模式,同一个session一个对象
*/
@Bean("user1")
@Scope
User user1() {
return new User(1, "bear", "12212");
}
// SpringTest.class
User user1 = (User) annotationConfigApplicationContext.getBean("user1");
User user2 = (User) annotationConfigApplicationContext.getBean("user1");
/**
* 当@Scope("singleton")时,为false
* 当@Scope("prototype")时,为true
*/
System.out.println(user1 == user2);
@Lazy只是在单例模式下使用,让对象需要创建的时候才注入到容器。
@ComponentScan
最早用spring项目的时候,@Controller、@Service、@Repository是不会添加到spring容器的。这个时候,我们通常使用一个包扫描的配置
<context:component-scan base-package="com.xxx 注解来使这些类加载到spring容器中。
@ComponentScan就相当于context:component-scan
// 1、添加Controller、Service、Repository
@Controller
public class UserController {
}
@Service
public class UserService {
}
@Repository
public class UserDao {
}
// 2、通过上面的test1方法,发现没有注入到spring容器
// 3、在springContext.xml中添加
<context:component-scan base-package="com.bearjun"></context:component-scan>
Controller、Service、Dao注入到spring容器中了
// 4、在SpringConfig的类上添加 @ComponentScan(value = "com.bearjun")
Controller、Service、Dao也注入到spring容器中了
@ComponentScan提供了很多包扫描的属性
// 当你想扫描多个包的时候,你可以配置value为一个数组
@ComponentScan(value = {"com.bearjun.controller", "com.bearjun.service"})
什么?你不想要某个包或者注解被扫描?
excludeFilters中Filter的type有很多类型供我们选择,常见的如下
- FilterType.ANNOTATION 根据注解类型
- FilterType.ASSIGNABLE_TYPE 按照给定的类型
- FilterType.CUSTOM 自定义
// FilterType.ANNOTATION类型。扫描com.bearjun整包,排除@Controller注解
@ComponentScan(value = {"com.bearjun"}, excludeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION, value = Controller.class)
})
// FilterType.ASSIGNABLE_TYPE类型。扫描com.bearjun整包,排除UserDao类
@ComponentScan(value = {"com.bearjun"}, excludeFilters = {
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = UserDao.class)
})
// FilterType.CUSTOM类型。
// 自定义一个类,实现 org.springframework.core.type.filter.TypeFilter;
public class MyFilter implements TypeFilter {
/**
* @param metadataReader 当前正在扫描的类 ComponentScan value的值的信息
* @param metadataReaderFactory 获取其他类的信息
* @return
*/
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) {
// 当前类的注解信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
// 当前的扫描的类信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
// 当前类资源
Resource resource = metadataReader.getResource();
String className = classMetadata.getClassName();
// 所有的 controller 返回 true
if (className.toLowerCase().contains("controller")) {
return true;
}
return false;
}
}
// 配置。扫描com.bearjun整包,但是满足 MyFilter 返回值为 true 的过滤掉
@ComponentScan(value = {"com.bearjun"}, excludeFilters = {
@ComponentScan.Filter(type = FilterType.CUSTOM, value = MyFilter.class)
})
@Conditional
@Conditional按照一定的条件判断,满足条件才注入到spring容器中。
@Configuration
public class SpringConfig {
/**
*
* @return
*/
@Bean("bear")
User bear() {
return new User(1, "bear", "123123");
}
@Bean("bearjun")
@Conditional(MyCondition.class)
User bearjun() {
return new User(2, "bearjun", "321321");
}
}
public class MyCondition implements Condition {
/**
* @param context 上下文(可以获取各种需要的信息)
* @param metadata 注释信息
* @return
*/
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
/**
* 获取bean的注册信息,可以新增,移除,判断。。bean
*/
BeanDefinitionRegistry registry = context.getRegistry();
/**
* 获取项目运行的环境
*/
Environment environment = context.getEnvironment();
/**
* 获取bean工厂
*/
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
/**
* 获取类加载器活资源加载器
*/
ClassLoader classLoader = context.getClassLoader();
ResourceLoader resourceLoader = context.getResourceLoader();
/**
* 如果项目中是否存在一个bean叫bear
*/
return context.getRegistry().containsBeanDefinition("bear");
}
}
@Test
public void test2() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
Map<String, User> beansOfType = context.getBeansOfType(User.class);
beansOfType.forEach((key, value)->{
System.out.println(key + " ==== " + value);
});
}
// 最后输出的结果为:
bear ==== User{userId=1, username='bear', password='123123'}
bearjun ==== User{userId=2, username='bearjun', password='321321'}
// 如果我们把@Bean("bear")换成@Bean("bear1")
最后的结果为:bear1 ==== User{userId=1, username='bear', password='123123'}
注意:@Conditional注解也可以还在类上,使用和加载方法上一样
@Import
根据前面的学习,我们现在知道,往spring中注册组件:
-
@ComponentScan + @Controller / @Service / @Repository
-
@Bean
-
@Import :默认的id为全类名
@Configuration
@ComponentScan(value = "com.bearjun")
@Import(value = {String.class, Math.class})
public class SpringConfig {
}
@Test
public void test3() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
String[] beanDefinitionName = context.getBeanDefinitionNames();
for (String name : beanDefinitionName) {
// 打印存在
// annotation = > java.lang.String
// annotation = > java.lang.Math
System.out.println("annotation = > " + name);
}
}
- @Import(value="ImportSelector.class")
@Configuration
@ComponentScan(value = "com.bearjun")
@Import(value = {MyImportSelector.class})
public class SpringConfig {
}
public class MyImportSelector implements ImportSelector {
/**
* @param importingClassMetadata 获取标注了当前@Import注解的所以注解信息
* @return
*/
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"java.lang.String"};
}
}
@Test
public void test3() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
String[] beanDefinitionName = context.getBeanDefinitionNames();
for (String name : beanDefinitionName) {
// 打印存在
// annotation = > java.lang.String
System.out.println("annotation = > " + name);
}
}
- @Import(value="ImportBeanDefinitionRegistrar.class")
@Configuration
@ComponentScan(value = "com.bearjun")
@Import(value = {ImportBeanDefinitionRegistrar.class})
public class SpringConfig {
}
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/**
* @param importingClassMetadata 当前类的注解信息
* @param registry BeanDefinition注册类
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// bean注册器中是否存在bearjun的组件
if (registry.containsBeanDefinition("bearjun")) {
// 往组件中添加id为string的组件
BeanDefinition definition = new RootBeanDefinition("java.lang.String");
registry.registerBeanDefinition("string", definition);
}
}
}
@Test
public void test3() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
String[] beanDefinitionName = context.getBeanDefinitionNames();
for (String name : beanDefinitionName) {
// 打印存在
// annotation = > string
System.out.println("annotation = > " + name);
}
}
- FactoryBean
spring系列笔记 - 第⼋章 Spring工厂创建复杂对象(3种方式)
@Lookup
单例组件依赖非单例组件,非单例组件获取需要使用方法。
// Car.class
@Component
@Scope("prototype")
public class Car {
private String brand;
// 省略get/set/toString
}
// User.class
@Component
public class User {
private Integer userId;
private String username;
private String password;
private Car car;
@Lookup
public Car getCar() {
return car;
}
public void setCar(Car car) {
this.car = car;
}
// 省略其他的get/set/toString
}
// SpringTest#test3
@Test
public void test3() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
User user1 = (User) context.getBean("user");
User user2 = (User) context.getBean("user");
System.out.println(user1 == user2);
// 打印:true
Car car1 = user1.getCar();
Car car2 = user2.getCar();
System.out.println(car1 == car2);
// 如果user#getCar不加@Lookup注解,打印为true
// 加@Lookup注解,打印为false
}
需要注意的是:
- @Lookup需要加在get方法上才生效
- 单例模式的组件(上述为User)不能是从配置类中放回的bean,否则 @Lookup 不生效
And please remember that lookup methods won't work on beans returned from methods in configuration classes
请记住,查找方法不适用于从配置类中的方法返回的 bean
评论区