admin管理员组

文章数量:1794759

Java注解实现以及应用

Java注解实现以及应用

Java注解的实现以及注解的作用
    • 四大元注解
    • 自定义注解
    • java反射
    • java注解在java框架中的使用

四大元注解

java注解是在jdk1.5版本出现的,注解(Annotation)可以理解为对代码的解释,可以做一些扩展或者补充。注解通常有这样几个作用范围、作用在class上、还有就是方法上method,还有就是字段上,还可以再注解上。注解与注释的不同就不用赘述了。可能我们在最开始学习或者入门做些小的开发时,很少去理解注解到底是怎么实现。但是只有当我们能真正理解注解,并能在实际中使用他。自己设计出非常精妙的注解,使我们的代码结构更加优雅,功能更加强大的时候,我想这是非常享受。尽管我也是初学者,但是我也很想享受代码在我指尖跳跃的感觉(哈哈哈哈哈哈哈哈哈)。以下都是我的浅见,只为每天坚持学习。 在学习注解之前,我认为应该有一定的java反射的知识,还有jvm的知识。这样我们理解起来就会更加深刻。 首先我们看一下什么是元注解,元注解表示的是对注解的注解,也就是我们在自定义注解的时候,通常都要使用到这些元注解,只有掌握好元注解,才能设计好自定注解。

注解名作用
@Target通常用于表示该注解作用的范围,有一个枚举类型表示ElementType
@Retention我理解该注解的生命周期,通过一个RetentionPolicy的枚举类型来表示注解存在的时间
@Documented描述在使用 javadoc 工具为类生成帮助文档时是否要保留其注解信。
@Inherited使被它修饰的注解具有继承性

以上就是四大元注解,其中上述提到的枚举类型,可以展示一下他们到底枚举的什么内容。

ElementType

类型解释
TYPE通常就用来表示该注解作用在类上
FIELD作用在字段上
METHOD作用在方法上
PARAMETER形式参数声明
CONSTRUCTOR构造函数声明
LOCAL_VARIABLE局部变量声明
ANNOTATION_TYPE注解上
PACKAGE包上
TYPE_PARAMETER
TYPE_USE

常用的就Type、FIELD、METHOD

RetentionPolicy

枚举名作用
SOURCE编译层面
CLASS注释将由编译器记录在类文件中但不需要在运行时被VM保留。这是默认值行为。
RUNTIME可以通过反射获取
自定义注解

自定义一个注解非常简单,1、@interface +注解名定义一个注解。2、在自定义的注解上使用元注解。3、注解内的元素类型,只能是public,并且只能是基本的数据类型(int,float…),以及String Class enum,Annotation,和以上数据类型的数组。4、可以指定默认值。但是在非基本类型数据在指定默认值时,都不能使用null作为默认值。例如,创建一个用于配置信的注解。

