Contenedor y especificación de servlet
Nuestro Spring MVC de uso común se implementa en función de la especificación de Servlet, así que revisemos primero el contenido relacionado con Servlet.
Si usamos directamente Servlet para desarrollar aplicaciones web, solo necesitamos heredar HttpServlet e implementar el método de servicio. HttpServlet hereda de Servlet. Los métodos comúnmente usados en Servlet son los siguientes
public interface Servlet {
// 初始化,只会被调用一次,在service方法调用之前完成
void init(ServletConfig config) throws ServletException;
ServletConfig getServletConfig();
// 处理请求
void service(ServletRequest req, ServletResponse res)throws ServletException, IOException;
String getServletInfo();
// 销毁
void destroy();
}
Cada Servlet tiene un ServletConfig, que se utiliza para almacenar la configuración relacionada con el Servlet.
Cada aplicación Web tiene un ServletContext, que se utiliza para almacenar la configuración relacionada con el contenedor.
Teniendo en cuenta que muchos socios pequeños pueden no estar familiarizados con muchos usos de Servlet, vamos a presentarlo brevemente y usar la configuración xml. Por supuesto, puede usar JavaConfig para cambiarlo
La estructura del proyecto es la siguiente
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<context-param>
<param-name>configLocation</param-name>
<param-value>test</param-value>
</context-param>
<servlet>
<servlet-name>userServlet</servlet-name>
<servlet-class>com.javashitang.controller.UserServlet</servlet-class>
<init-param>
<param-name>helloWord</param-name>
<param-value>hello sir</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>userServlet</servlet-name>
<url-pattern>/user.do</url-pattern>
</servlet-mapping>
<listener>
<listener-class>com.javashitang.listener.MyServletContextListener</listener-class>
</listener>
</web-app>
public class UserServlet extends HttpServlet {
private String helloWord;
@Override
public void init(ServletConfig config) throws ServletException {
this.helloWord = config.getInitParameter("helloWord");
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html");
PrintWriter out = resp.getWriter();
String userId = req.getParameter("userId");
out.println(helloWord + " " + userId);
}
}
En el archivo de configuración xml, puede usar la etiqueta init-param para establecer algunas configuraciones para el Servlet, y luego usar ServletConfig en el método init para obtener estas configuraciones y realizar la inicialización
访问
http://localhost:8080/user.do?userId=1
返回
hello sir 1
Puede ver que también hemos configurado la etiqueta de carga al inicio para este servlet, entonces, ¿cuál es el uso de esta etiqueta?
Load-on-startup significa que el servlet se inicializa cuando se inicia el contenedor. Cuanto más pequeña sea la matriz, mayor será la prioridad de inicio. Cuando esta etiqueta no está configurada, el servlet se inicializará cuando llegue la primera solicitud.
La etiqueta context-param es la configuración inicial del contenedor, puede llamar al método getInitParameter del contenedor para obtener el valor del atributo
Listener es un mecanismo de extensión que envía varios eventos cuando la aplicación web se inicia o se detiene. Podemos usar Listener para monitorear estos eventos y hacer un trabajo de inicialización. Como monitorear el evento de inicio para inicializar la conexión de la base de datos, etc.
Mi demostración acaba de obtener la ubicación del archivo de configuración y lo imprimió.
public class MyServletContextListener implements ServletContextListener {
// 容器启动
public void contextInitialized(ServletContextEvent sce) {
ServletContext sc = sce.getServletContext();
String location = sc.getInitParameter("configLocation");
System.out.println(location);
}
// 容器销毁
public void contextDestroyed(ServletContextEvent sce) {
}
}
Escribe una aplicación Spring MVC
Escribimos una aplicación Spring MVC basada en xml, y analizamos en base a esta aplicación. La estructura del proyecto es la siguiente:
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-context.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
spring-context.xml (servicio de configuración, capa dao)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.javashitang.service"/>
</beans>
spring-mvc.xml (configuración y configuración relacionada con spring mvc)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.javashitang.controller"/>
<mvc:annotation-driven/>
</beans>
@RestController
public class UserController implements ApplicationContextAware {
@Resource
private UserService userService;
private ApplicationContext context;
@RequestMapping("user")
public String index(@RequestParam("userId") String userId) {
return userService.getUsername(userId);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context = applicationContext;
System.out.println("UserController " + context.getId());
}
}
public interface UserService {
String getUsername(String userId);
}
@Service
public class UserServiceImpl implements UserService, ApplicationContextAware {
private ApplicationContext context;
@Override
public String getUsername(String userId) {
return userId;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context = applicationContext;
System.out.println("UserServiceImpl " + context.getId());
}
}
La razón por la que necesitamos usar 2 archivos de configuración es porque hay 2 contenedores en Spring MVC
El contenedor primario es inicializado por el ContextLoaderListener, generalmente utiliza para almacenar alguna capa capa DAO y el servicio de haba
sub-contenedores se inicializan por DispatcherServlet, que se utiliza generalmente para almacenar la capa del controlador de la haba
Se puede ver en el valor impreso después de que se inicia el proyecto que el Servicio y el Controlador se obtienen de 2 contenedores
UserServiceImpl org.springframework.web.context.WebApplicationContext:
UserController org.springframework.web.context.WebApplicationContext:/dispatcher
El contenedor secundario puede acceder al Bean del contenedor principal y el contenedor principal no puede acceder al Bean del contenedor secundario. Cuando no se puede encontrar el Bean correspondiente en el contenedor secundario, se encontrará en el contenedor principal.
Inicio del contenedor principal
ContextLoaderListener inicializa el contenedor principal. Cuando se inicia Tomcat, publica el evento de inicio, llama al método ContextLoaderListener # contextInitialized y luego llama al método initWebApplicationContext
Inicio contenedor infantil
El inicio del subcontenedor está en el método DispatcherServlet # init
DispatcherServlet no anuló el método init, por lo que está en la clase principal. HttpServletBean anula el método init.
Resuma el proceso con un diagrama de flujo
Si cree que el contenedor principal es inútil, puede poner todos los frijoles en el contenedor secundario