反射使用

java.lang.reflect

获取Class对象的三种方式

public class Demo001 {
    public static void main(String[] args) {
        Class s = Student.class; //方式1
        System.out.println(s.getName());
        System.out.println(s.getSimpleName());
        
        Class c2 = Class.forName("reflect.Student"); //方式2
        System.out.println(c2.getName());
        
        System.out.println(c1==c2); //返回true,说明是同一个对象
        
        Student s = new Student();//方式3
        Class c3 = s.getClass();
        System.out.println(c3==c2); //返回true,也是同一个对象
    }
}

为什么三种对象创建的方式创建出来的对象都是同一个呢?

Class 对象

​ 在 JVM 中,每个类在整个生命周期中只有一个 Class 对象。只有一份字节码。

类加载器 (ClassLoader) → 加载类 → 创建唯一的 Class 对象 → 缓存到 JVM
堆内存 (Heap):
┌─────────────────────────┐
│   Class 对象 (唯一)     │ ← c1, c2, c3 都指向这里
│  - 类名:reflect.Student│
│  - 字段信息             │
│  - 方法信息             │
│  - 构造函数信息         │
└─────────────────────────┘
          ↑
          │ c1, c2, c3 都指向同一个地址

实例对象

Student s1 = new Student();
Student s2 = new Student();
Student s3 = new Student();

这3种都不是同一个对象。

类构造器(构造方法)

Class c1 = Student.class;
Constructor[] constructors = c1.getConstructors();//获取全部构造方法 只能拿public构造方法
Constructor[] declaredConstructors = c1.getDeclaredConstructors();//获取全部构造方法
Constructor ct1 = c1.getConstructor(String.class, int.class); //获取指定参数的构造方法
Constructor ct2 = c1.getDeclaredConstructor(String.class, int.class); //获取指定参数的构造方法

通过类构造器初始化对象

 Constructor constructor = c1.getConstructor(String.class, int.class); //获取指定参数的构造方法
Constructor constructor2 = c1.getDeclaredConstructor(String.class, int.class); //获取指定参数的构造方法

Student s1 = (Student) constructor.newInstance("张三", 18);

Constructor cs4 = c1.getDeclaredConstructor();
cs4.setAccessible( true); //设置访问权限可以访问private构造方法
Student s2 = (Student) cs4.newInstance();

属性

Class c1 = Student.class;
Field[] fields = c1.getFields();//只能拿public字段
for (Field field : fields) {
    System.out.println(field.getName()+"--"+field.getType());
}
Field[] declaredFields = c1.getDeclaredFields();//获取全部字段
for (Field field : declaredFields) {
    System.out.println(field.getName()+"--"+field.getType());
}
//通过名字定位
//        Field name = c1.getField("name"); //拿不到private
//        System.out.println(name.getName()+"--"+name.getType());

Field age = c1.getDeclaredField("age");
System.out.println(age.getName()+"--"+age.getType());

        //为实例对象赋值
Student s = new Student("11",1);
age.setAccessible(true);
age.set(s,18);
System.out.println(s.getAge());

//取值
int ageValue = (int) age.get(s);
System.out.println(ageValue);

方法

public static void testGetMethod() throws Exception {
        Class c1 = Student.class;
        Method[] methods = c1.getMethods();//获取全部方法 只能拿public方法
        for (Method method : methods) {
            System.out.println(method.getName()+"--"+method.getParameterCount() +"---"+method.getReturnType());
        }
        Method[] declaredMethods = c1.getDeclaredMethods();//获取全部方法
        for (Method method : declaredMethods) {
            System.out.println(method.getName()+"--"+method.getReturnType());
        }
        Method getAge1 = c1.getMethod("getAge"); //获取指定参数的构造方法,只能拿public方法
        Method getAge2 = c1.getDeclaredMethod("getAge"); //获取指定参数的构造方法

        Method setAge1 = c1.getDeclaredMethod("setAge", int.class);
        //方法执行
        Student s = new Student("11",1);

        setAge1.invoke(s,18);
        System.out.println(s.getAge());
        Method show = c1.getDeclaredMethod("show");
        show.setAccessible(true);
        show.invoke(s);
}

