Java反射(反射与代理设计模式、反射与Annotation、自定义Annotation、反射整合工厂设计模式和代理设计模式)

简介: 1.反射与代理设计模式,2.反射与Annotation,3.自定义Annotation,4.Annotation整合工厂设计模式和代理设计模式



1.反射与代理设计模式

代理模式是指通过业务真实类实现业务接口,再通过设置代理类创建业务真实类子类从而间接访问业务真实类。但是这存在一个弊端,如果有1000个业务接口,对应的业务就需要实例化1000个对象,极大的浪费了系统的空间资源。因此引入了反射结合代理设计模式

案例:反射与代理设计模式的结合

普通的代理设计模式:

package Example1709;
//业务接口实现发送消息
interface Message{
    public void  send();
}
//业务接口真实实现
class MessageReal implements Message{
    @Override
    public void send() {
        System.out.println("传输信息Message");
    }
}
//代理类
class Proxy{
//    通过实例化子类进行间接实现功能
    private MessageReal real = new MessageReal();
    public void getMessage() {
        real.send();
    }
}
public class javaDemo {
    public static void main(String[] args) {
        Proxy p = new Proxy();
        p.getMessage();
    }
}

image.gif

image.gif编辑

可以看到代理类里面创建了Message实例对象,通过代理设计就可以实现客户端无需创建相应对象就能调用其中方法,只需要创建代理类即可。但是如果业务非常多,而我仅需要其中一个业务功能,那么代理类创建的对象将浪费。所以可以通过反射实现动态代理

java中特地有InvocationHandle接口实现动态代理,只需要让代理类实现该接口并且覆写其中方法invoke()方法调用 就能实现动态代理

案例代码:

package ExampleProxyeflect;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//业务接口发送信息
interface Message{
    public void send();
}
//多种业务实现类
class LetterMessage implements Message{
    @Override
    public void send() {
        System.out.println("使用邮件进行传输");
    }
}
class PhoneMessage implements  Message{
    @Override
    public void send() {
        System.out.println("使用手机进行信息传输");
    }
}
class ComputerMessage implements  Message{
    @Override
    public void send() {
        System.out.println("通过电脑进行信息传输");
    }
}
//信息发送代理类
class MessageProxy implements InvocationHandler {
    private  Object target;
    public Object bind(Object target){
        this.target = target;
//        创建动态代理类
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    }
//    方法实现的覆写
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object returnData = null;
        if (this.connect()){
             returnData = method.invoke(this.target,args);
             this.close();
        }
        return returnData;
    }
//    设置连接和断开函数
    public Boolean connect(){
        System.out.println("连接成功");
        return true;
    }
    public void close(){
        System.out.println("关闭连接");
    }
}
public class javaDemo {
    public static void main(String[] args) {
//        创建对象并调用方法
        Message msg = (Message) new MessageProxy().bind(new LetterMessage());
        msg.send();
    }
}

image.gif

image.gif编辑


2.反射与Annotation

在java.lang.reflect中通过AccesibleObject类可以获取Annotation(注解)。

AccessibleObject类获取Annotation的方法:

方法名 返回类型 描述
getAnnotations() Annotation[] 返回直接存在于此元素上的所有注解。
getAnnotation(Class<T> annotationClass) <T extends Annotation> T 如果此元素上存在指定类型的注解,则返回该注解;否则返回 null
getDeclaredAnnotations() Annotation[] 返回直接存在于此元素上的所有已声明注解。
getDeclaredAnnotation(Class<T> annotationClass) <T extends Annotation> T 如果此元素上存在指定类型的注解,则返回该注解;否则返回 null
isAnnotationPresent(Class<? extends Annotation> annotationClass) boolean 如果此元素上存在指定类型的注解,则返回 true;否则返回 false

案例代码:

package Example1711;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.AccessibleObject;
@Deprecated
@FunctionalInterface
interface Face{
     void face();
}
@SuppressWarnings("serial")
class Test implements Face, Serializable {
    @Override
    public void face() {
        System.out.println("No face");
    }
}
public class javaDemo {
    public static void main(String[] args) {
        Annotation tation[] = Face.class.getAnnotations();
        for (Annotation temp:tation) {
            System.out.println(temp);
        }
        Annotation taion2[] = Test.class.getAnnotations();
        for (Annotation temp:taion2) {
            System.out.println(temp);
        }
    }
}

image.gif

image.gif编辑

上图中可以发现输出的只有interface接口的注解Annotation 但是类的上面的还有类内部的@Override却没有输出。原因在于Annotation的定义范围有关

下面任意拆出一个注解比如@FunctionInterface,分析源码

