1 注解简单回顾
1.1 元注解
元注解(Meta-Annotation)是指注解上的注解,它是一种特殊的注解,用于标注其他注解的属性。元注解可以用来自定义注解的行为,包括定义注解的作用范围、生命周期、使用方式等信息。
在Java中,有四种元注解,分别是:
- @Target:用于指定注解的作用范围。它包括如下取值:
- ElementType.TYPE:适用于类、接口、枚举等类型;
- ElementType.FIELD:适用于字段、枚举常量等;
- ElementType.METHOD:适用于方法;
- ElementType.PARAMETER:适用于方法参数;
- ElementType.CONSTRUCTOR:适用于构造方法;
- ElementType.LOCAL_VARIABLE:适用于局部变量;
- ElementType.ANNOTATION_TYPE:适用于注解;
- ElementType.PACKAGE:适用于包。
- @Retention:用于指定注解的生命周期。它包括如下取值:
- RetentionPolicy.SOURCE:表示注解只在源代码中保留,在编译后就被丢弃;
- RetentionPolicy.CLASS:表示注解在编译时被保留在.class文件中,但在运行时不可见;
- RetentionPolicy.RUNTIME:表示注解在运行时可见,并且可以通过反射机制获取,存在与编译后的.class文件和JVM虚拟机中。
- @Documented:用于指定注解是否应该包含在JavaDoc文档中。如果一个注解使用了@Documented元注解,它将被包含在JavaDoc文档中,否则不会被包含。
- @Inherited:用于指定注解是否可以被继承。如果一个注解使用了@Inherited元注解,它将会被其子类继承,否则不会被继承。
通过使用元注解,我们可以自定义注解的作用范围、生命周期、可见性等信息,从而更加灵活地定义和使用注解。在使用自定义注解时,通常需要根据实际情况选择合适的元注解来对其进行修饰。
1.2 别名
@AliasFor是Spring Framework中的一个元注解,用于定义注解属性的别名,也可以用于指定注解的派生关系。
- 注解属性别名
它可以用来表示两个属性名称是互相等价的,如果使用了其中一个属性,另一个属性的值也会被自动赋值。
例如,假设我们有一个自定义的注解@MyAnnotation,它有两个属性name和value:
lessCopy code
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String name() default "";
String value() default "";
}
现在我们想要定义别名,使得name属性可以通过value属性来赋值,反之亦然。这时就可以使用@AliasFor元注解:
lessCopy code
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
@AliasFor("value")
String name() default "";
@AliasFor("name")
String value() default "";
}
在上面的代码中,我们通过@AliasFor(“value”)和@AliasFor(“name”)分别定义了name和value属性之间的互相别名关系。这样,在使用@MyAnnotation注解时,如果设置了name属性,value属性也会被自动赋值,反之亦然。
- 指定注解的派生关系
@AliasFor元注解也可以用于指定注解的派生关系,使得派生注解中的某些属性可以与父注解中的属性互相别名。在这种情况下,使用@AliasFor元注解的属性必须要与父注解中的属性名称相同。
例如,假设我们有一个自定义的注解@ParentAnnotation,它有两个属性name和value:
lessCopy code
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ParentAnnotation {
String name() default "";
String value() default "";
}
现在我们想要定义一个子注解@ChildAnnotation,它派生自@ParentAnnotation,并且将name属性与父注解中的value属性互相别名。这时可以使用@AliasFor元注解:
lessCopy code
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@ParentAnnotation
public @interface ChildAnnotation {
@AliasFor(annotation = ParentAnnotation.class, attribute = "value")
String name() default "";
}
在上面的代码中,我们通过@ParentAnnotation标注了@ChildAnnotation,表示它派生自@ParentAnnotation。同时,使用@AliasFor指定了name属性与父注解中的value属性互相别名。这样,在使用@ChildAnnotation注解时,如果设置了name属性,value属性也会被自动赋值为相同的值。
- 注意事项
使用@AliasFor元注解可以使自定义注解更加灵活和易用,尤其是在定义注解的派生关系时。但需要注意的是,@AliasFor元注解的使用需要谨慎,必须保证别名属性的类型和默认值与目标属性相同,否则可能会导致意想不到的问题。
2 常用声明Bean注解
这些注解可以用来声明不同类型的Bean,并且可以指定Bean的属性、作用域、初始化方式等。在实际使用中,根据具体的场景和需求选择合适的注解来声明Bean,可以更加方便地管理和组织Spring应用中的各个组件。
这里我们只讲解下常用的声明Bean的注解,其他注解自行查阅相关文档。
2.1 普通组件类
常用声明一个普通组件类的注解有以下4种:
- @Component
- @Controller
- @Service
- @Repository
源码如下2.1-1所示:
package org.springframework.stereotype;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 组件类声明
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component {
/**
* 组件名
*/
String value() default "";
}
/**
* 声明控制器
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
/**
* 组件名
*/
@AliasFor(annotation = Component.class)
String value() default "";
}
// 省略 @Service和@Repository,除了注解名不同,其他同Controller一样
在Spring框架中,@Controller、@Service和@Repository都是@Component注解的派生注解,即它们都包含了@Component注解的所有功能,同时还添加了一些特定功能。将它们定义为@Component注解的别名,主要是为了让开发者在不同的场景下使用更加方便。
具体来说,@Controller注解用于标识控制器类,表示这个类是一个处理HTTP请求的控制器;@Service注解用于标识服务层组件类,表示这个类是一个服务层组件;@Repository注解用于标识数据访问对象(DAO)类,表示这个类是一个数据访问对象。这些注解都是为了将不同类型的组件类在Spring应用中进行区分和管理。
使用@Component注解标记一个组件类时,需要为这个类指定一个具体的角色,如DAO、Service、Controller等。而通过将@Controller、@Service和@Repository注解定义为@Component注解的别名,就可以直接使用这些注解来标记组件类,而无需再指定角色。这样可以减少开发者的工作量,同时也使代码更加简洁明了。
总之,将@Controller、@Service和@Repository注解定义为@Component注解的别名,是为了让开发者在不同的场景下使用更加方便,同时也方便了Spring框架对不同类型的组件进行管理和扫描。
@Controller、@Service和@Repository在分层架构的应用中,使得可读性更强。
2.2 配置类
- @Configuration 和 @Bean
在Spring框架中,@Configuration和@Bean注解通常一起使用,用于定义Spring应用中的Bean组件。
@Configuration注解标记一个类为配置类,表示这个类中定义了一些Spring Bean组件的配置信息。通常,在@Configuration注解的类中,使用@Bean注解定义各个Bean组件,这些组件可以是Java对象、第三方库对象或其他Spring组件。
@Bean注解表示一个方法会返回一个对象,该对象会被注册到Spring容器中,并可以被其他组件所引用。通常,在@Bean注解的方法中,通过Java代码或其他方式创建一个对象,并返回这个对象的实例。这个实例就是一个Spring Bean组件,可以被Spring框架管理和使用。
@Configuration和@Bean注解的组合,让开发者可以通过Java代码的方式,对Spring应用中的Bean组件进行配置和管理。相比XML配置方式,这种方式更加灵活,可以充分利用Java语言的优势,实现更加高效和可读性强的配置方式。
- @ConfigurationProperties
@ConfigurationProperties是Spring Boot框架中的注解,它用于将配置文件中的属性值绑定到Java对象上。
通常情况下,在Spring Boot应用中,我们需要读取一些配置属性,如数据库连接信息、邮件服务器配置等。这些属性通常保存在application.properties或application.yml等配置文件中。使用@ConfigurationProperties注解,可以将这些属性值映射到一个Java对象上,方便我们在代码中进行访问和使用。
@ConfigurationProperties注解的使用步骤如下:
- 定义一个Java对象,并使用@Component或@Configuration注解将其注册到Spring容器中;
- 在Java对象上使用@ConfigurationProperties注解,指定属性的前缀,如:
@Component
@ConfigurationProperties(prefix = "myapp.db")
public class DatabaseConfig {
private String url;
private String username;
private String password;
// getters and setters
}
- 在配置文件中,添加对应的属性值,如:
myapp.db.url=jdbc:mysql://localhost:3306/myapp
myapp.db.username=root
myapp.db.password=123456
这样,在Spring Boot应用启动时,Spring框架会自动读取配置文件中的属性值,并将其绑定到DatabaseConfig对象上。我们可以通过@Autowired注解引用DatabaseConfig对象,并使用其中的属性值。
2.3 属性
- @Lazy:用于指定Bean的延迟初始化,即在首次使用时才进行初始化。
- @Primary:用于指定多个同类型的Bean中的首选项,当Spring无法确定使用哪个Bean时,会选择具有@Primary注解的Bean。
- @DependsOn:用于指定Bean之间的依赖关系,即指定一个Bean在另一个Bean之后进行初始化。
- 。。。
可以指定Bean的属性、作用域、初始化方式等,当然这些设置也可以在@Bean属性中指定,可以使用单独的注解指定。
3 选择性实例化Bean
在一个包中,我们有很多组件类,@Controller、@Service、@Repository和@component注解的类,但是因为业务需要,我们只需要一部分实例化,即加入容器管理,此时可以使用过滤器,来指定那些注解类需要实例化,那些注解不需要实例化。
我们把类放在一个文件中,仅做演示,实际中一般一个类一个单独文件。示例代码3-1如下所示:
package com.gaogzhen.spring6.bean2;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
/**
* @author: Administrator
* @createTime: 2023/02/26 09:28
*/
@Component
public class A {
public A() {
System.out.println("A 类无参构造方法");
}
}
@Controller
class B {
public B() {
System.out.println("B 类的无参构造方法");
}
}
@Service
class C {
public C() {
System.out.println("c 类的无参构造方法");
}
}
@Repository
class D {
public D() {
System.out.println("d 类的无参构造方法");
}
}
配置类做包扫描和过滤,如下嗲吗3-2所示:
package com.gaogzhen.spring6.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScans;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
/**
* @author gaogzhen
*/
@ComponentScans({
@ComponentScan(basePackages = "com.gaogzhen.spring6.bean2",
excludeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION, value = Controller.class),
@ComponentScan.Filter(type = FilterType.ANNOTATION, value = Repository.class)})
})
@Configuration
public class PackageConfig {
}
- excludeFilters:为指定排除过滤
- includeFilters:为指定包含过滤
测试代码3-3如下所示:
@Test
public void testChooseInstantiation() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(PackageConfig.class);
context.refresh();
List<String> beanNames = Arrays.asList("a", "b", "c", "d");
for (String beanName : context.getBeanDefinitionNames()) {
if (beanNames.contains(beanName)){
System.out.println("beanName: " + beanName);
System.out.println("bean: " + context.getBean(beanName));
}
}
}
测试结果如下所示:
A 类无参构造方法
c 类的无参构造方法
beanName: a
bean: com.gaogzhen.spring6.bean2.A@768ccdc5
beanName: c
bean: com.gaogzhen.spring6.bean2.C@4c6daf0
结语
如果小伙伴什么问题或者指教,欢迎交流。
❓QQ:806797785
⭐️源代码仓库地址:https://gitee.com/gaogzhen/spring6-study
参考:
[1]Spring框架视频教程[CP/OL].P69-75.
[2]ChatGPT