Annotation的技术风格为Java 5 之後所推畅出来的新模式,并将注解区分为作用在代码上及作用在注解上的注解,亦可使用其他框架套件进行处理这些标签,用於增强或修改程序行为等,另所有开发者所产生的Annotation皆可进行扩展,便於开发者将任何资料或元资料(Metadata)与程序元素(类、方法、成员变数等)进行关联,而Spring框架的三大核心运作思想为IoC(Inversion of Control,控制反转)和DI(Dependency Inject,依赖注入)及面向切面的程序设计(Aspect-oriented programming,AOP),故我们这边先透过原生Java开发一个小程序来达到三大类核心思维,以便读者先了解原生Java元件的运作概念。
注解本身就是继承一个@interface的特殊介面接口,处理注解的工具将接收那些实现了这个注解介面的对象,通过java类别反射(Relfection)行为来取得各类别的宣告参数(field)与方法(Method)是否当下有配置注解,并通过getAnnotation方法取得其开发者所配置的注解,其方法最终皆为透过产生一项代理动态物件进行产生调用(invoke)AnnotationInvocationHandler的类别方法,并注入建构子两项参数,分别为开发者所欲取得的Annotation及memberValues集合参数物件,memberValues 为放置注解成员的属性名称和值,该方法会从其中取得memberValues此Map的索引对应值,即可提供给使用者进行各类元数据解析开发。
其片段程序码为
(Annotation)Proxy.newProxyInstance(type.getClassLoader(), new Class[]{type}, new AnnotationInvocationHandler(type, memberValues));
Java 内置了一些常用的注解,其3项在java.lang中,剩下4个在java.lang.annotation中
代码性质注解
Annotation name | Descrption | |
---|---|---|
@Override | 在某个父类别(class)或实现的接口(interface)中进行配置该方法,并确保当前类别重写了该方法。若没有父类别无该方法,会编译报错。 | |
@Deprecated | 可修饰的范围很广,包括类、方法、参数,他表示对应的代码已经过时了,开发者不应该再使用它,编译时会警告。 | |
@SuppressWarnings | 表示会忽略编译过程中的警告 |
作用在注解上的注解
Annotation name | Descrption | |
---|---|---|
@Retention | 标识这个注解怎麽储存,是只在程序码中,还是编入class档案中,或者是在执行时可以通过反射存取。 | |
@Documented | 标记这些注解是否包含在使用者文件中 | |
@Target | 标记这个注解应该是哪种 Java 成员[AnnotationType,Constructor,Field,Local_Variable,Method,Module,Package,Parameter,Type,Type_Parameter,Type_use] | |
@Inherited | 标记这个注解是继承於哪个注解类别 |
Java 7 开始,额外添加了 3 个注解:
Annotation name | Descrption | |
---|---|---|
@SafeVarargs | Java 7 开始支援,忽略任何使用参数为泛型变数的方法或建构函式呼叫产生的警告。 | |
@FunctionalInterface | Java 8 开始支援,标识一个匿名函式或函数式介面。 | |
@Repeatable | Java 8 开始支援,标识某注解可以在同一个声明上使用多次。 |
以下为透过范例程序码仿照Spring框架壳新概念为出发点进行介绍,提供各位开发者进行参考
@Prejump为小编为此套小框架设计的注解模式,运用来配置栏位宣告值与属性,并注入上关联元件的宣告栏位,可以想像成是Spring框架的@Bean注解概念。
@Retention(RetentionPolicy.RUNTIME)
public @interface Prejump {
String value() default "";
String name() default "";
}
ObjectCloneHandler.INVOCATION_HANDLER 为一个Map配置池存放开发者所配置注解模式的暂存位置,可以想像成一个Spring IoC BeanFactory配置池概念,Spring采用CGLib进行切面式编程,小编这边采用ProxyInvocation进行切面式编程。
public class ObjectCloneHandler implements InvocationHandler {
private static volatile ObjectCloneHandler instance = null;
public static Map<String,Object> INVOCATION_HANDLER = new HashMap<String,Object>();
public static synchronized ObjectCloneHandler getInstance() {
if ( instance == null ) {
instance = new ObjectCloneHandler();
}
return instance;
}
private Object delegate;
public Object bind(Object delegate) {
//透过配置池取得对应元件
this.delegate = ObjectCloneHandler.getInstance().getInvocationHandler().get(delegate.getClass().getName());;
return this.delegate;
}
....
....
}
注册逻辑片段,配置完後存入Prejump配置池中。
// 将取得注册後Prejump的注解模式进行存入配置池中,等待开发者进行宣告注入在进行获取
private void filterAnnotations(String[] classes) {
Arrays.stream(classes).forEach( clazz -> {
try {
......
......
ObjectCloneHandler.getInstance().getInvocationHandler().put(clazz,object);
} catch (IllegalAccessException illegalAccessException) {
System.out.println("Core :: error :: IllegalAccessException ! ");
} catch (ClassNotFoundException classNotFoundException) {
System.out.println("Core :: error :: classNotFoundException ! ");
} catch (InstantiationException instantiationException) {
// System.out.println("Core :: error :: InstantiationException ! ");
}
});
测试片段,进行注入相关元件
public class AnnotationsTest {
private static final double DELTA = 1e-15;
MakePizzaService makePizzaService;
AnimalService animalService;
@Before
public void init() {
ApplicationBoot.getInstance().run(Main.class);
makePizzaService = (MakePizzaService) ObjectCloneHandler.getInstance().bind(new MakePizzaServiceImpl());
animalService = (AnimalService) ObjectCloneHandler.getInstance().bind(new AnimalServiceImpl());
}
...
...
...
@Test
public void testShowMyCompany() {
assertEquals(makePizzaService.showMyCompany(),"YO ! This is WEISTING COMPANY, We have 15.5 pizzas.");
System.out.println("show my company test pass ! ");
}
}
测试结果,可发现我们已将@Prejump字串注入对应的元件,提供开发者进行逻辑分析
Testing started at 12:30 上午 ...
> Task :cleanTest
> Task :compileJava UP-TO-DATE
> Task :processResources UP-TO-DATE
> Task :classes UP-TO-DATE
> Task :compileTestJava UP-TO-DATE
> Task :processTestResources UP-TO-DATE
> Task :testClasses UP-TO-DATE
> Task :test
show my company test pass !
透过小编提供的范例仿造一个Spring框架中,并以此运作方式先行提供给开发者了解注解在Spring框架中的强大功用与运作方式。
Structure Diagram
根据每个类别进行取得宣告参数与宣告方法的清单,并呼叫getAnnotation进行取得自身所配置的Annotation类别,其方法将会依照不同的对应代码配置的注解取得方式进行呼叫,各类别的宣告参数类型的注解取得来源为从Field类别中的declaredAnnotations方法,各类别中的方法配置注解取得来源为从Executable类别中declaredAnnotations方法,并统一呼叫AnnotationParser类别中的parseAnnotations、parseAnnotations2及annotationForMap取得其使用者所想获取之注解(Annotation / @interface),其最终呼叫的方法程序码为下面片段。
public static Annotation annotationForMap(final Class<? extends Annotation> type, final Map<String, Object> memberValues) {
return (Annotation)AccessController.doPrivileged(new PrivilegedAction<Annotation>() {
public Annotation run() {
return (Annotation)Proxy.newProxyInstance(type.getClassLoader(), new Class[]{type}, new AnnotationInvocationHandler(type, memberValues));
}
});
}
当每个类别的元数据已充分载入,会将此类别暂存在一个容器中,当开发人员要产生一个实体(Entity)时,我们会将预先产生的实体分配给此使用程序,已达到各类物件依赖性关系载入概念,此时便可依据不同的注解(Annotation)映射出不相同的实体类别。
Weisting's Annotation Romp source
Get All Classes Within A Package
由於组件可以在不同的context中使用,有几种方法可以解决这个问题,官方连结。 1.一次性情况的特...
经过了残酷的 PS3 一役、SONY 内部深刻的意识到:已不是能盲目复制过去的成功经验的时代了、必须...
本系列文章同步发布於笔者网站 昨天我们介绍了云端的五个必要条件,今天要接续昨天的云端定义,来介绍云端...
目前我最想先学习的程序语言是python,因为我现在大三在许多课程都会优先使用这个语言,如深度学习中...
在 JavaScript 里还有一个概念称为「Hoisting ( 提升 )」,底下先执行一段范例:...