1

反射机制

定义

通过反射,我们可以在运行时获得程序或程序集中每一个类型的成员和成员的信息。程序中一般的对象的类型都是在编译期就确定下来的,而 Java 反射机制可以动态地创建对象并调用其属性,这样的对象的类型在编译期是未知的。所以我们可以通过反射机制直接创建对象,即使这个对象的类型在编译期是未知的。

Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。本质是JVM得到class对象之后,再通过class对象进行反编译,从而获取对象的各种信息。

Java属于先编译再运行的语言,程序中对象的类型在编译期就确定下来了,而当程序在运行时可能需要动态加载某些类,这些类因为之前用不到,所以没有被加载到JVM。通过反射,可以在运行时动态地创建对象并调用其属性,不需要提前在编译期知道运行的对象是什么。

反射机制的优缺点

优点:

在运行时获得类的各种内容,进行反编译,对于Java这种先编译再运行的语言,能够让我们很方便的创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代码的链接,更加容易实现面向对象。

简单说:其目的,提供开发者能够更好封装框架实现扩展功能

缺点:

1、反射会消耗一定的系统资源,因此如果不需要动态地创建一个对象,那么就不需要用反射;

2、反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题。

反射的用途

反编译:.class --> .java

1、通过反射机制访问java对象的属性,方法,构造方法等

2、JDBC加载驱动连接  class.forName()

    Class.forName("com.mysql.jdbc.Driver");// 动态加载mysql驱动

3、Spring容器框架IOC实例化对象

<bean id="XxEntity" class="package.XxEntity" />

4、自定义注解生效(反射+AOP)

5、第三方核心的框架 mybatis orm

反射技术的使用

Class类

代表类的实体,在运行的Java应用程序中表示类和接口

Field类

代表类的成员变量(成员变量也称为类的属性)

Method类

代表类的方法

Constructor类

代表类的构造方法

1.getField、getMethod和getCostructor方法可以获得指定名字的域、方法和构造器。

2.getFields、getMethods和getCostructors方法可以获得类提供的public域、方法和构造器数组,其中包括超类的共有成员。

3.getDeclatedFields、getDeclatedMethods和getDeclaredConstructors方法可以获得类中声明的全部域、方法和构造器,其中包括私有和受保护的成员,但不包括超类的成员。

反射常用的api

1、Object-->getClass

2、任何数据类型(包括基本的数据类型)都有一个“静态”的class属性

3、通过class类的静态方法:forName(String className)(最常用)

Class aClass = Class.forName("package.XxEntity");

@Datapublic class Class01 {
    private String name;}
public class ClassTest01 {
    public static void main(String[] args) throws Exception {
        // 1.第一种获取class方式
        Class01 class01 = new Class01();
        Class test01 = class01.getClass();
        // 默认执行无参构造函数
        Class01 class02 = (Class01) test01.newInstance();        System.out.println(class01 == class02); // false
        // 2.第二种方式 直接获取class
        Class<Class01> aClass01 = Class01.class;
        Class01 class03 = aClass01.newInstance();
        System.out.println(class03);// Class01(name=null)
        // 3.第三种方式 类的完整路径地址
        Class<?> aClass02 = Class.forName("lll.xxh.class_s.entity.Class01");        Class01 class04 = (Class01) aClass02.newInstance();        System.out.println(class04);// Class01(name=null)
    }}

运行期间,一个类,只有一个Class对象产生

代码验证

// new对象Class01 class01 = new Class01();
Class<? extends Class01> aClass01 = class01.getClass();
// 直接.classClass<Class01> aClass02 = Class01.class;
// 类的完整路径Class<?> aClass03 = Class.forName("lll.xxh.class_s.entity.Class01");
System.out.println(aClass01 == aClass02);
// trueSystem.out.println(aClass01 == aClass03);// true

反射执行构造函数

执行无参构造函数

Class<?> aClass = Class.forName("lll.xxh.class_s.entity.Class01");
// 默认执行无参构造函数
Class01 class01 = (Class01) aClass.newInstance();System.out.println(class01);

执行有参构造函数

Class<?> aClass = Class.forName("lll.xxh.calss.entity.Class01");
Constructor<?> constructor = aClass.getConstructor(String.class);
Class01 class02 = (Class01) constructor.newInstance("test");
System.out.println(class02);

反射执行给属性赋值

反射执行给公有属性赋值

@Datapublic class UserEntity {
    private String name;
    public String remark;}
public class ClassTest04 {
    public static void main(String[] args) throws Exception {
        // 反射获取class文件
        Class<?> aClass = Class.forName("lll.xxh.class_s.entity.UserEntity");
        // 无参构造函数创建对象
        UserEntity userEntity = (UserEntity) aClass.newInstance();
        // class文件获取公有属性,并设置值
        Field remark = aClass.getField("remark");
        remark.set(userEntity, "备注");
        System.out.println(userEntity);
    }}

反射执行给私有属性赋值

@Datapublic class UserEntity {
    private String name;
    public String remark;}
public class ClassTest04 {
    public static void main(String[] args) throws Exception {
        // 反射获取class文件
        Class<?> aClass = Class.forName("lll.xxh.class_s.entity.UserEntity");
        // 无参构造函数创建对象
        UserEntity userEntity = (UserEntity) aClass.newInstance();
        // class文件获取公有属性,并设置值
        Field remark = aClass.getField("remark");
        remark.set(userEntity, "备注");
        // class文件获取私有属性,并设置值
        Field name = aClass.getDeclaredField("name");
        name.setAccessible(true);
// 设置允许访问私有属性
        name.set(userEntity, "userName");
        System.out.println(userEntity);
    }}

注意:

Exception in thread "main" 

    java.lang.IllegalAccessException: 

        UserEntity with modifiers "private"

解决方案

// 设置允许访问私有属性

field.setAccessible(true);

反射执行调用方法

反射执行调用公有方法

@Datapublic class UserEntity {
    private String name;
    public String remark;
    public void publicTest() {
        System.out.println("public test....");
    }
    private void privateTest() {
        System.out.println("private test...");
    }}
public class ClassTest05 {
    public static void main(String[] args) throws Exception {
        Class<?> aClass = Class.forName("lll.xxh.class_s.entity.UserEntity");
        UserEntity userEntity = (UserEntity) aClass.newInstance();
        Method publicTest = aClass.getDeclaredMethod("publicTest");
        publicTest.invoke(userEntity);    }}

反射执行调用私有方法

@Data
public class UserEntity {    
private String name;
public String remark;
public void publicTest() {
        System.out.println("public test....");
    }
private void privateTest() {
        System.out.println("private test...");
    }}
public class ClassTest05 {
    public static void main(String[] args) throws Exception {
        Class<?> aClass = Class.forName("lll.xxh.class_s.entity.UserEntity");        UserEntity userEntity = (UserEntity) aClass.newInstance();
        // 公有        Method publicTest = aClass.getDeclaredMethod("publicTest");        publicTest.invoke(userEntity);
        // 私有        Method privateTest = aClass.getDeclaredMethod("privateTest");        privateTest.setAccessible(true);// 设置允许调用私有方法        privateTest.invoke(userEntity);    }}

反射调用方法传递参数

@Datapublic class UserEntity {
    private String name;
    public String remark;
    public void publicTest(String i, Integer j) {
        System.out.println("String i:" + i + ",Integer j:" + j);    }}
public class ClassTest06 {
    public static void main(String[] args) throws Exception {
        Class<?> aClass = Class.forName("lll.xxh.class_s.entity.UserEntity");        UserEntity userEntity = (UserEntity) aClass.newInstance();
        // 公有
        Method publicTest = aClass.getDeclaredMethod("publicTest", String.class, Integer.class);
        publicTest.invoke(userEntity, "iString", 1);
    }}

通过反射越过泛型检查

public class ClassTest07 {
    public static void main(String[] args) throws Exception {
        ArrayList<String> strings = new ArrayList<>();
        strings.add("1");
        System.out.println(strings.size());// 1
        Class<? extends ArrayList> aClass = strings.getClass();
        Method add = aClass.getDeclaredMethod("add", Object.class);
        add.invoke(strings, 2);
        System.out.println(strings.size());
// 2    }}

泛型用在编译期,编译过后泛型擦除(消失掉),所以是可以通过反射越过泛型检查的。

2

注解

定义

注解用来给类声明附加额外信息,可以标注在类、字段、方法等上面,编译器、JVM以及开发人员等可以通过反射拿到注解信息,进而做一些相关处理。

例:SpringBoot全部都是采用注解化

常用注解

@Override

只能标注在子类覆盖父类的方法上面,有提示的作用

@Deprecated

标注在过时的方法或类上面,有提示的作用

@SuppressWarnings("unchecked")

标注在编译器认为有问题的类、方法等上面,用来取消编译器的警告提示,警告类型有serial、unchecked、unused、all

元注解

元注解

元注解用来在声明新注解时指定新注解的一些特性

@Target

指定新注解标注的位置,比如类、字段、方法等,取值有ElementType.Method等

@Retention

指定新注解的信息保留到什么时候,取值有RetentionPolicy.RUNTIME等

@Inherited

指定新注解标注在父类上时可被子类继承

常用注解

@Target(ElementType.METHOD) /

指定新注解可以标注在方法上

@Retention(RetentionPolicy.RUNTIME)

指定新注解保留到程序运行时期

@Inherited

指定新注解标注在父类上时可被子类继承

public @interface TestName {
   String name();
}

自定义注解 运行 :反射+aop

注解的Target

TYPE:

类、接口(包括注解类型)和枚举的声明

FIELD:

字段声明(包括枚举常量)

METHOD:

方法声明

PARAMETER:

参数声明

CONSTRUCTOR:

构造函数声明

LOCAL_VARIABLE:

本地变量声明

ANNOTATION_TYPE:

注解类型声明

PACKAGE:

包声明

TYPE_PARAMETER:

类型参数声明,JavaSE8引进,可以应用于类的泛型声明之处

TYPE_USE:

JavaSE8引进,此类型包括类型声明和类型参数声明

获取注解信息

// 1.获取当前类上的注解Class<?> aClass = Class.forName("lll.xxh.annotation.UserEntity");// 2.获取当前方法上的注解
Method userNameMethod = aClass.getDeclaredMethod("getUserName");TestName declaredAnnotation = userNameMethod.getDeclaredAnnotation(TestName.class);System.out.println(declaredAnnotation);
// 3.获取字段上的注解Field pubUserName = aClass.getDeclaredField("userName");declaredAnnotation = pubUserName.getDeclaredAnnotation(TestName.class);System.out.println(declaredAnnotation);
// 4.获得构造方法注解// 先获得构造方法对象Constructor<?> constructor = aClass.getConstructor(new Class[]{});// 拿到构造方法上面的注解实例declaredAnnotation = constructor.getAnnotation(TestName.class);System.out.println(declaredAnnotation);

注解实现案例

自定义限流注解

对我们接口实现 限流 比如 每s 只能访问1次 或者每s 访问两次。

Maven

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>    <version>2.2.1.RELEASE</version></parent><dependencies>
    <!--  springboot 整合web组件-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>18.0</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
        <version>3.12.0</version>
    </dependency></dependencies>

使用谷歌的guava例子

/** * 每秒生成2.0个令牌 */
private RateLimiter rateLimiter = RateLimiter.create(2.0);
@GetMapping("/get")
@TestName(name = "get", token = 1)
public String get() {
    boolean result = rateLimiter.tryAcquire();
    if (!result) {
        return "当前访问人数过多,请稍后重试!";
    }
    return "this is get";}

封装自定义注解限流框架

整合Aop实现接口限流

@Aspect
@Component
public class CurrentLimitAop {
    /**     * 每秒生成1.0个令牌 每s产生10 token     */
    private ConcurrentHashMap<String, RateLimiter> rateLimiters = new ConcurrentHashMap();
    @Around(value = "@annotation(lll.xxh.annotation.TestName)")
    public Object around(ProceedingJoinPoint joinPoint) {
        try {
            //获取拦截的方法名
            Signature sig = joinPoint.getSignature();
            //获取拦截的方法名
            MethodSignature methodSignature = (MethodSignature) sig;
            // 判断方法上是否有加上该注解,如果有加上注解则限流
  TestName testName =methodSignature.getMethod().getDeclaredAnnotation(TestName.class);
            if (testName == null) {
                // 执行目标方法
                return joinPoint.proceed();
            }
            // 获取注解上的name
            String name = testName.name();
            // 获取注解上的token
            double token = testName.token();
            RateLimiter rateLimiter = rateLimiters.get(name);
            if (rateLimiter == null) {
                rateLimiter = RateLimiter.create(token);
                rateLimiters.put(name, rateLimiter);
            }
            // 开始限流
            boolean result = rateLimiter.tryAcquire();
            if (!result) {
                return "当前访问人数过多,请稍后重试!";
            }
            return joinPoint.proceed();
        } catch (Throwable throwable) {
            return "系统出现了错误!";
        }
    }}

使用效果

@RestController
public class TestController {
    @GetMapping("/get")
    @TestName(name = "get", token = 1)
    public String get() {
        return "this is get";
    }
    @GetMapping("/add")
    @TestName(name = "add", token = 10)
    public String add(){
        return "this is add";
    }}

本篇文章来源于微信公众号: 他家的小朋友



微信扫描下方的二维码阅读本文

此作者没有提供个人介绍
最后更新于 2023-05-28