Java注解浅谈

       自Java5.0版本引入注解之后,它就成为了Java平台中非常重要的一部分。注解相当于一种标记,在程序中加了注解就等于为程序打上了某种标记。注解使得Java源代码中不但可以包含功能性的实现代码,还可以添加元数据。注解的功能类似于代码中的注释,所不同的是注解不是提供代码功能的说明,而是实现程序功能的重要组成部分。Java注解已经在很多框架中得到了广泛的使用,用来简化程序中的配置。开发过程中,我们也时常在应用代码中会看到诸如@Override,@Deprecated这样的注解。
Java SE5内置了三种标准注解:
   @Override,表示当前的方法定义将覆盖超类中的方法。
   @Deprecated,使用了注解为它的元素编译器将发出警告,因为注解@Deprecated是不赞成使用的代码,被弃用的代码。
   @SuppressWarnings,关闭不当编译器警告信息。

上面这三个注解多少我们都会在写代码的时候遇到。Java还提供了4中注解,专门负责新注解的创建。即元注解。

一.元注解。Java5.0定义的4种元注解:

  1.@Target,
  2.@Retention,
  3.@Documented,
  4.@Inherited
下面,分别看看这4种注解的详细说明。

1.1 @Target。(注解用于什么地方)

   @Target说明了Annotation所修饰的对象范围,注解用于什么地方。如果不明确指出,该注解可以放在任何地方。
   作用:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)

   取值(ElementType)有:

   CONSTRUCTOR:构造器的声明
   FIELD:域声明(包括enum实例)
   LOCAL_VARIABLE:局部变量声明
   METHOD:方法声明
   PACKAGE:包声明
   PARAMETER:参数声明
   TYPE:类、接口(包括注解类型)或enum声明
1.2 @Retention。(什么时候使用该注解。定义该注解的生命周期)

@Retention定义了该Annotation被保留的时间长短。表示需要在什么级别保存该注解信息。

作用:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效)

取值(RetentionPoicy)有:

SOURCE: 注解将被编译器丢弃(在源文件中有效,即源文件保留)
CLASS:  注解在class文件中可用,但会被VM丢弃(在class文件中有效,即class保留)
RUNTIME:VM将在运行期间保留注解,因此可以通过反射机制读取注解的信息(在运行时有效,即运行时保留)
1.3 @Documented。(将注解包含在Javadoc中)
@Documented一个简单的Annotations标记注解,表示是否将注解信息添加在java文档中。因此可以被javadoc此类的工具文档化。@Documented是一个标记注解,没有成员。有关标记注解,下文有说明。

1.4 @Inherited。(是否允许子类继承该注解)

@Inherited阐述了某个被标注的类型是被继承的。@Inherited是一个标记注解,没有成员。有关标记注解,下文有说明。
注意:@Inherited annotation类型是被标注过的class的子类所继承。类并不从它所实现的接口继承annotation,方法并不从它所重载的方法继承annotation。

二. 自定义注解。

     使用@interface自定义注解。在定义注解时,不能继承其他的注解或接口。@interface用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)。可以通过default来声明参数的默认值。

 定义注解格式:

public @interface 注解名 {定义体}

 注解参数的可支持数据类型:

    1.所有基本数据类型(int,float,boolean,byte,double,char,long,short)
    2.String类型
    3.Class类型
    4.enum类型
    5.Annotation类型
    6.以上所有类型的数组

Annotation类型里面的参数该怎么设定: 
  第一,只能用public或默认(default)这两个访问权修饰.例如,String value();这里把方法设为defaul默认类型;   
  第二,参数成员数据类型如上所述;  
  第三,如果只有一个参数成员,最好把参数名称设为"value"。

实例代码:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {	
	
}

ps: 1. 没有元素的注解称为标记注解,上面的@Table就是一个标记注解。

      2. 如果只有一个参数成员,最好把参数名称设为"value",例如,

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
	
	String value();

}
在使用注解时,可以不写属性名和等于号,直接写值即可,例如可以这么写@Table("user")。

自定义完注解后,最核心的东西便是要实现注解解析!注解解析主要是通过反射机制实现的。有关反射机制,可以看这篇文章,Java反射浅谈。此处不再详说。

三.  注解解析。

    首先,看看JDK提供的有关注解相关的API方法,

3.1 isAnnotationPresent(Class<? extends Annotation> annotationClass),

如果指定的注解位于此元素上,则返回‘true’,否则返回‘false’。

3.2 <T extends Annotation> T getAnnotation(Class<T> annotationClass),

如果存在此注释,则返回此元素的指定类型的注释,否则为空。

3.3 Annotation[] getAnnotations(),

返回此元素上的所有注释。如果这个元素上没有注释,则返回值是一个长度为0的数组。此方法的调用方可以自由修改返回的数组;它将对返回给其他调用方的数组没有影响。

