一、为什么要学习注解
①因为注解现在非常流行,在很多的时候都被应用,特别是框架相关代码,我们第一步也就是拿到别人用注解写的代码做到能看懂
②让编程更简洁,代码更加清晰
③逼格更高...
二、什么是注解
注解是在jdk1.5之后被引入的,Java提供了一种源程序中的元素关联任何信息和任何元数据的途径和方法。
三、Java中的常见注解
1.jdk自带的注解
@Override @Deprecated @Suppvisewarnings
@Override 覆盖的意思,实现接口,继承父类重写方法时的注解(代码演示在下方自定义注解的子类方法重写父类方法)
@Deprecated:表示该方法已过期,在接口里经常用到,表示该方法已经过期了,但还能用(代码演示见下面自定义注解中父类定义的一个过期的sing方法,在Test类中显示过期)@SuppressWarnings("deprecation"):该注解表示忽略了deprecation这样的一个警告
public class Test {
//该注解表示忽略了deprecation这样的一个警告
@SuppressWarnings("deprecation")
public void sing(){
Person s = new Son();
//sing()方法已经过时 会有警告
s.sing();
}
}
2.常见的第三方注解
①Spring
@Autowired @Service @Repository
@Autowired:自动生成实例,并注入进去
@InserProvider @UpdateProvider @Opitions
四、注解的分类
1.按照运行机制来分
源码注解 编译时注解 运行时注解
①源码注解:该注解只在源码中出现,当编译成.class文件的时候,该注解就木得了
②编译时注解:该注解在源码中和.class文件中都存在 jdk自带的注解都属于编译时注解
③运行时注解:在运行时也起作用,甚至会影响运行时的逻辑,比如Spring中的@Autowired
2.按照来源来分
来自jdk自带的注解 来自第三方的注解 自己定义的注解 元注解:给注解进行注解
五、自定义注解
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;
/**
* 自定义注解
* @author zhmm
*
*/
//元注解(注解的注解)
//说明的是该注解的作用域
@Target({ElementType.METHOD,ElementType.TYPE})
//说明的是该注解的生命周期
@Retention(RetentionPolicy.RUNTIME)
//标识性元注解,允许子类继承,只适用于类继承,不适用于接口继承,且只会继承类的注解,不会继承方法的注解
@Inherited
//标示性元注解,表示的是生成javadoc的时候会包含此注解
@Documented
//使用@interface关键字来定义注解
//成员的类型是受限的除了八种基本数据类型之外其成员类型还可以为String,Class,Annotation,Enumeration
public @interface Description{
// //成员以无参无异常方式声明
// String desc();
// String author();
// //可以用default来给成员定义一个默认的值
// int age() default 18;
String value();
}
注意事项:若注解只有一个成员,则该成员必须起名为value(),在使用的时候可以忽略成员名和赋值号(=)(因为当注解只一个成员的时候,在使用注解时,直接写该唯一成员的值就行,不用写成员名)注解类可以没有成员,没有成员的注解被称为标示注解。
元注解:就是在定义注解的时候所使用的注解,已在上面的代码中指出,不作赘述。
五、如何使用注解
1.使用注解的语法
@<注解名>(<成员名1>=<成员值1>,<成员名2>=<成员值2>,...)
例子:使用上个笔记自定义的注解
@Description(desc="i am eyeColor",author="zhangsan",age=18)
public String eyeColor(){
return "red";
}
2.有关自定义注解的代码演示
使用的是上面的自定义注解:
@Description("我是父类")
public class Person {
@Description("我是方法")
public String name() {
return null;
}
public int age() {
return 0;
}
//该注解表示该方法已经过时
//适用于接口里的方法已经不需要用的时候
@Deprecated
public void sing() {
}
}
//@Description("the name class annotation")
public class Son extends Person{
@Override
// @Description("the name method annotation")
public String name() {
return null;
}
@Override
public int age() {
return 0;
}
@Override
public void sing() {
}
}
六、解析注解
其实就是通过反射获取类、函数或成员上的运行时注解信息,从而实现动态控制程序运行的逻辑
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
/**
* 解析注解
* @author zhmm
*
*/
public class Test2 {
public static void main(String[] args) {
//1.第一步,使用类加载器加载使用该注解的类
try {
Class c = Class.forName("com.ann.test.Son");
//2.拿到该类上的类注解
boolean isExist = c.isAnnotationPresent(Description.class);
if(isExist){
//3.拿到该注解实例
Description d = (Description) c.getAnnotation(Description.class);
System.out.println(d.value());
}
//4.找到方法的注解
Method [] ms = c.getMethods();
for(Method m : ms){
boolean isMExist = m.isAnnotationPresent(Description.class);
if(isMExist){
Description d = (Description) m.getAnnotation(Description.class);
System.out.println(d.value());
}
}
//另外一种解析方法注解的方法
for(Method m : ms){
Annotation [] as = m.getAnnotations();
for(Annotation a:as){
if(a instanceof Description){
Description d = (Description) a;
System.out.println(d.value());
}
}
}
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
七。项目实战
1.项目说明:
该项目取自资格公司的持久层框架,用来代替hibernate的解决方案,核心代码就是通过注解来实现的
2.需求说明:①有一张用户表,字段包括用户的ID,用户名,昵称,年龄,性别,所在城市,邮箱,手机号
②方便的对每个字段或字段的组合条件进行检索,并打印出SQL
分析:因为我们要做持久化操作,所以必然会有一个和表对应的bean,我们给他取名Filter,需要表的注解个字段的注解,也就是两个注解@Table和@Column,还需要一个测试类来根据我们所输入的查询条件动态生成一个SQL语句来返回给数据库连接完成查询(因为和数据库交互代码固定,我们只做到动态生成SQL语句即可),下面来看代码:
package com.ann.project;
/**
* Filter对应的就是我们的bean,
* 它里面有一些属性和对应的get set方法
* 这个类用来和数据库来进行一个映射
* @author zhmm
* 当要查询的表不一样的时候我们只需要改一下Table注解的实参
* 还有@Column的实参以及对应bean中字段的信息即可
*
*/
//这个注解表示这个类对应的是数据库的student表
@Table("student")
public class Filter {
//这个注解表示的是数据库表中的相关字段
@Column("id")
private int stuId;
@Column("name")
private String stuName;
@Column("email")
private String email;
public int getStuId() {
return stuId;
}
public void setStuId(int stuId) {
this.stuId = stuId;
}
public String getStuName() {
return stuName;
}
public void setStuName(String stuName) {
this.stuName = stuName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
package com.ann.project;
import java.lang.annotation.ElementType;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
//作用域:类或者接口
@Target({ElementType.TYPE})
//生命周期:运行时
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
//我们只需要一个名字来表示表名即可
public String value();
}
package com.ann.project;
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.project;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* 1.项目说明:
* 该项目取自资格公司的持久层框架,用来代替hibernate的解决方案,核心代码就是通过注解来实现的
* 2.需求说明:
* ①有一张用户表,字段包括用户的ID,用户名,昵称,年龄,性别,所在城市,邮箱,手机号(由于数据原因我们只谢了ID 姓名和邮箱)
* ②方便的对每个字段或字段的组合条件进行检索,并打印出SQL
*
*
* 这个测试类其实就是完成查询的功能
* 根据不同的条件去检索数据
* @author zhmm
*
*/
public class Test {
public static void main(String[] args) {
//检索ID为10的数据
Filter f1 = new Filter();
f1.setStuId(10);
//检索名字为小猛同学的数据
Filter f2 = new Filter();
f2.setStuName("小猛同学"); //此处我们进行模糊查询
//检索邮箱为这两个任意一个的数据
Filter f3 = new Filter();
f3.setEmail("1158333762@qq.com,1710091321@qq.com");
//调用相关的SQL方法
String sql1 = query(f1);
String sql2 = query(f2);
String sql3 = query(f3);
//打印相关的SQL语句
System.out.println(sql1);
System.out.println(sql2);
System.out.println(sql3);
}
@SuppressWarnings("unchecked")
private static String query(Filter f) {
StringBuffer sb = new StringBuffer();
//1.获取到 class
Class c= f.getClass();
//判断这个注解是否存在
boolean isExist = c.isAnnotationPresent(Table.class);
if (!isExist) {
return null;
}
//2.获取表名
Table t = (Table) c.getAnnotation(Table.class);
String tableName = t.value();
//拼接SQL语句(把表名加入我们的SQL语句)
sb.append(" select * from ").append(tableName).append(" 1=1 ");
//循环遍历所有的字段名
Field[] fArray = c.getDeclaredFields();
for (Field field : fArray) {
//判断这个字段是否为我们想要的字段
boolean fExist = field.isAnnotationPresent( Column.class);
if (!fExist) {
continue;
}
//3.拿到字段名
Column column = field.getAnnotation(Column.class);
String columnName = column.value();
//4.拿到字段的值
//获取字段的名称
String fieldName = field.getName();
//拼装成获取该字段的get方法
String getMethodName = "get" +fieldName.substring(0, 1).toUpperCase()+fieldName.substring(1);
Object fieldValue=null;
try {
//类反射拿到该get方法
Method getMethod =c.getMethod(getMethodName);
//执行该get方法拿到该字段的值
fieldValue = getMethod.invoke(f);
} catch (Exception e) {
}
//5.拼装sql
//如果他是null或者当该字段为int类型为0的时候不作处理
if (fieldValue == null || (fieldValue instanceof Integer && (Integer)fieldValue == 0) ) {
continue;
}
sb.append(" and ").append( columnName );
if (fieldValue instanceof String){
//如果有,我们说明他是个子查询就进行子查询的拼接
if (((String) fieldValue ).contains(",")) {
//字段按,进行分割
String[] values = ((String) fieldValue ).split(",");
//加in
sb.append(" in ( ");
//因为都是字符串,所以每个都要加''
for (String v : values) {
sb.append("'").append(v).append("',");
}
//删掉最后一个逗号
sb.deleteCharAt(sb.length()-1);
sb.append(" )");
//是string类型的加''
} else{
sb.append(" = '").append(fieldValue).append("' ");
}
//如果是int类型的不加''
}else if (fieldValue instanceof Integer){
sb.append(" = ").append(fieldValue).append(" ");
}
}
//返回查询SQL语句
return sb.toString();
}
}