Java注解
注解:Java提供了一种原程序中的元素关联任何信息和任何元数据的途径和方法
JDK中的注解
@Override
表示覆盖父类的方法
@Deprecated
表示方法已经过时了,使用@SuppressWarnings("deprecation")
来忽略过时的警告
@SuppressWarnings("deprecation")
public void sing(){
Person person = new Child();
person.sing();
}
常见第三方注解
Spring的常见注解:
@Autowired
-可以对成员变量、方法、构造函数、类,进行注释。默认按类型装配@Service
-用于标注业务层组件@Repository
-用于标注数据访问组件,即DAO组件
注解的分类
按照运行机制分类:
- 源码注解 - 注解只在源码中存在,编译成.class文件就不存在了
- 编译时注解 - 注解在源码和.class文件中都存在(
@Override
,@Deprecated
,@SuppressWarnings
)都属于编译时注解 - 运行时注解 - 在运行阶段还起作用,甚至会影响运行逻辑的注解,例如Spring的
@Autowired
注解
按照来源来分:
- 来自JDK的注解
- 来自第三方的注解
- 自定义的注解
元注解对注解进行注解
自定义注解
自定义注解的语法要求
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Description {
String desc();
String author();
int age() default 18;
}
- 使用
@interface
关键字定义注解 - 成员以无参数无异常方式声明,如
String desc();
- 可以使用
default
为成员指定一个默认值,如int age() default 18;
- 成员类型是受限的,合法的类型包括原始类型及
String
,Class
,Annotation
,Enumeration
- 如果注解只有一个成员变量,则成员必须取名为
value()
,在使用时可以忽略成员名和赋值号(=) - 注解类可以没有成员,没有成员的注解成为标识注解
元注解
参考自:深入理解Java:注解(Annotation)自定义注解入门
元注解的作用就是负责注解其他注解。Java5.0定义了4个标准的meta-annotation
类型,它们被用来提供对其它annotation类型作说明。Java5.0定义的元注解:
- @Target 作用域
- @Retention 生命周期
- @Documented 生成javadoc时会包含注解
- @Inherited 允许子类继承
@Target
@Target说明了Annotation所修饰的对象范围:Annotation可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。在Annotation类型的声明中使用了target可更加明晰其修饰的目标。
作用:用于描述注解的使用范围(作用域)(即:被描述的注解可以用在什么地方)
取值(ElementType)有:
- CONSTRUCTOR:用于描述构造器
- FIELD:用于描述域
- LOCAL_VARIABLE:用于描述局部变量
- METHOD:用于描述方法
- PACKAGE:用于描述包
- PARAMETER:用于描述参数
- TYPE:用于描述类、接口(包括注解类型) 或enum声明
使用实例:
@Target(ElementType.TYPE)
public @interface Table {
/**
* 数据表名称注解,默认值为类名称
* @return
*/
public String tableName() default "className";
}
@Target(ElementType.FIELD)
public @interface NoDBColumn {
}
@Retention
@Retention定义了该Annotation被保留的时间长短:某些Annotation仅出现在源代码中,而被编译器丢弃;而另一些却被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略,而另一些在class被装载时将被读取(请注意并不影响class的执行,因为Annotation与class在使用上是被分离的)。使用这个meta-Annotation可以对 Annotation的“生命周期”限制。
作用:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效)
取值(RetentionPoicy)有:
- SOURCE:在源文件中有效(即源文件保留)
- CLASS:在class文件中有效(即class保留)
- RUNTIME:在运行时有效(即运行时保留)
例子:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
public String name() default "fieldName";
public String setFuncName() default "setField";
public String getFuncName() default "getField";
public boolean defaultDBValue() default false;
}
使用自定义注解
使用注解的语法,如下:
这里@Description
作用于eyeColor()
方法
解析注解
通过反射获取类、函数或成员上的运行时注解信息,从而实现动态控制程序运行的逻辑
创建一个名为Description
的注解,如下:
package com.imooc.zhujie;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Description {
String value();
}
Child类中的使用Description注解,如下:
package com.imooc.zhujie;
@Description("I am class annotation")
public class Child implements Person{
@Override
@Description("I am method annotation")
public String name() {
// TODO Auto-generated method stub
return null;
}
@Override
public int age() {
// TODO Auto-generated method stub
return 0;
}
@Override
public void sing() {
// TODO Auto-generated method stub
}
}
如何解析注解?
public static void main(String[] args) {
//使用类加载器加载类
try {
Class c = Class.forName("com.imooc.zhujie.Child");
//找到类上面的注解
boolean isExist = c.isAnnotationPresent(Description.class);
if (isExist) {
//拿到注解实例
Description d = (Description) c.getAnnotation(Description.class);
System.out.println(d.value());
}
//找到方法上的注解
Method[] ms = c.getMethods();
for (Method method : ms) {
boolean isMExist = method.isAnnotationPresent(Description.class);
if (isMExist) {
Description d = method.getAnnotation(Description.class);
System.out.println(d.value());
}
}
//另外一种解析方法
for (Method method : ms) {
Annotation[] as = method.getAnnotations();
for (Annotation annotation : as) {
if (annotation instanceof Description) {
Description d = (Description)annotation;
System.out.println(d.value());
}
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
应用
这个应用的实例是,代替Hibernate的解决方案,核心代码通过注解来实现
需求:
1.有一张用户表,字段包括用户ID,用户名,昵称,年龄,性别,所在城市,邮箱,手机号
2.方便对每个字段或字段组合进行检索,并打印出SQL
3.使用方式要足够简单
首先考虑的是代码如何与数据库进行映射?
数据库有表名,有字段名,那代码如何知道数据库的表名和字段名呢?考虑使用注解
创建Table
注解,用来注解类,表示表名
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
String value();
}
创建Column
注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
String value();
}
创建一个查询方法,通过对象来查询,步骤大致如下:
1.获取table的名字
2.遍历所有的字段,处理每个字段对应的sql
代码如下,详细过程参见注释
private static String query(Object f)
{
StringBuilder sb = new StringBuilder();
//1.获取class
Class c = f.getClass();
//2.获取到table的名字
boolean exists = c.isAnnotationPresent(Table.class);
if (!exists) {
return null;
}
Table t = (Table) c.getAnnotation(Table.class);
String tableName = t.value();
sb.append("select * from ").append(tableName).append(" where 1=1 ");
//3.遍历所有的字段
Field[] fields = c.getDeclaredFields();
for (Field field : fields) {
//4.处理每个字段对应的sql
//4.1获取到字段名 拼装sql
boolean fExists = field.isAnnotationPresent(Column.class);
if (!fExists) {
continue;
}
Column column = field.getAnnotation(Column.class);
String columnName = column.value();
//4.2获取到字段的值
String fieldName = field.getName();
String getMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
Object fieldValue = null;
try {
Method getMethod = c.getMethod(getMethodName);
fieldValue = getMethod.invoke(f);
} catch (Exception e) {
e.printStackTrace();
}
//4.3拼装sql
if (fieldValue == null || (fieldValue instanceof Integer && (Integer)fieldValue==0)) {
continue;
}
sb.append(" and ").append(columnName);
if (fieldValue instanceof String) {//字符串添加单引号
if (((String) fieldValue).contains(",")) {//in条件,分割
String[] values = ((String) fieldValue).split(",");
sb.append(" in(");
for (String v : values) {
sb.append("'").append(v).append("'").append(",");
}
sb.deleteCharAt(sb.length()-1);//删除最后一个逗号
sb.append(")");
}else{
sb.append(" = ").append("'").append(fieldValue).append("'");
}
}else if(fieldValue instanceof Integer){
sb.append(" = ").append(fieldValue);
}
}
return sb.toString();
}
测试如下:
Filter f1 = new Filter();
f1.setId(10);//查询id为10的用户
Filter f2 = new Filter();
f2.setUserName("lucy");//查询用户名为lucy的用户
Filter f3 = new Filter();
f3.setEmail("liu@123.com,zh@163.com,7777@qq.com");//查询邮箱为任意一个的用户
String sql1 = query(f1);
String sql2 = query(f2);
String sql3 = query(f3);
System.out.println(sql1);
System.out.println(sql2);
System.out.println(sql3);
控制台输出如下:
select * from user where 1=1 and id = 10
select * from user where 1=1 and user_name = 'lucy'
select * from user where 1=1 and email in('liu@123.com',' zh@163.com',' 7777@qq.com')
参考: