在开发Java程序,尤其是Java EE应用的时候,总是免不了与各种配置文件打交道。以Java EE中典型的S(pring)S(truts)H(ibernate)架构来说,Spring、Struts和Hibernate这三个框架都有自己的XML格式的配置文件。这些配置文件需要与Java源代码保存同步,否则的话就可能出现错误。而且这些错误有可能到了运行时刻才被发现。把同一份信息保存在两个地方,总是个坏的主意。理想的情况是在一个地方维护这些信息就好了。其它部分所需的信息则通过自动的方式来生成。JDK 5中引入了源代码中的注解(annotation)这一机制。注解使得Java源代码中不但可以包含功能性的实现代码,还可以添加元数据。注解的功能类似于代码中的注释,所不同的是注解不是提供代码功能的说明,而是实现程序功能的重要组成部分。Java注解已经在很多框架中得到了广泛的使用,用来简化程序中的配置。
从官网下载一个范例来说明注解的使用(如下为注解的最基本的使用方式)
package com.wang.annotations;
import java.util.function.Supplier;
import javax.xml.bind.ValidationException;
public enum Validator {
INTEGER_NUMBER {
@Override
void validate(Supplier> string) throws ValidationException {
try {
Integer.parseInt((String) string.get());
}
catch (NumberFormatException ex) {
throw new ValidationException("Error while validating "
+ string.get());
}
}
},
POSITIVE_NUMBER {
@Override
void validate(Supplier> string) throws ValidationException {
try {
if (Double.compare(0.0, Double.parseDouble(
(String) string.get())) > 0) {
throw new Exception();
}
}
catch (Exception ex) {
throw new ValidationException("Error while validating "
+ string.get());
}
}
};
;
abstract void validate(Supplier> string) throws ValidationException;
}
如下声明了一个注解
package com.wang.annotations;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(ValidateContainer.class)
public @interface Validate {
Validator value();
String description() default "";
}
@Retention(RetentionPolicy.RUNTIME)
@interface ValidateContainer {
Validate[] value();
}
如下为注解的使用方式
package com.wang.annotations;
import java.util.function.Supplier;
@Validate(value = Validator.INTEGER_NUMBER, description = "It's not an Integer")
@Validate(value = Validator.POSITIVE_NUMBER, description = "It's not a positive number")
public class PositiveIntegerSupplier implements Supplier {
@Override
public String get() {
// TODO Auto-generated method stub
return "200005";
}
}
如下为注解处理器的使用
package com.wang.annotations;
import java.util.function.Supplier;
import javax.xml.bind.ValidationException;
public class SupplierValidator {
public static boolean validate(Supplier> supplier) {
for (Validate annotation : supplier.getClass().getAnnotationsByType(Validate.class)) {
try {
annotation.value().validate(supplier);
}
catch (ValidationException e) {
System.out.println(annotation.description());
e.printStackTrace();
return false;
}
}
return true;
}
}
在很多时候,我们希望注解的整个解析的过程以某种机制plugin到代码当中,如下为另外一种调用注解的方式,当然两者的思路是一致的,不过后者不需要显式的生成对象并调用注解过程,仅仅需要实现相应的关于注解的接口即可
如下为注解处理器的接口实现
/*
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Oracle nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* This source code is provided to illustrate the usage of a given feature
* or technique and has been deliberately simplified. Additional steps
* required for a production-quality application, such as security checks,
* input validation and proper error handling, might not be present in
* this sample code.
*/
package checker;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import javax.xml.bind.JAXBContext;
import java.io.File;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import javax.xml.bind.JAXBException;
/**
* Reads the device configuration from the XML file specified by -Adevice=device.xml.
* For each class in a project, checks required modules. If the device doesn't have
* the required module, then a compilation error will be shown.
*/
@SupportedAnnotationTypes("checker.RequireContainer")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class PluginChecker extends javax.annotation.processing.AbstractProcessor {
/**
* Name of the option to get the path to the xml with device configuration.
*/
public static final String DEVICE_OPTION = "device";
private Device device;
/**
* Only the device option is supported.
*
* {@inheritDoc}
*/
@Override
public Set getSupportedOptions() {
return new HashSet<>(Arrays.asList(DEVICE_OPTION));
}
/**
* Initializes the processor by loading the device configuration.
*
* {@inheritDoc}
*/
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
try {
String deviceOption = processingEnv.getOptions().get(DEVICE_OPTION);
device = (Device) JAXBContext.newInstance(Device.class)
.createUnmarshaller().unmarshal(new File(deviceOption));
} catch (JAXBException e) {
throw new RuntimeException(
"Please specify device by -Adevice=device.xml\n"
+ e.toString(), e);
}
}
/**
* Processes @Require annotations and checks that Device meets requirements.
*
* {@inheritDoc}
*/
@Override
public boolean process(Set extends TypeElement> annotations,
RoundEnvironment roundEnv) {
for (Element el : roundEnv.getElementsAnnotatedWith(RequireContainer.class)) {
for (Require req : el.getAnnotationsByType(Require.class)) {
//for every Require annotation checks if device has module of required version.
Integer version = device.getSupportedModules().get(req.value());
if (version == null
|| version < req.minVersion()
|| version > req.maxVersion()) {
//if module is optional then show only warning not error
if (req.optional()) {
processingEnv.getMessager()
.printMessage(Diagnostic.Kind.WARNING,
"Plugin [" + el + "] requires " + req
+ "\n but device " + (version == null
? "doesn't have such module."
+ " This module is optional."
+ " So plugin will work but miss"
+ " some functionality"
: "has " + version
+ " version of that module"));
} else {
processingEnv.getMessager()
.printMessage(Diagnostic.Kind.ERROR,
"Plugin [" + el + "] requires " + req
+ "\n but device "
+ (version == null
? "doesn't have such module"
: "has " + version
+ " version of that module"));
}
}
}
return true;
}
return false;
}
}
如下为注解声明部分
/*
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Oracle nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* This source code is provided to illustrate the usage of a given feature
* or technique and has been deliberately simplified. Additional steps
* required for a production-quality application, such as security checks,
* input validation and proper error handling, might not be present in
* this sample code.
*/
package checker;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Indicates that a plug-in depends on a module.
*/
@Retention(RetentionPolicy.CLASS)
@Repeatable(RequireContainer.class)
public @interface Require {
/**
* Returns the required module.
*
* @return required module.
*/
Module value();
/**
* Returns the minimum supported version of a module.
*
* @return minimum supported version of a module.
*/
int minVersion() default 1;
/**
* Returns the maximum supported version of a module.
*
* @return maximum supported version of a module.
*/
int maxVersion() default Integer.MAX_VALUE;
/**
* Returns true if a module is optional. A module is optional if a system
* works without that module but is missing some functionality. Returns false if a system
* won't work without the specified module.
*
* @return true if module is optional. False otherwise.
*/
boolean optional() default false;
}
package checker;
/*
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Oracle nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* This source code is provided to illustrate the usage of a given feature
* or technique and has been deliberately simplified. Additional steps
* required for a production-quality application, such as security checks,
* input validation and proper error handling, might not be present in
* this sample code.
*/
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* A container for the repeatable @Require annotation.
*/
@Retention(RetentionPolicy.CLASS)
public @interface RequireContainer {
Require[] value();
}
详细的范例请到官网中下载最新的demos和sample
注解是一个使用起来很简单的一个机制,但是在代码当中作用很大,能够简化我们的配置等,spring等开源框架等广泛使用到了注解来简化开发。
下面对一些注解的基本知识是进行一些了解
元注解:
元注解的作用就是负责注解其他注解。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声明
注解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的枚举类型值。
Column注解的的RetentionPolicy的属性值是RUTIME,这样注解处理器可以通过反射,获取到该注解的属性值,从而去做一些运行时的逻辑处理
@Documented:
@Documented用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。Documented是一个标记注解,没有成员。
@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类型被发现,或者到达类继承结构的顶层
上述关于注解的基础知识摘自http://www.cnblogs.com/peida/archive/2013/04/24/3036689.html
java8新增了两个元注解
Native应该是一个用于源代码中基于常量的注解
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package java.lang.annotation;
/**
* Indicates that a field defining a constant value may be referenced
* from native code.
*
* The annotation may be used as a hint by tools that generate native
* header files to determine whether a header file is required, and
* if so, what declarations it should contain.
*
* @since 1.8
*/
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.SOURCE)
public @interface Native {
}
@Repeatable
表明某个注解是可以重复使用的,具体使用方式,请见上文提到的官网给出的范例
/*
* Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package java.lang.annotation;
/**
* The annotation type {@code java.lang.annotation.Repeatable} is
* used to indicate that the annotation type whose declaration it
* (meta-)annotates is repeatable. The value of
* {@code @Repeatable} indicates the containing annotation
* type for the repeatable annotation type.
*
* @since 1.8
* @jls 9.6 Annotation Types
* @jls 9.7 Annotations
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Repeatable {
/**
* Indicates the containing annotation type for the
* repeatable annotation type.
* @return the containing annotation type
*/
Class extends Annotation> value();
}