在spring中使用自定义注解注册监听器的方法

接口回调

监听器本质上就是利用回调机制,在某个动作发生前或后,执行我们自己的一些代码。在Java语言中,可以使用接口来实现。

实现一个监听器案例

为了方便,直接在spring环境中定义:以工作(work)为例,定义工作开始时(或结束时)的监听器。

1. 定义回调的接口

package com.yawn.demo.listener;

/**
 * @author Created by yawn on 2018-01-21 13:53
 */
public interface WorkListener {

  void onStart(String name);
}

2. 定义动作

package com.yawn.demo.service;

import com.yawn.demo.listener.WorkListener;

/**
 * @author Created by yawn on 2018-01-21 13:39
 */
@Service
public class MyService {

  @Resource
  private PersonService personService;

  private WorkListener listener;
  public void setWorkListener(WorkListener workListener) {
    this.listener = workListener;
  }

  public void work(String name) {
    listener.onStart(name);
    personService.work();
  }
}

动作work为一个具体的方法,在work()方法的适当时机,调用前面定义的接口。此外,在这个动作定义类中,需要提高设置监听器的方法。

3. 监听测试

@RunWith(SpringRunner.class)
@SpringBootTest
public class DemoSpringAnnotationApplicationTests {

  @Resource
  private MyService myService;

  @Test
  public void test1() {
    // 接口设置监听器
    myService.setWorkListener(new WorkListener() {
      @Override
      public void onStart(String name) {
        System.out.println("Start work for " + name + " !");
      }
    });
//    // lambda 表达式设置监听器
//    myService.setWorkListener(name -> System.out.println("Start work for " + name + " !"));
    // 工作
    myService.work("boss");
  }

 @Test
  public void test2() {
   // 继承实现类设置监听器
   myService.setWorkListener(new myWorkListener());
   // 工作
   myService.work("boss");
  }

  class myWorkListener extends WorkListenerAdaptor {
    @Override
    public void onStart(String name) {
      System.out.println("Start work for " + name + " !");
    }
  }
}

使用以上两种方法测试,得到了结果为:

Start work for boss !
working hard ...

说明在动作work发生之前,执行了我们在测试类中写下的监听代码,实现类监听的目的。

使用注解实现监听器

在以上代码中,调用 setWorkListener(WorkListener listener)  方法一般称作设置(注册)监听器,就是将自己写好的监听代码,设置为动作的监听器。然而,在每次注册监听器时,一般需要写一个类,实现定义好的接口或继承实现接口的类,再重写接口定义的方法即可。因此,聪明的程序员就想简化这个过程,所以就想出了使用注解的方法。使用注解,将监听代码段写在一个方法中,使用一个注解标记这个方法即可。

的确,使用变得简单了,但实现却不见得。

1. 定义一个注解

package com.yawn.demo.anno;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface WorkListener {
}

2. 解析注解

package com.yawn.demo.anno;
import com.yawn.demo.service.MyService;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.Map;
/**
 * @author Created by yawn on 2018-01-21 14:46
 */
@Component
public class WorkListenerParser implements ApplicationContextAware, InitializingBean {
  @Resource
  private MyService myService;
  private ApplicationContext applicationContext;

  @Override
  public void afterPropertiesSet() throws Exception {
    Map<String, Object> listenerBeans = getExpectListenerBeans(Controller.class, RestController.class, Service.class, Component.class);
    for (Object listener : listenerBeans.values()) {
      for (Method method : listener.getClass().getDeclaredMethods()) {
        if (!method.isAnnotationPresent(WorkListener.class)) {
          continue;
        }
        myService.setWorkListener(name -> {
          try {
            method.invoke(listener, name);
          } catch (Exception e) {
            e.printStackTrace();
          }
        });
      }
    }
  }

  /**
   * 找到有可能使用注解的bean
   * @param annotationTypes 需要进行扫描的类级注解类型
   * @return 扫描到的beans的map
   */
  private Map<String, Object> getExpectListenerBeans(Class<? extends Annotation>... annotationTypes) {
    Map<String, Object> listenerBeans = new LinkedHashMap<>();
    for (Class<? extends Annotation> annotationType : annotationTypes) {
      Map<String, Object> annotatedBeansMap = applicationContext.getBeansWithAnnotation(annotationType);
      listenerBeans.putAll(annotatedBeansMap);
    }
    return listenerBeans;
  }

