@Target

@Target说明了Annotation所修饰的对象范围:Annotation可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。在Annotation类型的声明中使用了target可更加明晰其修饰的目标。
作用:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)

取值(ElementType)有

public enum ElementType {/**用于描述类、接口(包括注解类型) 或enum声明 Class, interface (including annotation type), or enum declaration */TYPE,/** 用于描述域 Field declaration (includes enum constants) */FIELD,/**用于描述方法 Method declaration */METHOD,/**用于描述参数 Formal parameter declaration */PARAMETER,/**用于描述构造器 Constructor declaration */CONSTRUCTOR,/**用于描述局部变量 Local variable declaration */LOCAL_VARIABLE,/** Annotation type declaration */ANNOTATION_TYPE,/**用于描述包 Package declaration */PACKAGE,/*** 用来标注类型参数 Type parameter declaration* @since 1.8*/TYPE_PARAMETER,/***能标注任何类型名称 Use of a type* @since 1.8*/TYPE_USE

ElementType.TYPE_PARAMETER(Type parameter declaration) 用来标注类型参数, 栗子如下:

@Target(ElementType.TYPE_PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface TypeParameterAnnotation {}// 如下是该注解的使用例子
public class TypeParameterClass<@TypeParameterAnnotation T> {public <@TypeParameterAnnotation U> T foo(T t) {return null;}
}

ElementType.TYPE_USE(Use of a type) 能标注任何类型名称,包括上面这个(ElementType.TYPE_PARAMETER的),栗子如下:

public class TestTypeUse {@Target(ElementType.TYPE_USE)@Retention(RetentionPolicy.RUNTIME)public @interface TypeUseAnnotation {}public static @TypeUseAnnotation class TypeUseClass<@TypeUseAnnotation T> extends @TypeUseAnnotation Object {public void foo(@TypeUseAnnotation T t) throws @TypeUseAnnotation Exception {}}// 如下注解的使用都是合法的@SuppressWarnings({ "rawtypes", "unused", "resource" })public static void main(String[] args) throws Exception {TypeUseClass<@TypeUseAnnotation String> typeUseClass = new @TypeUseAnnotation TypeUseClass<>();typeUseClass.foo("");List<@TypeUseAnnotation Comparable> list1 = new ArrayList<>();List<? extends Comparable> list2 = new ArrayList<@TypeUseAnnotation Comparable>();@TypeUseAnnotation String text = (@TypeUseAnnotation String)new Object();java.util. @TypeUseAnnotation Scanner console = new java.util.@TypeUseAnnotation Scanner(System.in);}
}

@Retention

元注解的作用就是负责注解其他注解。Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它 annotation类型作说明。Java5.0定义的元注解:
    1.@Target,
    2.@Retention,
    3.@Documented,
    4.@Inherited
  这些类型和它们所支持的类在java.lang.annotation包中可以找到。下面我们看一下每个元注解的作用和相应分参数的使用说明。
  @Target:

@Target说明了Annotation所修饰的对象范围:Annotation可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。在Annotation类型的声明中使用了target可更加明晰其修饰的目标。

作用:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)

取值(ElementType)有:

1.CONSTRUCTOR:用于描述构造器
    2.FIELD:用于描述域
    3.LOCAL_VARIABLE:用于描述局部变量
    4.METHOD:用于描述方法
    5.PACKAGE:用于描述包
    6.PARAMETER:用于描述参数
    7.TYPE:用于描述类、接口(包括注解类型) 或enum声明

使用实例:  
  @Target(ElementType.TYPE)
public @interface Table {
/**
* 数据表名称注解,默认值为类名称
* @return
*/
public String tableName() default “className”;
}

@Target(ElementType.FIELD)
public @interface NoDBColumn {

}
注解Table 可以用于注解类、接口(包括注解类型) 或enum声明,而注解NoDBColumn仅可用于注解类的成员变量。

@Retention:

@Retention定义了该Annotation被保留的时间长短:某些Annotation仅出现在源代码中,而被编译器丢弃;而另一些却被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略,而另一些在class被装载时将被读取(请注意并不影响class的执行,因为Annotation与class在使用上是被分离的)。使用这个meta-Annotation可以对 Annotation的“生命周期”限制。

作用:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效)

