前言
随着Web开发的发展,越来越多的企业开始使用SpringMVC框架来构建自己的Web应用程序。在开发过程中,为了提高代码的可读性和可维护性,我们经常需要自定义注解。本文将介绍如何编写一个自定义注解,并通过一个简单的例子来演示如何使用它。
一、自定义注解
Java注解是附加在代码中的一些元信息,用于一些工具在编译、运行时进行解析和使用,起到说明、配置的功能。 注解相关类都包含在java.lang.annotation包中。
2. 注解的用处
- 配置和元数据:注解可以用于配置和描述类、方法、字段等元素的特性和行为。例如,我们可以使用注解来指定数据库表的名称、字段的长度、方法的访问权限等。
- 编译时检查:注解可以用于在编译时对代码进行静态检查,以确保代码的正确性和合规性。例如,我们可以使用注解来标记过时的方法或类,编译器会在编译时给出警告。
- 运行时处理:注解可以在运行时通过反射机制获取到,并根据注解的定义来执行相应的逻辑。例如,我们可以使用注解来实现自定义的AOP(面向切面编程)逻辑,或者在某些条件下动态地修改方法的行为。
- 文档生成:注解可以用于生成文档,以便开发者更好地理解代码的含义和使用方式。例如,我们可以使用注解来标记API的使用示例、参数的说明等。
3. 为什么要用注解
- 减少样板代码:通过使用注解,我们可以将一些重复性的代码抽象为注解,从而减少冗余的代码量。
- 提高代码可读性:注解可以用于描述代码的特性和行为,使代码更加易于理解和阅读。
- 增强代码的可维护性:通过使用注解,我们可以将一些与业务逻辑无关的代码与业务逻辑分离,使代码更加模块化和可维护。
- 提高开发效率:使用注解可以简化一些繁琐的操作,提高开发效率。
4.自定义注解的应用场景
应用场景十分广泛,举例说明几个:
- 在SpringMVC中,我们可以使用自定义注解来标记Controller中的方法,以实现请求的映射和处理。
- 在MyBatis中,我们可以使用自定义注解来标记Mapper接口中的方法,以实现SQL语句的映射和执行。
- 在JUnit中,我们可以使用自定义注解来标记测试方法,以实现自动化测试的执行。
5. 注解的分类
- JDK元注解:元注解是用于注解其他注解的注解,它可以用于指定注解的作用范围、生命周期等。常见的元注解有
@Target
、@Retention
、@Documented
等。 - JDK基本注解:标准注解是Java提供的一些常用注解,例如
@Override
、@Deprecated
、@SuppressWarnings
等。 - 自定义注解:自定义注解是开发者根据自己的需求定义的注解,用于实现特定的功能或逻辑。
- 框架注解:框架注解是一些常用框架提供的注解,用于实现框架的特定功能。例如,Spring框架提供的
@Autowired
、@RequestMapping
等注解。
6.如何定义并使用自定义注解
自定义注解的定义非常简单,只需要使用@interface
关键字即可。
1. package com.ctb.utils; 2. import java.lang.annotation.ElementType; 3. import java.lang.annotation.Retention; 4. import java.lang.annotation.RetentionPolicy; 5. import java.lang.annotation.Target; 6. 7. @Target(ElementType.METHOD) 8. @Retention(RetentionPolicy.RUNTIME) 9. public @interface CustomAnnotation { 10. String value() default ""; 11. }
我们定义了一个名为
CustomAnnotation
的注解。该注解可以应用于方法上,并且在运行时保留。注解中还定义了一个名为value
的属性,默认为空字符串。
使用自定义注解非常简单,只需要在需要标记的地方使用注解即可。
1. @CustomAnnotation("这是一个自定义注解示例") 2. public void doSomething() { 3. // 执行一些操作 4. 5. }
7.自定义注解三种使用案例
案例一:
1. 定义一个枚举类:用来表示一组固定的值。
1. package com.ctb.model; 2. 3. public enum TranscationModel { 4. Read, Write, ReadWrite 5. }
2. 定义三个不同注解方式的类:
MyAnnotation1 :
1. package com.ctb.annotation; 2. 3. import java.lang.annotation.*; 4. 5. /** 6. * MyAnnotation1注解可以用在类、接口、属性、方法上 7. * 注解运行期也保留 8. * 不可继承 9. */ 10. @Target({ElementType.TYPE, ElementType.FIELD,ElementType.METHOD}) 11. @Retention(RetentionPolicy.RUNTIME) 12. @Documented 13. public @interface MyAnnotation1 { 14. String name(); 15. }
MyAnnotation2 :
1. package com.ctb.annotation; 2. 3. import com.ctb.model.TranscationModel; 4. 5. import java.lang.annotation.*; 6. 7. /** 8. * MyAnnotation2注解可以用在方法上 9. * 注解运行期也保留 10. * 不可继承 11. */ 12. @Target(ElementType.METHOD) 13. @Retention(RetentionPolicy.RUNTIME) 14. @Documented 15. public @interface MyAnnotation2 { 16. TranscationModel model() default TranscationModel.ReadWrite; 17. }
MyAnnotation3 :
1. package com.ctb.annotation; 2. 3. import com.ctb.model.TranscationModel; 4. 5. import java.lang.annotation.*; 6. 7. 8. /** 9. * MyAnnotation3注解可以用在方法上 10. * 注解运行期也保留 11. * 可继承 12. */ 13. @Target(ElementType.METHOD) 14. @Retention(RetentionPolicy.RUNTIME) 15. @Inherited 16. @Documented 17. public @interface MyAnnotation3 { 18. TranscationModel[] models() default TranscationModel.ReadWrite; 19. }
Demo1测试类 : 获取类与方法上的注解值
1. package com.ctb.annotation.demo; 2. 3. import com.ctb.annotation.MyAnnotation1; 4. import com.ctb.annotation.MyAnnotation2; 5. import com.ctb.annotation.MyAnnotation3; 6. import com.ctb.model.TranscationModel; 7. 8. /** 9. * 获取类与方法上的注解值 10. */ 11. @MyAnnotation1(name = "abc") 12. public class Demo1 { 13. 14. @MyAnnotation1(name = "xyz") 15. private Integer age; 16. 17. @MyAnnotation2(model = TranscationModel.Read) 18. public void list() { 19. System.out.println("list"); 20. } 21. 22. @MyAnnotation3(models = {TranscationModel.Read, TranscationModel.Write}) 23. public void edit() { 24. System.out.println("edit"); 25. } 26. }
Demo1Test测试类:
1. package com.ctb.annotation.demo; 2. 3. import com.ctb.annotation.MyAnnotation1; 4. import com.ctb.annotation.MyAnnotation2; 5. import com.ctb.annotation.MyAnnotation3; 6. import com.ctb.model.TranscationModel; 7. import org.junit.Test; 8. 9. public class Demo1Test { 10. @Test 11. public void list() throws Exception { 12. // 获取类上的注解 13. MyAnnotation1 annotation1 = Demo1.class.getAnnotation(MyAnnotation1.class); 14. System.out.println(annotation1.name());//abc 15. 16. // 获取方法上的注解 17. MyAnnotation2 myAnnotation2 = Demo1.class.getMethod("list").getAnnotation(MyAnnotation2.class); 18. System.out.println(myAnnotation2.model());//Read 19. 20. // 获取属性上的注解 21. MyAnnotation1 myAnnotation1 = Demo1.class.getDeclaredField("age").getAnnotation(MyAnnotation1.class); 22. System.out.println(myAnnotation1.name());// xyz 23. } 24. 25. @Test 26. public void edit() throws Exception { 27. MyAnnotation3 myAnnotation3 = Demo1.class.getMethod("edit").getAnnotation(MyAnnotation3.class); 28. for (TranscationModel model : myAnnotation3.models()) { 29. System.out.println(model);//Read,Write 30. } 31. } 32. }
@Ingerited的使用
@Inherited注解是Java中的元注解之一,用于控制注解的继承性。当一个类或接口被@Inherited注解修饰时,它的子类将自动获得该注解。但需要注意的是,@Inherited只对类和接口有效,对方法、字段等无效。
普通的类继承是指子类可以继承父类的所有非private的成员变量和方法,包括构造器。子类可以使用父类中的方法和属性,也可以重写父类的方法,在其基础上进行扩展或修改。
执行list方法测试结果
执行edit方法测试结果
案例二:
1. 自定义注解类:
1. package com.ctb.annotation; 2. 3. import java.lang.annotation.ElementType; 4. import java.lang.annotation.Retention; 5. import java.lang.annotation.RetentionPolicy; 6. import java.lang.annotation.Target; 7. 8. //@Retention(RetentionPolicy.SOURCE) 9. @Retention(RetentionPolicy.RUNTIME) 10. @Target(ElementType.FIELD) 11. public @interface TestAnnotation { 12. String value() default "默认value值"; 13. 14. String what() default "这里是默认的what属性对应的值"; 15. }
2. 定义测试类:获取类属性上的注解属性值
1. package com.ctb.annotation.demo2; 2. 3. import com.ctb.annotation.TestAnnotation; 4. 5. /** 6. * 获取类属性上的注解属性值 7. */ 8. public class Demo2 { 9. @TestAnnotation(value = "这就是value对应的值_msg1", what = "这就是what对应的值_msg1") 10. private static String msg1; 11. 12. @TestAnnotation("这就是value对应的值1") 13. private static String msg2; 14. 15. @TestAnnotation(value = "这就是value对应的值2") 16. private static String msg3; 17. 18. @TestAnnotation(what = "这就是what对应的值") 19. private static String msg4; 20. }
3. 测试
1. package com.ctb.annotation.demo2; 2. 3. import com.ctb.annotation.TestAnnotation; 4. import org.junit.Test; 5. 6. public class Demo2Test { 7. @Test 8. public void test1() throws Exception { 9. TestAnnotation msg1 = Demo2.class.getDeclaredField("msg1").getAnnotation(TestAnnotation.class); 10. System.out.println(msg1.value()); 11. System.out.println(msg1.what()); 12. } 13. 14. @Test 15. public void test2() throws Exception{ 16. TestAnnotation msg2 = Demo2.class.getDeclaredField("msg2").getAnnotation(TestAnnotation.class); 17. System.out.println(msg2.value()); 18. System.out.println(msg2.what()); 19. } 20. 21. @Test 22. public void test3() throws Exception{ 23. TestAnnotation msg3 = Demo2.class.getDeclaredField("msg3").getAnnotation(TestAnnotation.class); 24. System.out.println(msg3.value()); 25. System.out.println(msg3.what()); 26. } 27. 28. @Test 29. public void test4() throws Exception{ 30. TestAnnotation msg4 = Demo2.class.getDeclaredField("msg4").getAnnotation(TestAnnotation.class); 31. System.out.println(msg4.value()); 32. System.out.println(msg4.what()); 33. } 34. }
案例三:
1.创建自定义注解:
1. package com.ctb.annotation; 2. 3. import java.lang.annotation.*; 4. 5. /** 6. * 非空注解:使用在方法的参数上,false表示此参数可以为空,true不能为空 7. */ 8. @Documented 9. @Target({ElementType.PARAMETER}) 10. @Retention(RetentionPolicy.RUNTIME) 11. public @interface IsNotNull { 12. boolean value() default false; 13. }
2. 创建测试类:
1. package com.ctb.annotation.demo3; 2. 3. import com.ctb.annotation.IsNotNull; 4. 5. /** 6. * 获取参数修饰注解对应的属性值 7. */ 8. public class Demo3 { 9. 10. public void hello1(@IsNotNull(true) String name) { 11. System.out.println("hello:" + name); 12. } 13. 14. public void hello2(@IsNotNull String name) { 15. System.out.println("hello:" + name); 16. } 17. }
3. 测试
1. package com.ctb.annotation.demo3; 2. 3. import com.ctb.annotation.IsNotNull; 4. import org.junit.Test; 5. 6. import java.lang.reflect.Method; 7. import java.lang.reflect.Parameter; 8. 9. public class Demo3Test { 10. 11. @Test 12. public void hello1() throws Exception { 13. Demo3 demo3 = new Demo3(); 14. for (Parameter parameter : demo3.getClass().getMethod("hello1", String.class).getParameters()) { 15. IsNotNull annotation = parameter.getAnnotation(IsNotNull.class); 16. if(annotation != null){ 17. System.out.println(annotation.value());//true 18. } 19. } 20. } 21. 22. @Test 23. public void hello2() throws Exception { 24. Demo3 demo3 = new Demo3(); 25. for (Parameter parameter : demo3.getClass().getMethod("hello2", String.class).getParameters()) { 26. IsNotNull annotation = parameter.getAnnotation(IsNotNull.class); 27. if(annotation != null){ 28. System.out.println(annotation.value());//false 29. } 30. } 31. } 32. 33. @Test 34. public void hello3() throws Exception { 35. // 模拟浏览器传递到后台的参数 解读@requestParam 36. String name = "zs"; 37. Demo3 demo3 = new Demo3(); 38. Method method = demo3.getClass().getMethod("hello1", String.class); 39. for (Parameter parameter : method.getParameters()) { 40. IsNotNull annotation = parameter.getAnnotation(IsNotNull.class); 41. if(annotation != null){ 42. System.out.println(annotation.value());//true 43. if (annotation.value() && !"".equals(name)){ 44. method.invoke(demo3,name); 45. } 46. } 47. } 48. } 49. }
二、Aop自定义注解的应用
1 .自定义注解类
1. package com.ctb.annotation; 2. 3. import java.lang.annotation.ElementType; 4. import java.lang.annotation.Retention; 5. import java.lang.annotation.RetentionPolicy; 6. import java.lang.annotation.Target; 7. 8. @Target(ElementType.METHOD) 9. @Retention(RetentionPolicy.RUNTIME) 10. public @interface MyLog { 11. String desc(); 12. }
2.切面类
1. package com.ctb.aspect; 2. 3. import com.ctb.annotation.MyLog; 4. import org.aspectj.lang.JoinPoint; 5. import org.aspectj.lang.annotation.Aspect; 6. import org.aspectj.lang.annotation.Before; 7. import org.aspectj.lang.annotation.Pointcut; 8. import org.aspectj.lang.reflect.MethodSignature; 9. import org.slf4j.Logger; 10. import org.slf4j.LoggerFactory; 11. import org.springframework.stereotype.Component; 12. 13. @Component 14. @Aspect 15. public class MyLogAspect { 16. private static final Logger logger = LoggerFactory.getLogger(MyLogAspect.class); 17. 18. /** 19. * 只要用到了com.javaxl.p2.annotation.springAop.MyLog这个注解的,就是目标类 20. */ 21. @Pointcut("@annotation(com.ctb.annotation.MyLog)") 22. private void MyValid() { 23. } 24. 25. @Before("MyValid()") 26. public void before(JoinPoint joinPoint) { 27. MethodSignature signature = (MethodSignature) joinPoint.getSignature(); 28. logger.debug("[" + signature.getName() + " : start.....]"); 29. System.out.println("[" + signature.getName() + " : start.....]"); 30. 31. MyLog myLog = signature.getMethod().getAnnotation(MyLog.class); 32. logger.debug("【目标对象方法被调用时候产生的日志,记录到日志表中】:"+myLog.desc()); 33. System.out.println("【目标对象方法被调用时候产生的日志,记录到日志表中】:" + myLog.desc()); 34. } 35. }
3.Controller层
1. package com.ctb.web; 2. 3. import com.ctb.annotation.MyLog; 4. import org.springframework.stereotype.Component; 5. 6. @Component 7. public class LogController { 8. @MyLog(desc = "这是结合spring aop知识,讲解自定义注解应用的一个案例") 9. public void testLogAspect(){ 10. System.out.println("记录日志..."); 11. } 12. }