1.什么是注解?
注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。相信很多学过spring等框架的同学对注解一定不陌生吧!比如@Service,@Repository,@Autowired等等。就算没有学过框架,但凡你只要学了java基础,你都会接触到注解。比如一个子类继承父类的方法,该方法前面会有@Override,这个其实就是注解!
2.如何声明注解?
使用@interface关键字就能声明一个注解。如下例:
public @interface TestAnno {
}
3.如何声明注解的生命周期和作用域?
@Target(ElementType.FIELD) //作用域 FIELD表示只能作用在字段中
@Retention(RetentionPolicy.RUNTIME) //生命周期 RUNTIME表示运行时有效
@Documented //让它定制文档化功能,使用使用此注解时必须设置RetentionPolicy为RUNTIME
@Inherited //让它允许继承,可作用到子类
public @interface Column {
/**
* 属性变量只能声明无参无异常的,当只有一个属性时,属性名称为value,
*/
String value();
}
ElementType中的所有值都可以作为@Column的作用域,而且作用域可以同时有多个值比如@Target(ElementType.TYPE,ElementType.FIELD)表示该注解可以作用在类上,也能作用在字段上。RetentionPolicy的取值有三个,分别是SOURCE,CLASS,RUNTIME。分别表示源码注解,注解会保留在源码中,在编译和运行时无效。编译注解,注解会保留在class文件中,编译时会识别,运行时不识别。运行时注解,注解会保留到class文件中,同时运行时也会被识别。
4.注解的应用
注解的应用十分广泛,这里只展示一小个方面来说明注解的使用原理。当我们使用数据库查询数据时,大多数时候需要我们自己来写sql语句,当我们的逻辑比较多,较复杂时,会显得非常不方便。下面将使用自定义注解实现自动拼接sql,然后自动封装查询结果
首先自定义两个注解
@Target(ElementType.FIELD) //作用域 FIELD表示只能作用在字段中
@Retention(RetentionPolicy.RUNTIME) //生命周期 RUNTIME表示运行时有效
public @interface Column {
/**
* 属性变量只能声明无参无异常的,当只有一个属性时,属性名称为value
*/
String value();
}
@Target(ElementType.TYPE) //作用域 TYPE表示只能作用在类中
@Retention(RetentionPolicy.RUNTIME)//生命周期 RUNTIME表示运行时有效
public @interface Table {
/**
* 属性变量只能是无参无异常的,当只有一个属性时,属性名称为value
*/
String value();
}
创建一个javaBean
@Table("USER")
public class User implements java.io.Serializable {
private static final long serialVersionUID = 1L;
private int id;
@Column("USER_ID")
private String userId; //用户ID
@Column("USER_NAME")
private String userName; //用户名称
@Column("PASSWORD")
private String password; //用户密码
@Column("SEX")
private String sex; //用户性别
@Column("EMAIL")
private String email; //电子邮件
@Column("PHONE")
private String phone; //联系电话
@Column("BIRTHDAY")
private Date birthday; //出生日期
@Column("ADDRESS")
private String address; //家庭住址
@Column("ACTIVE_FLAG")
private int activeFlag=1; //用户活跃标志:0 删除,1 活跃
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public int getActiveFlag() {
return activeFlag;
}
public void setActiveFlag(int activeFlag) {
this.activeFlag = activeFlag;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
创建一个JdbcUtil的工具类,使用的是c3p0连接池
public class JdbcUtil {
private static ComboPooledDataSource dataSource = new ComboPooledDataSource();
private static Connection getConnection() {
Connection connection = null;
try {
connection = dataSource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return connection;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public static List<Object> query(Object o) {
Class c = o.getClass();//获取类对象
StringBuffer sb = new StringBuffer();
boolean isExist = c.isAnnotationPresent(Table.class);//判断c中是否存在注解
String tableName = null;
if (isExist) {
//获取表名
Table table = (Table)c.getAnnotation(Table.class);
tableName = table.value();
sb.append("select * from ").append(tableName).append(" where 1=1");
}
Field[] fields = c.getDeclaredFields();
//遍历字段名
for (Field field:fields) {
//判断该字段是否存在注解
if (field.isAnnotationPresent(Column.class)) {
//获取字段名
Column column = field.getAnnotation(Column.class);
String columnName = column.value();
//获取字段值
String fieldName = field.getName();
String getMethodName = "get"+fieldName.substring(0, 1).toUpperCase()+fieldName.substring(1);
Object fieldValue = null;
try {
fieldValue = c.getMethod(getMethodName).invoke(o);
} catch (Exception e) {
e.printStackTrace();
}
//拼接sql
if (fieldValue!=null) {
sb.append(" and ").append(columnName).append("=");
if (fieldValue instanceof String) {
sb.append("'").append(fieldValue).append("'");
}else if (fieldValue instanceof Integer) {
sb.append(fieldValue);
}
}
}
}
String sql = sb.toString();
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet rs = null;
try {
connection = getConnection();
preparedStatement = connection.prepareStatement(sql);
rs = preparedStatement.executeQuery();
ResultSetMetaData rMetaData = rs.getMetaData();//获取元数据对象
List<Object> list = new ArrayList<Object>();
while (rs.next()) {
Object object = c.newInstance();//通过类对象获取实例对象
for (int i = 1; i <= rMetaData.getColumnCount(); i++) {
Object value = rs.getObject(i);//获取该列的值
for (int j = 0; j < fields.length; j++) {
if (fields[j].getName().equalsIgnoreCase(rMetaData.getColumnName(i).replace("_", ""))) {
fields[j].setAccessible(true);//私有变量,其它对象不具有访问权,故设置可访问标志为true,给fields[j]提供权限
fields[j].set(object, value);
fields[j].setAccessible(fields[j].isAccessible());//还原可访问标志
}
}
}
list.add(object);
}
return list;
} catch (Exception e) {
e.printStackTrace();
}finally {
close(connection, preparedStatement, rs);
}
return null;
}
private static void close(Connection connection,PreparedStatement preparedStatement,ResultSet resultSet) {
try {
if (connection!=null) {
connection.close();
}
if (preparedStatement!=null) {
preparedStatement.close();
}
if (resultSet!=null) {
resultSet.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
在该工具类中,query(Object o)方法就是通过反射的方式来解析注解,获取注解中的值,然后进行相应的处理
写个测试类来验证一下
public class Test {
@org.junit.Test
public void test1() {
User user = new User();
user.setUserName("tom352");
List<Object> list = JdbcUtil.query(user);
List<User> userList = new ArrayList<User>();
if (list!=null && list.size()>0) {
for (Object object : list) {
userList.add((User)object);
}
}
if (userList!=null && userList.size()>0) {
System.out.println(userList.get(0));
}
}
}
输出的值为
User [id=2, userId=201805314616, userName=tom352, password=123456, sex=0, email=tom352@qq.com, phone=13679124685, birthday=1987-02-12, address=?????????????, activeFlag=1]
5.总结
现在发现是不是使用注解方便多了,这个工具类的查询方法对任何javaBean都有效,我们无需去为每个javaBean写sql语句,也无需自己去编写sql语句,只需要将参数封装到对应的javaBean中,它就能根据你的参数为你添加查询条件。当然,这只不过是实现了最简单的where查询条件,有兴趣的同学可以去研究一下如何可以实现多种复杂的查询条件。这里只是给同学们展示了一下注解的原理和应用。其实,使用注解,最关键的如何去解析它,只要这步你搞清楚了,后面的问题也就迎刃而解了。