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

来自《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);

            }
        });
    }
}
复制代码

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

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

 

posted @   Mars.wang  阅读(236)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
点击右上角即可分享
微信分享提示