首先一句话结论:注解就是一种经过在类、方法、或者属性等上使用相似@xxx的方式进行“打标签”,而后能够经过反射机制对标签的内容进行解析并进行相应处理的手段。java
注解是java中的一个重要知识点,从java5后开始引入,尤为在spring框架中大量使用。比较经常使用的有@controller、@service等等各类,本文将从注解的实现原理出发,经过一些demo代码的实现,进行分析。web
直接上代码,看看spring中@Service注解的定义就知道了:spring
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {
String value() default "";
}
复制代码
能够看到注解的定义和接口定义很像,可是多了@字符,注解的定义上有如下约定:数组
所谓元注解就是java中默认实现的专门对注解进行注解的注解。元注解的总数就5个,下面咱们以上面讲到的@Service注解为例子各个击破:bash
此注解用于表示当前注解的使用范围,@Target({ElementType.TYPE})就表明着@Service这个注解是专门用来注解到类、接口、或者枚举类型上面的,当在方法上面加这个注解时,就会报错。能够看到注解位置是一个枚举类型,完整定义以下mvc
public enum ElementType {
/** 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
}
复制代码
此注解用于表示当前注解的生命周期,说人话就是这个注解做用会保留到何时,如@Retention(RetentionPolicy.RUNTIME)就表示在程序运行期间依然有效,此时就能够经过反射拿到注解的信息,完整的枚举定义以下app
public enum RetentionPolicy {
/**
* Annotations are to be discarded by the compiler.
*/
SOURCE,
/**
* Annotations are to be recorded in the class file by the compiler
* but need not be retained by the VM at run time. This is the default
* behavior.
*/
CLASS,
/**
* Annotations are to be recorded in the class file by the compiler and
* retained by the VM at run time, so they may be read reflectively.
*
* @see java.lang.reflect.AnnotatedElement
*/
RUNTIME
}
复制代码
当被此注解所注解时,使用javadoc工具生成文档就会带有注解信息。框架
此注解与继承有关,当A注解添加此注解后,将A注解添加到某类上,此类的子类就会继承A注解。ide
@Inherited
public @interface A{
}
@A
public class Parent{}
public class Son extends Parent{}//Son类继承了父类的A注解
复制代码
此注解顾名思义是拥有能够重复注解的能力。想象这样一个场景,咱们须要定时执行某个任务,须要在每周一和周三执行,而且这个时间是能够灵活调整的,此时这个元注解就能派上用场:工具
@Repeatable(Schedules.class)
public @interface Schedule {
String date();
}
public @interface Schedules {
Schedule[] value();
}
@Schedule(date = "周一")
@Schedule(date = "周三")
public class Executor {
}
复制代码
注意看到此元注解后面括号里内容,在这指定的类叫作容器注解,意思是保存这多个注解的容器,故咱们建立一个@Schedules注解做为@Schedule的容器注解,容器注解必须含有一个名字为value,返回类型为需放入此容器的注解数组的属性。
下面咱们以web项目中很是常见的鉴权场景为例本身实现一个自定义注解。 首先咱们定义系统的使用人员身份,有超级管理员、管理员、访客三种身份。
public enum IdentityEnums {
SUPER_ADMIN,
ADMIN,
VISIVOR
}
复制代码
接下来咱们定义一个权限注解:
@Target({ElementType.TYPE,ElementType.METHOD})
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Authorization {
IdentityEnums[] value();
}
复制代码
而后使用拦截器的方式,对全部页面进行统一的鉴权管理,此处只展现一些关键代码:
public class AuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (handler instanceof HandlerMethod)
{
IdentityEnums user = getIdentityFromRequset(request);//这里从request里获取帐号信息并判断身份,本身实现
Authorization auth =((HandlerMethod) handler).getMethodAnnotation(Authorization.class);//获取方法上面的注解
if (!Arrays.asList(auth.value()).contains(user)){
return false;
}
}
return true;
}
}
复制代码
最后在spring配置文件中对拦截器进行配置开启拦截器:
<!-- 拦截器 -->
<mvc:interceptors>
<mvc:interceptor>
<!-- 匹配的是url路径, 若是不配置或/**,将拦截全部的Controller -->
<mvc:mapping path="/**" />
<!-- 拦截器类 -->
<bean
class="com.xx.xx.AuthInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
复制代码
在实际使用中,咱们将在方法上面添加此自定义注解,当身份权限符合时,才能对页面进行访问,使用方式以下:
@ResponseBody
@RequestMapping(value = "/management")
@Authorization({IdentityEnums.ADMIN,IdentityEnums.SUPER_ADMIN})
public String management(HttpServletRequest request, HttpServletResponse response)
{
log.info("has permission!");
}
复制代码