取值(RetentionPoicy)有:

1.SOURCE:在源文件中有效(即源文件保留)
    2.CLASS:在class文件中有效(即class保留)
    3.RUNTIME:在运行时有效(即运行时保留)

Retention meta-annotation类型有唯一的value作为成员,它的取值来自java.lang.annotation.RetentionPolicy的枚举类型值。具体实例如下:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {public String name() default "fieldName";public String setFuncName() default "setField";public String getFuncName() default "getField"; public boolean defaultDBValue() default false;
}

Column注解的的RetentionPolicy的属性值是RUTIME,这样注解处理器可以通过反射,获取到该注解的属性值,从而去做一些运行时的逻辑处理

@Documented:

@Documented用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。Documented是一个标记注解,没有成员。

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Column {public String name() default "fieldName";public String setFuncName() default "setField";public String getFuncName() default "getField"; public boolean defaultDBValue() default false;
}

@Inherited:

@Inherited 元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。

注意:@Inherited annotation类型是被标注过的class的子类所继承。类并不从它所实现的接口继承annotation,方法并不从它所重载的方法继承annotation。

当@Inherited annotation类型标注的annotation的Retention是RetentionPolicy.RUNTIME,则反射API增强了这种继承性。如果我们使用java.lang.reflect去查询一个@Inherited annotation类型的annotation时,反射代码检查将展开工作:检查class和其父类,直到发现指定的annotation类型被发现,或者到达类继承结构的顶层。

实例代码:

/*** * @author peida**/
@Inherited
public @interface Greeting {public enum FontColor{ BULE,RED,GREEN};String name();FontColor fontColor() default FontColor.GREEN;
}

自定义注解:

使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口,由编译程序自动完成其他细节。在定义注解时,不能继承其他的注解或接口。@interface用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)。可以通过default来声明参数的默认值。

定义注解格式:
  public @interface 注解名 {定义体}

注解参数的可支持数据类型:

1.所有基本数据类型(int,float,boolean,byte,double,char,long,short)
    2.String类型
    3.Class类型
    4.enum类型
    5.Annotation类型
    6.以上所有类型的数组

Annotation类型里面的参数该怎么设定:
  第一,只能用public或默认(default)这两个访问权修饰.例如,String value();这里把方法设为defaul默认类型;   
  第二,参数成员只能用基本类型byte,short,char,int,long,float,double,boolean八种基本数据类型和 String,Enum,Class,annotations等数据类型,以及这一些类型的数组.例如,String value();这里的参数成员就为String;  
  第三,如果只有一个参数成员,最好把参数名称设为"value",后加小括号.例:下面的例子FruitName注解就只有一个参数成员。

简单的自定义注解和使用注解实例:

package annotation;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;/*** 水果名称注解* @author peida**/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitName {String value() default "";
}
package annotation;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;/*** 水果颜色注解* @author peida**/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitColor {/*** 颜色枚举* @author peida**/public enum Color{ BULE,RED,GREEN};/*** 颜色属性* @return*/Color fruitColor() default Color.GREEN;}
package annotation;import annotation.FruitColor.Color;public class Apple {@FruitName("Apple")private String appleName;@FruitColor(fruitColor=Color.RED)private String appleColor;public void setAppleColor(String appleColor) {this.appleColor = appleColor;}public String getAppleColor() {return appleColor;}public void setAppleName(String appleName) {this.appleName = appleName;}public String getAppleName() {return appleName;}public void displayName(){System.out.println("水果的名字是:苹果");}
}

注解元素的默认值:

注解元素必须有确定的值,要么在定义注解的默认值中指定,要么在使用注解时指定,非基本类型的注解元素的值不可为null。因此, 使用空字符串或0作为默认值是一种常用的做法。这个约束使得处理器很难表现一个元素的存在或缺失的状态,因为每个注解的声明中,所有元素都存在,并且都具有相应的值,为了绕开这个约束,我们只能定义一些特殊的值,例如空字符串或者负数,一次表示某个元素不存在,在定义注解时,这已经成为一个习惯用法。例如:

