Java注解编程

一、Java注解编程

在开发过程中我们会看到,继承接口会出现@Override注解,有时候还会提示写注解@SuppressWarnings
还有在使用Spring,Mybatis,Struts2等框架时,常常会使用注解,很多人都知道这些怎么使用,但是不知道他是怎么来的。
其实,这是Java的特性

通过学习注解编程我们可以

1.能够读懂别人写的代码,特别是框架相关的代码
2.编程更加简洁,代码更加清晰

Java注解是一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。

二、注解的分类

这里引用网友的一张图:http://www.cnblogs.com/xing901022/p/3966799.html

上图分类可以分为三类:注解大体上分为三种:标记注解,一般注解,元注解,也可以按照如下机制分类

2.1按照运机制分为
源码注解
编译时的注解
运行时的注解

源码注解:注解只在源码存在,编译成.class文件就不存在了
编译时注解:注解在源码和.class中都存在 (如@Override,@Deprecated,@Suppvisewarnings)
运行时注解:在运行阶段还会起作用,甚至会影响运行逻辑的注解(@Autowired)

2.2按照注解的来源
来自JDK的注解
来自第三方的注解
自定义的注解
2.3元注解
给注解进行的注解

三、常见的注解实例

@Override  //实现类实现接口的时候常常会见到,说明覆盖了父类或接口中的方法
@Deprecated //声明某个方法过时了
@Suppvisewarnings //忽略警告,抑制编译器发出特定的警告
@Override标识覆盖父类或父接口中的方法
实现接口或继承父类的时候,方法上就会有@Override,表示覆盖父类,或父接口中的方法
@Deprecated 标注某个方法过时了,不建议使用

@Suppvisewarnings 忽略警告,抑制编译器发出特定的警告

在我们调用过时的方法或其他情况下我们会看到编译器给我们发出告警

我们可以使用@Suppvisewarnings("抑制那种告警的注解名"),如下,告警消失了

还有一些常见的第三方框架的注解

Spring    @Autowired  @Service  @Repository  
Mybatis   @InserProvider @UpdateProvier  @Options


四、自定义注解

我们主要了解如下内容:
自定义注解的语法要求
注解的注解(元注解)
使用自定义注解
解析注解

4.1自定义注解的语法要求
自定义注解有如下要求:
1.成员类型是受限制的,合法的类型包括原始类型(基本数据类型)以及String,Class,Annotaion,Enumeration
2.注解可以设置默认值,如上图 defalut 18
3.如果只有个注解成员,成员应该为 String value();  //标准是这样建议的
4.注解成员必须是无参无异常方式声明
5.注解类可以没有成员,没有成员的注解称为标识注解

4.2注解的注解(元注解)
上图中的注解类中的注解都是元注解
@Target : 表示该注解的作用范围,可能的值在枚举类 ElemenetType 中,包括: 
          ElemenetType.CONSTRUCTOR        构造器声明 
          ElemenetType.FIELD            域声明(包括 enum 实例) 
          ElemenetType.LOCAL_VARIABLE     局部变量声明 
          ElemenetType.METHOD            方法声明 
          ElemenetType.PACKAGE           包声明 
          ElemenetType.PARAMETER          参数声明 
          ElemenetType.TYPE             类,接口(包括注解类型)或enum声明
@Retention : 表示在什么级别保存该注解信息。可选的参数值在枚举类型 RetentionPolicy 中,包括: 
          RetentionPolicy.SOURCE        注解将被编译器丢弃 
          RetentionPolicy.CLASS       注解在class文件中可用,但会被VM丢弃 
          RetentionPolicy.RUNTIME      VM将在运行期也保留注释,因此可以通过反射机制读取注解的信息。
@Documented : 将此注解包含在 javadoc 中 ,它代表着此注解会被javadoc工具提取成文档。在doc文档中的内容会因为此注解的信息内容不同而不同。
@Inherited :注解类中使用该注解标识,如果类中使用了该注解,子类会继承类中(只有在继承的时候生效,而且只会继承类中的注解,方法或属性中的注解不会生效)的注解

五、注解的解析

概念:通过反射获取类,函数或成员上的运行时的注解信息,从而实现动态控制程序运行的逻辑

自定义注解类:

@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Description {
	
	String value();
}

Person.java

package com.ann.test;

@Description("I am a class annotation")
public class Person {
	
	@Description("I am a method annotation")
	public String name() {
		return null;
	}
	
	@Description("I am a method annotation")
	public int age(){
		return 0;
	}
	
	@Deprecated  //注解说明该方法过时了
	public void sing(){
		
	}
}
Child.java,继承Person,验证@Inherited

