概述
- 注解,就是元数据,即一种描述数据的数据。所以,可以说注解就是源代码的元数据。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
- 作用分类:
- 编写文档:通过代码里标识的元数据生成文档【生成文档doc文档】
- 代码分析:通过代码里标识的元数据对代码进行分析【使用反射】
- 编译检查:通过代码里标识的元数据让编译器能够实现基本的编译检查【Override】
- 例如@Override注解。即使不使用@Override注解标记代码,程序也能够正常执行。那么,该注解表示什么?这么写有什么好处吗?事实上,@Override告诉编译器这个方法是一个重写方法(描述方法的元数据),如果父类中不存在该方法,编译器便会报错,提示该方法没有重写父类中的方法。并且使用注解有助于阅读程序。
为什么要引入注解?
- 使用Annotation之前(甚至在使用之后),XML被广泛的应用于描述元数据。不知何时开始一些应用开发人员和架构师发现XML的维护越来越糟糕了。他们希望使用一些和代码紧耦合的东西,而不是像XML那样和代码是松耦合的(在某些情况下甚至是完全分离的)代码描述。
- XML配置其实就是为了分离代码和配置而引入的。
- 假如你想为应用设置很多的常量或参数,这种情况下,XML是一个很好的选择,因为它不会同特定的代码相连。如果你想把某个方法声明为服务,那么使用Annotation会更好一些,因为这种情况下需要注解和方法紧密耦合起来,开发人员也必须认识到这点。
- 另一个很重要的因素是Annotation定义了一种标准的描述元数据的方式。在这之前,开发人员通常使用他们自己的方式定义元数据。例如,使用标记interfaces,注释,transient关键字等等。每个程序员按照自己的方式定义元数据,而不像Annotation这种标准的方式。
- 目前,许多框架将XML和Annotation两种方式结合使用,平衡两者之间的利弊。
注解的使用
基本内置注解
- Java 定义了一套注解,共有 7 个,3 个在 java.lang 中,剩下 4 个在 java.lang.annotation 中。
- 标准的注解@Override举例
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
- Annotations仅仅提供它定义的属性(类/方法/包/域)的信息。Annotations的用户(同样是一些代码)来读取这些信息并实现必要的逻辑。当我们使用Java的标注Annotations(例如@Override)时,JVM就是一个用户,它在字节码层面工作。
- 作用在代码的注解是:
- @Override:它的作用是对覆盖超类中方法的方法进行标记,如果被标记的方法并没有实际覆盖超类中的方法,则编译器会发出错误警告。
- Deprecated它的作用是对不应该再使用的方法添加注解,当编程人员使用这些方法时,将会在编译时显示提示信息,它与javadoc里的@deprecated标记有相同的功能。
- @SuppressWarnings 其参数有:
- deprecation,使用了过时的类或方法时的警告
- unchecked,执行了未检查的转换时的警告
- fallthrough,当 switch 程序块直接通往下一种情况而没有 break 时的警告
- path,在类路径、源文件路径等中有不存在的路径时的警告
- serial,当在可序列化的类上缺少serialVersionUID 定义时的警告
- finally ,任何 finally 子句不能正常完成时的警告
- all,关于以上所有情况的警告
- 作用在其他注解的注解元注解)是: 4个
- 从 Java 7 开始,额外添加了 3 个注解:
- @SafeVarargs - Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。
- @FunctionalInterface - Java 8 开始支持,标识一个匿名函数或函数式接口。
- @Repeatable - Java 8 开始支持,标识某注解可以在同一个声明上使用多次。
自定义注解
- 自定义Annotations: java.lang.annotation提供了四种元注解,专门注解其他的注解;
@Documented –注解是否将包含在JavaDoc中
@Retention –什么时候使用该注解
@Target? –注解用于什么地方
@Inherited – 是否允许子类继承该注解
- @Documented–一个简单的Annotations标记注解,表示是否将注解信息添加在java文档中。
- @Retention– 定义该注解的生命周期。
- SOURCE代表着注解仅保留在源级别中,并由编译器忽略。CLASS代表着注解在编译时由编译器保留,但Java虚拟机(JVM)会忽略。RUNTIME代表着标记的注解会由JVM保留,因此运行时环境可以使用它。
- RetentionPolicy.SOURCE – 在编译阶段丢弃。这些注解在编译结束之后就不再有任何意义,所以它们不会写入字节码。@Override, @SuppressWarnings都属于这类注解。 如果你想要在编译期间处理注解相关的逻辑,你需要继承AbstractProcessor 并实现process方法,@Data这样的注解就能有set/get等方法)。比如可以看到lombok就用AnnotationProcessor继承了AbstractProcessor。@Retention注解设置为SOURCE和CLASS这俩个级别,那么就需要继承并实现。
- RetentionPolicy.CLASS – 在类加载的时候丢弃。在字节码文件的处理中有用。注解默认使用这种方式。
- RetentionPolicy.RUNTIME– 始终不会丢弃,运行期也保留该注解,因此可以使用反射机制读取该注解的信息。我们自定义的注解通常使用这种方式。
Java代码
public enum RetentionPolicy {
// 此类型会被编译器丢弃
SOURCE,
// 此类型注解会保留在class文件中,但JVM会忽略它
CLASS,
// 此类型注解会保留在class文件中,JVM会读取它
RUNTIME
}
- @Target – 表示该注解用于什么地方。如果不明确指出,该注解可以放在任何地方。以下是一些可用的参数。需要说明的是:属性的注解是兼容的,如果你想给7个属性都添加注解,仅仅排除一个属性,那么你需要在定义target包含所有的属性。
public enum ElementType {
// 用于类,接口,枚举但不能是注解
TYPE,
// 字段上,包括枚举值
FIELD,
// 方法,不包括构造方法
METHOD,
// 方法的参数
PARAMETER,
// 构造方法
CONSTRUCTOR,
// 本地变量或catch语句
LOCAL_VARIABLE,
// 注解类型(无数据)
ANNOTATION_TYPE,
// Java包
PACKAGE
}
- @Inherited – 定义该注释和子类的关系
举例
- Annotations只支持基本类型、String及枚举类型。注释中所有的属性被定义成方法,并允许提供默认值。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface AOE {
enum Priority {
LOW,
MEDIUM,
HIGH
}
enum Status {
STARTED,
NOT_STARTED
}
String author() default "xj";
Priority priority() default Priority.LOW;
Status status() default Status.NOT_STARTED;
}
- 下面的例子演示了如何使用上面的注解。
public class User {
@AOE(priority = AOE.Priority.MEDIUM, author = "xj", status = AOE.Status.STARTED)
public void someth() {
//todo
}
}
- 如果注解中只有一个属性,可以直接命名为“value”,使用时无需再标明属性名。
@interface Author{
String value();
}
@Author("XJ")
public void someMethod() {
}
获取注解
- 所有这些对象都有getAnnotation()这个方法用来返回注解信息。我们需要把这个对象转换为我们自定义的注释(使用 instanceOf()检查之后),同时也可以调用自定义注释里面的方法。注意:要想使用反射去读取注解,必须将Retention的值选为Runtime
Java代码
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
//读取注解信息
public class ReadAnnotationInfoTest {
public static void main(String[] args) throws Exception {
// 测试AnnotationTest类,得到此类的类对象
Class c = Class.forName("com.iwtxokhtd.annotation.AnnotationTest");
// 获取该类所有声明的方法
Method[] methods = c.getDeclaredMethods();
// 声明注解集合
Annotation[] annotations;
// 遍历所有的方法得到各方法上面的注解信息
for (Method method : methods) {
// 获取每个方法上面所声明的所有注解信息
annotations = method.getDeclaredAnnotations();
// 再遍历所有的注解,打印其基本信息
System.out.println(method.getName());
for (Annotation an : annotations) {
System.out.println("方法名为:" + method.getName() + "其上面的注解为:" + an.annotationType().getSimpleName());
Method[] meths = an.annotationType().getDeclaredMethods();
// 遍历每个注解的所有变量
for (Method meth : meths) {
System.out.println("注解的变量名为:" + meth.getName());
}
}
}
}
}
注解用例
Spring 常用注解
-
声明bean的注解
- @Component 组件,没有明确的角色
- @Service 在业务逻辑层使用(service层)
- @Repository 在数据访问层使用(dao层)
- @Controller 在展现层使用,控制器的声明(C)
-
注入bean的注解
- @Autowired:由Spring提供
- @Inject:由JSR-330提供
- @Resource:由JSR-250提供
- 都可以注解在set方法和属性上,推荐注解在属性上。
-
java配置类相关注解
- @Configuration 声明当前类为配置类,相当于xml形式的Spring配置(类上)
- @Bean 注解在方法上,声明当前方法的返回值为一个bean,替代xml中的方式(方法上)
- @Configuration 声明当前类为配置类,其中内部组合
- @Component注解,表明这个类是一个bean(类上)
- @ComponentScan 用于对Component进行扫描,相当于xml中的(类上)
- @WishlyConfiguration 为@Configuration与@ComponentScan的组合注解,可以替代这两个注解
-
切面(AOP)相关注解:Spring支持AspectJ的注解式切面编程。
- @Aspect 声明一个切面(类上) 使用@After、@Before、@Around定义建言(advice),可直接将拦截规则(切点)作为参数。
- @After 在方法执行之后执行(方法上)
- @Before 在方法执行之前执行(方法上)
- @Around 在方法执行之前与之后执行(方法上)
- @PointCut 声明切点 在java配置类中使用@EnableAspectJAutoProxy注解开启Spring对AspectJ代理的支持(类上)
-
@Bean的属性支持
- @Scope 设置Spring容器如何新建Bean实例(方法上,得有@Bean) 其设置类型包括:
- Singleton (单例,一个Spring容器中只有一个bean实例,默认模式),
- Protetype (每次调用新建一个bean),
- Request (web项目中,给每个http request新建一个bean),
- Session (web项目中,给每个http session新建一个bean),
- GlobalSession(给每一个 global http session新建一个Bean实例)
- @StepScope 在Spring Batch中还有涉及
- @PostConstruct 由JSR-250提供,在构造函数执行完之后执行,等价于xml配置文件中bean的initMethod
- @PreDestory 由JSR-250提供,在Bean销毁之前执行,等价于xml配置文件中bean的destroyMethod
- @Scope 设置Spring容器如何新建Bean实例(方法上,得有@Bean) 其设置类型包括:
-
@Value注解
- @Value 为属性注入值(属性上)
- 注入普通字符 @Value(“Michael Jackson”)String name;
- 注入操作系统属性 @Value(“#{systemProperties[‘os.name’]}”)String osName;
- 注入表达式结果 @Value(“#{ T(java.lang.Math).random() * 100 }”) String randomNumber;
- 注入其它bean属性 @Value(“#{domeClass.name}”)String name;
- 注入文件资源 @Value(“classpath:com/hgs/hello/test.txt”)String Resource file;
- 注入网站资源 @Value(“http://www.javastack.cn”)Resource url;
- 注入配置文件 Value(“${book.name}”)String bookName;
- 注入配置使用方法:
- 编写配置文件(test.properties)book.name=《三体》
- @PropertySource 加载配置文件(类上) @PropertySource(“classpath:com/hgs/hello/test/test.propertie”)
- 还需配置一个PropertySourcesPlaceholderConfigurer的bean。
- @Value 为属性注入值(属性上)
-
环境切换
- @Profile 通过设定Environment的ActiveProfiles来设定当前context需要使用的配置环境。(类或方法上)
- @Conditional Spring4中可以使用此注解定义条件话的bean,通过实现Condition接口,并重写matches方法,从而决定该bean是否被实例化。(方法上)
-
异步相关
- @EnableAsync 配置类中,通过此注解开启对异步任务的支持,叙事性AsyncConfigurer接口(类上),点击这里了解使用详情。
- @Async 在实际执行的bean方法使用该注解来申明其是一个异步任务(方法上或类上所有的方法都将异步,需要@EnableAsync开启异步任务)
-
定时任务相关
- @EnableScheduling 在配置类上使用,开启计划任务的支持(类上)
- @Scheduled 来申明这是一个任务,包括cron,fixDelay,fixRate等类型(方法上,需先开启计划任务的支持)
-
@Enable*注解说明 这些注解主要用来开启对xxx的支持。
- @EnableAspectJAutoProxy 开启对AspectJ自动代理的支持
- @EnableAsync 开启异步方法的支持
- @EnableScheduling 开启计划任务的支持
- @EnableWebMvc 开启Web MVC的配置支持
- @EnableConfigurationProperties 开启对@ConfigurationProperties注解配置Bean的支持
- @EnableJpaRepositories 开启对SpringData JPA Repository的支持
- @EnableTransactionManagement 开启注解式事务的支持
- @EnableCaching 开启注解式的缓存支持
-
测试相关注解
- @RunWith 运行器,Spring中通常用于对JUnit的支持
- @RunWith(SpringJUnit4ClassRunner.class)
- @ContextConfiguration 用来加载配置ApplicationContext,其中classes属性用来加载配置类
- @ContextConfiguration(classes={TestConfig.class})
SpringBoot常用注解
- @SpringBootApplication:标识这是一个 Spring Boot 应用,用来开启 Spring Boot 的各项能力。
- 其实这个注解就是 @SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan 这三个注解的组合,也可以用这三个注解来代替 @SpringBootApplication 注解。
- @EnableAutoConfiguration:允许 Spring Boot 自动配置注解,开启这个注解之后,Spring Boot 就能根据当前类路径下的包或者类来配置 Spring Bean。
- @Configuration 这是 Spring 3.0 添加的一个注解,用来代替 applicationContext.xml 配置文件,所有这个配置文件里面能做到的事情都可以通过这个注解所在类来进行注册。
- @SpringBootConfiguration 这个注解就是 @Configuration 注解的变体,只是用来修饰是 Spring Boot 配置而已,或者可利于 Spring Boot 后续的扩展。
- @ComponentScan 这是 Spring 3.1 添加的一个注解,用来代替配置文件中的 component-scan 配置,开启组件扫描,即自动扫描包路径下的 @Component 注解进行注册 bean 实例到 context 中。
- @Conditional 这是 Spring 4.0 添加的新注解,用来标识一个 Spring Bean 或者 Configuration 配置文件,当满足指定的条件才开启配置。
- @ConditionalOnBean 组合 @Conditional 注解,当容器中有指定的 Bean 才开启配置。
- @ConditionalOnMissingBean 组合 @Conditional 注解,和 @ConditionalOnBean 注解相反,当容器中没有指定的 Bean 才开启配置。
- @ConditionalOnClass 组合 @Conditional 注解,当容器中有指定的 Class 才开启配置。
- @ConditionalOnMissingClass 组合 @Conditional 注解,和 @ConditionalOnMissingClass 注解相反,当容器中没有指定的 Class 才开启配置。
- @ConditionalOnWebApplication 组合 @Conditional 注解,当前项目类型是 WEB 项目才开启配置。
- 当前项目有以下 3 种类型:ANY,SERVLET,REACTIVE
- @ConditionalOnNotWebApplication :组合 @Conditional 注解,和 @ConditionalOnWebApplication 注解相反,当前项目类型不是 WEB 项目才开启配置。
- @ConditionalOnProperty 组合 @Conditional 注解,当指定的属性有指定的值时才开启配置
- @ConditionalOnExpression 组合 @Conditional 注解,当 SpEL 表达式为 true 时才开启配置。
- @ConditionalOnJava 组合 @Conditional 注解,当运行的 Java JVM 在指定的版本范围时才开启配置。
- @ConditionalOnResource 组合 @Conditional 注解,当类路径下有指定的资源才开启配置。
- @ConditionalOnJndi 组合 @Conditional 注解,当指定的 JNDI 存在时才开启配置。
- @ConditionalOnCloudPlatform 组合 @Conditional 注解,当指定的云平台激活时才开启配置
- @ConditionalOnSingleCandidate 组合 @Conditional 注解,当指定的 class 在容器中只有一个 Bean,或者同时有多个但为首选时才开启配置。
- @ConfigurationProperties 用来加载额外的配置(如 .properties 文件),可用在 @Configuration 注解类,或者 @Bean 注解方法上面。
- @EnableConfigurationProperties 一般要配合 @ConfigurationProperties 注解使用,用来开启对 @ConfigurationProperties 注解配置 Bean 的支持。
- @AutoConfigureAfter 用在自动配置类上面,表示该自动配置类需要在另外指定的自动配置类配置完之后。
- @Import 这是 Spring 3.0 添加的新注解,用来导入一个或者多个 @Configuration 注解修饰的类,这在 Spring Boot 里面应用很多。
- @ImportResource 这是 Spring 3.0 添加的新注解,用来导入一个或者多个 Spring 配置文件,这对 Spring Boot 兼容老项目非常有用,因为有些配置无法通过 Java Config 的形式来配置就只能用这个注解来导入。
Spring MVC常用注解
- Controller:注解一个类表示控制器,Spring MVC会自动扫描标注了这个注解的类。
- RestController:这个是Controller和ResponseBody的组合注解,表示@Controller标识的类里面的所有返回参数都放在response body里面。
- RequestMapping:请求路径映射,可以标注类,也可以是方法,可以指定请求类型,默认不指定为全部接收。
- RequestParam:放在参数前,表示只能接收参数a=b格式的数据,即 Content-Type为 application/x-www-form-urlencoded类型的内容。
- RequestBody:放在参数前,表示参数从request body中获取,而不是从地址栏获取,所以这肯定是接收一个POST请求的非a=b格式的数据,即 Content-Type不为 application/x-www-form-urlencoded类型的内容。
- ResponseBody:放在方法上或者返回类型前,表示此方法返回的数据放在response body里面,而不是跳转页面。一般用于ajax请求,返回json数据。
- PathVariable:路径绑定变量,用于绑定restful路径上的变量。
- @RequestHeader:放在方法参数前,用来获取request header中的参数值。
- @CookieValue;:放在方法参数前,用来获取request header cookie中的参数值。
- GetMapping PostMapping PutMapping… :Mapping的是Spring4.3加入的新注解,表示特定的请求类型路径映射,而不需要写RequestMethod来指定请求类型。
参考文档
- https://www.runoob.com/w3cnote/java-annotation.html 菜鸟教程
- https://mp.weixin.qq.com/s/FCxybEZK8f45_zI8AYbX6Q;
- https://mp.weixin.qq.com/s?__biz=MzI3ODcxMzQzMw==&mid=2247484331&idx=1&sn=acf8750f5b4174cf238527498f06e307&chksm=eb53869ddc240f8bfebeb73cd5bae0fed5ebf4b349f37a62129afc7f0432fbf99500245f129a&scene=21#wechat_redirect
- https://mp.weixin.qq.com/s?__biz=MzI3ODcxMzQzMw==&mid=2247488597&idx=1&sn=e3b44af93f36fce77fe69956c4e41251&chksm=eb539163dc2418754aebb15db1d6f245981bc02e9f7c19878765ba13385187ab7a777afbc3a6&scene=21#wechat_redirect
- https://baike.baidu.com/item/Java%20%E6%B3%A8%E8%A7%A3/4404368?fr=aladdin