Use actionListener if you want have a hook before the real business action get executed, e.g. to log it, and/or to set an additional property (by <f:setPropertyActionListener>), and/or to have access to the component which invoked the action (which is available by ActionEvent argument). The actionListener method is required to have the following signature:
public void actionListener(ActionEvent event) {
// ...
}
Note that you can't pass additional arguments by EL 2.2.
Use action if you want to execute a business action and if necessary handle navigation. The action method can return a String which will be used as navigation case outcome (the target view). The action method can be any valid MethodExpression, also the ones which uses EL 2.2 arguments, e.g:
<h:commandLink value="submit" action="#{bean.edit(item)}" />
with
public void edit(Item item) {
// ...
}
The actionListeners are always invoked before the action in the order they are been declared in the view. So, the following example
<h:commandLink value="submit" actionListener="#{bean.listener1}" action="#{bean.submit}">
<f:actionListener type="com.example.SomeActionListener" />
<f:setPropertyActionListener target="#{bean.property}" value="some" />
</h:commandLink>
will invoke Bean#listener1(), SomeActionListener#processAction(), Bean#setProperty() and Bean#submit() in this order.
Another subtle difference is that the actionListener swallows any exceptions and won't propagate it up (i.e. you won't see an error/exception page, JSF will log them however). This is done so because the business logic, exception handling and navigation handling job is up to the action method.