在开始之前,带着几个问题去看:
- 什么是注解?
- 注解有什么作用?
- 如何使用注解?
- 注解的应用场景?
一、注解及其作用
注解是在JDK 1.5之后引入的,是代码中的特殊标记(以“@注解名”)存在,这些标记可以在编译、类加载、运行时被读取,并执行相应的处理。引用Java编程思想里的描述:注解为我们在代码中添加信息提供了一种形式化的方法,使我们可以在稍后某个时刻非常方便的使用这些数据。
二、注解的分类
可以看上面导图,这里对几个元注解做一下简单说明
- @Targe:注解所修饰的对象范围。该值是一个ElementType类型的数组,可取值有FILED METHOD等
- @Retention:用于声明注解的保留策略,可取值有:RetentionPolicy.SOURCE CLASS和RUNTIME,分别表示源码级,编译时和运行时注解;运行时注解可以通过反射来获取该注解信息。
三、使用
3.1 定义注解
下面是一个自定义注解的例子
// 表示该注解为运行时注解,可根据需要设置其为其他两种类型
@Retention(RetentionPolicy.RUNTIME)
// 定义新的注解,使用@interface关键字
public @interface Student {
// 注解中只有成员变量,没有方法
// 定义成员变量,为下面这种无参方法的形式
// 可以使用default关键字为其指定默认值
String name() default "Joey";
int age() default 20;
}
// 如果定义了成员变量,使用该注解时,就必须为其指定值
//(定义了default值的情况除外)
@Student(name = "K", age = "21")
public void test() {
}
关于@Retention值的选择:
如果只是做一些检查操作,则使用SOURCE;
如果编译时预处理,例如生成辅助代码,则选择CLASS;
如果运行时动态获取注解信息,则选择RUNTIME
3.2 注解处理器
注解必须配合注解处理器才能有作用,下面是注解处理器实现的一般方法:
- 针对运行时注解,采用反射机制
- 编译时注解,采用AbstractProcessor处理
下面分别看一下:
- 运行时注解
还是用上面的例子,
@Retention(RetentionPolicy.RUNTIME)
public @interface Student {
String name() default "Joey";
int age() default 20;
}
public class AnnotationTest {
@Student(name = "K", age = "21")
public void test() {
}
}
// 下面是一个简单的注解处理器
public class AnnotationProcessor {
public static void main(String[] args) {
Method[] methods = AnnotationTest.class.getDeclaredMethods();
for (Method m: methods) {
Student student = m.getAnnotion(Student.class);
System.out.println(student.name() + " " + student.age());
}
}
}
getDeclaredMethods和getAnnotion方法都属于AnnotatedElement接口,Class,Method和Filed类都实现了该接口
- 编译时注解处理器
四、依赖注入
控制反转(Ioc):即Inversion of Control,用于实现对象间的解耦;指的是借助第三方实现具有依赖关系的对象之间的解耦;
举个例子,假设A和B对象相互依赖,那么当A在运行至某一点时,会主动去创建B的对象或者使用以及创建好的对象B,此时主动权在A手上;当加入IOC对象后,A与B之间失去了直接的联系,当A需要B时,IOC容器会主动创建一个B对象注入A需要的地方。可见,A创建B的过程由主动变为被动,就是控制反转的意思
依赖注入(DI):即Dependency Injection,值IoC容器运行期间,动态的将某种依赖关系注入到对象中。
依赖注入的实现方法:
这里举一个例子:
当代码中需要依赖其他类时,我们可能会这么写
public class Car {
private Engine engine;
public Car() {
engine = new ConcreteEngine();
}
}
这种方法存在的问题就是耦合度太高,car中需要知道具体的engine实现类,即ConcreteEngine类,一旦engine类型更改,car的构造方法也需要修改
将上面代码改为依赖注入的方法:
public class Car {
private Engine engine;
public Car(Engine engine) {
this.engine = engine;
}
}
这样一来,Car无需关心Engine的实现,即使后者类型变化了,Car也不需要做任何修改
一些简单的依赖注入方法有:
构造方法注入
Setter方法注入
接口注入
五、依赖注入框架
ButterKnife源码分析
参考:《Android进阶之光》