Diagrama do princípio de funcionamento do SpringMVC:
Processo SpringMVC
1. O usuário envia uma solicitação ao controlador front-end DispatcherServlet.
2. DispatcherServlet recebe a solicitação e chama o mapeador do processador HandlerMapping.
3. O mapeador do processador encontra o processador específico (pode ser pesquisado com base na configuração xml e nas anotações), gera o objeto do processador e o interceptador do processador (gerado, se houver) e os retorna ao DispatcherServlet.
4. DispatcherServlet chama o adaptador do processador HandlerAdapter.
5. HandlerAdapter chama o processador específico (Controller, também chamado de controlador back-end) por meio de adaptação.
6. O controlador retorna para ModelAndView após a conclusão da execução.
7. HandlerAdapter retorna o resultado da execução do controlador ModelAndView para DispatcherServlet.
8. DispatcherServlet passa ModelAndView para o resolvedor de visualização ViewReslover.
9. ViewReslover retorna a visualização específica após a análise.
10. DispatcherServlet renderiza a visualização com base na Visualização (ou seja, preenche os dados do modelo na visualização).
11. DispatcherServlet responde ao usuário.
Descrição do componente:
Os seguintes componentes são normalmente implementados usando estruturas:
DispatcherServlet: Como controlador front-end, centro de todo o controle do processo, controla a execução de outros componentes, unifica o agendamento, reduz o acoplamento entre os componentes e melhora a escalabilidade de cada componente.
HandlerMapping: implemente diferentes métodos de mapeamento estendendo o mapeador do processador, como método de arquivo de configuração, método de implementação de interface, método de anotação, etc.
HandlAdapter: Suporta mais tipos de processadores estendendo o adaptador do processador.
ViewResolver: Ao estender o resolvedor de visualização, ele suporta mais tipos de resolução de visualização, como: jsp, freemarker, pdf, excel, etc.
Componentes:
1. Controlador front-end DispatcherServlet (sem necessidade de desenvolvimento de engenheiros), fornecido pelo framework.Função
: receber solicitações, responder aos resultados, equivalente ao encaminhador, processador central. Com o dispatcherServlet, o acoplamento entre outros componentes é reduzido.
Quando a solicitação do usuário chega ao controlador front-end, é equivalente a c no modo MVC. O dispatcherServlet é o centro de todo o controle do processo. Ele chama outros componentes para tratar a solicitação do usuário. A existência do dispatcherServlet reduz o acoplamento entre componentes.
2. Mapeamento de Handler (sem necessidade de desenvolvimento de engenheiros), fornecido pelo framework.
Função: Encontrar o Handler de acordo com a URL solicitada.
HandlerMapping é responsável por encontrar o Handler, ou seja, o processador de acordo com a solicitação do usuário. Springmvc fornece diferentes mapeadores para implementar diferentes métodos de mapeamento, como: método de arquivo de configuração, método de implementação de interface, método de anotação, etc.
3.
A função do adaptador de processador HandlerAdapter: executar o Handler de acordo com regras específicas (as regras exigidas pelo HandlerAdapter) para
executar o processador através do HandlerAdapter. Esta é a aplicação do modo adaptador. Ao estender o adaptador, mais tipos de processadores podem ser executados.
4. Manipulador do processador (requer desenvolvimento de engenheiro)
Nota: Ao escrever o manipulador, siga os requisitos do HandlerAdapter, para que o adaptador possa executar corretamente o manipulador.
Handler é o controlador back-end seguindo o controlador front-end DispatcherServlet. Sob o controle de DispatcherServlet, as solicitações de usuário específicas do manipulador são processadas.
Como o Handler envolve solicitações comerciais específicas do usuário, os engenheiros geralmente precisam desenvolver o Handler de acordo com as necessidades do negócio.
5. Resolvedor de visualização (sem necessidade de desenvolvimento de engenheiros), fornecido pelo framework.
Função: executar a resolução da visualização e resolvê-la em uma visualização real (visualização) com base no nome da visualização lógica. O
View Resolver é responsável por gerar a visualização da visualização. a partir dos resultados do processamento. Visualize o Resolvedor primeiro com base na lógica O nome da visualização é analisado no nome da visualização física, que é o endereço da página específico, e então o objeto View é gerado. Finalmente, a Visualização é renderizada e os resultados do processamento são exibidos para o usuário através da página. A estrutura springmvc fornece muitos tipos de View, incluindo: jstlView, freemarkerView, pdfView, etc.
Geralmente, os dados do modelo precisam ser exibidos aos usuários por meio de tags de página ou tecnologia de modelo de página, e os engenheiros precisam desenvolver páginas específicas com base nas necessidades do negócio.
6. View View (os engenheiros são obrigados a desenvolver jsp...)
View é uma interface, e a classe de implementação suporta diferentes tipos de View (jsp, freemarker, pdf...)
As etapas específicas do processo da arquitetura principal são as seguintes:
1. Primeiro, o usuário envia uma solicitação——>DispatcherServlet. Após receber a solicitação, o controlador front-end não a processa, mas a delega a outros analisadores para processamento. Ele serve como um ponto de acesso unificado para controle global do processo; 2.
DispatcherServlet—— >HandlerMapping, HandlerMapping mapeará a solicitação para um objeto HandlerExecutionChain (incluindo um objeto processador Handler (controlador de página), vários interceptores HandlerInterceptor). Através deste modo de estratégia, é fácil adicionar novas estratégias de mapeamento; 3.
DispatcherServlet ——>HandlerAdapter, HandlerAdapter empacotará o processador como um adaptador para suportar vários tipos de processadores, ou seja, a aplicação do padrão de design do adaptador, facilitando o suporte a vários tipos de processadores; 4. HandlerAdapter——>Funções do
processador Ao chamar o método de processamento, o HandlerAdapter chamará o método de processamento de função do processador real de acordo com o resultado da adaptação para completar o processamento da função; e retornará um objeto ModelAndView (incluindo dados do modelo e nome da visualização lógica); 5. O nome da visualização lógica de ModelAndView
- -> ViewResolver, ViewResolver resolverá o nome da visualização lógica em uma visualização específica. Através deste modo de estratégia, é fácil substituir outras tecnologias de visualização; 6.
Visualização -> Renderização, a visualização será renderizada de acordo com os dados do modelo de entrada Este modelo é na verdade uma estrutura de dados de mapa, portanto pode suportar facilmente outras tecnologias de visualização;
7. Retorne o controle ao DispatcherServlet e o DispatcherServlet retorna uma resposta ao usuário. Este é o fim do processo.
Geralmente, os dois componentes a seguir precisam ser desenvolvidos:
Handler: Processador, ou seja, o controlador back-end é representado pelo controlador.
Visualização: Visualização é a interface exibida ao usuário. A visualização geralmente requer uma linguagem de rótulo para exibir os dados do modelo.
Como mostrado abaixo:
Interceptador personalizado SpringMVC
1.Interceptador SpringMVC
1.1 Introdução aos interceptadores
O interceptor do processador do Spring web MVC é semelhante ao filtro no desenvolvimento do Servlet, que é usado para pré-processar e pós-processar o processador.
1.2 Cenários comuns de aplicação
1. Logging: Logs que registram informações de solicitação
2. Verificações de permissão, como verificações de login
3. Teste de desempenho: Tempo de execução dos métodos de detecção
1.2.1 Registro
package com.yaspeed.web.interceptor;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Map;
import java.util.Map.Entry;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.NamedThreadLocal;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
/**
* 日志拦截器 <br>
* 记录信息:访问时间-Controller路径-对应方法名-请求参数信息-请求相对路径-请求处理时长
*
* @author Administrator
*
*/
public class LogInterceptor implements HandlerInterceptor {
public static final Logger LOGGER = LoggerFactory.getLogger(LogInterceptor.class);
private static final ThreadLocal<Long> startTimeThreadLocal = new NamedThreadLocal<Long>("ThreadLocal StartTime");
private String getParamString(Map<String, String[]> map) {
StringBuilder sb = new StringBuilder();
for (Entry<String, String[]> e : map.entrySet()) {
sb.append(e.getKey()).append("=");
String[] value = e.getValue();
if (value != null && value.length == 1) {
sb.append(value[0]).append("\t");
} else {
sb.append(Arrays.toString(value)).append("\t");
}
}
return sb.toString();
}
/**
* 将ErrorStack转化为String.
*/
public static String getStackTraceAsString(Throwable e) {
if (e == null) {
return "";
}
StringWriter stringWriter = new StringWriter();
e.printStackTrace(new PrintWriter(stringWriter));
return stringWriter.toString();
}
@Override
/**
* 该方法将在请求处理之前进行调用<br>
* 多个Interceptor,然后在SpringMVC会根据声明的前后顺序一个接一个的执行,而且所有的Interceptor中的preHandle方法都会在<br>
* COntroller方法之前调用。SpringMVC的这种Interceptor链式结构也是可以中断的,这种中断方式时令preHandler的返回值为false<br>
* 当prehandler的返回值为false的时候整个请求就结束了。
*/
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
long startTime = System.currentTimeMillis();
request.setAttribute("startTime", startTime);
startTimeThreadLocal.set(startTime); // 线程绑定变量(该数据只有当前请求的线程可见)
if (HandlerMethod.class.equals(handler.getClass())) {
StringBuilder sb = new StringBuilder(1000);
sb.append("-----------------------开始计时:").append(new SimpleDateFormat("hh:mm:ss.SSS").format(startTime))
.append("-------------------------------------\n");
HandlerMethod h = (HandlerMethod) handler;
sb.append("Controller: ").append(h.getBean().getClass().getName()).append("\n");
sb.append("Method : ").append(h.getMethod().getName()).append("\n");
sb.append("Params : ").append(getParamString(request.getParameterMap())).append("\n");
sb.append("URI : ").append(request.getRequestURI()).append("\n");
LOGGER.debug(sb.toString());
}
return true;
}
/**
* 在当前请求进行处理之后,也就是Controller 方法调用之后执行,但是它会在DispatcherServlet
* 进行视图返回渲染之前被调用,所以我们可以在这个方法中对Controller 处理之后的ModelAndView 对象进行操作。
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
long startTime = (Long) request.getAttribute("startTime");
long endTime = System.currentTimeMillis();
long executeTime = endTime - startTime;
if (HandlerMethod.class.equals(handler.getClass())) {
StringBuilder sb = new StringBuilder(1000);
sb.append("CostTime : ").append(executeTime).append("ms").append("\n");
sb.append("-------------------------------------------------------------------------------");
LOGGER.debug(sb.toString());
}
}
/**
* 该方法将在整个请求结束之后,也就是在DispatcherServlet 渲染了对应的视图之后执行。这个方法的主要作用是用于进行资源清理工作的。
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
// 打印JVM信息。
if (LOGGER.isDebugEnabled()) {
long beginTime = startTimeThreadLocal.get();// 得到线程绑定的局部变量(开始时间)
long endTime = System.currentTimeMillis(); // 2、结束时间
// 如果controller报错,则记录异常错误
if (ex != null) {
LOGGER.debug("Controller异常: " + getStackTraceAsString(ex));
}
LOGGER.debug("计时结束:" + new SimpleDateFormat("hh:mm:ss.SSS").format(endTime) + " 耗时:" + (endTime - beginTime)
+ " URI:" + request.getRequestURI());
startTimeThreadLocal.remove();
}
}
Método 1. Configuração em springmvc.xml
<!-- 配置拦截器 -->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.yaspeed.web.interceptor.LogInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
Método 2. Configuração do interceptor
package com.cy.pj.common.config;
import com.cy.pj.common.web.LogInterceptor ;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
//拦截器配置
@Configuration
public class SpringWebConfig implements WebMvcConfigurer {
//配置springMVC拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LogInterceptor ())
.addPathPatterns("/user/doLogin");
}
}
1.2.2 Verificação de permissão
/**
* 权限检查
*
* @author Administrator
*
*/
public class PermissionInterceptor implements HandlerInterceptor {
// 在执行handler之前来执行的
// 用于用户认证校验、用户权限校验
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// 得到请求的url
String url = request.getRequestURI();
// 判断是否是公开 地址
// 实际开发中需要公开 地址配置在配置文件中
// 从配置中取逆名访问url
List<String> open_urls = ResourcesUtil.gekeyList("anonymousURL");
// 遍历公开 地址,如果是公开 地址则放行
for (String open_url : open_urls) {
if (url.indexOf(open_url) >= 0) {
// 如果是公开 地址则放行
return true;
}
}
// 从配置文件中获取公共访问地址
List<String> common_urls = ResourcesUtil.gekeyList("commonURL");
// 遍历公用 地址,如果是公用 地址则放行
for (String common_url : common_urls) {
if (url.indexOf(common_url) >= 0) {
// 如果是公开 地址则放行
return true;
}
}
// 获取session
HttpSession session = request.getSession();
ActiveUser activeUser = (ActiveUser) session.getAttribute("activeUser");
// 从session中取权限范围的url
List<SysPermission> permissions = activeUser.getPermissions();
for (SysPermission sysPermission : permissions) {
// 权限的url
String permission_url = sysPermission.getUrl();
if (url.indexOf(permission_url) >= 0) {
// 如果是权限的url 地址则放行
return true;
}
}
// 执行到这里拦截,跳转到无权访问的提示页面
request.getRequestDispatcher("/WEB-INF/jsp/refuse.jsp").forward(request, response);
// 如果返回false表示拦截不继续执行handler,如果返回true表示放行
return false;
}
// 在执行handler返回modelAndView之前来执行
// 如果需要向页面提供一些公用 的数据或配置一些视图信息,使用此方法实现 从modelAndView入手
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("HandlerInterceptor1...postHandle");
}
// 执行handler之后执行此方法
// 作系统 统一异常处理,进行方法执行性能监控,在preHandle中设置一个时间点,在afterCompletion设置一个时间,两个时间点的差就是执行时长
// 实现 系统 统一日志记录
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("HandlerInterceptor1...afterCompletion");
}
}
1.2.3 Teste de desempenho
/**
* 实现统计应用性能 拦截器 的实现是单例的,因此不管用户请求多少次 都 只访问一个拦截器实例,即线程不安全<br>
* 解决方案:使用ThreadLocal,它是线程绑定的变量,提供线程局部变量 (一个线程一个ThreadLocal)
*
* @author Administrator
*
*/
public class TimeInterceptor implements HandlerInterceptor {
public static final Logger logger = LoggerFactory.getLogger(TimeInterceptor.class);
// 统计应用性能
private NamedThreadLocal<Long> startTimeThreadLocal = new NamedThreadLocal<>("StopWatch-StartTime");
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// 1,开始时间
long startTime = System.currentTimeMillis();
// 线程绑定变量(该数据只有当前请求的线程可见)
startTimeThreadLocal.set(startTime);
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
// 2.结束时间
long endTime = System.currentTimeMillis();
// 得到线程绑定的局部 变量(开始时间)
long beginTime = startTimeThreadLocal.get();
// 3.计算消耗时间
long consumeTime = endTime - beginTime;
logger.debug("监控==========================: "
+ String.format("%s consume %d millis", request.getRequestURI(), consumeTime));
startTimeThreadLocal.remove();
}
}