反射作用

  • 得到一个类的全部成分,然后操作。

  • 破坏封装性,直接可以调用私有的方法。

  • 主要是在java框架使用

注解使用

​ java里的特殊标记,让其他程序根据注解信息来决定怎么执行该程序。

注解本质是一个接口,继承于Annotation接口。

​ 使用@注解的过程就是创建一个实现类对象。

元注解

修饰注解的注解

  • @Target

    @Target(ElementType.TYOE)
    1.TYPE 类,接口
    2.FIELD 成员变量
    3.METHOD 成员方法
    4.PARAMETER 方法参数
    5.CONSTRUCTOR 构造方法
    6.LOCAL_VARIABLE 局部变量
    
  • @Retention 声明注解的保留周期

    1.SOURCE 只在源码阶段,字节码文件不存在
    2.CLASS 默认值,保留到字节码,运行阶段不存在
    3.RUNTIME 常用,保留到运行阶段
    

注解解析

  • ​ 要解析哪个类上面的注解,就要先拿到他。
  • ​ 先拿到Class对象,再解析上面的注解
  • ​ 拿到方法Method对象,在解析上面的注解。
  • ​ 由于Class,Method,Field,Constructor都实现了AnnotatedElement接口,所以都有解析能力。

测试

创建4个注解

import java.lang.annotation.*;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ClassAnnotationTest {
    /**
     * 权限标识(如:system:user:list)
     */
    String value();
    /**
     * 描述
     */
    String description() default "";
}
import java.lang.annotation.*;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FieldAnnotationTest {
    String value();
}

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MethodAnnotationTest {
    String value();
}

import java.lang.annotation.*;

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface ParameterAnnotationTest {
    String value();
}

创建使用注解的类

@ClassAnnotationTest(value = "admin",description = "测试")
public class AnnotationTest {
    @FieldAnnotationTest("x前缀-")
    private String name;
    private int age;
    @MethodAnnotationTest("system:user:list")
    public void login(){
        System.out.println("登录成功");
    }
    public void test(@ParameterAnnotationTest("参数前缀-") String name){
        System.out.println(name);
    }
}

运行测试类

public class Demo01 {
    //注解解析
    public static void main(String[] args) throws Exception {
//        getClassAnnotation();

//        getMethodAnnotation();

//        getFieldAnnotation();

        getParameterAnnotation();
    }


    public static void getParameterAnnotation() throws Exception {
        Class demo01Class = AnnotationTest.class;
        Method method = demo01Class.getDeclaredMethod("test", String.class);
        ParameterAnnotationTest annotation = method.getParameters()[0].getAnnotation(ParameterAnnotationTest.class);
        System.out.println(annotation.value());
    }

    /**
     * 获取属性注解
     */
    public static void getFieldAnnotation() throws NoSuchFieldException {
        Class demo01Class = AnnotationTest.class;
        //拿到属性全部注解
        Field[] fields = demo01Class.getDeclaredFields();
        for (Field field : fields) {
            System.out.println(field.getName());
        }
        Field name = demo01Class.getDeclaredField("name");
        //拿到属性上面全部注解
        if (name.isAnnotationPresent(FieldAnnotationTest.class)) {
            FieldAnnotationTest annotation = name.getAnnotation(FieldAnnotationTest.class);
            System.out.println(annotation.value());
        }
    }
    /**
     * 获取方法注解
     */
    public static void getMethodAnnotation() throws NoSuchMethodException {
        Class demo01Class = AnnotationTest.class;

        Method method = demo01Class.getDeclaredMethod("login");
        //拿到方法上面全部注解
        Annotation[] annotations = method.getDeclaredAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }

        if(method.isAnnotationPresent(MethodAnnotationTest.class)){
            MethodAnnotationTest annotation = method.getAnnotation(MethodAnnotationTest.class);
            System.out.println(annotation.value());
        }
    }
    public static void getClassAnnotation() {
        Class demo01Class = AnnotationTest.class;
        //拿到类上面全部注解
        Annotation[] annotations = demo01Class.getDeclaredAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }

        //判断注解是否存在
        boolean annotationPresent = demo01Class.isAnnotationPresent(ClassAnnotationTest.class);
        System.out.println(annotationPresent);
        if(annotationPresent){
            //拿到指定注解
            ClassAnnotationTest checkPermission = (ClassAnnotationTest) demo01Class.getDeclaredAnnotation(ClassAnnotationTest.class);
            System.out.println(checkPermission.value());
            System.out.println(checkPermission.description());
        }
    }
}

模仿Junit框架

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyTest {
    String value() default "";
}
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class AnnotationTest {
    @MyTest
    public void test1(){
        System.out.println("test1");
    }
    @MyTest
    public void test2(){
        System.out.println("test2");
    }
//    @MyTest
    public void test3(){
        System.out.println("test3");
    }

    public static void main(String[] args) throws Exception {
        AnnotationTest annotationTest = new AnnotationTest();
        Class demo01Class = AnnotationTest.class;
        //拿到所有方法
        Method[] methods = demo01Class.getDeclaredMethods();
        for (Method method : methods) {
            //判断方法是否有@MyTest注解
            if(method.isAnnotationPresent(MyTest.class)){
                //执行方法
                method.invoke(annotationTest);
            }
        }
    }
}

鉴权

结合前面学的反射和注解实现一个简单的鉴权框架

①注解

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Perms {
    /**
     * 权限标识
     */
    String value();
}

②数据库

public class RolePermsDBService {
    /*假设从数据库拿到角色权限*/
    public String[] getRolePerms(){
        String[] rolePerms = {"dept:add","dept:find"};
        return rolePerms;
    }
}

③使用注解的对象

public class DeptController {
    @Perms("dept:add")
    public void addDept(){
        System.out.println("添加部门");
    }
    @Perms("dept:update")
    public void updateDept(){
        System.out.println("修改部门");
    }
    @Perms("dept:delete")
    public void deleteDept(){
        System.out.println("删除部门");
    }

    @Perms("dept:find")
    public void findDept(){
        System.out.println("查询部门");
    }
}

④反射处理对象

public class DeptControllerBean {
    DeptController deptController = new DeptController();
    Class demo01Class = DeptController.class;
    private List<String> rolePerms;

    private DeptControllerBean(){
        //初始化时获取数据库中的权限
        RolePermsDBService rolePermsService = new RolePermsDBService();
        rolePerms = Arrays.stream(rolePermsService.getRolePerms()).toList();
    }
    private static final DeptControllerBean instance = new DeptControllerBean();

    public static DeptControllerBean getInstance(){
        return instance;
    }
    private void invoke(String methodName) {
        try{
            Method addDept = demo01Class.getDeclaredMethod(methodName);
            //判断权限
            Perms annotation = addDept.getAnnotation(Perms.class);
            if(annotation!=null){//存在注解
                if (rolePerms.contains(annotation.value())) {
                    addDept.invoke(deptController);
                    return;
                }else{
                    System.out.println("没有权限");
                    return;
                }
            }else{ //不存在注解
                addDept.invoke(deptController);
            }
            addDept.invoke(deptController);
        }catch (Exception e){
            System.out.println("addDept方法不存在");
        }
    }
    public void addDept(){
        invoke("addDept");
    }

    public void updateDept(){
        invoke("updateDept");
    }
    public void deleteDept(){
        invoke("deleteDept");
    }
    public void findDept(){
        invoke("findDept");
    }
}

⑤启动类

public class RunApplication {
    public static void main(String[] args) {
        DeptControllerBean instance = DeptControllerBean.getInstance();
        instance.addDept();
        instance.updateDept();
        instance.deleteDept();
        instance.findDept();
    }
}