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