@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface FunctionalInterface{}

image.gif

可以看到其中还定义的范围是Rentention,这里是RUNTIME意思是运行,

@Retention注解用于指定注解的保留策略,有三个可选值:

    • RetentionPolicy.SOURCE:注解只保留在源代码中,编译时会被忽略。
    • RetentionPolicy.CLASS:注解保留在编译后的字节码文件中,但在运行时无法获取到。
    • RetentionPolicy.RUNTIME:注解保留在编译后的字节码文件中,并且可以在运行时通过反射获取到。

    3.自定义Annotation

    开发者可以根据自己的需要自定义Annotation,其中定义Annotaion时候需要使用@interface进行标记,同时通过@Target定义范围

    Annotation操作范围:

    元素类型 @Target 取值 描述
    类或接口 ElementType.TYPE 应用于类、接口或枚举类型。
    字段 ElementType.FIELD 应用于字段(成员变量)。
    方法 ElementType.METHOD 应用于方法。
    构造方法 ElementType.CONSTRUCTOR 应用于构造方法。
    方法参数 ElementType.PARAMETER 应用于方法的参数。
    局部变量 ElementType.LOCAL_VARIABLE 应用于局部变量。
    注解 ElementType.ANNOTATION_TYPE 应用于注解类型。
    ElementType.PACKAGE 应用于包声明。
    泛型类型参数 ElementType.TYPE_PARAMETER 应用于泛型类型参数。
    泛型类型参数的边界 ElementType.TYPE_USE 应用于泛型类型参数的使用处,例如类型转换、instanceof 表达式等
    类型导入声明(Java 9+) ElementType.TYPE_IMPORT_DECLARATION 应用于类型导入声明。
    模块导入声明(Java 9+) ElementType.MODULE_IMPORT_DECLARATION 应用于模块导入声明。
    类型使用 ElementType.TYPE_USEElementType.TYPE_PARAMETER 应用于类型使用或泛型类型参数。

    自定义案例代码:

    如果@DefaultInterface对象有必要的数据传入,可以在@interface下设置value值。

    package Example1713;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    import java.lang.reflect.Method;
    @Target({ElementType.METHOD,ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @interface useMessage{
        public String title();
        public String value();
        public String Default()default "默认值,如果调用则返回这个默认";
    }
    class Message {
        @useMessage(title="Annotation的使用标题",value = "value的值")
        public void send(String str){
            System.out.println("输出信息"+str);
        }
    }
    public class javaDemo {
        public static void main(String[] args) throws Exception{
            String str;
    //        调用方法
            Method mehod = Message.class.getMethod("send", String.class);
    //        获取指定的Annotation
            useMessage msg = mehod.getAnnotation(useMessage.class);
            str = msg.Default();
    //        实现方法调用
            mehod.invoke(Message.class.getDeclaredConstructor().newInstance(),str);
            str = msg.title();
            mehod.invoke(Message.class.getDeclaredConstructor().newInstance(),str);
            str = msg.value();
            mehod.invoke(Message.class.getDeclaredConstructor().newInstance(),str);
        }
    }

    image.gif


    4.Annotation整合工厂设计模式和代理设计模式

    使用Annotation进行开发时候最大的特点就是可以将相应的配置信息写入Annotation后,在项目启动的时候就能够通过反射获取到相应的Annotation定义并操作

    以下案例实现了通过Annotation整合工厂设计模式和代理设计模式

    案例代码:

    package Example1714;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    //业务接口
    interface Message{
        public void send(String msg);
    }
    //业务实现类
    class NetMessageImp implements Message{
        @Override
        public void send(String msg) {
            System.out.println("通过网络进行发送消息"+msg);
        }
    }
    class CloudMessageImp implements Message{
        @Override
        public void send(String msg) {
            System.out.println("通过云网络进行发送消息"+msg);
        }
    }
    //定义工厂类
    class Factory{
    //    私有化工厂类无法在外部实例化
        private Factory(){};
    //    通过getInstance获取实例化的对象
        public static<T> T getInstance(Class<T> clazz){
            try {
                return (T) new MessageProxy().bind(clazz.getDeclaredConstructor().newInstance());
            } catch (Exception e){
                e.printStackTrace();
                return null;
            }
        }
    }
    //设置自定义Annotation
    @Target({ElementType.TYPE,ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @interface RealInstance {
        public Class<?> claszz();
        }
    @RealInstance(claszz = NetMessageImp.class)
    class MessageService{
        private Message msg;
        public MessageService(){
    //        getAnnotation需要对应的class
            RealInstance cls = MessageService.class.getAnnotation(RealInstance.class);
            this.msg = (Message) Factory.getInstance(cls.claszz());
        }
        public void send(String msg){
            this.msg.send(msg);
        }
    }
    //设置代理类
    class MessageProxy implements InvocationHandler{
        private Object target;
    //    绑定并创建指定对象
        public  Object bind(Object target){
            this.target = target;
            return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),this);
        }
    //    设置连接函数
        public boolean connect(){
            System.out.println("连接成功");
            return true;
        }
    //    设置关闭函数
        public void close(){
            System.out.println("断开连接");
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            try {
                if (this.connect()){
                    return method.invoke(this.target,args);
                }
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                this.close();
            }
            return null;
        }
    }
    public class javaDemo {
        public static void main(String[] args) {
            MessageService instance = new MessageService();
            instance.send("我去,这东西是真的复杂啊,需要好好仔细读完啊");
        }
    }

    image.gif

    image.gif编辑

      1. Message接口是一个业务接口,定义了发送消息的方法。
      2. NetMessageImpCloudMessageImp是两个实现了Message接口的具体业务类,分别通过网络和云网络发送消息。
      3. Factory是一个工厂类,通过使用代理模式创建并返回代理对象的实例。
      4. RealInstance是一个自定义注解,用于标记需要被代理的具体业务类。
      5. MessageService是一个具有注解的类,其中msg字段是通过工厂类创建的代理对象,用于发送消息。
      6. MessageProxy是一个实现了InvocationHandler接口的代理类,负责在发送消息前后进行额外的处理,比如连接和关闭连接。
      7. 在主函数中,创建MessageService实例并调用send方法发送消息。

      问1:public static<T> T getInstance(Class<T> clazz)为什么要用<T>,作为泛型方法是如何使用的?

      public static <T> T getInstance(Class<T> clazz)中的<T>是泛型声明,它允许我们在方法中使用泛型类型。其中,T是一个类型参数,表示方法的返回类型和传入参数的类型。通过在方法声明中使用泛型,我们可以在调用时指定具体的类型,并在编译时进行类型检查。

      问2:在这段整合工厂设计和代理设计时候,工厂类和代理类分别担任了什么样的角色?他们完成了什么职责,为什么要加入MessageService?

        1. 工厂类(Factory)的角色是创建并返回代理对象的实例。它的职责是根据传入的业务接口类型,使用代理模式创建该接口的代理对象。工厂类的目的是为了提供一种通用的方式来创建代理对象。
        2. 代理类(MessageProxy)的角色是实现了InvocationHandler接口的代理类。它的职责是在代理对象的方法调用前后进行额外的处理。在这个例子中,它负责在发送消息前后进行连接和关闭连接的操作。
        3. MessageService是一个业务类,其中的msg字段通过工厂类创建的代理对象,用于发送消息。MessageService通过使用代理对象,可以在发送消息的过程中加入额外的处理逻辑,而无需直接引用具体的业务实现类。

        问3:Proxy.newProxyInstance()方法如何使用,该怎么传入参数?

        Proxy.newProxyInstance()方法用于创建代理对象。它接受三个参数:

          • ClassLoader loader:类加载器,用于在运行时加载代理类。一般可以使用业务接口的类加载器。
          • Class<?>[] interfaces:代理类要实现的接口数组。代理对象将实现这些接口,并将方法调用委托给InvocationHandlerinvoke()方法。
          • InvocationHandler h:代理对象在方法调用时的处理器,需要实现InvocationHandler接口

          问4:方法调用method.invoke()如何传入参数?

          问4:method.invoke()方法用于调用代理对象的方法。它接受两个参数:

            • Object obj:方法所属的对象,即代理对象。
            • Object... args:方法的参数数组。

            问5:在设计模式中,工厂设计模式,代理设计模式,业务处理类的作用?

              1. 在设计模式中,工厂模式的作用是将对象的创建过程封装起来,并且提供一个统一的接口来获取对象的实例。通过使用工厂类,可以实现对象的创建和管理的解耦,使得代码更加灵活和可维护。
              2. 代理模式的作用: 代理模式充当了客户端和实际业务对象之间的中介角色,为客户端提供一种间接访问对象的方式。具体来说,代理类封装了实际业务对象并提供了一个类似的接口,使得客户端可以通过代理类来访问实际业务对象。代理类还可以在访问被代理对象之前或之后执行额外的逻辑(例如验证、缓存、日志记录等)。代理模式的优点是它可以在不修改客户端的情况下对实际业务对象进行控制和扩展。
              3. 业务处理类的作用: 业务处理类是实际执行业务逻辑的类。它是根据特定需求实现业务功能的地方。在代理模式中,业务处理类是被代理的真实对象。它定义了代理类需要代理的具体业务逻辑,代理类会将请求传递给业务处理类并最终由业务处理类完成实际的业务操作。
              目录
              相关文章
              |
              4天前
              |
              设计模式 算法 Java
              Java一分钟之-设计模式:策略模式与模板方法
              【5月更文挑战第17天】本文介绍了策略模式和模板方法模式,两种行为设计模式用于处理算法变化和代码复用。策略模式封装不同算法,允许客户独立于具体策略进行选择,但需注意选择复杂度和过度设计。模板方法模式定义算法骨架,延迟部分步骤给子类实现,但过度抽象或滥用继承可能导致问题。代码示例展示了两种模式的应用。根据场景选择合适模式,以保持代码清晰和可维护。
              9 1
              |
              4天前
              |
              设计模式 Java
              Java一分钟之-设计模式:装饰器模式与代理模式
              【5月更文挑战第17天】本文探讨了装饰器模式和代理模式,两者都是在不改变原有对象基础上添加新功能。装饰器模式用于动态扩展对象功能,但过度使用可能导致类数量过多;代理模式用于控制对象访问,可能引入额外性能开销。文中通过 Java 代码示例展示了两种模式的实现。理解并恰当运用这些模式能提升代码的可扩展性和可维护性。
              19 1
              |
              4天前
              |
              设计模式 Java
              Java一分钟之-设计模式:工厂模式与抽象工厂模式
              【5月更文挑战第17天】本文探讨了软件工程中的两种创建型设计模式——工厂模式和抽象工厂模式。工厂模式提供了一个创建对象的接口,延迟实例化到子类决定。过度使用或违反单一职责原则可能导致问题。代码示例展示了如何创建形状的工厂。抽象工厂模式则用于创建一系列相关对象,而不指定具体类,但添加新产品可能需修改现有工厂。代码示例展示了创建颜色和形状的工厂。根据需求选择模式,注意灵活性和耦合度。理解并恰当运用这些模式能提升代码质量。
              14 2
              |
              6天前
              |
              Java C++
              Java反射的简单使用
              Java反射的简单使用
              22 3
              |
              3天前
              |
              数据采集 监控 安全
              java数字工厂MES系统全套源码Java+idea+springboot专业为企业提供智能制造MES解决方案
              "MES" 指的是制造执行系统(Manufacturing Execution System)。MES在制造业中扮演着至关重要的角色,它是位于企业资源计划(ERP)系统和车间控制系统之间的系统,用于实时收集、管理、分析和报告与制造过程相关的数据。
              10 0
              |
              3天前
              |
              安全 Java API
              JAVA-不安全的反射--RCE
              JAVA不安全的反射造成的RCE小案例
              |
              3天前
              |
              移动开发 监控 供应链
              JAVA智慧工厂制造生产管理MES系统,全套源码,多端展示(app、小程序、H5、台后管理端)
              一开始接触MES系统,很多人会和博主一样,对MES细节的应用不了解,这样很正常,因为MES系统相对于其他系统来讲应用比较多!
              15 1
              JAVA智慧工厂制造生产管理MES系统,全套源码,多端展示(app、小程序、H5、台后管理端)
              |
              4天前
              |
              设计模式 Java
              Java一分钟之-设计模式:观察者模式与事件驱动
              【5月更文挑战第17天】本文探讨了Java中实现组件间通信的观察者模式和事件驱动编程。观察者模式提供订阅机制,当对象状态改变时通知所有依赖对象。然而,它可能引发性能问题、循环依赖和内存泄漏。代码示例展示了如何实现和避免这些问题。事件驱动编程则响应用户输入和系统事件,但回调地狱和同步/异步混淆可能造成困扰。JavaFX事件驱动示例解释了如何处理事件。理解这两种模式有助于编写健壮的程序。
              9 1
              |
              4天前
              |
              设计模式 SQL 安全
              Java一分钟之-设计模式:单例模式的实现
              【5月更文挑战第16天】本文介绍了单例模式的四种实现方式:饿汉式(静态初始化)、懒汉式(双检锁)、静态内部类和枚举单例,以及相关问题和解决方法。关注线程安全、反射攻击、序列化、生命周期和测试性,选择合适的实现方式以确保代码质量。了解单例模式的优缺点,谨慎使用,提升设计效率。
              19 3
              |
              6天前
              |
              设计模式 Java
              【JAVA基础篇教学】第十四篇:Java中设计模式
              【JAVA基础篇教学】第十四篇:Java中设计模式