参考书:《Java核心技术·卷Ⅱ》
1. 注解
注解的作用是对Java中类,方法,成员变量做标记,然后进行特殊处理。例如,Junit
框架中,@Test
加在方法上,该方法可以被当成测试方法执行,而没有标记的就不能当作测试方法执行。
1.1 自定义注解
自定义注解就是自己设计一个注解来使用。书写的格式如下:
public @interface 注解名称 {
public 属性类型 属性名() default 默认值;
}
在注解中,如果只有一个value
属性,在使用该属性时可以省略value
名称不写,但是如果有多个属性,且没有默认值时,value
名称不能省略,示例如下:
//只有一个value时
public class example{
@MyAnnotation("ok")
public void test(){
}
}
//假设还有属性arg,且没有默认值时,名称不能省略
public class example{
@MyAnnotation(value = "ok", arg = "hello")
public void test(){
}
}
1.2 元注解
元注解就是注解注解的注解。翻译一下就是标注注解的注解,也就是使用在自定义注解上的注解。常用有四种:
@Target
指明自定义注解在哪些地方使用。
常用:
ElementType.TYPE//用于类,接口
ElementType.FIELD//成员变量
ElementType.METHOD//成员方法
ElementType.PARAMETER//方法参数
ElementType.CONSTRUCTOR//构造器
ElementType.LOCAL_VARIABLE//局部变量
@Retention
声明注解的生命周期。
常用:
RetentionPolicy.SOURCE//注解在源码阶段有效,生成的字节码文件中不存在该注解。
RetentionPolicy.CLASS//注解在源码阶段,字节码文件中有效,在运行时没有。这也是默认值。
RetentionPolicy.RUNTIME//注解在源码阶段,字节码文件,运行阶段都有效。开发常用这个值。
@Documented
:执行javadoc
命令时,被该元注解修饰的自定义注解也会生成在文档中。
@Inherited
:如果父类所使用的注解有此修饰,则子类可以继承该注解,否则不能。
1.3 注解解析
注解解析就是判断是否有注解,存在注解就解析出内容。
注解的顶级接口是:Annotation
。所有注解都实现了这个接口。
注解解析的接口是:ElementedAnnotation
,定义了与注解解析相关的方法。所有Class
,Method
,Field
,Constructor
都实现了ElementedAnnotation
接口,都具有注解解析能力。
注解解析是先获取对象,再调用对象的注解解析方法,解析出内容。注解在Class
上就先获取Class
类对象,再拿上面的注解。
2. 动态代理
代理(proxy)就是被代理者没有能力或者不愿意完成某件事情,需要找个人代替自己完成这件事情,动态代理就是对业务功能(方法)进行增强。
动态代理分为JDK动态代理和Cglib动态代理。前者要求有接口,后者要求有父类。
2.1 JDK动态代理
要创建一个代理对象,需要调用Proxy
类下面的newProxyInstance
方法。这个方法有三个参数,第一个是类加载器,负责加载代理类到内存中使用,可以通过Class类对象调用getClassLoader
方法获取。第二个参数是代理对象实现的全部接口,代理要为全部接口的全部方法进行代理,可以通过Class类
对象调用getInterfaces
方法获取。第三个参数是调用处理器,是代理的核心处理逻辑。第三个参数可以new InvocationHandler
,并实现invoke
方法。在invoke
方法中,第一个参数是代理对象本身,一般不管,第二个参数是正在被代理的Method
对象,第三个参数是被代理方法应该传入的参数。在invoke
中通过method.invoke(obj,args)
调用被代理的方法,在此前后都能加其他逻辑对方法进行增强。
2.2 动态代理的优点
- 灵活,可以为任意接口的实现类对象做代理,也可以直接为接口本身做代理。
- 可以为被代理对象的所有方法做代理
- 在不改变方法源码情况下,实现对方法功能增强。
- 提高了程序可拓展性,提高了开发效率。