O livro "JAVA and Patterns" do Dr. Yan Hong começa com uma descrição do modelo de Cadeia de Responsabilidade:
O padrão de cadeia de responsabilidade é um padrão de comportamento de objeto. No padrão de cadeia de responsabilidade, muitos objetos são conectados para formar uma cadeia pela referência de cada objeto ao seu descendente. A solicitação é passada para cima na cadeia até que um objeto na cadeia decida tratar a solicitação. O cliente que faz a solicitação não sabe qual objeto da cadeia trata da solicitação, o que permite que o sistema se reorganize e atribua responsabilidades dinamicamente sem afetar o cliente.
Começando por bater tambores e espalhar flores
Tocar tambores e passar flores é um jogo de bebida animado e tenso. Durante o banquete, os convidados sentam-se em ordem e uma pessoa toca o tambor. O local onde o tambor é tocado e o local onde as flores são passadas são separados para mostrar justiça. Quando começa a batida dos tambores, os buquês começam a ser distribuídos e, assim que os tambores caem, se o buquê estiver na mão de alguém, essa pessoa tem que beber.
Por exemplo, Jia Mu, Jia She, Jia Zheng, Jia Baoyu e Jia Huan são cinco passadores de flores que participam do jogo de percussão e passagem de flores. O baterista passou as flores para Jia Mu e iniciou o jogo de passagem de flores. A flor foi passada de Jia Mu para Jia She, de Jia She para Jia Zheng, de Jia Zheng para Jia Baoyu, de Jia Baoyu para Jia Huan, de Jia Huan para Jia Mu e assim por diante, conforme mostrado na figura abaixo. Quando o tambor parar, a pessoa que estiver com a flor na mão deverá realizar a ordem de beber.
Bater tambores e espalhar flores é a aplicação do modelo de cadeia de responsabilidade. A cadeia de responsabilidade pode ser uma linha reta, uma cadeia ou parte de uma estrutura em árvore.
A estrutura do modelo de cadeia de responsabilidade
A implementação mais simples de um padrão de cadeia de responsabilidade é usada abaixo.
As funções envolvidas no modelo de cadeia de responsabilidade são as seguintes:
● Função de manipulador abstrato (Handler): define uma interface para processamento de solicitações. Se necessário, a interface pode definir um método para definir e retornar uma referência para a próxima interface. Essa função geralmente é implementada por uma classe abstrata Java ou interface Java. O relacionamento de agregação da classe Handler na figura acima fornece a referência da subclasse específica para a próxima. O método abstrato handleRequest() padroniza a operação da subclasse no processamento de solicitações.
● Função de manipulador concreto (ConcreteHandler): Após receber a solicitação, o manipulador concreto pode optar por processar a solicitação ou encaminhá-la para a próxima parte. Como o processador concreto mantém uma referência para a próxima casa, o processador concreto pode acessar a próxima casa, se necessário.
código fonte
função de manipulador abstrato
Copie o código do código da seguinte forma:
manipulador de classe abstrata pública {
/**
* Possui objetos de responsabilidade sucessor
*/
sucessor do manipulador protegido;
/**
* Indica o método de processamento da solicitação, embora este método não passe parâmetros
* No entanto, os parâmetros podem realmente ser passados. Você pode escolher se deseja passar os parâmetros de acordo com necessidades específicas.
*/
público abstrato void handleRequest();
/**
* Método de valor
*/
manipulador público getSucessor() {
sucessor de retorno;
}
/**
* Método de atribuição para definir objetos de responsabilidade subsequentes
*/
public void setSuccessor(sucessor do manipulador) {
this.sucessor = sucessor;
}
}
Função específica do processador
Copie o código do código da seguinte forma:
classe pública ConcreteHandler estende Handler {
/**
* Método de processamento, chame este método para processar a solicitação
*/
@Substituir
public void handleRequest() {
/**
* Determinar se existe um objeto responsável sucessor
*Se houver, encaminhe a solicitação para o objeto responsável subsequente
* Caso contrário, processe a solicitação
*/
if(getSucessor()! = null)
{
System.out.println("Deixe a solicitação ir");
getSucessor().handleRequest();
}outro
{
System.out.println("Processando solicitação");
}
}
}
Classe de cliente
Copie o código do código da seguinte forma:
classe pública Cliente {
public static void main(String[] args) {
//Montar a cadeia de responsabilidade
Manipulador manipulador1 = new ConcreteHandler();
Manipulador manipulador2 = new ConcreteHandler();
manipulador1.setSucessor(manipulador2);
//Enviar solicitação
manipulador1.handleRequest();
}
}
Pode-se ver que o cliente cria dois objetos manipuladores e especifica que o próximo objeto manipulador do primeiro objeto manipulador é o segundo objeto manipulador, enquanto o segundo objeto manipulador não tem próximo home. O cliente então passa a solicitação para o primeiro objeto manipulador.
Porque a lógica de transferência deste exemplo é muito simples: enquanto houver um próximo proprietário, ele será passado para o próximo proprietário para processamento, se não houver um próximo proprietário, ele será tratado por si mesmo; Portanto, após o primeiro objeto manipulador receber a solicitação, ele passará a solicitação para o segundo objeto manipulador. Como o segundo objeto manipulador não tem home, ele trata a solicitação sozinho. O diagrama de sequência de atividades é mostrado abaixo.
Cenários de uso
Consideremos tal função: candidatar-se à gestão de despesas de jantares.
Muitas empresas contam com esse tipo de benefício, ou seja, a equipe ou departamento do projeto pode solicitar algumas despesas de jantar da empresa, que é utilizado para organizar jantares para integrantes da equipe do projeto ou integrantes do departamento.
O processo geral para solicitação de despesas de jantar é geralmente: o solicitante primeiro preenche o formulário de inscrição e depois o envia ao líder para aprovação. Se a solicitação for aprovada, o líder notificará o solicitante de que a aprovação foi aprovada. e então o solicitante irá ao departamento financeiro para cobrar a taxa. Caso não seja aprovado, o líder irá O solicitante será notificado de que a aprovação não foi aprovada e o assunto será arquivado.
Os líderes em diferentes níveis têm limites de aprovação diferentes. Por exemplo, o gerente de projeto só pode aprovar inscrições com limite de 500 yuans;
Ou seja, quando alguém faz uma solicitação de despesas de jantar, a solicitação será processada adequadamente por um dos gerentes de projeto, gerentes de departamento e gerentes gerais, mas quem fez a solicitação não sabe o que acontecerá no final . Quem tratará do seu pedido Geralmente, o candidato submete a sua candidatura ao gestor do projecto e talvez o gestor geral trate finalmente do seu pedido.
Você pode usar o modelo de cadeia de responsabilidade para realizar a função acima: quando alguém faz uma solicitação de despesas de jantar, a solicitação será repassada em uma cadeia de processamento de liderança, como gerente de projeto -> gerente de departamento -> gerente geral A pessoa. quem fez a solicitação não Sabendo quem atenderá sua solicitação, cada líder decidirá se deve atender a solicitação ou entregá-la a um líder de nível superior com base em seu próprio escopo de responsabilidades. a transferência acabou.
É necessário isolar o processamento de cada líder e implementá-lo em um objeto de processamento de responsabilidade separado e, em seguida, fornecer-lhes um objeto de responsabilidade pai abstrato e comum, de modo que a cadeia de responsabilidade possa ser combinada dinamicamente no cliente para atingir diferentes requisitos funcionais. .
código fonte
Classe de função do manipulador abstrato
Copie o código do código da seguinte forma:
manipulador de classe abstrata pública {
/**
* Contém o objeto que trata da próxima solicitação
*/
sucessor do manipulador protegido = null;
/**
* Método de valor
*/
manipulador público getSucessor() {
sucessor de retorno;
}
/**
* Defina o próximo objeto para tratar a solicitação
*/
public void setSuccessor(sucessor do manipulador) {
this.sucessor = sucessor;
}
/**
* Processar solicitações de despesas de festas
* @param usuário candidato
* @param fee A quantidade de dinheiro solicitada
* @return Notificação específica de sucesso ou falha
*/
público abstrato String handleFeeRequest (String usuário, taxa dupla);
}
Função específica do processador
Copie o código do código da seguinte forma:
classe pública ProjectManager estende o manipulador {
@Substituir
public String handleFeeRequest(String usuário, taxa dupla) {
Stringstr = "";
//A autoridade do gerente de projeto é relativamente pequena e só pode estar dentro de 500
se(taxa <500)
{
//Para fins de teste, mantenha as coisas simples e concorde apenas com o pedido de Zhang San.
if("Zhang San".é igual a(usuário))
{
str = "Sucesso: O gerente do projeto concorda com a taxa do jantar de [" + usuário + "], o valor é " + taxa + "yuan";
}outro
{
//Ninguém mais concorda
str = "Falha: O gerente do projeto discorda da taxa do jantar de [" + usuário + "], o valor é " + taxa + "yuan";
}
}outro
{
//Se exceder 500, continue a passá-lo para pessoas de nível superior para processamento.
if(getSucessor()! = null)
{
return getSucessor().handleFeeRequest(usuário, taxa);
}
}
retornar string;
}
}
Copie o código do código da seguinte forma:
classe pública DeptManager estende o manipulador {
@Substituir
public String handleFeeRequest(String usuário, taxa dupla) {
Stringstr = "";
//A autoridade do gerente do departamento só pode estar dentro de 1000
se(taxa <1000)
{
//Para fins de teste, mantenha as coisas simples e concorde apenas com o pedido de Zhang San.
if("Zhang San".é igual a(usuário))
{
str = "Sucesso: O gerente do departamento concorda com a taxa do jantar de [" + usuário + "], o valor é " + taxa + "yuan";
}outro
{
//Ninguém mais concorda
str = "Falha: O gerente do departamento discorda da taxa do jantar de [" + usuário + "], o valor é " + taxa + "yuan";
}
}outro
{
//Se exceder 1000, continue a passá-lo para pessoas de nível superior para processamento.
if(getSucessor()! = null)
{
return getSucessor().handleFeeRequest(usuário, taxa);
}
}
retornar string;
}
}
Copie o código do código da seguinte forma:
classe pública GeneralManager estende manipulador {
@Substituir
public String handleFeeRequest(String usuário, taxa dupla) {
Stringstr = "";
//O gerente geral tem grande autoridade. Desde que o pedido chegue aqui, ele pode atender.
se(taxa >= 1000)
{
//Para fins de teste, mantenha as coisas simples e concorde apenas com o pedido de Zhang San.
if("Zhang San".é igual a(usuário))
{
str = "Sucesso: O gerente geral concorda com as despesas do jantar de [" + usuário + "], o valor é " + taxa + "yuan";
}outro
{
//Ninguém mais concorda
str = "Falha: O gerente geral não concorda com a taxa do jantar de [" + usuário + "], o valor é " + taxa + "yuan";
}
}outro
{
//Se houver objetos de processamento subsequentes, continue a passá-los
if(getSucessor()! = null)
{
return getSucessor().handleFeeRequest(usuário, taxa);
}
}
retornar string;
}
}
Classe de cliente
Copie o código do código da seguinte forma:
classe pública Cliente {
public static void main(String[] args) {
//Primeiro monte a cadeia de responsabilidade
Manipulador h1 = novo GeneralManager();
Manipulador h2 = new DeptManager();
Manipulador h3 = new ProjectManager();
h3.setSucessor(h2);
h2.setSucessor(h1);
//Começa o teste
String test1 = h3.handleFeeRequest("Zhang San", 300);
System.out.println("teste1 = " + teste1);
String test2 = h3.handleFeeRequest("李思", 300);
System.out.println("teste2 = " + teste2);
System.out.println("------------------------------------------");
String test3 = h3.handleFeeRequest("Zhang San", 700);
System.out.println("teste3 = " + teste3);
String test4 = h3.handleFeeRequest("李思", 700);
System.out.println("teste4 = " + teste4);
System.out.println("------------------------------------------");
String test5 = h3.handleFeeRequest("Zhang San", 1500);
System.out.println("teste5 = " + teste5);
String test6 = h3.handleFeeRequest("李思", 1500);
System.out.println("teste6 = " + teste6);
}
}
Os resultados da execução são os seguintes:
Modelos de cadeia de responsabilidade puros e impuros
Um modelo de cadeia de responsabilidade pura exige que um objeto processador específico possa escolher apenas uma de duas ações: uma é assumir a responsabilidade, mas passá-la para a próxima parte. Não é permitido que um objeto processador específico passe a responsabilidade após assumir algumas responsabilidades.
Em um modelo de cadeia de responsabilidade pura, uma solicitação deve ser recebida por um objeto manipulador; em um modelo de cadeia de responsabilidade impuro, uma solicitação não pode ser recebida por nenhum objeto receptor.
Exemplos reais do modelo de cadeia de responsabilidade pura são difíceis de encontrar, e os exemplos geralmente vistos são implementações do modelo de cadeia de responsabilidade impuro. Algumas pessoas pensam que uma cadeia de responsabilidade impura não é de todo um modelo de cadeia de responsabilidade, e isto pode fazer sentido. Mas nos sistemas reais é difícil encontrar uma cadeia pura de responsabilidade. Se insistirmos que a cadeia de responsabilidade é impura, não é um modelo de cadeia de responsabilidade, então o modelo de cadeia de responsabilidade não fará muito sentido.
Aplicação do modelo de cadeia de responsabilidade no Tomcat
Como todos sabemos, o Filtro no Tomcat usa o modelo de cadeia de responsabilidade. Além de fazer as configurações correspondentes no arquivo web.xml, a criação de um Filtro também requer a implementação da interface javax.servlet.Filter.
Copie o código do código da seguinte forma:
classe pública TestFilter implementa Filter{
public void doFilter (solicitação ServletRequest, resposta ServletResponse,
Cadeia FilterChain) lança IOException, ServletException {
chain.doFilter(solicitação,resposta);
}
public void destruir() {
}
public void init(FilterConfig filterConfig) lança ServletException {
}
}
Os resultados vistos usando o modo DEBUG são os seguintes:
Na verdade, antes de executar a classe TestFilter, ela passará por muitas classes internas do Tomcat. A propósito, a configuração do contêiner do Tomcat também é um modelo de cadeia de responsabilidade. Preste atenção às classes circuladas pela caixa vermelha. Do mecanismo ao host, ao contexto ao wrapper, as solicitações são passadas por uma cadeia. Existe uma classe chamada ApplicationFilterChain no local circulado pela caixa verde. A classe ApplicationFilterChain desempenha a função de processador abstrato, e a função de processador específica é desempenhada por cada Filtro.
A primeira pergunta é onde o ApplicationFilterChain armazena todos os filtros?
A resposta é armazenada em uma matriz de objetos ApplicationFilterConfig na classe ApplicationFilterChain.
Copie o código do código da seguinte forma:
/**
*Filtros.
*/
filtros privados ApplicationFilterConfig[] =
novo ApplicationFilterConfig[0];
Então, qual é o objeto ApplicationFilterConfig?
ApplicationFilterConfig é um contêiner de filtro. A seguir está a declaração da classe ApplicationFilterConfig:
Copie o código do código da seguinte forma:
/**
* Implementação de um <code>javax.servlet.FilterConfig</code> útil em
* gerenciar as instâncias de filtro instanciadas quando um aplicativo da web
* é iniciado pela primeira vez.
*
* @autor Craig R. McClanahan
* @versão $Id: ApplicationFilterConfig.java 1201569 14/11/2011 01:36:07Z kkolinko $
*/
ApplicationFilterConfig é instanciado automaticamente quando um aplicativo Web é iniciado pela primeira vez. Ele lê as informações de filtro configuradas do arquivo web.xml do aplicativo Web e, em seguida, carrega-as no contêiner.
Acabei de ver que o comprimento do array ApplicationFilterConfig criado na classe ApplicationFilterChain é zero. Quando foi reatribuído?
Copie o código do código da seguinte forma:
filtros privados ApplicationFilterConfig[] =
novo ApplicationFilterConfig[0];
É ao chamar o método addFilter() da classe ApplicationFilterChain.
Copie o código do código da seguinte forma:
/**
* O int que fornece o número atual de filtros na cadeia.
*/
privado int n = 0;
público estático final int INCREMENT = 10;
Copie o código do código da seguinte forma:
void addFilter(ApplicationFilterConfig filterConfig) {
// Evita que o mesmo filtro seja adicionado diversas vezes
for(filtro ApplicationFilterConfig:filtros)
if(filtro==filtroConfig)
retornar;
if (n == filtros.comprimento) {
ApplicationFilterConfig[] novosFilters =
novo ApplicationFilterConfig[n + INCREMENTO];
System.arraycopy(filtros, 0, novosFiltros, 0, n);
filtros = novosFiltros;
}
filtros[n++] = filterConfig;
}
A variável n é usada para registrar o número de filtros na cadeia de filtros atual. Por padrão, n é igual a 0 e o comprimento da matriz do objeto ApplicationFilterConfig também é igual a 0, portanto, quando o método addFilter() é chamado. na primeira vez, if (n == filtros .length) for verdadeiro e o comprimento da matriz ApplicationFilterConfig for alterado. Em seguida, filter[n++] = filterConfig; coloque a variável filterConfig na matriz ApplicationFilterConfig e adicione 1 ao número de filtros na cadeia de filtros atual.
Então, onde é chamado o método addFilter() de ApplicationFilterChain?
Está no método createFilterChain() da classe ApplicationFilterFactory.
Copie o código do código da seguinte forma:
aplicação públicaFilterChain createFilterChain
(solicitação ServletRequest, wrapper wrapper, servlet Servlet) {
// obtém o tipo do despachante
DispatcherType despachante = null;
if (request.getAttribute(DISPATCHER_TYPE_ATTR) != nulo) {
despachante = (DispatcherType) request.getAttribute(DISPATCHER_TYPE_ATTR);
}
String requestPath = null;
Atributo do objeto = request.getAttribute(DISPATCHER_REQUEST_PATH_ATTR);
if (atributo! = nulo){
requestPath = atributo.toString();
}
//Se não houver servlet para executar, retorne null
if (servlet == nulo)
retornar (nulo);
cometa booleano = falso;
// Cria e inicializa um objeto de cadeia de filtros
ApplicationFilterChain filterChain = null;
if (solicitação de instância de solicitação) {
Solicitação req = (Solicitação) solicitação;
cometa = req.isComet();
if (Globals.IS_SECURITY_ENABLED) {
// Segurança: Não recicle
filterChain = new ApplicationFilterChain();
se (cometa) {
req.setFilterChain(filterChain);
}
} outro {
filterChain = (ApplicationFilterChain) req.getFilterChain();
if (filterChain == null) {
filterChain = new ApplicationFilterChain();
req.setFilterChain(filterChain);
}
}
} outro {
//Solicita despachante em uso
filterChain = new ApplicationFilterChain();
}
filterChain.setServlet(servlet);
filterChain.setSupport
(((StandardWrapper)wrapper).getInstanceSupport());
// Adquira os mapeamentos de filtros para este Contexto
Contexto StandardContext = (StandardContext) wrapper.getParent();
FilterMap filterMaps[] = context.findFilterMaps();
// Se não houver mapeamentos de filtros, terminamos
if ((filterMaps == null) || (filterMaps.length == 0))
retornar (filtroChain);
// Adquira as informações que precisaremos para corresponder aos mapeamentos de filtros
String servletName = wrapper.getName();
// Adicione os filtros mapeados por caminho relevantes a esta cadeia de filtros
for (int i = 0; i < filterMaps.length; i++) {
if (!matchDispatcher(filterMaps[i],dispatcher)) {
continuar;
}
if (!matchFiltersURL(filterMaps[i], requestPath))
continuar;
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
context.findFilterConfig(filterMaps[i].getFilterName());
if (filterConfig == null) {
// FIXME - problema de configuração de log
continuar;
}
booleano isCometFilter = falso;
se (cometa) {
tentar {
isCometFilter = filterConfig.getFilter() instância do CometFilter;
} catch (Exceção e) {
// Nota: O try catch existe porque getFilter tem muitos
// exceções declaradas No entanto, o filtro é muito alocado.
// mais cedo
Lançável t = ExceptionUtils.unwrapInvocationTargetException(e);
ExceptionUtils.handleThrowable(t);
}
if (isCometFilter) {
filterChain.addFilter(filterConfig);
}
} outro {
filterChain.addFilter(filterConfig);
}
}
// Adiciona filtros que correspondam ao nome do servlet em segundo lugar
for (int i = 0; i < filterMaps.length; i++) {
if (!matchDispatcher(filterMaps[i],dispatcher)) {
continuar;
}
if (!matchFiltersServlet(filterMaps[i], servletName))
continuar;
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
context.findFilterConfig(filterMaps[i].getFilterName());
if (filterConfig == null) {
// FIXME - problema de configuração de log
continuar;
}
booleano isCometFilter = falso;
se (cometa) {
tentar {
isCometFilter = filterConfig.getFilter() instância do CometFilter;
} catch (Exceção e) {
// Nota: O try catch existe porque getFilter tem muitos
// exceções declaradas No entanto, o filtro é muito alocado.
// mais cedo
}
if (isCometFilter) {
filterChain.addFilter(filterConfig);
}
} outro {
filterChain.addFilter(filterConfig);
}
}
//Retorna a cadeia de filtros concluída
retornar (filtroChain);
}
O código acima pode ser dividido em duas seções, a primeira seção antes da linha 51 e a segunda seção após a linha 51.
O objetivo principal do primeiro parágrafo é criar o objeto ApplicationFilterChain e definir alguns parâmetros.
O objetivo principal do segundo parágrafo é obter todas as informações de filtro do contexto e, em seguida, usar um loop for para percorrer e chamar filterChain.addFilter(filterConfig);
Então, onde é chamado o método createFilterChain() da classe ApplicationFilterFactory?
É chamado no método Invoke() da classe StandardWrapperValue.
Como o método invocar() é longo, muitos lugares são omitidos.
Copie o código do código da seguinte forma:
public final void invocar (solicitação de solicitação, resposta de resposta)
lança IOException, ServletException {
...Omitir código intermediário // Cria a cadeia de filtros para esta solicitação
Fábrica ApplicationFilterFactory =
ApplicationFilterFactory.getInstance();
ApplicationFilterChain filterChain =
factory.createFilterChain(solicitação, wrapper, servlet);
... omitir código intermediário
filterChain.doFilter(request.getRequest(), resposta.getResponse());
... omitir código intermediário
}
O processo normal deve ser assim:
Chame o método createFilterChain() da classe ApplicationFilterChai no método activate() da classe StandardWrapperValue ---> chame o método addFilter() da classe ApplicationFilterChain no método createFilterChain() da classe ApplicationFilterChai ---> chame o Método addFilter() da classe ApplicationFilterChain Atribua um valor ao array ApplicationFilterConfig.
De acordo com o código acima, pode-se observar que o método invoke() da classe StandardWrapperValue continuará a executar o método doFilter() da classe ApplicationFilterChain após executar o método createFilterChain(), e então o método internalDoFilter() será chamado no método doFilter().
A seguir está parte do código do método internalDoFilter()
Copie o código do código da seguinte forma:
//Chama o próximo filtro se houver
se (pos < n) {
//Obtém o próximo filtro e move o ponteiro uma posição para baixo
//pos identifica qual filtro o ApplicationFilterChain atual (cadeia de filtros atual) executa
ApplicationFilterConfig filterConfig = filtros[pos++];
Filtro filtro = null;
tentar {
//Obtém a instância do filtro atualmente apontado
filtro = filterConfig.getFilter();
support.fireInstanceEvent(InstanceEvent.BEFORE_FILTER_EVENT,
filtro, solicitação, resposta);
if (request.isAsyncSupported() && "falso".equalsIgnoreCase(
filterConfig.getFilterDef().getAsyncSupported())) {
request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,
Boolean.FALSE);
}
if(Globais.IS_SECURITY_ENABLED) {
final ServletRequest req = solicitação;
final ServletResponse res = resposta;
Diretor principal =
((HttpServletRequest) req).getUserPrincipal();
Object[] args = new Object[]{req, res, this};
SecurityUtil.doAsPrivilege
("doFilter", filtro, classType, args, principal);
} outro {
//Chama o método doFilter() do Filter
filter.doFilter(solicitação, resposta, isto);
}
O filter.doFilter(request, response, this); aqui é para chamar o método doFilter() no TestFilter que criamos anteriormente. O método doFilter() em TestFilter continuará a chamar o método chain.doFilter(request, response); e esta cadeia é na verdade o ApplicationFilterChain, então o processo de chamada volta a chamar o dofilter e chamar o método internalDoFilter acima, e isso a execução continua até que o filtro dentro de Execute todos eles.
Se dois filtros forem definidos, os resultados da depuração serão os seguintes: