首页 > 其他分享 >用注解实现给对象动态添加方法

用注解实现给对象动态添加方法

时间:2023-03-11 22:15:06浏览次数:37  
标签:frame source 添加 new yellowButton 注解 动态 public panel

来自《Java核心技术II高级特性》

我们知道在GUI编程中,有很多对象的事件监听方法,类似下面:

yellowButton.addActionListener(e -> yellowBackground());

 

首先我们先使用比较传统的方式实现这个功能

逻辑非常简单,我们定义了一个框架frame,上面有一个按钮yellowButton,点击按钮,frame会变成黄色

当然变色这个动作是通过在yellowButton上定义监听器实现的

public class ButtonFrame extends JFrame {
    private JPanel panel;
    private JButton yellowButton;
    

    public ButtonFrame() throws HeadlessException {
        setSize(300,200);
        panel=new JPanel();
        add(panel);
        yellowButton=new JButton("Yellow");

        panel.add(yellowButton);
        yellowButton.addActionListener(e -> yellowBackground());

        
    }

    public void yellowBackground(){
        panel.setBackground(Color.YELLOW);
    }

    

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                ButtonFrame frame = new ButtonFrame();
                frame.setTitle("test");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setVisible(true);

            }
        });
    }
}

 

如果我们不想每个button都addListener一遍,那有没有更优雅的办法,有!

可以通过注解+注解处理器的方式,给按钮动态添加一个监听器方法

首先我们定义一个注解ActionListennerFor

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ActionListenerFor {
    String source();
}

接着我们实现一个注解处理器,其中processAnnotations方法主要通过反射获取拥有自定义注解的方法,然后根据注解上的source名称获取对象的某个属性

接下来通过一个addListener方法动态的给这个属性添加一个监听器

import java.awt.event.ActionListener;
import java.lang.reflect.*;

public class ActionListenerInstaller {
    public static void processAnnotations(Object obj){
        try {
            Class<?> cl = obj.getClass();
            //遍历对象的所有方法,查看方法上是否有我们自定义的注解ActionListenerFor
            for (Method m : cl.getDeclaredMethods()) {
                ActionListenerFor a = m.getDeclaredAnnotation(ActionListenerFor.class);
                if(a!=null){
    
                    String source = a.source();
                    //通过名称获取对象属性
                    Field field = cl.getDeclaredField(source);
                    field.setAccessible(true);
                    //给这个属性自动添加一个监听器
                    addListener(field.get(obj),obj,m);
                }
            }
        } catch (NoSuchFieldException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
            e.printStackTrace();
        }

    }

    public static void addListener(Object source,final Object param,final Method m) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        //匿名内部类
        InvocationHandler handler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method mm, Object[] args) throws Throwable {
                return m.invoke(param);
            }
        };

        //用动态代理在运行时实现一个监听器对象
        Object listener = Proxy.newProxyInstance(null, new Class[]{ActionListener.class}, handler);
        //通过反射获取button的addActionListener方法
        Method adder = source.getClass().getMethod("addActionListener", ActionListener.class);
        adder.invoke(source,listener);
    }
}            

addListener是这个功能的核心部分,他是通过动态代理的方式,动态生成了一个实现了ActionListener接口的类对象

有一点要注意,handler中的invoke方法,实际调用的是被注解ActionListenerFor标记的方法

接下来又通过反射的方式,获取source所代表的button的addActionLisener这个方法,我们把它命名为adder

最后一步,adder方法invoke,参数1是所在对象source,第二个是方法的参数——listener对象

下面我把最终的代码放出来

import javax.swing.*;
import java.awt.*;

public class ButtonFrame extends JFrame {
    private JPanel panel;
    private JButton yellowButton;
    private JButton blueButton;

    public ButtonFrame() throws HeadlessException {
        setSize(300,200);
        panel=new JPanel();
        add(panel);
        yellowButton=new JButton("Yellow");

        panel.add(yellowButton);
        yellowButton.addActionListener(e -> yellowBackground());

        blueButton=new JButton("Blue");
        panel.add(blueButton);

        ActionListenerInstaller.processAnnotations(this);
    }

    public void yellowBackground(){
        panel.setBackground(Color.YELLOW);
    }

    @ActionListenerFor(source="blueButton")
    public void blueBackground(){
        panel.setBackground(Color.BLUE);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                ButtonFrame frame = new ButtonFrame();
                frame.setTitle("test");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setVisible(true);

            }
        });
    }
}

执行一下,发现点击黄色钮,背景变黄,点击蓝按钮,背景变蓝

我们通过了注解+注解处理器的方式,动态生成了一行代码!

 

标签:frame,source,添加,new,yellowButton,注解,动态,public,panel
From: https://www.cnblogs.com/wangbin2188/p/17207140.html

相关文章

  • vue项目vuerouter动态添加路由坑中之坑
    出了什么问题请求到的动态路由数据该如何应用?addRoute()?你想的太简单了!!!起因最近在用通过网络请求到的路由记录来进行动态路由渲染后端大佬说路由全部放前端不......
  • 2023-03-11 Java中的动态数组
    类似C++中的vector,动态数组需要满足以下功能增(insert)删(remove)改(set)查(get和contain)支持泛型自动扩容和缩容上面的实现实际相当于JDK标准库中的java.util......
  • 11、NFS-CSI网络存储、SC提供动态制备模板 PV和PVC动态制备
    PV和PVC在Pod级别定义存储卷有两个弊端◼卷对象的生命周期无法独立于Pod而存在◼用户必须要足够熟悉可用的存储及其详情才能在Pod上配置和使用卷PV和PVC可用于降低这种耦......
  • 注解与反射
    注解与反射注解注解Annotation是从JDK5.0开始引入的新技术.Annotation的作用:不是程序本身,可以对程序作出解释.(这一点和注释(comment)没什么区别)可以被其他程序(比......
  • 使用Unity3D在PC+Android端截图保存,再添加到数据库中
    前提:1.使用的工具为Unity2021+VS2019+mysql8.02.使用的支持包         3.Unity组件  代码usingUnityEngine;usingSy......
  • oracle建表语句,添加主键、索引、注释,插入数据,添加序列
    createtableFND_COMPANIES_42624(COMPANY_IDnumber(3)primarykey,--公司IDnumber序列COMPANY_CODEVARCHAR2(30)notnull,--公司......
  • 动态规划
    dp  数字三角形例题1:AcWing1015.摘花生-AcWing题意:从(1,1)到(r,c)摘花生,不可走回头路,最多可以摘多少思路:f[i,j]:(1,1)到(i,j)的最大摘花生数核心代码:for(int......
  • Java动态代理
    前言这周工作比较忙,在学习上面就温习了一下动态代理,以前也看过这块的知识,但是一直没有动手去写下代码。学习就是这样,不动手写一写总有种没有掌握的感觉,记录下这个学习过程......
  • 20.注解小记
    Json相关注解JsonInclude 如果不加这个注解:{"a":"111","b":1,"children":[]}加上注解并标记NON_EMPTY:{"a":"111","b":1}数据库相关注解Tab......
  • P1433 吃奶酪 标签: 动态规划,dp | 状态压缩
    详见:https://www.luogu.com.cn/problem/P1433就不写基础原理了,直接看注释吧点击打开非map版#include<iostream>#include<cstdio>#include<cmath>#include<alg......