自定义注解的使用

自定义注解的申明

自定义注解annotation
一些元注解的说明:

@Retention: 什么时候使用该注解,我们定义为运行时;

  • SOURCE, 编译器处理完该注解后不存储在class中
  • CLASS, 编译器把该注解存储在class字节码文件中
  • RUNTIME, 编译器把该注解存储在class字节码文件中,并且可以由JVM读取,在运行时可以通过反射获取到

@Target: 注解用于什么地方,我们定义为作用于方法和类上;

  • @Target(ElementType.TYPE) //接口、类、枚举、注解
  • @Target(ElementType.METHOD) //方法上
  • @Target(ElementType.FIELD) //字段、枚举的常量
  • @Target(ElementType.PARAMETER) //方法参数
  • @Target(ElementType.CONSTRUCTOR) //构造函数
  • @Target(ElementType.LOCAL_VARIABLE) //局部变量
  • @Target(ElementType.ANNOTATION_TYPE) //注解
  • @Target(ElementType.PACKAGE) //包

@Documented: 这个Annotation可以被写入javadoc;

@Inherited: 这个Annotation 可以被继承,如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。

@Target(ElementType.METHOD)   
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface MyAnnotation {
    // 参数,可以在aop里获取到
  	String value() default "";
}

声明完成,该注解就可以使用了

配合aop使用(常用)

引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
    <version>2.7.2</version>
  </dependency>
aspectj相关注解的作用:

@Aspect :定义一个切面,标注在类上
@Pointcut :定义一个切点(在切点前后增强),定义需要拦截的内容,切点分为execution方式和annotation方式。前者可以用路径表达式指定哪些类织入切面,后者可以指定被哪些注解修饰的代码织入切面。

execution表达式: 以 execution(* com.mutest.controller….(…))表达式为例:

  • 第一个 * 号的位置:表示返回值类型,* 表示所有类型。
    包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,在本例中指 com.mutest.controller包、子包下所有类的方法。
  • 第二个 * 号的位置:表示类名,* 表示所有类。
    (…):这个星号表示方法名, 表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数。

annotation() 表达式:
annotation() 方式是针对某个注解来定义切点。

定义一个切面

@Component
@Aspect
@Slf4j
public class MyAnnotationAspect {
    // 注解类
    // 如果是切入注解使用annotation,如果是正常类使用execution
    @Pointcut("@annotation(com.feng.study.annotation.MyAnnotation)")
    public void pointcut(){}
    
    /**前置增强方法*/
    @Before("pointcut()")
    //@Before("execution(public * com.bc.aop..*.*(..))")可以给方法单独指定切入点
    public void beforeLogger(JoinPoint jp) {
			
		//比如说拦截的是controller里的方法,我想记录每个调用的ip地址,也是可以获取到请求信息的
		 //ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        //HttpServletRequest request = attributes.getRequest();
       // String ip = request.getRemoteAddr();

        logger.info("这是MyLogger类的before方法!");
        System.out.println("切入点方法入参列表:" + Arrays.toString(jp.getArgs()));
        System.out.println("切入点方法签名对象:" + jp.getSignature());
        System.out.println("切入点所在目标对象:" + jp.getTarget());
        System.out.println("代理对象本身:" + jp.getThis());
    }

    /**后置增强方法,此方法对void 返回值的无效*/
    @AfterReturning(pointcut = "pointcut()", returning = "result")
    public void afterReturning(JoinPoint jp, Integer result) {
        logger.info("这是MyLogger类的after-returning方法!");
        System.out.println("切入点方法入参列表:" + Arrays.toString(jp.getArgs()));
        System.out.println("切入点方法签名对象:" + jp.getSignature());
        System.out.println("切入点所在目标对象:" + jp.getTarget());
        System.out.println("代理对象本身:" + jp.getThis());
        System.out.println("切入点方法返回对象:" + result);
    }

    /**后置异常增强方法*/
    @AfterThrowing(pointcut = "pointcut()", throwing = "e")
    public void afterThrowing(JoinPoint jp, Exception e) {
        logger.info("这是MyLogger类的after-Throwing方法!");
        System.out.println("切入点方法入参列表:" + Arrays.toString(jp.getArgs()));
        System.out.println("切入点方法签名对象:" + jp.getSignature());
        System.out.println("切入点所在目标对象:" + jp.getTarget());
        System.out.println("代理对象本身:" + jp.getThis());
        System.out.println("异常:" + e);
    }

    /**最终增强方法*/
    @After("pointcut()")
    public void after(JoinPoint jp) {
        logger.info("这是MyLogger类的after方法!");
        System.out.println("切入点方法入参列表:" + Arrays.toString(jp.getArgs()));
        System.out.println("切入点方法签名对象:" + jp.getSignature());
        System.out.println("切入点所在目标对象:" + jp.getTarget());
        System.out.println("代理对象本身:" + jp.getThis());
    }

