注解
注解是放在Java源码的类、方法、字段、参数前的一种特殊“注释”。是附加在代码中的一些元信息,用于编译和运行时进行解析和使用,起到说明、配置的功能。注解不会影响代码的实际逻辑,仅仅起到辅助性的作用。包含在java.lang.annotation包中。
注释不会打包到class文件中,注解则可以被编译器打包进入class文件,因此,注解是一种用作标注的“元数据”。
注解大致分为三类
- 第一类是由编译器使用的注解【这类注解不会被编译进入
.class
文件,它们在编译后就被编译器扔掉了。】
@Override
:让编译器检查该方法是否正确地实现了覆写;
@SuppressWarnings
:告诉编译器忽略此处代码产生的警告
第二类是由工具处理.class
文件使用的注解
有些工具会在加载class的时候,对class做动态修改,实现一些特殊的功能。这类注解会被编译进入.class
文件,但加载结束后并不会存在于内存中。这类注解只被一些底层库使用,一般我们不必自己处理。
第三类是在程序运行期能够读取的注解【使用@Retention(RetentionPolicy.RUNTIME) 注解修饰】
它们在加载后一直存在于JVM中,这也是最常用的注解。例如,一个配置了@PostConstruct
的方法会在调用构造方法后自动被调用(这是Java代码读取该注解实现的功能,JVM并不会识别该注解)。
注解的使用
Java语言使用@interface
语法来定义注解(Annotation
),它的格式如下.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| package route; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME) public @interface pathMap { String uri() default ""; }
|
注解的参数类似无参数方法,可以用default
设定一个默认值(强烈推荐).
元注解
有一些注解可以修饰其他注解,这些注解就称为元注解(meta annotation)。Java标准库已经定义了一些元注解,我们只需要使用元注解,通常不需要自己去编写元注解。
最常用的元注解是@Target
。使用@Target
可以定义Annotation
能够被应用于源码的哪些位置:
另一个重要的元注解@Retention
定义了Annotation
的生命周期:
仅编译期:RetentionPolicy.SOURCE
;
仅class文件:RetentionPolicy.CLASS
;
运行期:RetentionPolicy.RUNTIME
。
如果@Retention
不存在,则该Annotation
默认为CLASS
。因为通常我们自定义的Annotation
都是RUNTIME
,所以,务必要加上@Retention(RetentionPolicy.RUNTIME)
这个元注解.
处理注解
Java的注解本身对代码逻辑没有任何影响。根据@Retention
的配置:
SOURCE
类型的注解在编译期就被丢掉了;
CLASS
类型的注解仅保存在class文件中,它们不会被加载进JVM;
RUNTIME
类型的注解会被加载进JVM,并且在运行期可以被程序读取。
如何使用注解完全由工具决定。SOURCE
类型的注解主要由编译器使用,因此我们一般只使用,不编写。CLASS
类型的注解主要由底层工具库使用,涉及到class的加载,一般我们很少用到。只有RUNTIME
类型的注解不但要使用,还经常需要编写。
因为注解定义后也是一种class
,所有的注解都继承自java.lang.annotation.Annotation
,因此,读取注解,需要使用反射API。
Java提供的使用反射API读取Annotation
的方法包括。
判断某个注解是否存在于Class
、Field
、Method
或Constructor
:
Class.isAnnotationPresent(Class)
Field.isAnnotationPresent(Class)
Method.isAnnotationPresent(Class)
Constructor.isAnnotationPresent(Class)
使用反射API读取Annotation:
Class.getAnnotation(Class)
Field.getAnnotation(Class)
Method.getAnnotation(Class)
Constructor.getAnnotation(Class)
简单的路由设计
第一步编写路由注解:上边的pathMap,用来在controller里使用。
第二步编写使用注解的controller:
1 2 3 4 5 6 7 8 9 10 11
| package route; //使用注解定义路径 @pathMap(uri = "/test") public class Controller { @pathMap(uri = "hello") public void hello() { System.out.println("hello"); } }
|
第三步由于需要使用反射,或者自动加载某些controller类,所以需要扩展一个自己的自动加载类,用来加载controller类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
| package route;
import java.io.FileInputStream; import java.util.ArrayList;
public class loadClass extends ClassLoader {
private ArrayList<String> filePathArr;
public loadClass() { this.filePathArr = new ArrayList<String>(); }
public loadClass addFilePath(String filePath) { this.filePathArr.add(filePath); return this; }
public Class<?> findClass(String fileName) throws ClassNotFoundException { try { byte[] data = this.loadByte(fileName); return defineClass(fileName, data, 0, data.length); } catch (Exception e) { e.printStackTrace(); throw new ClassNotFoundException(); }
}
private byte[] loadByte(String name) throws Exception { name = name.replaceAll("\\.", "/"); byte[] data; FileInputStream fis = new FileInputStream(name + ".class"); int len = fis.available(); data = new byte[len]; fis.read(data); fis.close(); return data;
} }
|
第四步:编写路由类,加载和解析使用注解的controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
| //处理注解的route类 package route;
import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.HashMap; public class route {
private HashMap<String,Object> controllers = new HashMap<>();
private HashMap<String,Action> action = new HashMap<>();
protected class Action { private Object object;
private Method method;
public Action(Object object,Method method) { this.method = method; this.object = object; }
public Object call() { try { method.invoke(object); }catch (Exception e){
} return null; } }
public void add(String classPath) throws Exception { Class<?> cls = (new loadClass()).addFilePath("./").loadClass(classPath); // 反射class中所有方法 Method[] methods = cls.getDeclaredMethods(); Annotation[] annotations; String rootPath = ""; //获取类的注解 annotations = cls.getAnnotations(); for (Annotation annotation : annotations) { // 如果注解类型是RouteMapping, 解析其URI if (annotation.annotationType() == pathMap.class) { pathMap anno = (pathMap) annotation; // 路由uri rootPath = anno.uri(); } } String startWith,endWith; if(!rootPath.startsWith("/")) { rootPath = "/" + rootPath; } if(!rootPath.endsWith("/")) { rootPath = rootPath + "/"; } //遍历方法的所有注解 for (Method method : methods) { // 反射方法所有注解 annotations = method.getAnnotations(); for (Annotation annotation : annotations) { // 如果注解类型是RouteMapping, 解析其URI if (annotation.annotationType() == pathMap.class) { pathMap anno = (pathMap)annotation; // 路由uri String uri = anno.uri(); // 保存Bean单例 if (!controllers.containsKey(cls.getName())) { controllers.put(cls.getName(), cls.newInstance()); } // 保存uri -> (obj,method) System.out.println(rootPath + uri); action.put(rootPath + uri, new Action(controllers.get(cls.getName()), method)); } } } }
public void test(String urlPath) { Action ac = action.get(urlPath); if (ac != null) { ac.call(); } else { System.out.println(urlPath + " is not found"); } }
public static void main(String[] argv) { try { route r = new route(); r.add("route.Controller"); r.test("/test/hello"); }catch (Exception e) { System.out.println(e.getMessage()); } } }
|