spring事件与自定义注解

 

目录

一、注解的定义

二、metadata元数据

三、元注解

四、标题


一、自定义注解

1.1、注解的定义

        Annotation(注解)就是Java提供了一种元程序中的元素关联任何信息和着任何元数据(metadata)的途径和方法。Annotion(注解)是一个接口,程序可以通过反射来获取指定程序元素的 Annotion对象,然后通过 Annotion对象 来获取注解里面的元数据。

        Annotation(注解)是JDK5.0及以后版本引入的。它可以用于创建文档,跟踪代码中的依赖性,甚至执行基本编译时检查。从某些方面看,annotation 就像修饰符一样被使用,并应用于包、类 型、构造方法、方法、成员变量、参数、本地变量的声明中。这些信息被存储在 Annotation 的 “name=value” 结构对中。

1.2、metadata元数据

        元数据从 metadata 一词译来,就是“关于数据的数据”的意思。
元数据的功能作用有很多,比如:你可能用过 Javadoc 的注释自动生成文档。这就是元数据功能的一种。总的来说,元数据可以用来创建文档,跟踪代码的依赖性,执行编译时格式检查,代替已有的配置文件。如果要对于元数据的作用进行分类,目前还没有明确的定义,不过我们可以根据它所起的作用,大致可分为三类:

  1. 编写文档:通过代码里标识的元数据生成文档。
  2. 代码分析:通过代码里标识的元数据对代码进行分析。
  3. 编译检查:通过代码里标识的元数据让编译器能实现基本的编译检查。

1.3、元注解

        元注解的作用就是负责注解其他注解,Java 5.0定义了 4个meta-annotation 类型,用来提供对其他的 annotation 类型做说明。

  • @Target
    用来限定某个自定义注解能被用在那些Java元素上面。
public enum ElementType {
    /** 类,接口(包括注解类型)或枚举的声明 */
    TYPE,
 
    /** 属性的声明 */
    FIELD,
 
    /** 方法的声明 */
    METHOD,
 
    /** 方法形式参数声明 */
    PARAMETER,
 
    /** 构造方法的声明 */
    CONSTRUCTOR,
 
    /** 局部变量声明 */
    LOCAL_VARIABLE,
 
    /** 注解类型声明 */
    ANNOTATION_TYPE,
 
    /** 包的声明 */
    PACKAGE
}
  • @Retention

用来定义注解生效的阶段

public enum RetentionPolicy {
    /**
     * 源文件阶段,不会在编译和运行期间起到任何作用,和注释一个效果
     */
    SOURCE,

    /**
     *编译阶段
     */
    CLASS,

    /**
     * 运行时阶段
     */
    RUNTIME
}
  • @Document
    用来指定自定义注解是否能锁着被定义的JAVA文件生成到文档中
  • @Inhrited
    指的是自定义注解写在了父类的声明部分,那么子类的生命部分也能自动拥有该注解。
    只有@Target定义了ElementType.TYPE的自定义注解起作用。

1.4、自定义注解

        使用 @interface 自定义注解时,自动继承了 java.lang.annotation.Annotation 接口,由编译程序自动完成其他细节。在定义注解时,不能继承其他的注解或接口。@interface 用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是 基本类型、Class、String、enum)。可以通过 default 来声明参数的默认值。

编写一个自定义注解:在运行时有效、只能标注在方法上

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface Bind {

     String evenetType() default "";

}

1.5将@Bind注解加入spring扫描

@Configuration
public class BindRegisterPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        //将Bind注解加入spring扫描
        ClassPathBeanDefinitionScanner scanner=new ClassPathBeanDefinitionScanner(registry);
        scanner.addIncludeFilter(new AnnotationTypeFilter(Bind.class));
        //扫描的包
        scanner.scan("com.example.mylearn");
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    }
}

二、spring事件发布与监听

        ApplicationEvent以及Listener是Spring为我们提供的一个事件监听、订阅的实现,内部实现原理是观察者设计模式,设计初衷也是为了系统业务逻辑之间的解耦,提高可扩展性以及可维护性。事件发布者并不需要考虑谁去监听,监听具体的实现内容是什么,发布者的工作只是为了发布事件而已。事件监听的作用与消息队列有一点类似。

模拟场景:service每个方法上都标注了章节1中的@Bind注解,每个注解中的eventType不一样。

@Service
public class TestService {

    @Bind(evenetType="1111")
    public void eventTest(Object msg){
        System.out.println("eventTest:"+msg);
    }

    @Bind(evenetType="222")
    public void eventTest2(Object msg){
        System.out.println("eventTest2:"+msg);;
    }
}

        publisher通过发布同一个的事件,但事件中的eventType不同。listener监听到后,根据事件的eventType借助反射调用不同的方法。

