目录
一,Java注解
Java注解是一种元数据形式,可以被添加到Java代码中的各种元素(类、方法、字段等)上,以提供关于这些元素的额外信息。注解是在Java 5中引入的一项特性,它们不直接影响代码的执行,而是提供了一种机制来对代码进行标记和解释。
注解通常以@
符号开头,放置在注解目标前面。Java提供了一些内置的注解(如@Override
和@Service等),同时也可以自定义注解。
二,元注解
提起自定义注解 就绕不开元注解,就像饭菜离不开食材。
那什么是元注解,可以理解为其他普通注解进行解释说明,下面是5种元注解的简单总结:
1,@Target
该注解的使用范围,限定应用场景。枚举类 ElemenetType 中
TYPE:类,接口
FIELD:字段,枚举的常量
METHOD:函数(方法)
PARAMETER:参数
CONSTRUCTOR:构造函数
ANNOTATION_TYPE:注解类型
LOCAL_VARIABLE:局部变量
PACKAGE:包
2,@Retention
该注解的生存周期,相当于时间戳。
注解生命周期的三个阶段:java源文件是一个阶段;class文件是一个阶段;内存中的字节码是一个阶段。
javac把java源文件编译成.class文件时,去掉里面的RUNTIME注解
类加载器把.class文件加载到内存时,去掉里面的CLASS注解
最后剩下的就是SOURCE注解
枚举类型 RetentionPolicy 中
SOURCE:在源文件中有效,编译后会被丢弃(如@Override,@Deprecated)
CLASS:在class文件中有效,在jvm丢弃
RUNTIME:在运行时有效,class文件保留,jvm运行时保留(很多框架运用反射调用)
2.1 RUNTIME => @Service
2.2 CLASS =>
暂时没遇上,后续遇上会补上
2.3 SOURCE => @Override
@Override是伪代码,表示重写(当然不写也可以),不过写上有如下好处:
1>可以当注释用,方便阅读
2>编译器可以给你验证@Override下面的方法名是否是你父类中所有的,如果没有则报错
2.4 那怎么来选择合适的注解生命周期呢?
首先要明确生命周期长度 SOURCE < CLASS < RUNTIME ,所以前者能作用的地方后者一定也能作用。一般如果需要在运行时去动态获取注解信息,那只能用 RUNTIME 注解; 如果要 在编译时进行一些预处理操作,比如生成一些辅助代码(如 ButterKnife) ,就用 CLASS注解; 如果 只是做一些检查性的操作,比如 @Override 则 可选用 SOURCE 注解。
3, @Documented
javadoc文档生成工具的使用
4, @Inherited
允许子类继承父类中的注解。
常见的是@SpringBootApplication和@Service对比:
5, @Repeatable
同一种注解可多次使用
三,作用和案例
注解为代码元素提供了额外的元数据信息。这些信息可以用于在编译时或运行时进行处理,以实现各种功能。
例如:
@Override注解用于标记方法覆盖父类的行为,这在编译时会进行检查。
@Service注解用于类上,标记当前类是一个service类,加上该注解会将当前类自动注入到spring容器中,不需要再在applicationContext.xml文件定义bean了。
个人现阶段任务自定义注解的作用是标定,用于标定某些具有共性的类,然后使用反射统一处理这些具有共性的类,下面例子就围绕这个进行:
1,路由需求
目前有多个不同的实例报文,需要进行处理,从控制器路由分发到具体的逻辑处理实现中是这样写的,要求是对此做出简化。
D001001ServiceImpl d001001Service = ServiceFactory.getServiceFactory().getApplicationContext().getBean(D001001ServiceImpl.class);
d001001Service.dowork(requestParam);
1.1,自定义注解
首先是写一个服务路由的注解:@ServiceRoute
其中@Service的添加是由于使用的service类都需要@Service,为了精简代码。
package com.example.core.comm;
import org.springframework.stereotype.Service;
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Service
public @interface ServiceRoute {
String routeKey();
}
1.2,自定义注解逻辑实现
使用注解处理器可以在编译时或运行时处理注解信息。注解处理器可以通过反射机制获取注解信息,并根据需要执行相应的操作。
实现一个工厂类ServiceFactory用于实现对应的路由逻辑:
逻辑就是获取spring容器中的bean,根据map特性使用注解中的标记属性找到对应的处理方法。
package com.example.core.comm;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Component
public class ServiceFactory implements ApplicationContextAware {
private ApplicationContext applicationContext;
private static ServiceFactory serviceFactory = new ServiceFactory();
private static Map<String,Object> sourceMap = new ConcurrentHashMap<>();
public static ServiceFactory getServiceFactory() {
return serviceFactory;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public ApplicationContext getApplicationContext() {
return applicationContext;
}
/**
* @PostConstruct该注解被用来修饰一个非静态的void()方法。被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器执行一次。PostConstruct在构造函数之后执行,init()方法之前执行。
* 通常我们会是在Spring框架中使用到@PostConstruct注解 该注解的方法在整个Bean初始化中的执行顺序:
* Constructor(构造方法) -> @Autowired(依赖注入) -> @PostConstruct(注释的方法)
*/
@PostConstruct
public void getSourceMap(){
//获取到所有拥有特定注解的Beans集合,然后遍历所有bean实现业务场景。
Map<String,Object> map = this.applicationContext.getBeansWithAnnotation(ServiceRoute.class);
for (String key:map.keySet()){
Object obj = map.get(key);
//根据bean,getAnnotation方法来获取指定注释类型的注释
ServiceRoute sf = obj.getClass().getAnnotation(ServiceRoute.class);
sourceMap.put(sf.routeKey(),obj);
}
}
/**
*获取指定注解的服务
*/
public Object getService(String routeKey){
for (String key:sourceMap.keySet()) {
if (key.contains(routeKey)){
return sourceMap.get(key);
}
}
return null;
}
}
1.3,自定义注解调用
控制器Controller中的调用改为以下方式:
根据标识获取指定逻辑实现类
IBaseService iBaseService = (IBaseService) ServiceFactory.getServiceFactory().getService("D001.001.001");
if (iBaseService != null){
return iBaseService.dowork(requestParam);
}