目录
[2. 注解的用处](#2. 注解的用处)
[3. 为什么要用注解](#3. 为什么要用注解)
[5. 注解的分类](#5. 注解的分类)
[1 .自定义注解类](#1 .自定义注解类)
前言
随着Web开发的发展,越来越多的企业开始使用SpringMVC框架来构建自己的Web应用程序。在开发过程中,为了提高代码的可读性和可维护性,我们经常需要自定义注解。本文将介绍如何编写一个自定义注解,并通过一个简单的例子来演示如何使用它。
一、自定义注解
1.Java注解简介
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**关键字即可。
package com.ctb.utils;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomAnnotation {
String value() default "";
}
我们定义了一个名为
CustomAnnotation
的注解。该注解可以应用于方法上,并且在运行时保留。注解中还定义了一个名为value
的属性,默认为空字符串。
使用自定义注解非常简单,只需要在需要标记的地方使用注解即可。
@CustomAnnotation("这是一个自定义注解示例")
public void doSomething() {
// 执行一些操作
}
7.自定义注解三种使用案例
案例一:
**1. 定义一个枚举类:**用来表示一组固定的值。
package com.ctb.model;
public enum TranscationModel {
Read, Write, ReadWrite
}
2. 定义三个不同注解方式的类:
MyAnnotation1 :
package com.ctb.annotation;
import java.lang.annotation.*;
/**
* MyAnnotation1注解可以用在类、接口、属性、方法上
* 注解运行期也保留
* 不可继承
*/
@Target({ElementType.TYPE, ElementType.FIELD,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAnnotation1 {
String name();
}
MyAnnotation2 :
package com.ctb.annotation;
import com.ctb.model.TranscationModel;
import java.lang.annotation.*;
/**
* MyAnnotation2注解可以用在方法上
* 注解运行期也保留
* 不可继承
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAnnotation2 {
TranscationModel model() default TranscationModel.ReadWrite;
}
MyAnnotation3 :
package com.ctb.annotation;
import com.ctb.model.TranscationModel;
import java.lang.annotation.*;
/**
* MyAnnotation3注解可以用在方法上
* 注解运行期也保留
* 可继承
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface MyAnnotation3 {
TranscationModel[] models() default TranscationModel.ReadWrite;
}
**Demo1测试类 :**获取类与方法上的注解值
package com.ctb.annotation.demo;
import com.ctb.annotation.MyAnnotation1;
import com.ctb.annotation.MyAnnotation2;
import com.ctb.annotation.MyAnnotation3;
import com.ctb.model.TranscationModel;
/**
* 获取类与方法上的注解值
*/
@MyAnnotation1(name = "abc")
public class Demo1 {
@MyAnnotation1(name = "xyz")
private Integer age;
@MyAnnotation2(model = TranscationModel.Read)
public void list() {
System.out.println("list");
}
@MyAnnotation3(models = {TranscationModel.Read, TranscationModel.Write})
public void edit() {
System.out.println("edit");
}
}
Demo1Test测试类:
package com.ctb.annotation.demo;
import com.ctb.annotation.MyAnnotation1;
import com.ctb.annotation.MyAnnotation2;
import com.ctb.annotation.MyAnnotation3;
import com.ctb.model.TranscationModel;
import org.junit.Test;
public class Demo1Test {
@Test
public void list() throws Exception {
// 获取类上的注解
MyAnnotation1 annotation1 = Demo1.class.getAnnotation(MyAnnotation1.class);
System.out.println(annotation1.name());//abc
// 获取方法上的注解
MyAnnotation2 myAnnotation2 = Demo1.class.getMethod("list").getAnnotation(MyAnnotation2.class);
System.out.println(myAnnotation2.model());//Read
// 获取属性上的注解
MyAnnotation1 myAnnotation1 = Demo1.class.getDeclaredField("age").getAnnotation(MyAnnotation1.class);
System.out.println(myAnnotation1.name());// xyz
}
@Test
public void edit() throws Exception {
MyAnnotation3 myAnnotation3 = Demo1.class.getMethod("edit").getAnnotation(MyAnnotation3.class);
for (TranscationModel model : myAnnotation3.models()) {
System.out.println(model);//Read,Write
}
}
}
@Ingerited的使用
@Inherited注解是Java中的元注解之一,用于控制注解的继承性。当一个类或接口被@Inherited注解修饰时,它的子类将自动获得该注解。但需要注意的是,@Inherited只对类和接口有效,对方法、字段等无效。
普通的类继承是指子类可以继承父类的所有非private的成员变量和方法,包括构造器。子类可以使用父类中的方法和属性,也可以重写父类的方法,在其基础上进行扩展或修改。
执行list方法测试结果
执行edit方法测试结果
案例二:
1. 自定义注解类:
package com.ctb.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//@Retention(RetentionPolicy.SOURCE)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface TestAnnotation {
String value() default "默认value值";
String what() default "这里是默认的what属性对应的值";
}
2. 定义测试类:获取类属性上的注解属性值
package com.ctb.annotation.demo2;
import com.ctb.annotation.TestAnnotation;
/**
* 获取类属性上的注解属性值
*/
public class Demo2 {
@TestAnnotation(value = "这就是value对应的值_msg1", what = "这就是what对应的值_msg1")
private static String msg1;
@TestAnnotation("这就是value对应的值1")
private static String msg2;
@TestAnnotation(value = "这就是value对应的值2")
private static String msg3;
@TestAnnotation(what = "这就是what对应的值")
private static String msg4;
}
3. 测试
package com.ctb.annotation.demo2;
import com.ctb.annotation.TestAnnotation;
import org.junit.Test;
public class Demo2Test {
@Test
public void test1() throws Exception {
TestAnnotation msg1 = Demo2.class.getDeclaredField("msg1").getAnnotation(TestAnnotation.class);
System.out.println(msg1.value());
System.out.println(msg1.what());
}
@Test
public void test2() throws Exception{
TestAnnotation msg2 = Demo2.class.getDeclaredField("msg2").getAnnotation(TestAnnotation.class);
System.out.println(msg2.value());
System.out.println(msg2.what());
}
@Test
public void test3() throws Exception{
TestAnnotation msg3 = Demo2.class.getDeclaredField("msg3").getAnnotation(TestAnnotation.class);
System.out.println(msg3.value());
System.out.println(msg3.what());
}
@Test
public void test4() throws Exception{
TestAnnotation msg4 = Demo2.class.getDeclaredField("msg4").getAnnotation(TestAnnotation.class);
System.out.println(msg4.value());
System.out.println(msg4.what());
}
}
案例三:
1.创建自定义注解:
package com.ctb.annotation;
import java.lang.annotation.*;
/**
* 非空注解:使用在方法的参数上,false表示此参数可以为空,true不能为空
*/
@Documented
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface IsNotNull {
boolean value() default false;
}
2. 创建测试类:
package com.ctb.annotation.demo3;
import com.ctb.annotation.IsNotNull;
/**
* 获取参数修饰注解对应的属性值
*/
public class Demo3 {
public void hello1(@IsNotNull(true) String name) {
System.out.println("hello:" + name);
}
public void hello2(@IsNotNull String name) {
System.out.println("hello:" + name);
}
}
3. 测试
package com.ctb.annotation.demo3;
import com.ctb.annotation.IsNotNull;
import org.junit.Test;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
public class Demo3Test {
@Test
public void hello1() throws Exception {
Demo3 demo3 = new Demo3();
for (Parameter parameter : demo3.getClass().getMethod("hello1", String.class).getParameters()) {
IsNotNull annotation = parameter.getAnnotation(IsNotNull.class);
if(annotation != null){
System.out.println(annotation.value());//true
}
}
}
@Test
public void hello2() throws Exception {
Demo3 demo3 = new Demo3();
for (Parameter parameter : demo3.getClass().getMethod("hello2", String.class).getParameters()) {
IsNotNull annotation = parameter.getAnnotation(IsNotNull.class);
if(annotation != null){
System.out.println(annotation.value());//false
}
}
}
@Test
public void hello3() throws Exception {
// 模拟浏览器传递到后台的参数 解读@requestParam
String name = "zs";
Demo3 demo3 = new Demo3();
Method method = demo3.getClass().getMethod("hello1", String.class);
for (Parameter parameter : method.getParameters()) {
IsNotNull annotation = parameter.getAnnotation(IsNotNull.class);
if(annotation != null){
System.out.println(annotation.value());//true
if (annotation.value() && !"".equals(name)){
method.invoke(demo3,name);
}
}
}
}
}
二、Aop自定义注解的应用
1 .自定义注解类
package com.ctb.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyLog {
String desc();
}
2.切面类
package com.ctb.aspect;
import com.ctb.annotation.MyLog;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class MyLogAspect {
private static final Logger logger = LoggerFactory.getLogger(MyLogAspect.class);
/**
* 只要用到了com.javaxl.p2.annotation.springAop.MyLog这个注解的,就是目标类
*/
@Pointcut("@annotation(com.ctb.annotation.MyLog)")
private void MyValid() {
}
@Before("MyValid()")
public void before(JoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
logger.debug("[" + signature.getName() + " : start.....]");
System.out.println("[" + signature.getName() + " : start.....]");
MyLog myLog = signature.getMethod().getAnnotation(MyLog.class);
logger.debug("【目标对象方法被调用时候产生的日志,记录到日志表中】:"+myLog.desc());
System.out.println("【目标对象方法被调用时候产生的日志,记录到日志表中】:" + myLog.desc());
}
}
3.Controller层
package com.ctb.web;
import com.ctb.annotation.MyLog;
import org.springframework.stereotype.Component;
@Component
public class LogController {
@MyLog(desc = "这是结合spring aop知识,讲解自定义注解应用的一个案例")
public void testLogAspect(){
System.out.println("记录日志...");
}
}