    /**环绕增强方法*/
    @Around("pointcut()")
    public Object aroundLogger(ProceedingJoinPoint jp) throws Throwable {
        logger.info("这是MyLogger类的around方法!");
        System.out.println("切入点方法入参列表:" + Arrays.toString(jp.getArgs()));
        System.out.println("切入点方法签名对象:" + jp.getSignature());
        System.out.println("切入点所在目标对象:" + jp.getTarget());
        System.out.println("代理对象本身:" + jp.getThis());
        System.out.println("-------------------------------");
        //  System.out.println("执行切入点方法");
        // 调用proceed方法,会执行切点,返回方法结果,可以在这里进行干扰操作
        // Object result = jp.proceed();  //执行切点,会依次调用@Before -> 接口逻辑代码 -> @After -> @AfterReturning
        // 一个示例 比如说我想统计方法执行耗时
        // long start = System.currentTimeMillis();  //记录调用接口的开始时间
        // Object result = joinPoint.proceed(); // 就需要调用该方法执行,获取结果在返回 
        // long end = System.currentTimeMillis();  //记录结束时间
        // System.out.println("Time-Consuming: "+ (end - start) / 1000+" s");  //记录耗时
        // return result;  //返回接口参数结果
        System.out.println("-------------------------------");
        System.out.println("执行切入点方法并改变参数");

        return jp.proceed(new Object[]{7,9});

    }

	// 示例非注解拦截的指定方法,并且获取到方法上的参数
	// @Around("execution(* com.jeeplus.modules.mail.service.MailBoxService.save(com.jeeplus.modules.mail.entity.MailBox)) && args(mailBox)")
	// 或者 @Around("execution(* com.jeeplus.modules.mail.service.MailBoxService.save(..)) && args(mailBox)")
    // public void interceptSave(ProceedingJoinPoint jp, MailBox mailBox) throws Throwable {
         // 在方法调用前执行的逻辑
         // System.out.println("Before save method");
         // 调用原始方法
         // jp.proceed();
         // System.out.println("切入点方法入参:" + mailBox);
         // 在方法调用后执行的逻辑
         // System.out.println("After save method");
   // }

     // 被拦截的请求方法
    // @Service
	// @Transactional(readOnly = true)
	// public class MailBoxService{
	// 	@Transactional(readOnly = false)
	//	public void save(MailBox mailBox) {
	//		 System.out.println("被拦截的请求方法");
	//	}
    // }


}

如果说我们想在增强时获取到注解上的参数就需要用下面的写法

MyAnnotation 就是定义注解的类

    @Before("@annotation(myAnnotation)") // 注意的是 两个 myAnnotation的名称要一致
    public void beforeLogger(JoinPoint jp,MyAnnotation myAnnotation) {
        System.out.println(myAnnotation.value());
    }

通过反射获取注解信息(不常用)
public class TestMyAnnotation {
    public static void main(String[] args) throws Exception {
        Person person = new Person();
        //通过反射获取person对象的方法
        Method method = person.getClass().getMethod("print", Integer.class, String.class);
        //执行对应的print方法
        method.invoke(person,new Object[]{15,"pag"});
        //从方法中获取注解信息
        getAnnotationByMethod(method);
    }

    private static void getAnnotationByMethod(Method method) {
        //首先判断该方法是否包含MyAnnotation注解
        if (method.isAnnotationPresent(MyAnnotation.class)) {
            MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
            //再获取注解的属性值,进行输出打印
            String value1 = annotation.value1();
            String value2 = annotation.value2();
            System.out.println("value1: " + value1 + ",value2: " + value2);
        }
        //同时可以获取该方法的所有注解信息
        Annotation[] annotations = method.getAnnotations();
        for (Annotation annotation: annotations) {
            System.out.println(annotation);
        }
    }
}

配合拦截器使用(常用)
/**
 * @Author: chenl
 * @Date: 2023/5/11
 *
 * 自定义注解 配合拦截器使用
 *
 */
@Component
public class AnnotationAndInterceptor {

    // 自定义拦截器
    public class MyInterceptor implements HandlerInterceptor {

        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            System.out.println("preHandle方法在业务处理器处理请求之前被调用");
            // 获取拦截到的方法,判断该方法上是否存在指定注解
            HandlerMethod method = (HandlerMethod) handler;
            MyAnnotation methodAnnotation = method.getMethodAnnotation(MyAnnotation.class);
            if(methodAnnotation==null){
                return true;
            }

            //方法上有自定义注解
            response.setContentType("application/json;charset=utf-8");
            response.getWriter().write("你访问的资源需要先进行登录");
            response.flushBuffer();
            return false;
        }

        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            System.out.println("postHandle方法在业务处理器处理请求执行完成后,生成视图之前执行");
        }

        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            System.out.println("afterCompletion方法在DispatcherServlet完全处理完请求后被调用,可用于清理资源等");
        }
    }

    // 注册拦截器
    @Configuration
    public class MyWebMvcConfigurer implements WebMvcConfigurer{

        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**");
        }
    }


}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值