3.4 Annotation[] getDeclaredAnnotations(),

返回直接在该元素上的注释。此方法忽略了继承的注释。如果在这个元素上没有直接存在的注释,返回值是一个长度为0的数组。此方法的调用方可以自由修改返回的数组;它将对返回给其他调用方的数组没有影响。


下文,结合具体的场景,来实现注解以及注解解析。

 从原理上讲,注解解析器就是通过反射机制获取被检查方法上的注解信息,然后根据注解元素的值进行特定的处理。

四. 注解实战。

    打算使用注解生成SQL语句。

4.1 自定义注解。

(1)  自定义一个Table(表)注解,作用域是类,在运行时有效,只有一个属性,所有命名为‘value’,

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
	String value();
}
(2)  自定义一个Column(列)注解,作用域是类,在运行时有效,只有一个属性,所有命名为‘value’,
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
	String value();
}
4.2 定义一个实体类,使用注解,具体代码如下,

@Table("user")
public class User {
	
	@Column("id")	
	private int id;
	@Column("user_name")
	private String userName;
	@Column("nick_name")
	private String nickName;
	@Column("age")
	private int age;
	@Column("city")
	private  String city;
	@Column("email")
	private String email;
	@Column("mobile")
	private String mobile;
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getUserName() {
		return userName;
	}
	public void setUserName(String userName) {
		this.userName = userName;
	}
	public String getNickName() {
		return nickName;
	}
	public void setNickName(String nickName) {
		this.nickName = nickName;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public String getCity() {
		return city;
	}
	public void setCity(String city) {
		this.city = city;
	}
	public String getEmail() {
		return email;
	}
	public void setEmail(String email) {
		this.email = email;
	}
	public String getMobile() {
		return mobile;
	}
	public void setMobile(String mobile) {
		this.mobile = mobile;
	}
}
其中‘@Table("user")’表示数据库中表名,而类似‘@Column("id")      private int id;’表示 该字段(属性)在表中对应的列名。
 注解部分已经实现了,接下来就需要实现注解解析,具体代码如下,

4.3 注解解析,具体代码如下,

/**
	 * 生成sql
	 * @param f
	 * @return
	 */
	private static String query(Object f) {
		StringBuilder builder=new StringBuilder();
		//1 获取class
		Class c=f.getClass();
		//是否有Table注解
		boolean exists=c.isAnnotationPresent(Table.class);
		if(!exists){
			return null;
		}
		//2 获取table的名字
		Table t=(Table)c.getAnnotation(Table.class);
		String tableName=t.value();
		builder.append("select * from ").append(tableName).append(" where 1=1");
		//3 遍历所有字段
		Field[] fArray=c.getDeclaredFields();
		for(Field field:fArray)
		{
			//4 处理每个字段对应的sql
			// //是否有Column注解
			boolean fExists=field.isAnnotationPresent(Column.class);
			if(!fExists){
				continue;
			}
			// 4.1 拿到字段名
			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); 
			Method getMethod;
			Object fieldValue = null;
			try {
				getMethod = c.getMethod(getMethodName);
				 fieldValue=(Object) getMethod.invoke(f);
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			//如果值为null或者值为Integer类型并且等于0,则不生成sql
			if(fieldValue==null||(fieldValue instanceof Integer && (Integer)fieldValue==0)){
				continue;
			}
			// 4.3 组装sql
			builder.append(" and ").append(fieldName);
			//
			if(fieldValue instanceof String){
				//生成模糊查询的sql
				if(((String) fieldValue).contains(",")){
					String[] values=((String) fieldValue).split(",");
					builder.append(" in(");
					for (String v:values) {
						builder.append("'").append(v).append("'").append(",");
					}
					builder.deleteCharAt(builder.length()-1);
					builder.append(")");
				}else{
					builder.append("=").append("'").append(fieldValue).append("'");
				}
			}else 	if(fieldValue instanceof Integer){
				builder.append("=").append(fieldValue);
			}
		}		
		return builder.toString();
	}
代码都有注释,不多说了。下面,我们测试一下这个注解解析器生成的sql,

(1) 测试代码1,

	public static void main(String[] args) {
		User f1=new User();
		f1.setId(10);
		User f2=new User();
		f2.setUserName("xingxing");
		User f3=new User();
		f3.setEmail("123456789@163.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);
		
	}	
  运行结果截图如下,

(2) 测试代码2

	public static void main(String[] args) {
		User f1=new User();
		f1.setEmail("123456789@163.com,wangwei@sina.com,zhuhe@qq.com");
		String 	sql1=query(f1);
		System.out.println(sql1);
	}
  运行结果截图如下,


通过注解生成的sql与我们想要的sql基本一致。

五、总结。

   通过本篇文章,我们对注解有一个新的认识!如果你觉得很炫,那么赶紧动手吧!


  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值