I'll admit that I do web development, so I don't have much experience with Swing.
But I've always thought that the way I'd approach it would be to break the app into packages /view, /model, and /controller. The relationships would be unidirectional: /controller would know about both /model and /view, but neither would import any classes from /controller or each other.
The /view layer components would never be JFrames; they'd always be JPanel or another suitable container that could be composed together into JFrames as needed. Each would have references to Listener interfaces, initialized in constructors, and would defer to these for event handling:
public class ExamplePanel extends JPanel implements ActionListener
{
private JButton button;
private ActionListener buttonListener;
public ExamplePanel(ActionListener buttonListener)
{
this.button = new JButton("Do Something");
this.buttonListener = buttonListener;
this.button.addListener(this.buttonListener);
}
public void actionPerformed(ActionEvent e)
{
this.buttonListener.actionPerformed(e);
}
}
This arrangement works well with dependency injection, because now the controller can choose to use a local or remote implementation of that Listener interface, changing the behavior in a way that doesn't affect the client at all.
I'll admit that I've never followed it all the way through.
The Spring folks have a rich Swing client module, but it appears to have fallen out of favor. It looks like they've decided that a BlazeDS direction is a better choice for rich clients. But perhaps you can glean some design ideas from their approach.