MVC
MVC é um design pattern implementado por vários frameworks, inclusive o NEO, que é uma sigla para Model, View, Controller (Modelo, Visão, Controle). Cada parte do MVC tem uma responsabilidade na aplicação. O NEO fornece um controller padrão e várias tags para ser utilizadas na visão. A camada Model é escrita pelo desenvolvedor da aplicação.
Controller
O controller base do NEO é o MultiActionController, responsável por responder as requisições que vieram do usuário. Um controller responde a determinada URL que deve ser configurada através de annotations. É necessário também configurar um servlet do NEO no web.xml que repassará as requisições para os controllers corretos.
Registrando o servlet no web.xml
Para utilizar o controller do NEO é necessário registrar um servlet específico no web.xml. Esse servlet receberá a requisição do cliente e rapassará ao controller correto. Esse servlet é o DispatcherServlet. Cada DispatcherServlet registrado é reponsável por um módulo.
Importante: Utilize o DispatcherServlet do NEO. br.com.linkcom.neo.controller.DispatcherServlet. O Spring também possui um DispatcherServlet.
Veja um exemplo de web.xml com um módulo configurado:
[JSP]
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" 
    xmlns="http://java.sun.com/xml/ns/j2ee" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee 
    http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
    
    <listener>
        <listener-class>br.com.linkcom.neo.core.web.init.ContextLoaderListener</listener-class>
    </listener>
    
    <servlet>
        <servlet-name>x</servlet-name>
        <servlet-class>br.com.linkcom.neo.controller.DispatcherServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>x</servlet-name>
        <url-pattern>/x/*</url-pattern>
    </servlet-mapping>
    
    <servlet>
        <servlet-name>resourceServlet</servlet-name>
        <servlet-class>br.com.linkcom.neo.view.ResourceServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>resourceServlet</servlet-name>
        <url-pattern>/resource/*</url-pattern>
    </servlet-mapping> 
    
</web-app> 
Módulo 'x' configurado
A configuração é feita uma vez para cada módulo. Após a configuração do web.xml a configuração dos controllers é feita através de annotations.
Criando um MultiActionController
Após configurar o DispatcherServlet no web.xml o Controller já pode ser criado. É recomendável que o controller seja uma subclasse de br.com.linkcom.neo.controller.MultiActionController. Fique atento para importar a classe do NEO e não a do Spring. Além de extender o MultiActionController o controller deve informar em qual URL ele responderá através da anotação Controller. Veja o exemplo:
package teste;

import br.com.linkcom.neo.controller.Controller;
import br.com.linkcom.neo.controller.MultiActionController;

@Controller(path="/x/primeiro")
public class PrimeiroController extends MultiActionController {

}

A URL pode ser dividida em duas partes, a primeira é o módulo e a segunda pode ser um nome qualquer que desejamos para o controller. A URL onde o controller deve ser registrado sempre deve começar com o nome de algum módulo. No caso, o módulo escolhido foi o 'x'. Um controller só será capaz de responder a uma requisição se existir um DispatcherServlet cadastrado para o módulo.

A classe está correta mas ainda não possui nenhum código. O MultiActionController possui o conceito de Actions. Actions são funcionalidades que desejamos executar no sistema. Um MultiActionController pode ter quantas Actions forem necessárias, cada Action é representada por um método. Um método para ser uma action deve possuir uma determinada assinatura.
Deve ser enviado na requisição um parâmetro especial indicando qual é a Action desejada. Se o parâmetro não for informado, a Action executada será a Action padrão. Veja como ficaria o controller com apenas uma Action padrão:
package teste;

import org.springframework.web.servlet.ModelAndView;

import br.com.linkcom.neo.controller.Controller;
import br.com.linkcom.neo.controller.DefaultAction;
import br.com.linkcom.neo.controller.MultiActionController;
import br.com.linkcom.neo.core.web.WebRequestContext;

@Controller(path="/x/primeiro")
public class PrimeiroController extends MultiActionController {

	@DefaultAction
	public ModelAndView actionPadrao(WebRequestContext request){
		...
	}
}
Para definirmos um método como Action padrão basta anotar esse método com @DefaultAction. Um método para ser Action deve ter a seguinte assinatura:
public ModelAndView [nome](WebRequestContext request)
O método deve ser public retornar um ModelAndView e receber um parâmetro do tipo WebRequestContext. O nome do método pode ser qualquer um. O método pode, opcionalmente, lançar qualquer exceção.
O ModelAndView é uma classe do Spring, ela representa o modelo e a visão que devem ser utilizados. O modelo é simplismente um atributo colocado na requisição equivalente a (request.setAttribute) e a visão é o JSP que desejamos mostrar.
Supondo que desejamos mostrar o seguinte jsp:
[JSP]
<html>
	<body>
	Olá
	</body>
</html>
Devemos retornar um ModelAndView no controller com um nome lógico para esse JSP. Veja o exemplo:
package teste;

import org.springframework.web.servlet.ModelAndView;

import br.com.linkcom.neo.controller.Controller;
import br.com.linkcom.neo.controller.DefaultAction;
import br.com.linkcom.neo.controller.MultiActionController;
import br.com.linkcom.neo.core.web.WebRequestContext;

@Controller(path="/x/primeiro")
public class PrimeiroController extends MultiActionController {

	@DefaultAction
	public ModelAndView actionPadrao(WebRequestContext request){
		return new ModelAndView("jspOla");
	}
}
O nome lógico do nosso jsp como informado pelo controller é jspOla. Esse nome deve ser transformado para o nome físico do JSP. Essa transformação é apenas a concatenação de algumas informações

/WEB-INF/jsp/[modulo]/[nome informado pelo controller].jsp

Onde [modulo] é o nome do módulo onde o controller foi registrado. E [nome informado pelo controller] é a string utilizada no construtor do ModelAndView.
No exemplo utilizado o nome do JSP seria:
/WEB-INF/jsp/x/jspOla.jsp

Então, se desejarmos que aquele jsp seja mostrado na tela do usuário devemos salvá-lo com o nome jspOla.jsp no diretório /WEB-INF/jsp/x

Quando pedirmos no browser http://[host]:[porta]/[app]/x/primeiro a requisição cairá no DispatcherServlet configurado no web.xml que por sua vez repassará a requisição para o PrimeiroController. Como nenhuma Action foi informada, o método que for a Action padrão será executado, nesse caso actionPadrao. Esse método retorna um ModelAndView com o model jspOla. O NEO receberá esse ModelAndView e fará a tradução para /WEB-INF/jsp/x/jspOla.jsp e executará um forward da requisição para esse JSP.
Várias Actions em um MultiActionController
É possível que um controller possua várias Actions. Isso é útil para fazer com que ações relacionadas fiquem dentro da mesma classe. Poderiamos querer uma action para listar os usuários e outra para salvar um usuário no banco de dados, com o MultiActionController é possível criar dois métodos (um para listar e outro para salvar) e chamá-los de acordo com a ação desejada. Para escolher qual método deve ser chamado no MultiActionController um parâmetro ACAO deve ser enviada na requisição. Por exemplo, suponhamos o seguinte controller:
package teste;

import org.springframework.web.servlet.ModelAndView;

import br.com.linkcom.neo.controller.Controller;
import br.com.linkcom.neo.controller.DefaultAction;
import br.com.linkcom.neo.controller.MultiActionController;
import br.com.linkcom.neo.core.web.WebRequestContext;

@Controller(path="/modulo/exemplo")
public class ExemploController extends MultiActionController {

	@DefaultAction
	public ModelAndView padrao(WebRequestContext request){
		return new ModelAndView("pagina1");
	}
	public ModelAndView acao1(WebRequestContext request){
		return new ModelAndView("pagina1");
	}
	public ModelAndView acao2(WebRequestContext request){
		return new ModelAndView("pagina1");
	}
}
Se pedirmos no browser: .../modulo/exemplo?ACAO=acao1 a requisição cairá no método acao1.
Se pedirmos no browser: .../modulo/exemplo?ACAO=acao2 a requisição cairá no método acao2.
Se pedirmos no browser: .../modulo/exemplo?ACAO=padrao a requisição cairá no método padrao.
Se pedirmos no browser: .../modulo/exemplo a requisição cairá no método padrao. Quando nenhuma ação é enviada o método escolhido será o anotado com @DefaultAction

É possível também indicar outro nome de ação para o método através da annotation @Action. Veja o exemplo:
package teste;

import org.springframework.web.servlet.ModelAndView;

import br.com.linkcom.neo.controller.Action;
import br.com.linkcom.neo.controller.Controller;
import br.com.linkcom.neo.controller.DefaultAction;
import br.com.linkcom.neo.controller.MultiActionController;
import br.com.linkcom.neo.core.web.WebRequestContext;

@Controller(path="/modulo/exemplo")
public class ExemploController extends MultiActionController {

	@DefaultAction
	public ModelAndView padrao(WebRequestContext request){
		return new ModelAndView("pagina1");
	}
	@Action("executar")
	public ModelAndView acao1(WebRequestContext request){
		return new ModelAndView("pagina1");
	}
	public ModelAndView acao2(WebRequestContext request){
		return new ModelAndView("pagina1");
	}
}

Se pedirmos no browser: .../modulo/exemplo?ACAO=executar a requisição cairá no método acao1.
Se pedirmos no browser: .../modulo/exemplo?ACAO=acao1 a requisição cairá no método acao1.
Se pedirmos no browser: .../modulo/exemplo?ACAO=acao2 a requisição cairá no método acao2.
Se pedirmos no browser: .../modulo/exemplo?ACAO=padrao a requisição cairá no método padrao.
Se pedirmos no browser: .../modulo/exemplo a requisição cairá no método padrao.

Commands
As actions do MultiActionController também podem receber um segundo parâmetro chamado command. O command é um POJO com propriedades que são configuradas através dos parâmetros da requisição. Se o command for de uma classe que possua o atributo nome, e for enviado um parâmetro nome na requisição, o valor desse parâmetro será mapeado no atributo nome do POJO. Exemplo:
package teste;

public class MeuBean {

	String nome;

	public String getNome() {
		return nome;
	}

	public void setNome(String nome) {
		this.nome = nome;
	}
}
package teste;

import org.springframework.web.servlet.ModelAndView;

import br.com.linkcom.neo.controller.Controller;
import br.com.linkcom.neo.controller.DefaultAction;
import br.com.linkcom.neo.controller.MultiActionController;
import br.com.linkcom.neo.core.web.WebRequestContext;

@Controller(path="/modulo/verificaNome")
public class VerificaNomeController extends MultiActionController {

	@DefaultAction
	public ModelAndView executar(WebRequestContext request, MeuBean meuBean){
		String nome = meuBean.getNome();
		request.setAttribute("nome", nome);
		return new ModelAndView("mostraNome");
	}
	
	
}
Se pedirmos no browser: .../modulo/exemplo?ACAO=executar&nome=joao a requisição cairá no método executar e irá configurar o atributo nome da classe MeuBean com o valor joao. A chamada meuBean.getNome() irá retornar joao.
Se não tivesse sido informado o parâmetro ACAO a requisição seria enviada para o método executar do mesmo jeito, já que ele está anotado com @DefaultAction.
Os atributos da classe não precisam ser do tipo String. O NEO irá converter os parâmetros para os tipos necessários. Para converter os parâmetros o NEO utiliza um DataBinder que extende o DataBinder do Spring. Esse dataBinder utiliza PropertyEditors para converter de String para o tipo necessário. O NEO já possui conversores para a maioria dos tipos de dados, mas se desejar informar outro conversor é necessário sobrescrever o método initBinder do MultiActionController. Exemplo:
package teste;

import java.beans.PropertyEditor;
import java.util.Date;

import javax.servlet.ServletRequest;

import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.servlet.ModelAndView;

import br.com.linkcom.neo.controller.Controller;
import br.com.linkcom.neo.controller.DefaultAction;
import br.com.linkcom.neo.controller.MultiActionController;
import br.com.linkcom.neo.core.web.WebRequestContext;

@Controller(path="/modulo/verificaNome")
public class VerificaNomeController extends MultiActionController {

	@DefaultAction
	public ModelAndView executar(WebRequestContext request, MeuBean meuBean){
		String nome = meuBean.getNome();
		request.setAttribute("nome", nome);
		return new ModelAndView("mostraNome");
	}
	
	@Override
	protected void initBinder(ServletRequest request, ServletRequestDataBinder binder) throws Exception {
		binder.registerCustomEditor(Date.class, new MyDatePropertyEditor());
	}
}
Sempre que for necessário mapear os parâmetro da requisição para um controller um binder é criado. O método initBinder é chamado passando-se uma referência desse binder para uma possível inicialização. Nesse caso estamos registrando um tradutor para o tipo Date.
View
A camada View no neo é na verdade um JSP. Após o controller realizar as tarefas necessárias ele indica para qual JSP deve ser continuada a requisição. Essa indicação é feita através de um objeto da classe ModelAndView.
A classe ModelAndView possui um construtor que recebe uma argumento do tipo String, esse argumento é o nome do JSP que deve ser utilizado. O nome informado é um nome lógico e deve ser transformado pelo NEO para se obter o nome real do JSP. O nome real do JSP é formado da seguinte forma:

/WEB-INF/jsp/ + o módulo onde está registrado o controller + nome informado do jsp + .jsp

Um controller registrado na path /gestao/controllers/AdicionarRecurso que retornasse
new ModelAndView("adicionarRecurso"); iria redirecionar para
/WEB-INF/jsp/gestao/adicionarRecurso.jsp.

JSP Base

Para evitar que o cabeçalho e o rodapé de todos os JSPs sejam copiados em cada página é possível utilizar o JSP base. O JSP base é uma página JSP que incluirá a página requisitada. Ao invés da requisição ir direto para a página indicada pelo ModelAndView ela irá para o base que incluirá no local desejado a página pedida. O JSP base deve ter o nome base.jsp e deve ficar dentro do diretório /WEB-INF/jsp/[módulo] onde [módulo] é o módulo onde o controller está registrado. Exemplo (suponha que o controller está registrado no módulo modulo1 e o retorno do ModelAndView foi hello):
[JSP]
<HTML>
	<BODY>
			<jsp:include page="${bodyPage}" />
	</BODY>
</HTML>
/WEB-INF/jsp/modulo1/base.jsp

[JSP]
Hello World!!!
/WEB-INF/jsp/modulo1/hello.jsp

Quando a requisição for redirecionada para o JSP ela será desviada para o base.jsp e o ele incluirá através do código <jsp:include page="${bodyPage}" /> o JSP requisitado. É como se tivéssemos apenas um JSP com o seguinte código:
[JSP]
<HTML>
	<BODY>
			Hello World!!!
	</BODY>
</HTML>
O NEO irá detectar a presença do base.jsp automaticamente. Não é necessária nenhuma configuração.
Resumo
No NEO, utilizamos o padrão MVC para responder às requisições. O controller no NEO é representado pelo MultiActionController e deve ser anotado com @Controller para informar em qual URL o controller responderá. A primeira parte da URL deve ser um módulo que esteja registrado no web.xml através do DispatcherServlet. O MultiActionController permite que várias Actions sejam escritas na mesma classe. Uma Action é um método reponsável por atender determinada requisição. Para escolher qual o método (Action) adequado, o MultiActionController verifica o parâmetro ACAO que veio na requisição. A Action pode ter um command, se tiver o MultiActionController mapeia os parâmetros da requisição aos atributos da classe command. A Action retorna um ModelAndView que representa qual JSP deve ser acessado. O NEO traduz o nome do JSP para o nome físico do JSP e faz o redirecionamento. Se existir um arquivo base.jsp para o módulo em questão será feito o redirecionamento para esse arquivo, e esse arquivo inclui o JSP desejado.