2.1自定义事件

         首先,要自定义事件Myevent,事件中的eventType属性与@Bind中的EventType一直。自定义事件需要继承ApplicationEvent。 

import com.example.mylearn.entity.User;
import org.springframework.context.ApplicationEvent;

public class MyEvent extends ApplicationEvent {
    private String eventType;

    public MyEvent(Object source) {
        super(source);
    }

    public MyEvent(String eventType, User user) {
        super(user);
        this.eventType=eventType;
    }

    public String getEventType(){
        return this.eventType;
    }

    @Override
    public Object getSource() {
        return super.getSource();
    }
}

2.2自定义监听器

      当监听到事件以后,获取事件的eventType,根据eventType调用service中的不同方法,详情见2.4章节。

import com.example.mylearn.Event.MyEvent;
import com.example.mylearn.config.ServiceContainer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

@Component
public class MyEventListener implements ApplicationListener<MyEvent> {

    @Autowired
    private ServiceContainer container;

    @Override
    public void onApplicationEvent(MyEvent event) {
        container.doHandle(event.getEventType(),event.getSource());
    }
}

2.3发布事件

发布一个evnetType为111的事件。

import com.example.mylearn.Event.MyEvent;
import com.example.mylearn.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {

    @Autowired
    private ApplicationEventPublisher eventPublisher;

    @PostMapping("test")
    public String testEvent(@RequestBody User user){
        eventPublisher.publishEvent(new MyEvent("1111",user));
        return "true";
    }

}

2.4反射执行方法

       为了实现上述场景,通过反射调用方法,首先需要获取所有标注了@Bind注解的方法实例,与方法所在的类的对象。 如果每发布一次事件,都需要扫描整个包来获取,效率底下。因此可以在springboot容器启动以后,就扫描@bind注解所在的方法和类,并将二者与@bind中的evnetType(与自定义事件中的eventType一直,自定义事件定义详情见2.1章节)关系其存储到HashMap中,每次事件过来以后,可直接根据事件的eventType从map中获取方法,不用每次都描扫整个包。

       可以借助实现CommandLineRunner类来实现,在springboot容器启动完成后,获取所有标注@Bind注解的方法,并将他们存储到HashMap中。

import com.example.mylearn.annotation.Bind;
import com.example.mylearn.config.ServiceContainer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Map;

@Component
public class ContextConfig implements CommandLineRunner {

    @Autowired
    private ServiceContainer container;

    @Autowired
    private ApplicationContext applicationContext;

    @Override
    public void run(String... args) throws Exception {
        //获取标注了@service的类
        Map<String, Object> beansWithAnnotation = applicationContext.getBeansWithAnnotation(Service.class);
        beansWithAnnotation.entrySet().forEach(item->{
            //获取类的class实列
            Class<?> aClass = item.getValue().getClass();
            //获取类中的所有方法
            Method[] methods = aClass.getMethods();
            Arrays.asList(methods).stream().forEach(method -> {
                //获取方法上的@Bind注解
                Bind annotation = method.getAnnotation(Bind.class);
                if(annotation!=null){//如果有注解,则将类对象,方法实列存储到Map中。
                    String type = annotation.evenetType();
                    container.registerServer(method,item.getValue(),type);
                }
            });
        });
    }
}

       其中ServiceContainer 定义了methContainer与objectContainer两个HashMap。key对应@Bind注解的eventType。

import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.HashMap;

@Component
public class ServiceContainer {

    //存储key与方法实列的关系
    private static HashMap<String, Method> methContainer=new HashMap<>();
    //存储key与类对象的关系
    private static HashMap<String, Object> objectContainer=new HashMap<>();

    //注册key与类对象,方法实列关系
    public void registerServer(Method method,Object o,String key){
        if(methContainer.get(key)==null){
            methContainer.put(key,method);
            objectContainer.put(key,o);
        }
    }

    public void doHandle(String key,Object msg){
        Method method = methContainer.get(key);
        Object o = objectContainer.get(key);
        try {
            //反射调用方法
            Object invoke = method.invoke(o, msg);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

整体流程如下:

1、服务启动以后,ContextConfig扫描标注了@service的类的对象,与类中包含了@Bind注解的所有方法的实例。

2、将类对象、方法实例、方法上@Bind注解中的eventType三者之间的关系,存储到ServiceContainer中的methContainer、objectContainer中。

3、controller发布eventType为111的事件。

4、listenr监听到该事件后,根据eventType,获取对应的方法实例与类对象,执行方法eventTest(Object msg)。

  • 19
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值