欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java注解及自定义注解的使用

程序员文章站 2022-05-23 18:14:24
...

1、我们为什么要用注解?

与之前的开发相比,我们发现基于xml的配置过于复杂,难于维护,与代码的耦合度太高,例如:在一个Controller中引用了n多个service,就得在controller中引n个service实例,这样总归是太麻烦了些。本人在平时的开发中经常使用到自定义注解,结合具体的需求来看你会发现通过自定义注解来实现的功能点普遍都比较简明优雅。

public class PersonImpl implements Person {
   private PersonIDao personIDao;
   public void setUserDao(){
       this.personIDao=personIDao;
   }
   ……
}

<beans>
    <bean id="personImpl" class="×××.PersonImpl">
        <property name="personDao" ref="personDao"/>
    </bean>

    <bean id="personDao" class="×××.PersonDao">
        <property name="sessionFactory" ref="×××"/>
    </bean>
</beans>
引入注解后,只需:
public class PersonImpl implements Person {
    @Autowired
    private PersonDao personDao;
    //TODO ……
}

2、注解是什么?

注解也称为元数据,提供了一种原程序中的元素关联任何信息和任何元数据的途径和方法。注解的定义比较简单,只需使用@interface关键字申明,其余与常规类的使用无差异,注意点:
2.1->成员类型是受限制的,合法类型包括 原始类型、String、Class、Enum、Annotation
2.2->若只有一个成员,则成员名必须取名为value()
2.3->注解类可以没有成员,成为标识注解
2.4->注解的成员 "无参无异常"

3、java 5+后内置三种标准注解

@Override(方法重写)
@SuppressWarnings(忽略指定的警告)
@Deprecated(方法或类不再推荐使用)
public interface Person {
    public String getInfo();
}

public class PersonImpl implements Person {
   @Override
   @Deprecated
    public String getInfo() {   //当这个方法过时,但是项目中有还存在调用方使用此方法,故加上@Deprecated,表示此方法已过时,新接口不再使用此方法
        return null;
    }
}

public class Test {
    @SuppressWarnings("deprecation")  //getInfo()方法已过时却还在引用,故加上@SuppressWarnings忽略方法过时警告
    public void test() {
        PersonImpl person = new PersonImpl();
        person.getInfo();
    }
}

4、元注解

java内置了4种元注解,如下(图片引自java编程思想)
Java注解及自定义注解的使用

5、自定义注解demo

@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface Person {
    int age() default 20;
    String name() default "simons";
}

@Person(age = 25,name = "mike")
public class PersonInfo {
    public void getInfo(){
        //TODO ……
    }
}

6、注解是如何工作的?如何解析注解?

注解通过反射解析后便可以获取到注解中的属性值,然后进行相应的处理。自定义注解的解析通过反射解析的,框架里的注解也是通过反射解析的,像常用的@RequestMapping注解,反射解析注解的操作是包装在源码里。

@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface Person {
    int age() default 20;
    String name() default "simonsfan";
}  

@Person(age = 25,name = "mike")
public class PersonInfo {
    @Person(age = 30,name = "jack")
    public void getInfo(){
        //TODO ……
    }
}

public class AnnotationHandler {
    public static void main(String[] args) throws ClassNotFoundException {
        Class<?> clazz = Class.forName("com.simonsfan.PersonInfo"); //获取类类型
        boolean isAnnotation = clazz.isAnnotationPresent(Person.class);//当前类级别上是否使用了@Person注解
        if (isAnnotation) {
            Person annotation = (Person) clazz.getAnnotation(Person.class);  //获取类级别注解实例
            System.out.println(annotation.name() + ":" + annotation.age());
        }

        Method[] methods = clazz.getMethods();
        for (Method ms : methods) {
            boolean ap = ms.isAnnotationPresent(Person.class);      
            if (ap) {
                Person annotation = ms.getAnnotation(Person.class);               //获取方法级别注解实例
                System.out.println(annotation.name() + ":" + annotation.age());
            }
        }
        //另一种方法
        for (Method md : methods) {
            Annotation[] annos = md.getAnnotations();
            for (Annotation a : annos) {
                if (a instanceof Person) {
                    Person ps = (Person) a;
                    System.out.println(ps.name() + ":" + ps.age());
                }
            }
        }
    }

}

//***output***
mike:25
jack:30
jack:30

7、自定义注解实战示例

功能: 自定义注解+拦截器 实现 API接口限流(比如10秒内限制访问4次)
@Inherited
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AccessLimit {

    int limit();  //次数

    int seconds();  //时间(s)
}
@Component
public class AccessLimitInterceptor extends HandlerInterceptorAdapter {

    @Autowired
    private RedisService redisService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            /*Method method = handlerMethod.getMethod();
            if(!method.isAnnotationPresent(AccessLimit.class)){
                return true;
            }*/
            //获取之前可用上面方法判断是否使用了AccessLimit注解,我这里暂时注释掉了
            AccessLimit accessLimit = handlerMethod.getMethodAnnotation(AccessLimit.class);  
            if (accessLimit == null) {
                return true;
            }
            int limit = accessLimit.limit();      //获取次数
            int seconds = accessLimit.seconds();  //获取时间
            AccessKey accessKey = AccessKey.withExpire(seconds);  
            String requestURI = request.getRequestURI();
            Integer maxAccess = redisService.get(accessKey, ":"+requestURI, Integer.class);
            if (maxAccess == null) {  //时间段外或者redis数据已经过期则放在redis中(set方法里面包含了设置过期时间=限制时间操作,这里是10s)
                redisService.set(accessKey, ":"+requestURI, 1);   
            } else if (maxAccess < limit) {  //若访问次数小于limit,则访问一次就增加1
                redisService.incr(accessKey, ":"+requestURI);  
            } else {
                outPut(response, "访问太频繁!");
                return false;
            }
        }
        return true;
    }

    private void outPut(HttpServletResponse response, String msg) throws IOException {
        response.setContentType("application/json;charset=UTF-8");
        OutputStream out = null;
        try {
            out = response.getOutputStream();
            out.write(msg.getBytes("UTF-8"));
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            out.flush();
            out.close();
        }
    }

}
@Controller
@RequestMapping("/aop")
public class AopController {
    @AccessLimit(limit = 4,seconds = 10)
    @ResponseBody
    @RequestMapping("/intercept")
    public String intercept(){
        return "hello world!";
    }
}
把拦截器加入到spring的配置文件中:
<mvc:interceptors>
   <mvc:interceptor>
      <mvc:mapping path="/**"/>
      <bean class="com.simonsfan.AccessLimitInterceptor"/>
   </mvc:interceptor>
</mv:interceptors>

效果如图,10秒内访问4次以上,出现自定义提示拦截请求,缓存时间10s过期后,正常访问

Java注解及自定义注解的使用