  @Override
  public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    this.applicationContext = applicationContext;
  }
}

在注解的解析过程中,设置监听器。

在解析类中,实现了接口ApplicationContextAware,为了在类中拿到ApplicationContext的引用,用于得到 IOC 容器中的 Bean;而实现接口InitializingBean,则是为了在一个合适的时机执行解析注解、设置监听器的代码。 如果不这样做,可以在CommandLineRunner执行时调用解析、设置的代码,而ApplicationContext也可以自动注入。

3. 测试

在执行完以上代码后,监听器就已经设置好了,可以进行测试了。

package com.yawn.demo.controller;
import com.yawn.demo.anno.WorkListener;
import com.yawn.demo.service.MyService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;

/**
 * @author Created by yawn on 2018-01-21 13:28
 */
@RestController
public class TestController {
  @Resource
  private MyService myService;
  @GetMapping("/work")
  public Object work() {
    myService.work("boss");
    return "done";
  }

  @WorkListener
  public void listen(String name) {
    System.out.println("Start work for " + name + " !");
  }
}

写一个监听方法,参数类型和个数与接口相同,然后加上自定义的注解即可。当启动环境后,监听器就已经设置好了。

然后通过url调用myService的work()方法,可以看到结果:

Start work for boss !
working hard ...

已经调用了监听方法。在接下来的开发中,就可以使用这个注解注册监听器了。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

您可能感兴趣的文章:

  • 监听器获取Spring配置文件的方法
  • SpringBoot定义过滤器、监听器、拦截器的方法
  • Spring Boot的listener(监听器)简单使用实例详解
(0)