package annotation;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;/*** 水果供应者注解* @author peida**/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitProvider {/*** 供应商编号* @return*/public int id() default -1;/*** 供应商名称* @return*/public String name() default "";/*** 供应商地址* @return*/public String address() default "";
}

定义了注解,并在需要的时候给相关类,类属性加上注解信息,如果没有响应的注解信息处理流程,注解可以说是没有实用价值。如何让注解真真的发挥作用,主要就在于注解处理方法,下一步我们将学习注解信息的获取和处理!

二、注解的使用:

第一步:新建一个annotation,名字为:MyAnnotation.java。

package com.dragon.test.annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** Created by gmq on 2015/9/10.*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation
{String hello () default "hello";String world();
}

第二步:建立一个MyTest.java 来使用上面的annotation。

package com.dragon.test.annotation;/*** Created by gmq on 2015/9/10.*/
public class MyTest
{@MyAnnotation(hello = "Hello,Beijing",world = "Hello,world")public void output() {System.out.println("method output is running ");}
}

第三步:用反射机制来调用注解中的内容

package com.dragon.test.annotation;import java.lang.annotation.Annotation;
import java.lang.reflect.Method;/*** 用反射机制来调用注解中的内容* Created by gmq on 2015/9/10.*/
public class MyReflection
{public static void main(String[] args) throws Exception{// 获得要调用的类Class<MyTest> myTestClass = MyTest.class;// 获得要调用的方法,output是要调用的方法名字,new Class[]{}为所需要的参数。空则不是这种Method method = myTestClass.getMethod("output", new Class[]{});// 是否有类型为MyAnnotation的注解if (method.isAnnotationPresent(MyAnnotation.class)){// 获得注解MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);// 调用注解的内容System.out.println(annotation.hello());System.out.println(annotation.world());}System.out.println("----------------------------------");// 获得所有注解。必须是runtime类型的Annotation[] annotations = method.getAnnotations();for (Annotation annotation : annotations){// 遍历所有注解的名字System.out.println(annotation.annotationType().getName());}}
}

输出:

Hello,Beijing
Hello,world

@Documented

Documented注解表明这个注释是由 javadoc记录的,在默认情况下也有类似的记录工具。 如果一个类型声明被注释了文档化,它的注释成为公共API的一部分。

@Component

@Component and @Bean do two quite different things, and shouldn’t be confused.

@Component (and @Service and @Repository) are used to auto-detect and auto-configure beans using classpath scanning. There’s an implicit one-to-one mapping between the annotated class and the bean (i.e. one bean per class). Control of wiring is quite limited with this approach, since it’s purely declarative.

@Bean is used to explicitly declare a single bean, rather than letting Spring do it automatically as above. It decouples the declaration of the bean from the class definition, and lets you create and configure beans exactly how you choose.

看了一些文章,这两个注解可以互换使用,但还有一些使用目的进行区别的。

@Component被用在要被自动扫描和装配的类上。

@Bean主要被用在方法上,来显式声明要用生成的类。

现在项目上,本工程中的类,一般都使用@Component来生成bean。在把通过web service取得的类,生成Bean时,使用@Bean和getter方法来生成bean。

@Conditional

Conditional 是由 SpringFramework 提供的一个注解,位于 org.springframework.context.annotation 包内,定义如下。

package org.springframework.context.annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Conditional {Class<? extends Condition>[] value();}

Conditional 注解类里只有一个 value 属性,需传入一个 Condition 类型的数组,我们先来看看这个 Condition 接口长什么样。

package org.springframework.context.annotation;import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.core.type.AnnotatedTypeMetadata;
public interface Condition {boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);}

其中,matches() 方法传入的参数 ConditionContext 是专门为 Condition 而设计的一个接口类,可以从中获取到Spring容器的以下对象信息。

当一个 Bean 被 Conditional 注解修饰时,Spring容器会对数组中所有 Condition 接口的 matches() 方法进行判断,只有当其中所有 Condition 接口的 matches()方法都为 ture 时,才会创建 Bean 。
自定义Conditional
接下来,我们将以一个国际化 I18n Bean 动态创建为例(根据配置中的 i18n.lang 属性值来动态地创建国际化 I18n Bean),对如何使用 Conditional 注解进行简单举例:

