JDK自带的三种常见注解
- @Override:表示覆盖或重写父类的方法
- @DeDeprecated:表示该方法或类已过期,不推荐使用,但是JDK仍保留其用法,使用时编译器会报警报信息
- @SuppressWarnings:表示忽略警报信息
代码示例:
Person接口:
package com.whut.java;
public interface Person {
public String name(String name);
public String sex(String sex);
public void sing();
}
Chid子类:
package com.whut.java;
public class Child implements Person {
@Override
public String name(String name) {
return name;
}
@Override
public String sex(String sex) {
return sex;
}
/**
* @Override:表示覆盖父类的方法
* @Deprecated:表示该方法已过期,不推荐使用
*/
@Override
@Deprecated
public void sing() {
System.out.println("sing...");
}
}
测试类:
@SuppressWarnings("Deprecated")
public static void singTest(){
Child child = new Child();
child.sing();
}
复制代码
注解的分类
- 按运行机制(注解存在于程序的阶段)可以将注解分为三类:源码注解(注解只存在于源码)、编译时注解(注解存在于源码和.class文件,JDK自带的注解都是编译器注解(上面代码测试过))、运行时注解(注解存在于源码、.class文件和运行阶段)
- 按照注解来源可以分为如下三类:
- JDK自带注解:Java内置的三种标准注解:@Override、@Deprecated、@SuppressWarnings 和 四种元注解(元注解-注解的注解):@Target、@Retention、@Documented、@Inherited
- 第三方注解,也是我们使用得最多的注解,例如Spring注解、MyBatis注解等
- 自定义注解,根据实际功能需要自己编写的注解
自定义注解
-
注解的基本语法
-
元注解
- @Target:注解的作用域,表示注解可以使用在什么地方上,多个作用域间用逗号隔开
- @Retention:注解的生命周期,表示注解存活的阶段
- @Inherited:标志性注解,表示该注解可以由子注解继承
- @Documented:表示生成javadoc时会包含注解
- 自定义注解的使用和解析 代码示例-自定义注解和解析
自定义注解:
package com.whut.java;
import java.lang.annotation.*;
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Description {
public String name();
public int age();
public String sex() default "male";
}
注解的使用:
package com.whut.java;
import java.lang.annotation.*;
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Description {
public String name();
public int age();
public String sex() default "male";
}
注解解析:
/**
* 注解解析:通过反射获取类、函数或成员上运行时注解信息,从而实现动态控制程序运行的逻辑
*
*获取类上的注解信息
*/
public static void getClassAnnotation(String className){
try {
Class clazz = Class.forName(className);
// 判断类上是否存在指定的注解
boolean isExist = clazz.isAnnotationPresent(Description.class);
if (isExist){
// 获取注解
Description description = (Description) clazz.getAnnotation(Description.class);
System.out.println(String.format("name = %s, age = %d, sex = %s", description.name(), description.age(), description.sex()));
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 注解解析:通过反射获取类、函数或成员上运行时注解信息,从而实现动态控制程序运行的逻辑
*
* 获取类方法上的注解信息
*/
public static void getMethodAnnotation(String className){
try {
Class clazz = Class.forName(className);
// 获取类的所有声明方法
Method[] ms = clazz.getDeclaredMethods();
for (Method method : ms){
// 判断方法上是否存在指定的注解
boolean isExist = method.isAnnotationPresent(Description.class);
if (isExist){
// 获取注解
Description description = method.getAnnotation(Description.class);
System.out.println(String.format("name = %s, age = %d, sex = %s", description.name(), description.age(), description.sex()));
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
复制代码
代码示例-注解的继承
注解接口:
package com.whut.java;
@Description(name = "nike", age = 20)
public interface Person {
public String name(String name);
public String sex(String sex);
public void sing();
}
接口继承:
package com.whut.java;
public class Teenager implements Person{
@Override
public String name(String name) {
return null;
}
@Override
public String sex(String sex) {
return null;
}
@Override
public void sing() {
}
}
注解类和方法:
package com.whut.java;
@Description(name = "jack", age = 18)
public class Author {
public String name;
public int age;
public String sex;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Description(name = "lee", age = 19)
public void printMessage(){
System.out.println(String.format("name = %s, age = %d, sex = %s", this.name, this.age, this.sex));
}
}
注解类和方法继承:
package com.whut.java;
public class AuthorChild extends Author {
public void printMessage(){
System.out.println("AuthorChild...");
}
}
继承测试:
public static void main(String[] args){
getClassAnnotation("com.whut.java.Author");
getMethodAnnotation("com.whut.java.Author");
/*
* Interface接口中的注解是不会被继承的:
* Person接口使用了@Description注解,Teenager实现Person接口,但是注解不会被Teenager继承下来,所以没有输出任何信息
*/
getClassAnnotation("com.whut.java.Teenager");
/*
类上的注解会被继承下来:
Author类上使用了@Description注解,AuthorChild类继承Author类,Author类上的注解也会被继承下来,所以会输出注解的信息
总结:
注解的继承只能作用于类上,方法上的注解和Interface上的注解都不会被继承
*/
getClassAnnotation("com.whut.java.AuthorChild");
/*
* 方法上的注解是不会被继承的:
* Author类的成员方法上使用了@Description注解,AuthorChild继承Author类,但是方法上的注解不会被继承下来,所以没有输出任何信息
*/
getMethodAnnotation("com.whut.java.AuthorChild");
}
复制代码
注解的项目实战一
自定义一个持久层框架,用来代替Hibernate。
- 步骤一:model类与数据库实现映射
model类:
package com.whut.java.orm;
@Table("user")
public class Filter {
@Column("id")
private int id;
@Column("user_name")
private String userName;
@Column("email")
private String email;
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 getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
数据库映射注解类:
@Table:
package com.whut.java.orm;
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类:
package com.whut.java.orm;
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();
}
复制代码
- 步骤二:使用反射实现sql语句拼接
package com.whut.java.orm;
import javafx.scene.control.Tab;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class HibernateORM {
public static String query(Filter filter){
StringBuilder builder = new StringBuilder();
Class clazz = filter.getClass();
// 获取数据库表名
boolean isExist = clazz.isAnnotationPresent(Table.class);
if (!isExist){
return null;
}
Table table = (Table) clazz.getAnnotation(Table.class);
// 拼接sql语句
String tableName = table.value();
builder.append("select * from ").append(tableName).append(" where 1 = 1");
// 获取所有的字段名和字段值
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields){
// 1. 获取字段名
boolean isFieldExist = field.isAnnotationPresent(Column.class);
if (!isFieldExist){
continue;
}
Column column = field.getAnnotation(Column.class);
String columndName = column.value();
// 2. 获取字段值:因为字段都是private,只能通过getXxx()获取字段值,所以只能使用方法反射获取字段值
// 2.1 拼接字段的getXxx方法名
String fieldName = field.getName();
String getFieldName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
// 2.2 方法反射
Object object = null;
try {
Method method = clazz.getMethod(getFieldName);
// 获取返回值
object = method.invoke(filter);
} catch (Exception e) {
e.printStackTrace();
}
// 3. 拼接sql语句
// 3.1 去除字段值为默认值的字段
if (object == null || (object instanceof Integer && (Integer)object == 0 )) {
continue;
}
builder.append(" and ").append(columndName);
// 3.2 字符串拼接
if (object instanceof String){
if (((String) object).contains(",")){
// 3.2.1 "123@qq.com, 456@163.com, 678@alibaba.com" -> in 查询
String[] strings = ((String) object).split(",");
builder.append(" in (");
for (String str : strings){
builder.append("'").append(str).append("'");
}
builder.deleteCharAt(builder.length() - 1);
builder.append(")");
}else {
// 3.2.2 "jack" -> 字符串直接拼接
builder.append(" = ").append("'").append((String) object).append("'");
}
}else {
// 3.3 基本数据类型拼接
builder.append(" and ").append(columndName).append(" = ").append(object);
}
}
builder.append(";");
return builder.toString();
}
public static void main(String[] args){
Filter filter = new Filter();
filter.setId(1);
Filter filter2 = new Filter();
filter2.setUserName("jack");
Filter filter3 = new Filter();
filter3.setEmail("123@qq.com, 456@163.com, 678@alibaba.com");
String sql = query(filter);
String sql2 = query(filter2);
String sql3 = query(filter3);
System.out.println(sql);
System.out.println(sql2);
System.out.println(sql3);
}
}
复制代码
注解的项目实战二
自定义一个简单的Spring框架,用于实现依赖注入。
主要包括以下两步:
- 自定义注解
- 解析注解
- 自定义注解
自定义@Component注解:
package com.whut.java.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* User: Chunguang Li
* Date: 2018/3/15
* Email: 1192126986@foxmail.com
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
}
复制代码
自定义@Controller注解:
package com.whut.java.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* User: Chunguang Li
* Date: 2018/3/15
* Email: 1192126986@foxmail.com
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {
}
复制代码
自定义@AutomaticLoad注解:
package com.whut.java.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* User: Chunguang Li
* Date: 2018/3/15
* Email: 1192126986@foxmail.com
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutomaticLoad {
}
复制代码
使用枚举存储自定义注解:
package com.whut.java.enumation;
import com.whut.java.annotation.AutomaticLoad;
import com.whut.java.annotation.Component;
import com.whut.java.annotation.Controller;
/**
* User: Chunguang Li
* Date: 2018/3/15
* Email: 1192126986@foxmail.com
*/
public enum EnumClass {
COMPONENT(Component.class, "Component"),
CONTROLLER(Controller.class, "Controller"),
AUTOMATICLOAD(AutomaticLoad.class, "AutomaticLoad"),
;
EnumClass(Class clazz, String name) {
this.clazz = clazz;
this.name = name;
}
Class clazz;
String name;
public Class getClazz() {
return clazz;
}
public String getName() {
return name;
}
}
复制代码
- 使用注解
使用@Component注解:
package com.whut.java.model;
import com.whut.java.annotation.Component;
/**
* User: Chunguang Li
* Date: 2018/3/15
* Email: 1192126986@foxmail.com
*/
@Component
public class HelloService {
public void printService(){
System.out.println("this is helloService...");
}
}
复制代码
使用@Controller和@AutomaticLoad注解:
package com.whut.java.model;
import com.whut.java.annotation.AutomaticLoad;
import com.whut.java.annotation.Controller;
/**
* User: Chunguang Li
* Date: 2018/3/15
* Email: 1192126986@foxmail.com
*/
@Controller
public class HelloController {
public HelloService getHelloService() {
return helloService;
}
public void setHelloService(HelloService helloService) {
this.helloService = helloService;
}
@AutomaticLoad
private HelloService helloService;
public void helloController(){
helloService.printService();
}
}
复制代码
- 解析注解
package com.whut.java;
import com.whut.java.enumation.EnumClass;
import com.whut.java.model.HelloController;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* User: Chunguang Li
* Date: 2018/3/15
* Email: 1192126986@foxmail.com
*/
public class Application {
public static void main(String[] args) {
List<String> fileList = new ArrayList<>();
String packageName = getPackage();
getClassName(packageName, fileList);
Map<String, Object> map = new HashMap<>();
getAnnotationByFile(fileList, map, EnumClass.COMPONENT.getClazz());
getAnnotationByFile(fileList, map, EnumClass.CONTROLLER.getClazz());
HelloController helloController = (HelloController) map.get("com.whut.java.model.HelloController");
helloController.helloController();
}
/**
* 获取Sources Root路径
*/
public static String getPackage(){
// 利用反射获取主类的包名
Class clazz = Application.class;
String sourceRoot = clazz.getPackage().getName();
return sourceRoot;
}
/**
* 遍历Sources Root路径下的所有Java包
*/
public static List<String> getClassName(String sourceRoot, List<String> fileList){
// 纠正路径分割符
String packagePath = sourceRoot.replace(".", "/");
// 获取应用程序类加载器
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// 获取包的URL
URL url = classLoader.getResource(packagePath);
if (url != null){
// 获取URL的协议
String type = url.getProtocol();
// 判断protocol是否为文件(file)
if (type.equals("file")){
getClassNameByFile(url.getPath(), fileList, sourceRoot);
}
}
return fileList;
}
/**
* 获取class文件的全限定类名
*/
public static void getClassNameByFile(String urlPath, List<String> fileList, String sourceRoot){
File file = new File(urlPath);
File[] files = file.listFiles();
for (int i = 0; i < files.length; i++) {
// 判断是否为Java包
if (files[i].isDirectory()){
// 获取包名
String dir = files[i].getName();
// 遍历包下的所有Class文件
File[] targetFiles = files[i].listFiles();
for (int j = 0; j < targetFiles.length; j++) {
// 判断是否为Class文件
if (targetFiles[j].getPath().endsWith(".class")){
// 获取Class文件的名称
String fileName = targetFiles[j].getName();
// 拼接Class文件的全限定类名,并存储到list中
// sourceRoot + 包名 + Class文件名:com.whut.java.model.HelloController,注意添加分割符.
fileList.add(sourceRoot + "." + dir + "." + fileName.substring(0, fileName.lastIndexOf(".")));
}
}
}
}
}
/**
* 根据class的注解完成依赖注入
* @param fileList
* @param map:存储bean,key:全限定类名;value:bean实例
* @param annotation:注解的类型
*/
public static void getAnnotationByFile(List<String> fileList, Map<String, Object> map, Class annotation){
// 遍历Class文件
for (int i = 0; i < fileList.size(); i++) {
try {
// 使用反射实例化每一个Class文件的对象
Class clazz = Class.forName(fileList.get(i));
// 判断class实例是否存在指定类型的注解
if (clazz.isAnnotationPresent(annotation)){
// 如果注解类型为@Component, 直接实例化对象即可
if (annotation.getSimpleName().equals(EnumClass.COMPONENT.getName()))
// 以key:实例化对象的全限定类名,value实例化对象的形式存储到map中
map.put(fileList.get(i), clazz.newInstance());
// 如果注解类型为@Controller,先检查该类是否有@AutomaticLoad,如果有,需要先注入@AutomaticLoad修饰的字段
else if (annotation.getSimpleName().equals(EnumClass.CONTROLLER.getName())){
// 获取class的所有自定义的字段
Field[] fields = clazz.getDeclaredFields();
for (int j = 0; j < fields.length; j++) {
// 判断字段是否被@AutomaticLoad修饰
if (fields[j].isAnnotationPresent(EnumClass.AUTOMATICLOAD.getClazz())){
// 获取字段的类型(全限定类名)
String fileType = fields[j].getType().getName();
// 根据字段的类型从map中取出实例化对象
Object object = map.get(fileType);
Object instance = clazz.newInstance();
// 获取字段的名称
String filedName = fields[j].getName();
// 因为字段访问权限为private,不能使用对象直接赋值,只能调用对象的setXxx方法注入
// 拼接setXxx方法名
String setMethodName = "set" + filedName.substring(0, 1).toUpperCase() + filedName.substring(1);
// 根据指定的方法名和参数列表获取setXxx方法
Method method = clazz.getMethod(setMethodName, fields[j].getType());
// 调用setXxx方法实现注入
method.invoke(instance, object);
// 存储到map中
map.put(fileList.get(i), instance);
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
结果输出:
this is helloService...
复制代码