Please note: Although this question specifically calls out Swing, I believe this to be a pure Guice (4.0) question at heart and which highlights a potential generic issue with Guice and other opinionated frameworks.
In Swing, you have your application UI, which extends JFrame:
// Pseudo-code
class MyApp extends JFrame {
// ...
}
Your app (JFrame) needs a menubar:
// Pseudo-code
JMenuBar menuBar = new JMenuBar()
JMenu fileMenu = new JMenu('File')
JMenu manageMenu = new JMenu('Manage')
JMenuItem widgetsSubmenu = new JMenuItem('Widgets')
manageMenu.add(widgetsSubmenu)
menuBar.add(fileMenu)
menuBar.add(manageMenu)
return menuBar
And your menu items need action listeners, many of which will updated your app's contentPane:
widgetsSubmenu.addActionListener(new ActionListener() {
@Override
void actionPerformed(ActionEvent actionEvent) {
// Remove all the elements from the main contentPane
yourApp.contentPane.removeAll()
// Now add a new panel to the contentPane
yourApp.contentPane.add(someNewPanel)
}
})
And so there is intrinsically a circular dependency:
- Your app/
JFrameneeds aJMenuBarinstance - Your
JMenuBarneeds 0+JMenusandJMenuItems - Your
JMenuItems(well, the ones that will update yourJFrame'scontentPane') need action listeners - And then, in order to update the
JFrame'scontentPane, these action listeners need to reference yourJFrame
Take the following module:
// Pseudo-code
class MyModule extends AbstractModule {
@Override
void configure() {
// ...
}
@Provides
MyApp providesMyApp(JMenuBar menuBar) {
// Remember MyApp extends JFrame...
return new MyApp(menuBar, ...)
}
@Provides
JMenuBar providesMenuBar(@Named('widgetsListener') ActionListener widgetsMenuActionListener) {
JMenuBar menuBar = new JMenuBar()
JMenu fileMenu = new JMenu('File')
JMenu manageMenu = new JMenu('Manage')
JMenuItem widgetsSubmenu = new JMenuItem('Widgets')
widgetsSubmenu.addActionListener(widgetsMenuActionListener)
manageMenu.add(widgetsSubmenu)
menuBar.add(fileMenu)
menuBar.add(manageMenu)
return menuBar
}
@Provides
@Named('widgetsListener')
ActionListener providesWidgetsActionListener(Myapp myApp, @Named('widgetsPanel') JPanel widgetsPanel) {
new ActionListener() {
@Override
void actionPerformed(ActionEvent actionEvent) {
// Here is the circular dependency. MyApp needs an instance of this listener to
// to be instantiated, but this listener depends on a MyApp instance in order to
// properly update the main content pane...
myApp.contentPane.removeAll()
myApp.contentPane.add(widgetsPanel)
}
}
}
}
This will produce circular dependency errors at runtime, such as:
Exception in thread "AWT-EventDispatcher" com.google.inject.ProvisionException: Unable to provision, see the following errors:
1) Tried proxying com.me.myapp.MyApp to support a circular dependency, but it is not an interface.
while locating com.me.myapp.MyApp
So I ask: what's the way of circumventing this? Does Guice have an API or extension library for dealing with this sort of problem? Is there a way to refactor the code to break the circular dependency? Some other solution?
Update
Please see my guice-swing-example project on GitHub for a SSCCE.