当 i18n.lang=zh_CN 就创建中文 I18nChs Bean,
当 i18n.lang=en_US 就创建英文 I18nEng Bean。
创建好的两个 Condition 实现类 I18nChsCondition 和 I18nEngCondition 代码如下。

public class I18nChsCondition extends SpringBootCondition {@Overridepublic ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {String lang = context.getEnvironment().getProperty("i18n.lang");ConditionOutcome outCome = new ConditionOutcome("zh_CN".equals(lang), "i18n.lang=" + lang);return outCome;}
}
public class I18nEngCondition extends SpringBootCondition {@Overridepublic ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {String lang = context.getEnvironment().getProperty("i18n.lang");ConditionOutcome outCome = new ConditionOutcome("en_US".equals(lang), "i18n.lang=" + lang);return outCome;}}

I18n 接口定义如下。

public interface I18n {// 获取 name 属性的值String i18n(String name);}

I18n 接口的两个实现类 I18nChs 和 I18nEng 定义如下。

@Component
@Conditional(I18nChsCondition.class)
public class I18nChsImpl implements I18n {Map<String, String> map = new HashMap<String, String>() {private static final long serialVersionUID = 1L;{put("lang", "中文");}};@Overridepublic String i18n(String name) {return map.get(name);}
}
@Component
@Conditional(I18nEngCondition.class)
public class I18nEngImpl implements I18n {Map<String, String> map = new HashMap<String, String>() {private static final long serialVersionUID = 1L;{put("lang", "English");}};@Overridepublic String i18n(String name) {return map.get(name);}}

在启动类中添加测试代码代码如下。

@SpringBootApplication
public class App
{public static void main( String[] args ){ConfigurableApplicationContext context = SpringApplication.run(App.class, args);I18n i18n = context.getBean(I18n.class);System.out.println(i18n.getClass().getName());System.out.println(i18n.i18n("lang"));context.close();}
}

配置 application.properties 内容如下:

# language : zh_CN/Chinese,en_US/America
i18n.lang=zh_CN

运行程序,打印结果:

com.pengjunlee.condition.I18nChsImpl
中文

配置 application.properties 内容如下:


# language : zh_CN/Chinese,en_US/America
i18n.lang=en_US

再次运行程序,打印结果:

com.pengjunlee.condition.I18nEngImpl
English

为了书写和调用方便,我们还可以把上面的条件定义成注解,以 I18nChsCondition 为例,定义代码如下。

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(I18nChsCondition.class)
public @interface I18nChs {}

将 I18nChs 注解添加到 I18nChsImpl 上。

@Component
@I18nEng
public class I18nChsImpl implements I18n {//内容同上,此处省略}

SpringBoot 扩展注解
从上面的示例不难看出,如果要使用我们自定义条件类实现起来还是有点小麻烦的,不过比较庆幸的是, SpringBoot 在 Conditional 注解的基础上已经提前为我们定义好了一系列功能丰富的注解,我们可以直接使用。

接下来我们使用 ConditionalOnProperty 注解来实现上面的国际化示例。

仅需修改 I18nChsImpl 和 I18nEngImpl 两个实现组件类,其他代码不变,程序执行结果与之前相同。


@Component
@ConditionalOnProperty(name = "i18n.lang", havingValue = "zh_CN", matchIfMissing = true)
public class I18nChsImpl implements I18n {//内容同上,此处省略}
@Component
@ConditionalOnProperty(name = "i18n.lang", havingValue = "en_US", matchIfMissing = false)
public class I18nEngImpl implements I18n {//内容同上,此处省略}

spring-boot注解详解(六)相关推荐

  1. Spring Boot注解详解

    文章目录 使用注解的优势 注解详解(配备了完善的释义) 注解列表如下 JPA注解 springMVC相关注解 全局异常处理 项目中具体配置解析和使用环境 使用注解的优势 采用纯java代码,不在需要配 ...

  2. 《深入理解 Spring Cloud 与微服务构建》第十六章 Spring Boot Security 详解

    <深入理解 Spring Cloud 与微服务构建>第十六章 Spring Boot Security 详解 文章目录 <深入理解 Spring Cloud 与微服务构建>第十 ...

  3. Spring Boot 单元测试详解+实战教程

