网络异常,图片无法展示
|
前言
平时在用springBoot的使用,常常会用到@Service
,@Compent
等等注解,简化了我们的开发流程,提升了开发效率.那如何自己来写一个注解呢?下面就来介绍一下。
写一个注解
创建一个注解主要分两部分,一部分是创建注解类,一部分是创建一个切面类。
创建注解类
@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnn { String value() default "d"; }
创建注解类的关键字就是@interface
,这个注解类设置了一个value
变量,默认值为d;
在注解类上面还有@Target
和@Retention
注解,下面来说说创建注解类时需要用到的几个注解:
@Target
用来标记这个注解可以用于哪些地方,与ElementType
枚举类搭配使用,那这个枚举类里面有什么内容呢?
public enum ElementType { /** 类,接口(包括注释类型)或枚举声明*/ TYPE, /** 字段声明(包括枚举常量)*/ FIELD, /** 方法声明*/ METHOD, /** 形式参数(形参-调用方法时传入的参数)声明 */ PARAMETER, /** 构造函数声明 */ CONSTRUCTOR, /** 局部变量声明 */ LOCAL_VARIABLE, /** 注释类型声明 */ ANNOTATION_TYPE, /** 包声明 */ PACKAGE, /** * 类型参数声明 * java8新特性: * @since 1.8 */ TYPE_PARAMETER, /** * 任何类型声明 * java8新特性: * @since 1.8 */ TYPE_USE }
@Retention
该注解表示自定义注解的生命周期
public enum RetentionPolicy { /** * 注释将被编译器丢弃。 */ SOURCE, /** * 注释由编译器记录在类文件中 * 但不必在运行时由VM保留。 这是默认值 */ CLASS, /** *注释由编译器记录在类文件中,并且 *在运行时由VM保留,因此可以以反射方式读取它们。 */ RUNTIME }
写一个切面类
因为用到了切面,所以我们要先导入Spring AOP
这个依赖包。
<!--SpringBoot项目导入AOP--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
创建切面类
@Aspect @Component public class MyAnnAop { private Logger logger= LoggerFactory.getLogger(MyAnnAop.class); @Pointcut("@annotation(com.example.demo.annotation.MyAnn)") public void ann(){ } @Before("ann()") public void before(JoinPoint joinPoint){ logger.info("打印:开始前"); } @AfterReturning(value = "ann()",returning = "res") public Object dochange(JoinPoint joinPoint,Object res){ logger.info("AfterReturning通知开始-获取数据:{}",res); //获取数据 Map<String,String> map= (Map<String, String>) res; //添加新值 map.put("s1","我是在AOP中添加的新值"); return map; } }
Spring AOP说明
具体可以查阅Spring AOP相关资料
注解 | 说明 |
@Before |
前置通知,在连接点方法前调用 |
@Around |
环绕通知,它将覆盖原有方法,但是允许你通过反射调用原有方法 |
@After |
后置通知,在连接点方法后调用 |
@AfterReturning |
返回通知,在连接点方法执行并正常返回后调用,要求连接点方法在执行过程中没有发生异常 |
@AfterThrowing |
异常通知,当连接点方法异常时调用 |
使用自定义的注解
这里使用普通的SpringBoot
来使用注解,创建一个Service,在里面使用注解,然后才控制层调用
//服务层 @Service public class TestService { @MyAnn public Map test(){ Map<String,String> map=new HashMap<>(); map.put("t1","我是在Service设置的值"); return map; } } //控制层 @RestController public class Test2 { private Logger logger= LoggerFactory.getLogger(Test2.class); @Autowired private TestService testService; @GetMapping("/test") public String test(String id){ Map<String,String> s=testService.test(); logger.info("控制层输出:{}",s.get("s1")); return "sccess"; } }
输出
com.example.demo.aop.MyAnnAop : AfterReturning通知开始-获取数据:{t1=我是在Service设置的值} com.example.demo.web.Test2 : 控制层输出:我是在AOP中添加的新值
注意事项
上面那样使用注解是没问题的,但是如果是下面这样使用,AOP
就会失效
@RestController public class Test2 { private Logger logger= LoggerFactory.getLogger(Test2.class); @Autowired private TestService testService; @GetMapping("/test") public String test(String id){ Map<String,String> s=this.test2(); logger.info("控制层输出:{}",s.get("s1")); return "sccess"; } @MyAnn public Map test2(){ Map<String,String> map=new HashMap<>(); map.put("t1","我是在控制层设置的值"); return map; } }
输出
com.example.demo.web.Test2 : 控制层输出:null
这是应为内部方法调用,调用的是具体方法,并没有调用使用AOP后生成的代理方法