目录
一.注解
1.1 注解概述
Java注解又称Java标注,是 JDK5.0 引入的一种注释机制。Java语言中的类、构造器、成员变量、成员方法、参数都可以被注解标注。
1.2 注解的作用
对Java中类、方法、成员变量做标记,然后进行特殊处理,至于到底做何种处理由业务需求来决定。
例如:JUnit 框架中,标记了注解 @Test 的方法就可以被当成测试方法执行,而没有标记的就不能当成测试方法执行。
1.3 自定义注解
1.3.1 什么是自定义注解
自定义注解就是自己做一个注解来使用
1.3.2 自定义注解格式
代码示例:
public @interface Book{
String value();
int age() default 11;
}
注意事项
- value是特殊属性,如果只有一个value属性的情况下,注解中的value名称可以省略不写
- 但是如果有多个属性,并且除value之外的其它属性没有默认值,那么value属性的名称是不能省略的
1.4 元注解
1.4.1 什么是元注解
用来对注解进行注解的注解叫做元注解
1.4.2 种类
常见的元注解如 @Target、@Retention、@Documented、@Inherited
1.@Target:约束自定义注解只能在哪些地方使用。@Target中可使用的值定义在 ElementType 枚举类中,常用值如下:
- TYPE:类,接口
- FIELD:成员变量
- METHOD:成员方法
- PARAMETER:方法参数
- CONSTRUCTOR:构造器
- LOCAL_VARIABLE:局部变量
2.@Retention:申明注解的生命周期。@Retention中可使用的值定义在 RetentionPolicy 枚举类中,常用值如下:
- SOURCE: 注解只作用在源码阶段,生成的字节码文件中不存在
- CLASS: 注解作用在源码阶段,字节码文件阶段,运行阶段不存在,默认值.
- RUNTIME:注解作用在源码阶段,字节码文件阶段,运行阶段(开发常用)
3.@Documented:描述在使用javadoc工具为类生成帮助文档时是否要保留其注解信息
4.@Inherited:被他修饰的注解具有继承性。如果某个类使用了被@Inherited修饰的注解,则其子类将自动具有该注解。
例子:
首先创建一个注解 MyInheritedAnnotation,并用元注解@Inherited修饰这个注解
@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyInheritedAnnotation {
public String name() default "pengjunlee";
}
创建一个父类 Parent,并用 @MyInheritedAnnotation 注解修饰这个类
@MyInheritedAnnotation(name = "parent")
public class Parent {
}
创建一个子类 Child 继承父类 Parent,在子类中编写代码查看是否能够获取父类注解@MyInheritedAnnotation中的属性
public class Child extends Parent {
public static void main(String[] args) {
Class<Child> child = Child.class;
MyInheritedAnnotation annotation = child.getAnnotation(MyInheritedAnnotation.class);
System.out.println(annotation.name());
}
}
最后得到一个输出:parent。证明子类确实能够继承父类的注解
1.5 注解解析
1.5.1 什么是注解解析
注解的操作中经常需要进行解析,注解的解析就是判断是否存在注解,如果存在就解析出内容。
1.5.2 相关接口
Annotation: 注解的顶级接口,注解都是Annotation类型的对象(自定义注解默认实现Annotation)
AnnotatedElement:该接口定义了与注解解析相关的解析方法
注意:
所有的类成分Class,Method,Field,Constructor,都实现了AnnotatedElement接口,他们都拥有解析注解的能力。
1.5.3 解析注解的技巧
- 注解在哪个成分上,我们就先拿哪个成分对象
- 比如注解作用成员方法,则要获得该成员方法对应的Method对象,再来拿上面的注解
- 比如注解作用在类上,则要该类的Class对象,再来拿上面的注解
- 比如注解作用在成员变量上,则要获得该成员变量对应的Field对象,再来拿上面的注解
1.5.4 案例
案例一
注解代码:
被注解标注的代码:
解析代码:
案例二:模拟Junit框架
需求:定义若干个方法,只要加了MyTest注解,就可以在启动时被触发执行
分析:
- 定义若干个方法,只要有@MyTest注解的方法就能在启动时被触发执行,没有这个注解的方法不能执行
- 定义一个自定义注解MyTest,只能注解方法,存活范围是一直都在。
注解代码:
测试代码:
二.动态代理
2.1 什么是动态代理
代理就是被代理者没有能力或者不愿意去完成某件事,需要找个人代替自己去完成这件事,动态代理就是用来对业务功能进行代理的。
2.2 步骤
- 创建一个接口
- 创建一个实现类要实现接口,代理通常是基于接口实现的
- 创建代理类
- 创建测试类进行测试
2.3 动态代理的优点
- 非常的灵活,支持任意接口类型的实现类对象做代理,也可以直接为接本身做代理。
- 可以为被代理对象的所有方法做代理。
- 可以在不改变方法源码的情况下,实现对方法功能的增强。(重要)
- 不仅简化了编程工作、提高了软件系统的可扩展性,同时也提高了开发效率。
2.4 代码示例
由于代码太多,这里只放代理类的代码
代理类:
public class MyProxy {
//通过一个静态方法,为用户业务对象返回一个代理对象
public static <T> T getProxy(T obj){
/**
* 第一个参数:为代理对象设置类加载器,类加载器会将代理类加载到内存中,代理对象会根据代理类创建
* 第二个参数:设置代理对象需要继承的接口
* 第三个参数:设置被代理对象要增强的功能
*/
return (T)Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new InvocationHandler() {
/**
*
* @param proxy:用户业务类的类对象
* @param method:用户业务类的对象方法
* @param args:用户业务类的对象方法需要的参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long startTime = System.currentTimeMillis();
Object o = method.invoke(obj,args);
long enTime = System.currentTimeMillis();
System.out.println("该方法花费时间位"+(enTime-startTime)/1000.0+"s");
return o;
}
});
}
}