概述
从 JDK 5.0 开始, Java 增加了对元数据(MetaData) 的支持, 也就是 Annotation(注解)。
什么是Annotation,以及注解的作用?三个基本的 Annotation:
@Override: 限定重写父类方法, 该注解只能用于方法
@Deprecated: 用于表示某个程序元素(类, 方法等)已过时
@SuppressWarnings: 抑制编译器警告.
Annotation 其实就是代码里的特殊标记, 在Java技术里注解的典型应用是:可以通过反射技术去得到类里面的注解,以决定怎么去运行类。
掌握注解技术的要点:
如何定义注解
如何反射注解
自定义Annotation
定义新的 Annotation 类型使用 @interface 关键字
特殊属性value:如果注解中有一个名称为value的属性,那么使用注解时,可以省略value=部分,例如:@MyAnnotation(“xxx")。
声明注解的属性:
Annotation 的
属性声明方式:
String name();
Annotation 属性
默认值声明方式:
String name() default “xxx”;
@Target(value={ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
//@Inherited
public @interface MyAnnotation1 {
String name() default "ydj";
String password() default "lalal";
int age() default 25;
Gender gender() default Gender.男;
MyAnnotation2 my2() default @MyAnnotation2(name="llll");
Class clazz() default String.class;
String[] ss() default {"aa","bbb"};
int[] i() default {1,2};
}
JDK 的元Annotation
元 Annotation指修饰Annotation的Annotation。JDK中定义了如下元Annotation:
@Retention: 只能用于修饰一个 Annotation 定义, 用于指定该 Annotation 可以保留的域, @Rentention 包含一个 RetentionPolicy 类型的成员变量, 通过这个变量指定域。
RetentionPolicy.CLASS: 编译器将把注释记录在 class 文件中. 当
运行 Java 程序时, JVM 不会保留注释. 这是
默认值
RetentionPolicy.RUNTIME:编译器将把注释记录在 class 文件中. 当运行 Java 程序时,
JVM 会保留注释. 程序可以
通过反射获取该注释
RetentionPolicy.SOURCE: 编译器直接
丢弃这种策略的注释
@Target: 指定注解用于修饰类的哪个成员. @Target 包含了一个名为 value 的成员变量.
@Documented: 用于指定被该元 Annotation 修饰的 Annotation 类将
被 javadoc 工具提取成文档.
@Inherited: 被它修饰的 Annotation 将具有继承性.如果某个类使用了被 @Inherited 修饰的 Annotation, 则其
子类将自动具有该注解
提取Annotation 信息
JDK 5.0 在 java.lang.reflect 包下新增了 AnnotationElement 接口, 该接口代表程序中可以接受注释的程序元素
当一个 Annotation 类型被定义为运行时 Annotation 后, 该注释才是运行时可见, 当 class 文件被载入时保存在 class 文件中的 Annotation 才会被虚拟机读取
通过属性set方法上的注解注入类
@Retention(RetentionPolicy.RUNTIME)
public @interface Inject {
String driverClass() default "com.mysql.jdbc.Driver";
String jdbcUrl() default "jdbc:mysql://localhost:3306/bookstore";
String user() default "root";
String password() default "root";
}
public class BookDao {
private ComboPooledDataSource ds; //class
@Inject
public void setDs(ComboPooledDataSource ds) {
this.ds = ds;
}
public ComboPooledDataSource getDs() {
return ds;
}
public void add(Book book){
}
}
public class DaoFactory {
public static BookDao createBookDao(){
BookDao dao = new BookDao();
//向dao注入一个池
//解析出dao所有属性
try{
//1.内省BookDao的属性
BeanInfo info = Introspector.getBeanInfo(dao.getClass(),Object.class);
PropertyDescriptor pds[] = info.getPropertyDescriptors();
for(int i=0;pds!=null && i<pds.length;i++){
//2.得到bean的每一个属性描述器
PropertyDescriptor pd = pds[i];
//3.得到属性相应的set方法
Method setMethod = pd.getWriteMethod();
//4.看set方法上有没有inject 注解
Inject inject = setMethod.getAnnotation(Inject.class);
if(inject==null){//5.没有则跳过
continue;
}
//6.方法有注解,则用注解配置的信息,创建一个连接池
//获取属性的类型(此处为:DataSource.class)
Class propertyType = pd.getPropertyType();
//7.创建一个该属性类型的一个实例对象
Object datasource = propertyType.newInstance();
//8.解析该属性set方法上的注解信息,设置到实例对象datasource里
DataSource ds = (DataSource)createDataSourceByInject(inject,datasource);
//9.调用set方法,将datasource赋值给BookDao的DataSource属性
setMethod.invoke(dao, ds);
}
}catch (Exception e) {
throw new RuntimeException();
}
return dao;
}
//用注解的信息,为池配置属性
private static Object createDataSourceByInject(Inject inject,Object ds) {
//1.获取到注解所有属性相应的方法 如: driverClass url equals hashcode
Method methods[] = inject.getClass().getMethods();
for(Method m : methods){
//2.得到注解的每一个属性名
String methodName = m.getName(); //equals url() password()
PropertyDescriptor pd = null;
try {
//3.得到datasource里相同属性名的描述器
pd = new PropertyDescriptor(methodName,ds.getClass()); //geturl
//4.得到注解属性的值
Object value = m.invoke(inject, null);
//5.调用datasource相应属性的set方法,把注解的值设置进去
pd.getWriteMethod().invoke(ds, value);
} catch (Exception e) {
continue;
}
}
return ds;
}
}
public static void main(String[] args) throws SQLException {
BookDao dao = DaoFactory.createBookDao();
DataSource ds = dao.getDs();
Connection conn= ds.getConnection();
System.out.println(conn);
}
通过属性上的注解注入类
public class BookDao {
@Inject
private ComboPooledDataSource ds; //class
public void setDs(ComboPooledDataSource ds) {
this.ds = ds;
}
public ComboPooledDataSource getDs() {
return ds;
}
public void add(Book book){
}
}
public class DaoFactory2 {
public static BookDao createBookDao(){
BookDao dao = new BookDao();
//1.反射BookDao的所有字段
Field fields[] = dao.getClass().getDeclaredFields();
for(int i=0;fields!=null && i<fields.length;i++){
Field f = fields[i];
f.setAccessible(true);
//2.获取每个字段上的@Inject注解
Inject inject = f.getAnnotation(Inject.class);
//3.没有注解则跳过
if(inject==null){
continue;
}
//4.代表当前获取到的字段上有Inject这个注解,则用注解信息,创建一个池赋到字段上
try{
//5.创建字段需要的连接池
DataSource ds = (DataSource) f.getType().newInstance();
//6.用注解的信息,配置上面创建的池
inject2Datasource(inject,ds);
//7.将创建的池赋值给该字段
f.set(dao, ds);
}catch (Exception e) {
throw new RuntimeException(e);
}
}
return dao;
}
//用注解的信息,配置池
private static void inject2Datasource(Inject inject, DataSource ds) {
//1.获得注解的所有方法
Method methods[] = inject.getClass().getMethods();
for(Method method : methods){
//2.得到注解的每一方法,例如jdbcUrl(),user(),password(),toString(),hashcode()
String name = method.getName();
try{
//3.获取ds上与方法名相应的属性
PropertyDescriptor pd = new PropertyDescriptor(name,ds.getClass());
//4.得到注解属性的值
Object value = method.invoke(inject, null);
//5.把值赋到ds的属性上
pd.getWriteMethod().invoke(ds, value);
}catch (Exception e) {
continue;
}
}
}
}
public class TestFactory2 {
public static void main(String[] args) throws SQLException {
BookDao dao = DaoFactory2.createBookDao();
DataSource ds = dao.getDs();
Connection conn= ds.getConnection();
System.out.println(conn);
}
}