注解是一种元数据, 能够添加到java代码中. 类、方法、变量、参数、包均可以被注解,注解对注解的代码没有直接影响.前端
定义注解用的关键字是 @interfacejava
在Annotation以前,XML被普遍的应用于描述元数据。可是XML是松耦合的并且维护比较麻烦。 有时使用一些和代码紧耦合的东西更加合适(好比一些服务),Annotation应运而生,并且它更加方便维护。git
目前,许多框架将XML和Annotation两种方式结合使用,平衡二者之间的利弊。例如ButterKnife, EventBus, Retrofit, Dagger等github
Annotations仅仅是元数据,和业务逻辑无关。也就是说Annotations只是指定了业务逻辑,它的用户来 完成其业务逻辑,JVM即是它的用户,它工做在字节码层面.api
固然,前端编译生成字节码阶段,编译器针对注释作了处理,若是有注解错误等,没法正常编译成字节码.只有成功编译生成字节码后.在运行期JVM就能够进行业务逻辑处理.bash
java内置的注解有Override, Deprecated, SuppressWarnings等, 做用相信你们都知道.
元注解就是用来定义注解的注解.其做用就是定义注解的做用范围, 使用在什么元素上等等框架
JDK5.0版本开始提供注解支持: @Documented、@Retention、@Target、@Inheriteddom
@Documented : 是否会保存到 Javadoc 文档中。ide
@Retention : 定义该注解的生命周期。 它有三个枚举类型: RetentionPolicy.SOURCE(只在源码中可用)、 RetentionPolicy.CLASS(在源码和字节码中可用,注解默认使用这种方式)、 RetentionPolicy.RUNTIME(在源码,字节码,运行时都可用,咱们自定义的注解一般使用这种方式) Tips : RetentionPolicy.SOURCE – 在编译阶段丢弃。这些注解在编译结束以后就再也不有任何意义,因此它们不会写入字节码。 @Override, @SuppressWarnings都属于这类注解测试
@Target : 表示该注解用于什么地方。若是不明确指出,该注解能够放在任何地方。如下是一些可用的参数。 Tips : 属性的注解是兼容的,你能够添加多个属性。 ElementType.TYPE:用于描述类、接口或enum声明 ElementType.FIELD:用于描述实例变量 ElementType.METHOD:方法 ElementType.PARAMETER参数 ElementType.CONSTRUCTOR构造器 ElementType.LOCAL_VARIABLE本地变量 ElementType.ANNOTATION_TYPE 另外一个注释 ElementType.PACKAGE 用于记录java文件的package信息
@Inherited : 是否能够被继承,默认为false
如下代码所有经过Idea开发
建立一个注解类
@Retention(RetentionPolicy.RUNTIME)
public @interface SingleAnno {
String value() default "shy";
}
复制代码
引用它
public class MyClass {
@SingleAnno("single")
public void run(){ }
}
复制代码
经过反射获取值
public class TestDemo {
@Test
public void test(){
Class<MyClass> myClass = MyClass.class;
for (Method method : myClass.getDeclaredMethods()){
SingleAnno anno = method.getAnnotation(SingleAnno.class);
if(anno != null){
System.out.println(method.getName());//打印方法名
System.out.println(anno.value());//打印注解值
}
}
}
}
复制代码
控制台能够看到,输出的是single
run
single
复制代码
Annotations只支持基本类型、String及枚举类型。注释中全部的属性被定义成方法,并容许提供默认值。
①定义注解类型(称为A),最好给A加上运行Retention的RUNTIME注解.默认应该是SOURCE类型. ②定义属性,实际上是方法表示.提供默认值. ③在其余类方法(称为M)等添加A注解,并给A指定属性值. ④能够在其余地方获取M方法,而后获取M的注解,并获取注解值等.
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MultiAnno {
enum Priority{HIGH,MID,LOW}
enum Status {START,PAUSE,STOP}
String name() default "TheShy";
Priority priority() default Priority.HIGH;
Status status() default Status.START;
}
复制代码
核心: 经过聚合来实现,让代理类持有委托类的引用便可.
一个小例子: 咱们用一个随机睡眠时间模拟火车运行的时间。若是我要计算运行时间,而且这个类没法改动.
public interface Runnable {
void running();
}
public class Train implements Runnable {
public void running() {
System.out.println("Train is running......");
int ranTime = new Random().nextInt(1000);
try {
Thread.sleep(ranTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
复制代码
这里有不少解决方案: 例如在调用方法地方的先后记录,继承(继承Train调用父类方法),聚合(新建Train2,构造方法传入Train对象,而后调用running).
可是若是再增长需求:在running方法先后打印日志,并控制执行顺序,固然是用继承仍是能够实现,可是要继续建立新的子类,致使无限扩展......
这时候修改聚合,使其成为静态代理就能够完美解决这个问题: 将构造方法改传入Runnable接口:
//代理-在方法先后打印日志
public class TrainLogProxy implements Runnable {
private Runnable runnable;
public TrainLogProxy(Runnable runnable) {
this.runnable = runnable;
}
public void running() {
System.out.println("Train running start...");
runnable.running();
System.out.println("Train running end...");
}
}
复制代码
//代理-计算执行时间
public class TrainTimeProxy implements Runnable {
private Runnable runnable;
public TrainTimeProxy(Runnable runnable) {
this.runnable = runnable;
}
public void running() {
long start = System.currentTimeMillis();
runnable.running();
long end = System.currentTimeMillis();
System.out.println("run time = " + (end - start));
}
}
复制代码
接下来:
Train train = new Train();
//想先计算执行时间,后打印log
// TrainTimeProxy trainTimeProxy = new TrainTimeProxy(train);
// TrainLogProxy trainLogProxy = new TrainLogProxy(trainTimeProxy);
// trainLogProxy.running();
//想先打印log,后计算执行时间
TrainLogProxy trainLogProxy = new TrainLogProxy(train);
TrainTimeProxy trainTimeProxy = new TrainTimeProxy(trainLogProxy);
trainTimeProxy.running();
复制代码
继承和聚合的区别:
接下来,观察上面的类TimeProxy,在它的fly方法中咱们直接调用了Runable->run()方法。换而言之,TrainTimeProxy其实代理了传入的Runnable对象,这就是典型的静态代理实现。 从表面上看,静态代理已经完美解决了咱们的问题。但是,试想一下,若是咱们须要计算SDK中100个方法的运行时间,一样的代码至少须要重复100次,而且建立至少100个代理类。往小了说,若是Train类有多个方法,咱们须要知道其余方法的运行时间,一样的代码也至少须要重复屡次。所以,静态代理至少有如下两个局限性问题:
那么,咱们是否可使用同一个代理类来代理任意对象呢?咱们以获取方法运行时间为例,是否可使用同一个类(例如:TrainProxy)来计算任意对象的任一方法的执行时间呢?甚至再大胆一点,代理的逻辑也能够本身指定。好比,获取方法的执行时间,打印日志,这类逻辑均可以本身指定。
核心原理 :
首先经过Proxy.newProxyInstance方法获取代理类实例,然后能够经过这个代理类实例调用代理类的方法,对代理类的方法的调用实际上都会调用中介类(调用处理器)的invoke方法,在invoke方法中咱们调用委托类的相应方法,而且能够添加本身的处理逻辑。
Runnable runnable = (Runnable) Proxy.newProxyInstance(Runnable.class.getClassLoader(), new Class[]{Runnable.class}, new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before");
Object invoke = method.invoke(new Train(), args);
System.out.println("after");
return invoke;
}
});
runnable.running();
复制代码
以上咱们就成功的经过不修改Train类在执行running()先后打印了日志.
代理模式最大的特色就是代理类和实际业务类实现同一个接口(或继承同一父类),代理对象持有一个实际对象的引用,外部调用时操做的是代理对象,而在代理对象的内部实现中又会去调用实际对象的操做
Java动态代理其实内部也是经过Java反射机制来实现的,即已知的一个对象,而后在运行时动态调用其方法,这样在调用先后做一些相应的处理
![]()
经过动态代理+注解,完成相似retrofit效果,在InvocationHandler的Invoke方法处获取方法、方法注解、方法参数注解、方法参数等信息,根据状况设置adapter,完成业务逻辑. (固然,这里省略了adapter的动做)
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Get {
public String value();
}
复制代码
public interface ServerAPI {
@Get("https://www.baidu.com/")
public String getBaiduHome(@Query("type") String type);
@Post("https://www.baidu.com/update")
public String getBaiduUser(@Field("name") String name, @Field("age") String age);
}
复制代码
public class APICreater {
public static ServerAPI create(Class<ServerAPI> api){
ServerAPI serverAPI = (ServerAPI) Proxy.newProxyInstance(api.getClassLoader(), new Class[]{api}, new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
getMethodMsg(method, args);
if ("getBaiduHome".equals(method.getName())) {
return "I am getBaiduHome return by proxy";
}
if ("getBaiduUser".equals(method.getName())) {
return "I am getBaiduUser return by proxy";
}
ServerAPI obj = getAPI();
return method.invoke(obj, args);
}
});
return serverAPI;
}
private static ServerAPI getAPI() {
return new ServerAPI() {
@Override
public String getBaiduHome(String type) {
return null;
}
@Override
public String getBaiduUser(String name, String age) {
return null;
}
};
}
// 获取了注解信息和参数信息,结合起来就能够实现本身的自定义方法.
private static void getMethodMsg(Method method, Object[] args) {
AnnoBean bean = new AnnoBean();
bean.setMethodName(method.getName());
Annotation[] annotations = method.getAnnotations();
for (Annotation annotation : annotations) {
if (annotation instanceof Get) {
Get getAnni = (Get) annotation;
String value = getAnni.value();
bean.setMethodAnniType("Get");
bean.setMethodAnniValue(value);
}
if (annotation instanceof Post) {
Post getAnni = (Post) annotation;
String value = getAnni.value();
bean.setMethodAnniType("Post");
bean.setMethodAnniValue(value);
}
}
bean.setMethodArgs(Arrays.asList(args));
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
for (Annotation[] annotation : parameterAnnotations) {
for (Annotation annotation1 : annotation) {
if (annotation1 instanceof Field) {
List<String> list = bean.getParamAnniList();
if (list == null) {
list = new ArrayList<String>();
}
list.add("paramAnniType: field " + " value: " + ((Field) annotation1).value());
bean.setParamAnniList(list);
}
if (annotation1 instanceof Query) {
List<String> list = bean.getParamAnniList();
if (list == null) {
list = new ArrayList<String>();
}
list.add("paramAnniType: query " + " value: " + ((Query) annotation1).value());
bean.setParamAnniList(list);
}
}
}
System.out.println(bean.toString());
}
}
复制代码
public class TestRetrofitDemo {
@Test
public void testRetrofit(){
ServerAPI serverAPI = APICreater.create(ServerAPI.class);
String homeeeeee = serverAPI.getBaiduHome("Homeeeeee");
System.out.println("-----" + homeeeeee);
}
}
复制代码
最后测试一下输出结果:
AnniBean{methodName='getBaiduHome', methodArgs=[Homeeeeee], methodAnniType='Get', methodAnniValue='https://www.baidu.com/', paramAnniList=[paramAnniType: query value: type]}
-----I am getBaiduHome return by proxy
复制代码
github地址 : github.com/saurylip/An…