    转载自   Spring Boot 单元测试详解+实战教程 Spring Boot 的测试类库 Spring Boot 提供了许多实用工具和注解来帮助测试应用程序,主要包括以下两个模块. spring ...

  4. 全面的Spring Boot配置文件详解

    全面的Spring Boot配置文件详解 Spring Boot在工作中是用到的越来越广泛了,简单方便,有了它,效率提高不知道多少倍.Spring Boot配置文件对Spring Boot来说就是入门 ...

  5. Spring Boot 配置文件详解

    2019独角兽企业重金招聘Python工程师标准>>> 第二篇 : Spring Boot配置文件详解 文章首发于微信公众号<程序员果果> 地址:https://mp.w ...

  6. spring boot配置文件详解

    spring boot配置文件详解 application.properties是spring-boot的核心配置文件,这个配置文件基本可以取代我们ssm或者ssh里面的所有的xml配置文件. 当我们 ...

  7. Spring boot——Actuator 详解

    一.什么是 Actuator Spring Boot Actuator 模块提供了生产级别的功能,比如健康检查,审计,指标收集,HTTP 跟踪等,帮助我们监控和管理Spring Boot 应用. 这个 ...

  8. (转) SpringBoot非官方教程 | 第二篇:Spring Boot配置文件详解

    springboot采纳了建立生产就绪spring应用程序的观点. Spring Boot优先于配置的惯例,旨在让您尽快启动和运行.在一般情况下,我们不需要做太多的配置就能够让spring boot正 ...

  9. SpringBoot非官方教程 | 第二篇:Spring Boot配置文件详解

    springboot采纳了建立生产就绪Spring应用程序的观点. Spring Boot优先于配置的惯例,旨在让您尽快启动和运行.在一般情况下,我们不需要做太多的配置就能够让spring boot正 ...

  10. Spring Boot原理详解(一)

    概念: Spring Boot是由Pivotal团队提供的全新框架,属于spring旗下的一个项目,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,它使 ...

最新文章

  1. 2 构建Mysql+heartbeat+DRBD+LVS集群应用系统系列之MySql的搭建
  2. 为节省内存,Firefox 将用新方式阻止加载没用到的标签页
  3. 【vSphere系列二】安装 Openfiler 存储
  4. 推荐系统笔记(评价指标及效果)
  5. 网页设计制作必须知道的10个秘诀
  6. A watermeten 《Before an Exam》
  7. 计算机的硬件简介,计算机基础之硬件简介(Day2)(示例代码)
  8. 2015年要学习和巩固的知识点
  9. mysql share mode_mysql锁:mysql lock in share mode 和 select for update
  10. PJzhang:kali linux安装网易云音乐、Visual Studio Code、skype
  11. 万兆局域网方案_局域网组建实施方案.doc
  12. 计算机限制无线网络,无线网络连接受限制怎么办 无线网络连接受限解决方法【图文】...
  13. Ubuntu常用软件安装,持续更新中。。。
  14. Python实现摇骰子效果
  15. php文本转图片自动换行功能
  16. 数仓工具—Hive集成篇之Kafka(03)
  17. 淘宝特价版正式改版!为搏杀下沉市场而来!
  18. 修改freeradius服务器配置
  19. 数字化校园平台问题-OA密码忘记如何处理
  20. 暑期实习|转行|宝洁供应链训练营面经

热门文章

  1. python多线程为什么要用队列_Python程序中的线程操作-线程队列
  2. 自动取款机如何使用无卡取款_云南铝管自动抛光机如何使用_利琦抛光机械
  3. 大数据平台容量评估_大数据平台
  4. php执行zip压缩,PHP执行zip与rar解压缩方法实现代码
  5. html php滚动代码,html中滚动条的代码是什么?如何设置html滚动条?
  6. csp怎么给线条描边_PS的四种“描边”方式你都知道吗?Photoshop小知识
  7. python3 sleep 并发_python异步编程之asyncio(百万并发)
  8. MySQL服务迁移到opt_mysql文件*.opt *.frm *.MYI *.MYD的迁移
  9. 四五月份:关键词是沟通、绘画和SQL
  10. 电商第一季函数笔记(1)