public class AppConfig { @Config("123") private String appId; @Config("456") private String appSecret; private String Key; public AppConfig() { System.out.println("appconfig..."); } public String getKey() { return Key; } public void setKey(String key) { Key = key; } public String getAppId() { return appId; } public void setAppId(String appId) { this.appId = appId; } public String getAppSecret() { return appSecret; } public void setAppSecret(String appSecret) { this.appSecret = appSecret; } @Param(clazz = Person.class,id = 1) public void doPost(){ } @Override public String toString() { return "AppConfig{" + "appId='" + appId + '\\'' + ", appSecret='" + appSecret + '\\'' + ", Key='" + Key + '\\'' + '}'; } } @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Param { Class<?> clazz(); int id(); } @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface TypeTest { boolean flag() default true; } @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Config { public String value() default ""; } public class AppConfigService { public static void main(String[] args) { AppConfig appConfig = initAppConfig(); System.out.println("通过初始化的值:"+appConfig.toString()); } public static AppConfig initAppConfig(){ try{ Class clazz = Class.forName("cn.hbmy.AppConfig"); AppConfig appConfig = (AppConfig) clazz.newInstance(); /** * 对于private 属性通过getDeclaredFields方法可以获取到 * */ Field[] fields = clazz.getDeclaredFields(); for(Field field:fields){ System.out.println("获取class中的字段"+field); } /** *获取类上的注解 * */ TypeTest typeTest =(TypeTest) clazz.getAnnotation(TypeTest.class); System.out.println("获取类上注解值:"+typeTest.flag()); /** * 获取方法 * */ Method[] methods = clazz.getDeclaredMethods(); for(Method method:methods) { if(method.isAnnotationPresent(Param.class)){ //判断这个方法是否包含Param注解 method.setAccessible(true); Param param = (Param) method.getAnnotation(Param.class);//得到方法上的注解 Method[] methods1 = param.annotationType().getDeclaredMethods(); for(Method me:methods1){ System.out.println("注解值:"+me.invoke(param, null)); } System.out.println("获取class中的方法"+method.getName()+" "+method.getParameters()); } } /** * 获取构造函数,获取私有的通过getDeclaredConstructors方法 * */ Constructor<AppConfig> [] constructors = clazz.getConstructors(); for(Constructor constructor:constructors) { System.out.println("构造函数"+constructor); } /** * 获取注解 * */ for(Field field:fields){ if(!field.isAccessible()){ field.setAccessible(true); } Config config = field.getAnnotation(Config.class); if(config!=null) { field.set(appConfig,config.value());//设置注解值 System.out.println("config.value:"+field.getName()+" "+config.value()); } } return appConfig; }catch(Exception e){ e.printStackTrace(); } return null; } }

通过上述的例子可以总结自定注解主要包括两步,1、定义一个注解。2、编写注解解析器。如果注解没有解析恐怕注解将毫无作用。 上述例子可能不能很好地体现出注解的作用,现在假设你希望提供一些对象映射的功能,能够自动生成数据库表。用以存储javaBean对象。如果使用xml的话,需要指明类名,以及字段的相关信。然而如果使用注解的话,我们只需将相关信保存在javaBean中。为此我们需要定义一些新注解,用以定义与bean关联的表名,以及与bean属性相关连的列的名字和数据类型。

定义表名注解

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

定义两种数据类型

@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface SQLString { int value() default 0; String name() default ""; Constraints constraints() default @Constraints;//默认初始 } @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface SQLInteger { String name() default ""; Constraints constraints() default @Constraints; }

定义约束

public @interface Constraints { boolean primary_key()default false; boolean unique() default false; boolean allow_null() default true; }

定义Bean

@TableDB(name = "Member_T") public class Member { @SQLInteger(constraints = @Constraints(primary_key = true)) public int id;//主键 @SQLString(20) public String userName; //姓名 @SQLString(20) public String passWord; //密码 @SQLInteger public int age; //年龄 }

注解解析器

public class AnnotationParse { public static String parseDBAnnotation(){ Class clazz = Member.class; TableDB tableDb =(TableDB)clazz.getAnnotation(TableDB.class); StringBuffer res = new StringBuffer(); String name = tableDb.name(); res.append("CREATE TABLE ").append(name).append("(").append("\\n"); // Field[] fields = clazz.getDeclaredFields(); for(Field field:fields){ field.setAccessible(true); if(field.isAnnotationPresent(SQLInteger.class)){ SQLInteger sqlInteger = field.getAnnotation(SQLInteger.class); res.append(field.getName()).append(" ").append("INT ").append(getConstraints(sqlInteger.constraints())).append(","); res.append("\\n"); }else if(field.isAnnotationPresent(SQLString.class)){ SQLString sqlString = field.getAnnotation(SQLString.class); res.append(field.getName()).append(" ").append("VARCHAR(").append(sqlString.value()).append(")"); res.append(getConstraints(sqlString.constraints())).append(","); res.append("\\n"); } } res.append(");"); System.out.println(res.toString()); return res.toString(); } public static String getConstraints(Constraints constraints){ StringBuffer s = new StringBuffer(); if(!constraints.allow_null()){ s.append("NOT NULL"); } if(constraints.primary_key()){ s.append("PRIMARY KEY"); } if(constraints.unique()){ s.append("UNIQUE"); } return s.toString(); } public static void main(String[] args) throws ClassNotFoundException { parseDBAnnotation(); } }

运行效果图,处理一下最后一个,就可以了。

java反射

Java的注解需要依赖于Java的反射机制,Java反射是在运行时可以通过类名.class或者Class.forName等方法获取Class。该Class时类加载过程中加载这一阶段会实现的。

java注解在java框架中的使用

注解在框架中使用有@Autowire、@RequestMapping、@Mapper、@Controller、@Service…等等,还有SpringBoot中的自动装配原理也是通过组合注解实现的,因此注解使得我们需要进行繁琐的操作变得更加简单。用好注解,写好注解,理解注解的底层实现可以让我们更能理解这一特性。不在每天依靠记忆去记住。

本文标签: 注解java