相关推荐

  • 监听器获取Spring配置文件的方法

    我们在做项目的时候,会用到监听器去获取Spring的配置文件,然后从中拿出我们需要的bean出来,比如做网站首页,假设商品的后台业务逻辑都做好了,我们需要创建一个监听器,在项目启动时将首页的数据查询出来放到application里,即在监听器里调用后台商品业务逻辑的方法,也就是说我们需要在监听器里获取Spring中配置的相应的bean.先把监听器创建出来: 1. 创建InitDataListener 创建一个监听器InitDataListener继承ServletContextListener:

  • Spring Boot的listener(监听器)简单使用实例详解

    监听器(Listener)的注册方法和 Servlet 一样,有两种方式:代码注册或者注解注册 1.代码注册方式 通过代码方式注入过滤器 @Bean public ServletListenerRegistrationBean servletListenerRegistrationBean(){ ServletListenerRegistrationBean servletListenerRegistrationBean = new ServletListenerRegistrationBean

  • SpringBoot定义过滤器、监听器、拦截器的方法

    一.自定义过滤器 创建一个过滤器,实现javax.servlet.Filter接口,并重写其中的init.doFilter.destory方法. package com.example.filter; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.Se

  • 在spring中使用自定义注解注册监听器的方法

    接口回调 监听器本质上就是利用回调机制,在某个动作发生前或后,执行我们自己的一些代码.在Java语言中,可以使用接口来实现. 实现一个监听器案例 为了方便,直接在spring环境中定义:以工作(work)为例,定义工作开始时(或结束时)的监听器. 1. 定义回调的接口 package com.yawn.demo.listener; /** * @author Created by yawn on 2018-01-21 13:53 */ public interface WorkListener

  • 快速理解spring中的各种注解

    Spring中的注解大概可以分为两大类: 1)spring的bean容器相关的注解,或者说bean工厂相关的注解: 2)springmvc相关的注解. spring的bean容器相关的注解,先后有:@Required, @Autowired, @PostConstruct, @PreDestory,还有Spring3.0开始支持的JSR-330标准javax.inject.*中的注解(@Inject, @Named, @Qualifier, @Provider, @Scope, @Singlet

  • Spring AOP 实现自定义注解的示例

    自工作后,除了一些小项目配置事务使用过 AOP,真正自己写 AOP 机会很少,另一方面在工作后还没有写过自定义注解,一直很好奇注解是怎么实现他想要的功能的,刚好做项目的时候,经常有人日志打得不够全,经常出现问题了,查日志的才发现忘记打了,所以趁此机会,搜了一些资料,用 AOP + 自定义注解,实现请求拦截,自定义打日志,玩一下这两个东西,以下是自己完的一个小例子,也供需要的同学参考. 1. 注解如下: package cn.bridgeli.demo.annotation;   import j

  • spring中的特殊注解@RequiredArgsConstructor详解

    目录 1.注解注入: 2.构造器注入: 3.setter注入: Lombok在spring中的特殊注解@RequiredArgsConstructor 在了解lombok中的特殊注解之前:了解一下spring的常用注入方式戳这里 1.注解注入: Controller public class FooController { @Autowired //@Inject private FooService fooService; //简单的使用例子,下同 public List<Foo> list

  • springboot中使用自定义两级缓存的方法

    工作中用到了springboot的缓存,使用起来挺方便的,直接引入redis或者ehcache这些缓存依赖包和相关缓存的starter依赖包,然后在启动类中加入@EnableCaching注解,然后在需要的地方就可以使用@Cacheable和@CacheEvict使用和删除缓存了.这个使用很简单,相信用过springboot缓存的都会玩,这里就不再多说了.美中不足的是,springboot使用了插件式的集成方式,虽然用起来很方便,但是当你集成ehcache的时候就是用ehcache,集成redi

  • spring中@Reference注入为空的解决方法

    线上发生事故了 前天晚上上线一波,发生了一个挺有意思的事,昨天复盘了一下,今天分享一下. 晚上的时候,我负责的系统和收银系统同时上线一波(用的是Dubbo).然后很神奇的事情发生了,收银系统用@Reference注解注入我的接口,然后这个接口的实现类居然为空. 其实我们当时没排查出来是什么原因? 重启了一下就好了,毕竟重启大法好. 但本着不能给用户充钱的路上造成阻碍,还是要排查一波这个代理对象为空是如何造成的. 线上dubbo的版本为2.8.9,注意包名是(com.alibaba) 为了方便大家

  • Spring与Mybatis基于注解整合Redis的方法

    基于这段时间折腾redis遇到了各种问题,想着整理一下.本文主要介绍基于Spring+Mybatis以注解的形式整合Redis.废话少说,进入正题. 首先准备Redis,我下的是Windows版,下载后直接启动redis-server就行了,见下图: 一,先上jar包 二,创建实体类 package com.sl.user.vo; import java.io.Serializable; import com.fasterxml.jackson.databind.PropertyNamingSt

  • spring中使用mybatis plus连接sqlserver的方法实现

    本文主要关注如何使用mybatis/mybatis plus连接SQL Server数据库,因此将省略其他项目配置.代码. 框架选择 应用框架:spring boot ORM框架:mybatis plus(对于连接数据库而言,mybatis和mybatis plus其实都一样) 数据库连接池:druid pom依赖 此处仅给出我的配置,mybatis/druid请依据自己项目的需要进行选择. 方便起见我用的是mybatis plus <!--mybatis plus --> <depen

  • Spring中bean的生命周期之getSingleton方法

    Spring中bean的生命周期 要想讲清楚spring中bean的生命周期,真的是不容易,以AnnotationConfigApplicationContext上下文为基础来讲解bean的生命周期,AnnotationConfigApplicationContext是基于注解的上下文,使用XML的方式现在很少见,所以以此上下文为基础,使用XML的上下文ClassPathXmlApplicationContext的步骤和AnnotationConfigApplicationContext类似.

  • java中反射和注解的简单使用方法

    目录 什么反射? Java反射机制提供的功能 反射相关的主要API Class 类 获取Class 类的实例( 四种方法) 哪些类型可以有Class 对象? 演示Class类的常用方法 有了Class对象,能做什么? 调用运行时类的指定结构 1. 调用指定方法 关于setAccessible 调用Class对象的newInstance()方法 综合案例: 注解 什么是注解? 常见的Annotation JDK 中的元注解 自定义 Annotation 最后通过反射获取注解信息: 总结 什么反射?

随机推荐