Java注解(Annotation):请不要小看我!
java注解是一系列元数据,它提供数据用来解释程序代码,但是注解并非是所解释的代码本身的一部分。注解对于代码的运行效果没有直接影响。
网络上对注解的解释过于严肃、刻板,这并不是我喜欢的风格。尽管这样的解释听起来非常的专业。
为了缓解大家对“注解”的陌生感,我来说点有意思的。其实我对“注解”这个词的第一印象并不是java的注解,而是朱熹的名作《四书章句集注》。为什么我会有这么大的脑洞呢?因为当我试着去翻译annotation
这个单词的时候,得到的结果是“注释”而不是“注解”。《四书章句集注》正是朱熹对《大学》、《中庸》、《论语》、《孟子》四书做出的重要的注释。要知道,该书可是明清以后科举考试的题库和标准答案!
注解(annotation
)是在 java se 5.0 版本中开始引入的概念,同class
和interface
一样,也属于一种类型。很多开发人员认为注解的地位不高,但其实不是这样的。像@transactional
、@service
、@restcontroller
、@requestmapping
、@crossorigin
等等这些注解的使用频率越来越高。
01、为什么要使用注解呢?
为什么要使用注解呢?让我们从另外一个问题说起。
“跨域”这两个字就像一块狗皮膏药黏在每一个前端开发者的身上;我也不例外,虽然我并不是一个纯粹的前端开发者。
跨域问题的出现,源于浏览器的同源策略——限制一个源加载的脚本去访问另外一个源的资源,可有效地隔离潜在的恶意文件,是一种重要的安全机制。
跨域问题的解决方案也有很多,比如说:
1)jsonp
2)nginx代理
3)"跨域资源共享"(cross-origin resource sharing),简称cors
,可以说是处理跨域问题的标准做法。
记得第一次遇到跨域问题的时候,我特意向一个同学请教了解决方案,他告诉我的答案如下。
第一步,在web.xml添加filter。
<filter>
<filter-name>contextfilter</filter-name>
<filter-class>com.cmower.filter.webcontextfilter</filter-class>
</filter>
<filter-mapping>
<filter-name>contextfilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
第二步,实现webcontextfilter类。
public class webcontextfilter implements filter {
@override
public void destroy() {
}
@override
public void dofilter(servletrequest request, servletresponse response, filterchain chain)
throws ioexception, servletexception {
httpservletresponse httpservletresponse = (httpservletresponse) response;
httpservletresponse.setheader("access-control-allow-origin", "*");
httpservletresponse.setheader("access-control-allow-headers", "accept,content-type");
httpservletresponse.setheader("access-control-allow-methods", "options,get,post,delete,put");
chain.dofilter(request, httpservletresponse);
}
@override
public void init(filterconfig arg0) throws servletexception {
}
}
看到这样的解决方案,我真的是蛮崩溃的。不就一个跨域问题嘛,用得着这么多代码吗?
我对这样的解决方案非常的不满意。于是下定决心要好好的研究一番,大概花了半天的时间吧,我终于搞清楚了“跨域”问题,以及它的标准解决方案cors
。并且找到了一个极为简洁的解决方案——@crossorigin
,只要在controller类上加上这个注解,就可以轻松地解决跨域问题。
代码如下。
@restcontroller
@requestmapping("course")
@crossorigin
public class coursecontroller {
}
如果没有找到@crossorigin
这个注解,我真的就要按照同学提供的方案去解决跨域的问题了。但那样做就好像,我们卖掉家里的小汽车,然后出行的时候驾一辆马车一样。
这也正是我想告诉你的,为什么要使用注解的原因:它让我们的代码看起来更简洁,更有时代的进步感。
02、该如何定义注解呢?
注解需要通过@interface
关键字(形式和接口非常的相似,只是前面多了一个@
)进行定义。我们可以打开@crossorigin
的源码来看一下。
@target({ elementtype.method, elementtype.type })
@retention(retentionpolicy.runtime)
@documented
public @interface crossorigin {
/**
* list of allowed origins, e.g. {@code "http://domain1.com"}.
* <p>these values are placed in the {@code access-control-allow-origin}
* header of both the pre-flight response and the actual response.
* {@code "*"} means that all origins are allowed.
* <p>if undefined, all origins are allowed.
* @see #value
*/
@aliasfor("value")
string[] origins() default {};
/**
* list of request headers that can be used during the actual request.
* <p>this property controls the value of the pre-flight response's
* {@code access-control-allow-headers} header.
* {@code "*"} means that all headers requested by the client are allowed.
* <p>if undefined, all requested headers are allowed.
*/
string[] allowedheaders() default {};
/**
* list of supported http request methods, e.g.
* {@code "{requestmethod.get, requestmethod.post}"}.
* <p>methods specified here override those specified via {@code requestmapping}.
* <p>if undefined, methods defined by {@link requestmapping} annotation
* are used.
*/
requestmethod[] methods() default {};
}
从上面的代码可以看得出来,“注解”真的很“注解”,除了注释多和“元注解”多之外,真没有别的了。
“元注解”?什么是“元注解”呢?
“元注解”是用来注解(动词)注解(名词)的注解(名词)。请感受汉语的博大精深。@target
、@retention
和@documented
就是所谓的元注解。
1)@target
target是目标的意思,@target
指定了注解运用的场景。都有哪些场景值呢?
-
elementtype.annotation_type
:可以给注解进行注解 -
elementtype.constructor
:可以给构造方法进行注解 -
elementtype.field
:可以给字段进行注解 -
elementtype.local_variable
:可以给局部变量进行注解 -
elementtype.method
:可以给方法进行注解 -
elementtype.package
:可以给包进行注解 -
elementtype.parameter
:可以给方法内的参数进行注解 -
elementtype.type
:可以给类型进行注解,比如类、接口和枚举
2)@retention
retention这个单词的意思为保留期。也就是说,当@retention
应用到一个注解上的时候,它解释说明了这个注解的存活时间。来看它的取值范围。
-
retentionpolicy.source
:注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。 -
retentionpolicy.class
:注解只被保留到编译进行的时候,并不会被加载到 jvm 中。 -
retentionpolicy.runtime
:注解可以保留到程序运行的时候,它会被加载进入到 jvm 中,所以在程序运行时可以获取到它们。
3)@documented
documented
就比较容易理解,它和文档有关。作用就是能够将注解中的元素包含到 javadoc 中。
当我们了解了元注解的概念后,再回头看一下@crossorigin
的源码,是不是感觉清晰多了呢?
如果能够细致地读一读源码中的注释,你就会看到webcontextfilter类中出现的关键字,诸如access-control-allow-origin
、access-control-allow-headers
、access-control-allow-methods
。也就是说,当我们通过@crossorigin
对controller类注解后,springmvc就能够在运行时对这个类自动加上解决跨域问题的过滤器。
03、注解可以反射吗?
注解是可以通过反射获取的。
1)可以通过 class 对象的 isannotationpresent()
方法判断该类是否应用了某个指定的注解。
2)通过 getannotation()
方法来获取注解对象。
3)当获取到注解对象后,就可以获取使用注解时定义的属性值。
示例如下:
@crossorigin(origins = "http://qingmiaokeji.com", allowedheaders = "accept,content-type", methods = { requestmethod.get, requestmethod.post })
public class testcontroller {
public static void main(string[] args) {
class c = testcontroller.class;
if (c.isannotationpresent(crossorigin.class)) {
crossorigin crossorigin = (crossorigin) c.getannotation(crossorigin.class);
system.out.println(arrays.aslist(crossorigin.allowedheaders()));
system.out.println(arrays.aslist(crossorigin.methods()));
system.out.println(arrays.aslist(crossorigin.origins()));
}
}
}
// 输出:[accept,content-type]
// [get, post]
// [http://qingmiaokeji.com]
04、注解经常用在哪里呢?
1)@transactional
:spring 为事务管理提供的功能支持。
2)@ service
:spring在进行包扫描的时候,会自动将这个类注册到spring容器中。
3)@restcontroller
:是@responsebody
和@controller
的组合注解。
也就是说,下面这段代码与下下面的代码等同。
@restcontroller
public class hellocontroller {
@requestmapping(value="hello")
public string sayhello(){
return "hello";
}
}
@controller
@responsebody
public class hellocontroller {
@requestmapping(value="hello")
public string sayhello(){
return "hello";
}
}
4)@requestmapping
:spring web 应用程序中最常用到的注解之一,将 http 请求映射到 mvc 和 rest 控制器的处理方法上。
5)@select
:mybatis提供的查询语句注解。示例如下:
@select("select * from city")
list<city> getcitys();
6)还有很多很多,就不再一一列举了。
最后
我想说的是,注解有许多用处,主要有:
- 提供信息给编译器: 编译器可以利用注解来探测错误和警告信息。
- 编译阶段时的处理: 软件工具可以利用注解信息来生成代码、html文档。
- 运行时的处理: 某些注解可以在程序运行的时候接受代码的提取。
别忘了: