1. 回顾注解

注解的存在主要是为了简化XML的配置。Spring6倡导全注解开发。

我们来回顾一下:

● 第一:注解怎么定义,注解中的属性怎么定义?

● 第二:注解怎么使用?

● 第三:通过反射机制怎么读取注解?

注解怎么定义,注解中的属性怎么定义?

package com.powernode.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(value = {ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Component {
    String value();
}

以上是自定义了一个注解:Component

该注解上面修饰的注解包括:Target注解和Retention注解,这两个注解被称为元注解。

Target注解用来设置Component注解可以出现的位置,以上代表表示Component注解只能用在类和接口上。

Retention注解用来设置Component注解的保持性策略,以上代表Component注解可以被反射机制读取。

String value(); 是Component注解中的一个属性。该属性类型String,属性名是value。

1.1 自定义注解  69

package com.powernode.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

//回顾注解  69
//自定义注解

// 标注注解的注解,叫做元注解。@Target注解用来修饰@Component可以出现的位置。
// 以下表示@Component注解可以出现在类上、属性上。
//@Target(value = {ElementType.TYPE, ElementType.FIELD})
// 以下表示@Component注解可以出现在类上
//@Target(value = {ElementType.TYPE})
// 使用某个注解的时候,如果注解的属性名是value的话,value可以省略。
//@Target({ElementType.TYPE})
// 使用某个注解的时候,如果注解的属性值是数组,并且数组中只有一个元素,大括号可以省略。
@Target(ElementType.TYPE)
// @Retention 也是一个元注解。用来标注@Component注解最终保留在class文件当中,并且可以被反射机制读取。
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {

    // 定义注解的属性
    // String是属性类型
    // value是属性名
    String value();

    // 其他的属性
    // 属性类型String
    // 属性名是name
    //String name();

    // 数组属性
    // 属性类型是:String[]
    // 属性名:names
    //String[] names();

    //int[] ages();

    //int age();

}

2. 注解怎么使用? 70

package com.powernode.bean;

//使用自定义注解  70

import com.powernode.annotation.Component;

//@Component(属性名 = 属性值, 属性名 = 属性值, 属性名 = 属性值....)
//@Component(value = "userBean")
// 如果属性名是value,value可以省略。
@Component("userBean")
public class User {
}

用法简单,语法格式:@注解类型名(属性名=属性值, 属性名=属性值, 属性名=属性值......)

userBean为什么使用双引号括起来,因为value属性是String类型,字符串。

另外如果属性名是value,则在使用的时候可以省略属性名

2.1 通过反射机制怎么读取注解?70

package com.powernode.client;

import com.powernode.annotation.Component;

/**
 * 通过反射机制读取注解  70
 **/
public class ReflectAnnotationTest1 {
    public static void main(String[] args) throws Exception{
        // 通过反射机制怎么读取注解
        // 获取类
        Class<?> aClass = Class.forName("com.powernode.bean.User");
        // 判断类上面有没有这个注解
        if (aClass.isAnnotationPresent(Component.class)) {
            // 获取类上的注解
            Component annotation = aClass.getAnnotation(Component.class);
            // 访问注解属性
            System.out.println(annotation.value());
        }
    }
}

回顾注解开发_注解

3. 注解之组件扫描原理  71

假设我们现在只知道包名:com.powernode.bean。至于这个包下有多少个Bean我们不知道。哪些Bean上有注解,哪些Bean上没有注解,这些我们都不知道,如何通过程序全自动化判断。

提示文件路径中不可有中文,所以这里我将代码放在

E:\java\spring6buchong的course17目录中

E:\java\spring6buchong\course17\src\main\java\com\powernode\client

ComponentScan

package com.powernode.client;

import com.powernode.annotation.Component;

import java.io.File;
import java.net.URL;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * 注解之组件扫描原理  71
 **/
public class ComponentScan {
    public static void main(String[] args){
        Map<String,Object> beanMap = new HashMap<>();
        // 目前只知道一个包的名字,扫描这个包下所有的类,当这个类上有@Component注解的时候,实例化该对象,然后放到Map集合中。
        String packageName = "com.powernode.bean";
        // 开始写扫描程序。
        // . 这个正则表达式代表任意字符。这里的"."必须是一个普通的"."字符。不能是正则表达式中的"."
        // 在正则表达式当中怎么表示一个普通的"."字符呢?使用 \. 正则表达式代表一个普通的 . 字符。
        String packagePath = packageName.replaceAll("\\.", "/");
        //System.out.println(packagePath);
        // com是在类的根路径下的一个目录。getSystemClassLoader系统类加载器
        URL url = ClassLoader.getSystemClassLoader().getResource(packagePath);
        String path = url.getPath();
        //System.out.println(path);
        // 获取一个绝对路径下的所有文件
        File file = new File(path);
        File[] files = file.listFiles();//获取bean目录下的所有子文件
        Arrays.stream(files).forEach(f -> {
            try {
                //System.out.println(f.getName());
                //System.out.println(f.getName().split("\\.")[0]);
                String className = packageName + "." + f.getName().split("\\.")[0];
                //System.out.println(className);
                // 通过反射机制解析注解
                Class<?> aClass = Class.forName(className);
                // 判断类上是否有这个注解
                if (aClass.isAnnotationPresent(Component.class)) {
                    // 获取注解
                    Component annotation = aClass.getAnnotation(Component.class);
                    String id = annotation.value();
                    // 有这个注解的都要创建对象
                    Object obj = aClass.newInstance();
                    beanMap.put(id, obj);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

        });

        System.out.println(beanMap);
    }
}

回顾注解开发_注解_02