目录
一、自定义注解
1.1、注解的定义
Annotation(注解)就是Java提供了一种元程序中的元素关联任何信息和着任何元数据(metadata)的途径和方法。Annotion(注解)是一个接口,程序可以通过反射来获取指定程序元素的 Annotion对象,然后通过 Annotion对象 来获取注解里面的元数据。
Annotation(注解)是JDK5.0及以后版本引入的。它可以用于创建文档,跟踪代码中的依赖性,甚至执行基本编译时检查。从某些方面看,annotation 就像修饰符一样被使用,并应用于包、类 型、构造方法、方法、成员变量、参数、本地变量的声明中。这些信息被存储在 Annotation 的 “name=value” 结构对中。
1.2、metadata元数据
元数据从 metadata 一词译来,就是“关于数据的数据”的意思。
元数据的功能作用有很多,比如:你可能用过 Javadoc 的注释自动生成文档。这就是元数据功能的一种。总的来说,元数据可以用来创建文档,跟踪代码的依赖性,执行编译时格式检查,代替已有的配置文件。如果要对于元数据的作用进行分类,目前还没有明确的定义,不过我们可以根据它所起的作用,大致可分为三类:
- 编写文档:通过代码里标识的元数据生成文档。
- 代码分析:通过代码里标识的元数据对代码进行分析。
- 编译检查:通过代码里标识的元数据让编译器能实现基本的编译检查。
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)。