Java 注解

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文件和运行阶段)

  • 按照注解来源可以分为如下三类:
  1. JDK自带注解:Java内置的三种标准注解:@Override、@Deprecated、@SuppressWarnings 和 四种元注解(元注解-注解的注解):@Target、@Retention、@Documented、@Inherited
  2. 第三方注解,也是我们使用得最多的注解,例如Spring注解、MyBatis注解等
  3. 自定义注解,根据实际功能需要自己编写的注解

自定义注解

  • 注解的基本语法

  • 元注解

  1. @Target:注解的作用域,表示注解可以使用在什么地方上,多个作用域间用逗号隔开
  2. @Retention:注解的生命周期,表示注解存活的阶段
  3. @Inherited:标志性注解,表示该注解可以由子注解继承
  4. @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框架,用于实现依赖注入。

主要包括以下两步:

  1. 自定义注解
  2. 解析注解
  • 自定义注解
自定义@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...
复制代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值