package com.ann.test;

public class Child extends Person {

	@Override
	public String name() {
		return null;
	}

	@Override
	public int age() {
		return 0;
	}

	@Override
	public void sing() {

	}

}


ParseAnn.java解析注解,主要通过反射解析

package com.ann.test;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

/*
 * 通过反射解析注解
 * */
public class ParseAnn {

	public static void main(String[] args) {
		//1.使用类加载器加载类
		try{
			Class c = Class.forName("com.ann.test.Child");
			//2.找到类上边的注解
			boolean isExist = c.isAnnotationPresent(Description.class);
			if(isExist){
				//3.拿到注解实例
				Description description = (Description) c.getAnnotation(Description.class);
				System.out.println(description.value()+"---1");
			}
			
			//4.找到方法上的注解
			Method[] methods =c.getMethods();
			for(Method method:methods){
				boolean isMExist = method.isAnnotationPresent(Description.class);
				if(isMExist){
					//5.拿到方法上的注解实例
					Description dmethod = method.getAnnotation(Description.class);
					System.out.println(dmethod.value()+"---2");
				}
			}
			
			//获取所有的方法上的注解
			for(Method m:methods){
				Annotation[] annotations = m.getAnnotations();
				for(Annotation annotation:annotations){
					if(annotation instanceof Description){
						Description d = (Description)annotation;
						System.out.println(d.value()+"---3");
					}
				}
			}
		}catch(ClassNotFoundException e){
			e.printStackTrace();
		}
	}
	
}

结果:

I am a class annotation---1

从结果可以看出,只有类上的注解被继承类,通过反实现一个仿照Hibernate的解决方案,核心代码通过注解实现

六、仿照Hibernate自定义注解实现

需求:1.有一张用户表,字段包括用户ID,用户名,昵称,年龄,性别,所在城市,邮箱,手机号。
2.为方便对每个字段的组合条件进行检索,并打印出SQL
3.使用方式足够简单射可以很方便的解析出注解

自定义注解类:用于注解表Table

package com.ann.test2;

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();
}

注解的使用:

package com.ann.test2;

@Table("user")
public class Filter {
	
	@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;
	}
	
}


注解解析成sql

package com.ann.test2;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Test {

	public static void main(String[] args) {
		Filter filter = new Filter();
		filter.setId(10);//查询id为10的用户
		
		Filter filter2 = new Filter();
		filter2.setUserName("lucy"); //查询用户名为lucy的用户
		
		Filter filter3 = new Filter();
		filter3.setEmail("liu@sina.com,zh@163.com,88888@qq.com");
			
		String sql1= query(filter);
		String sql2= query(filter2);
		String sql3= query(filter3);
		
		System.out.println(sql1);
		System.out.println(sql2);
		System.out.println(sql3);
	}
	
	private static String query(Filter filter){
		StringBuilder sb = new StringBuilder();
		//1.获取class
		Class c = filter.getClass();
		//2.获取到Table的名字
		boolean exists = c.isAnnotationPresent(Table.class);
		if(!exists){
			return null;
		}
		Table table = (Table) c.getAnnotation(Table.class);
		String tableName = table.value();
		sb.append("select * form ").append(tableName).append(" where 1=1 ");
		//3.遍历所有的字段
		Field[] fArray = c.getDeclaredFields();
		for(Field f:fArray){
			//4.处理每个字段对应的sql
			//4.1拿到字段的名字
			boolean fExists = f.isAnnotationPresent(Column.class);
			if(!fExists){
				continue;
			}
			Column column = f.getAnnotation(Column.class);
			//4.2拿到字段的值
			String columnName = column.value();
			//获取属性的值
			String filedName = f.getName();
			String getMethodName = "get"+filedName.substring(0, 1).toUpperCase()+filedName.substring(1);
			try {
				Method method = c.getMethod(getMethodName);
				Object fileValue =method.invoke(filter);
				//4.3拼接sql
				if(fileValue==null || (fileValue instanceof Integer &&(Integer)fileValue==0)){
					continue;
				}
				sb.append(" and ").append(filedName);
				if(fileValue instanceof String){// in()
					if(((String) fileValue).contains(",")){
						String[] values = ((String)fileValue).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(fileValue).append("'");
					}
				}else if(fileValue instanceof Integer){
					sb.append("=").append(fileValue);
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return sb.toString();
	}
}

结果:

select * form user where 1=1  and id=10
select * form user where 1=1  and userName='lucy'
select * form user where 1=1  and email in ('liu@sina.com','zh@163.com','88888@qq.com')


还可以参见博客:http://www.cnblogs.com/pepcod/archive/2013/02/20/2918719.html


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值