从 JDK 5.0 开始, Java 增加了对元数据(MetaData) 的支持, 也就是 Annotation(注解)。
什么是Annotation,以及注解的作用?
三个基本的 Annotation:
上面的注解,在我们平时的编程中都是可以看见的,比如定义一个Parent类和一个MyIterface的接口,用Child的子类去继承Parent并实现MyInterface.就可以看到
Parent
public class Parent {
public void m1(){
}
public void l1l1l1l1llll1ll1l1(){
}
}
MyInterface
public interface MyInterface {
void m2();
}
Annotation 其实就是代码里的特殊标记, 它用于替代配置文件,也就是说,传统方式通过配置文件告诉类如何运行,有了注解技术后,开发人员可以通过注解告诉类如何运行。在Java技术里注解的典型应用是:可以通过反射技术去得到类里面的注解,以决定怎么去运行类。
掌握注解技术的要点:
定义新的 Annotation 类型使用 @interface 关键字
public @interface 注解的名称{}
注解属性的作用:原来写在配置文件中的信息,可以通过注解的属性进行描述。
类型 属性名称() [default 默认值]
;
Annotation 的属性声明方式:String name();
属性默认值声明方式:String name() default “xxx”
;
特殊属性value:如果注解中有一个名称value的属性,那么使用注解时可以省略value=部分,如@MyAnnotation(“xxx")
,但是出现多个属性时,那就应该要写上value值,这样比较明确。
定义一个MyAnno1的注解,同时定义一个MyAnno2 ,MyAnno2 作为MyAnno1的一个属性。
public @interface MyAnno2 {
int num();
}
//所有定义的注解都是java.lang.annotation.Annotation的子类
//定义注解
public @interface MyAnno1 {
String name() default "";//注解的属性
int age() default 0;
// MyAnno2[] myann(); //属性还可以是注解,注解数组
String [] value() default "";
}
使用自定义的注解
//使用自定义的注解
public class UseMyAnno1 { // @MyAnno1(name = "abc", age = 19, myann = { @MyAnno2(num = 20), // @MyAnno2(num = 200) }) //myann是一个注解数组。
// public void method() {
//
// }
//@MyAnno1("abc") 就是给注解的value属性赋值
@MyAnno1(value={"def","ddd"},age=10)
public void method() { } }
特别注意:类型必须是基本类型、String、Class、注解类型、枚举及以上类型的一维数组。
JDK 5.0 在 java.lang.reflect 包下新增了 AnnotationElement 接口, 该接口代表程序中可以接受注释的程序元素,具有如下方法
Class、Method、Field、Constructor等都实现了该接口。
实例:编写一个自己的注解Anno1,在MyClass1中使用该注解,MyClass2继承MyClass1,这样,MyClass2也具有MyClass1上的注解,但是两者还是有差别的,看如下代码
package com.itheima.other.metaanno;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})
@Documented
@Inherited
public @interface Anno1 {
}
MyClass1
package com.itheima.other.metaanno;
/** * 这是一个测试类 * @author wzhting * */
@Anno1
public class MyClass1 {
}
MyClass2
package com.itheima.other.metaanno;
import java.lang.annotation.Annotation;
public class MyClass2 extends MyClass1 {
public static void main(String[] args) {
Class clazz = MyClass2.class;
// Anno1 a1 = (Anno1) clazz.getAnnotation(Anno1.class);
// System.out.println(a1);
Annotation ans[] = clazz.getDeclaredAnnotations();//获取直接存在的注解
for(Annotation a:ans)
System.out.println(a);
}
}
比如:判断Object这个类上面有没有MyAnn1的注解
Class clazz = Object.class;
Boolean b = clazz.isAnnotationPresent(MyAnn1.class);
2.1 AnnotatedElement元注解
只能用在注解上的注解,就是元注解。JDK中定义了如下元Annotation:
@Retention
:@Retention: 只能用于修饰一个 Annotation 定义, 用于指定该 Annotation 可以保留的域, @Rentention 包含一个 RetentionPolicy 类型的成员变量, 通过这个变量指定域。
@Target
:指示注解能用在何处 TYPE (Class,Interface), FIELD, METHOD, PARAMETER, ANNOTATION_TYPE
等声明常量。实例:自定义注解类MyTest
首先定义两个实体类,用于单元测试分别为PersonDaoImpl 和测试类PersonDaoImplTest
PersonDaoImpl
package com.itheima.dao.impl;
public class PersonDaoImpl {
public void add(){
System.out.println("保存了");
}
public void del(){
System.out.println("执行了删除");
}
}
PersonDaoImplTest
package com.itheima.dao.impl;
import org.junit.Test;
public class PersonDaoImplTest {
private PersonDaoImpl dao = new PersonDaoImpl();
//测试方法:必须是public的;没有返回值;没有参数
@MyTest(timeout=1)//===>自己编写MyTest注解
public void testAdd() throws InterruptedException{
// Thread.sleep(100);
dao.add();
}
public void testDel(){
dao.del();
}
}
自定义一个类MyTest,和JUnit的@Test类相似。
package com.itheima.dao.impl;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)//编译时,JVM保留注解
@Target(ElementType.METHOD)//使用于方法。
public @interface MyTest {
long timeout() default -1;//没有时间限制。单位是毫秒
}
反射注解的方法及属性(重点)
通过反射技术,编写MyTestRunner 类,实现MyTest注解的功能。
package com.itheima.dao.impl;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class MyTestRunner {
public static void main(String[] args) throws Exception{
test1();
}
//反射注解的属性
private static void test2() throws IllegalAccessException,
InvocationTargetException, InstantiationException {
Class clazz = PersonDaoImplTest.class;
Method ms[] = clazz.getMethods();
for(Method m:ms){
MyTest myTest = m.getAnnotation(MyTest.class);//如果当前方法上面没有@MyTest注解,返回的是null
if(myTest!=null){
//执行该方法,判断是否超时
long time = System.currentTimeMillis();
m.invoke(clazz.newInstance(), null);
long actualUseTime = System.currentTimeMillis()-time;//实际耗时
//获取注解上配置的timeout属性的值
long planUserTime = myTest.timeout();
if(planUserTime>=0){
//说明用户指定了时间
if(actualUseTime>planUserTime)
throw new RuntimeException("超时");
}
}
}
}
//反射方法上面的注解
private static void test1() throws IllegalAccessException,
InvocationTargetException, InstantiationException {
//得到测试类的字节码
Class clazz = PersonDaoImplTest.class;
//得到所有的方法
Method ms[] = clazz.getMethods();
//判断方法上面有没有叫做@MyTest的注解
for(Method m:ms){
boolean b = m.isAnnotationPresent(MyTest.class);
System.out.println(m.getName()+":"+b);
//如果有:调用该方法
if(b)
m.invoke(clazz.newInstance(), null);
}
}
/* testAdd:true testDel:false wait:false wait:false wait:false equals:false toString:false hashCode:false getClass:false notify:false notifyAll:false*/
}
以下两个了解即可。
@Documented
:用于指定被该元 Annotation 修饰的 Annotation 类将被 javadoc 工具提取成文档.@Inherited
:被它修饰的 Annotation 将具有继承性.如果某个类使用了被 @Inherited 修饰的 Annotation, 则其子类将自动具有该注解在servlet3.0以后,就可以采用注解的方式取代web.xml文件里的servlet映射了
比如如下servlet
package com.itheima.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet(value = { "/servlet/ServletDemo1", "/servlet/ServletDemo11" }, initParams = {
@WebInitParam(name = "encoding", value = "UTF-8"),
@WebInitParam(name = "XXX", value = "YYY") })
public class ServletDemo1 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.write("ServletDemo1<hr/>");
ServletConfig cfg = getServletConfig();
Enumeration e = cfg.getInitParameterNames();
while(e.hasMoreElements()){
String paramName = (String)e.nextElement();
out.write(paramName+"="+cfg.getInitParameter(paramName)+"<br/>");
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
当然,在过滤器和监听器中,也一样可以采用注解的方法取代web.xml的配置。