@ServletComponentScanServletРегистрация сервлетов через кодРегистрация сервлетов с аннотациямиFilterАннотировать с помощью @WebFilterНаследовать HttpFilterРеализовать интерфейс фильтраListener@WebListenerСуммировать
@ServletComponentScan
Исходный код выглядит следующим образом:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({ServletComponentScanRegistrar.class})
public @interface ServletComponentScan {
@AliasFor("basePackages")
String[] value() default {};
@AliasFor("value")
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
}
Ключевым моментом в аннотации @ServletComponentScan является ссылка на класс ServletComponentScanRegistrar, который является классом реализации интерфейса ImportBeanDefinitionRegistrar и будет анализироваться контейнером Spring. ServletComponentScanRegistrar проанализирует аннотацию @ServletComponentScan внутри, а затем зарегистрирует ServletComponentRegisteringPostProcessor в контейнере Spring, который является BeanFactoryPostProcessor.Он проанализирует, имеет ли отсканированный класс три аннотации: @WebServlet, @WebListener и @WebFilter.Этот тип класса преобразуется в ServletRegistrationBean, FilterRegistrationBean или ServletListenerRegistrationBean, а затем позволяет контейнеру Spring разрешаться.
После использования аннотации @ServletComponentScan в SpringBootApplication Servlet, Filter и Listener могут быть автоматически зарегистрированы напрямую через аннотации @WebServlet, @WebFilter, @WebListener без дополнительного кода.
Вышеупомянутое содержимое является кратким изложением аннотации @ServletComponentScan.Далее мы в основном расскажем, как добавить сервлет, фильтр и прослушиватель в Spring Boot.
Три аннотации @WebServlet, @WebFilter и @WebListener, упомянутые только что, на самом деле являются новыми аннотациями в Servlet 3.0.
Сервлет 3.0 был выпущен со спецификацией Java EE 6 как часть стека спецификаций Java EE 6. Эта версия основана на предыдущей версии (Servlet 2.5) с несколькими новыми функциями, упрощающими разработку и развертывание веб-приложений. Внедрение некоторых из этих функций взволновало разработчиков и получило много похвал от сообщества Java:
- Поддержка асинхронной обработки: До Servlet3.0 поток сервлета должен блокироваться до завершения процесса, чтобы повторно экспортировать бизнес-ответ, и, наконец, конец потока сервлета. Servlet3.0 и, при получении запроса, поток сервлета может выполнять трудоемкую операцию, делегированную другому потоку, чтобы завершить их возврат в контейнер без генерации ответа. Для более трудоемких бизнес-процессов, которые значительно уменьшат объем ресурсов сервера и повысят скорость параллельной обработки.
- Добавлена поддержка аннотаций: в этой версии добавлено несколько аннотаций для упрощения объявления сервлета, фильтра и прослушивателя, что делает файл описания развертывания web.xml более не нужным в этой версии.
- Поддержка подключаемости: Разработчики, знакомые со Struts2, наверняка помнят функции интеграции различных распространенных фреймворков, включая Spring, через плагины. Соответствующие подключаемые модули упаковываются в пакеты JAR и помещаются в путь к классам, а среда выполнения Struts2 может автоматически загружать эти подключаемые модули. Теперь Servlet 3.0 предоставляет аналогичные функции, и разработчики могут легко расширять функции существующих веб-приложений с помощью подключаемых модулей, не изменяя исходные приложения.
Для ознакомления с новыми функциями Servlet 3.0 см.:Подробное объяснение новых возможностей Servlet 3.0
Servlet
До Servlet3.0 мы могли настраивать сервлеты только из файла web.xml. web.xml — это файл в Java EE, который можно дополнительно использовать для описания развертывания приложений, чтобы контейнер сервлетов мог загружать и развертывать приложения.Этот файл можно использовать для объявления сервлетов, сопоставлений доступа к сервлетам, настройки прослушивателей и другой информации, а описать внешние ресурсы.
Например, когда Tomcat запускает и развертывает веб-приложение, он загружает файл web.xml на этапе инициализации, затем загружает сервлет и загружает отношения сопоставления между сервлетом и API и, наконец, предоставляет услуги внешнему миру. . На этом этапе каждый раз, когда мы разрабатываем новые функции и добавляем новые сервлеты, нам нужно модифицировать файл web.xml, который громоздок в настройке.
После Servlet3.0 создание сервлетов может иметь следующие методы:
Сначала пользовательский класс наследует HttpServlet, а затем регистрация кода контролируется ServletRegistrationBean. Его также можно реализовать напрямую, реализуя интерфейс ServletContextInitializer;
После использования аннотации @ServletComponentScan в SpringBootApplication сервлеты могут быть автоматически зарегистрированы непосредственно через аннотацию @WebServlet без дополнительного кода.
Регистрация сервлетов через код
ServletRegistrationBean объявляется в классе запуска Spring Boot.
класс сервлета:
public class MyServlet extends HttpServlet {
private static final long serialVersionUID = -8685285401859800066L;
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println(">>>>>>>>>>service()<<<<<<<<<<<");
resp.getWriter().println("receive by MyServlet");
}
}
Сначала определите сервлет и перепишите службу, чтобы реализовать собственную бизнес-логику.
Класс запуска Spring Boot:
@SpringBootApplication
public class SpbGuide3Application {
@Bean
public ServletRegistrationBean servletRegistrationBean(){
return new ServletRegistrationBean(new MyServlet(),"/servlet/*");
}
public static void main(String[] args) {
SpringApplication.run(SpbGuide3Application.class, args);
}
}
Затем вставьте экземпляр компонента типа ServletRegistrationBean в контейнер Spring с помощью аннотации @Bean и создайте экземпляр пользовательского сервлета в качестве параметра, чтобы пользовательский сервлет был добавлен в Tomcat.
Запустите проект и откройте URL-адрес: http://localhost:8080/servlet.
Как упоминалось ранее, его также можно реализовать напрямую, реализуя интерфейс ServletContextInitializer следующим образом:
Класс сервлета:
package com.example.servlet;
public class MyServlet2 extends HttpServlet {
private static final long serialVersionUID = -8685285401859800066L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println(">>>>>>>>>>doGet()<<<<<<<<<<<");
doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println(">>>>>>>>>>doPost()<<<<<<<<<<<");
resp.setContentType("text/html");
PrintWriter out = resp.getWriter();
out.println("<html>");
out.println("<head>");
out.println("<title>Hello World</title>");
out.println("</head>");
out.println("<body>");
out.println("<h1>MyServlet2 doPost()</h1>");
out.println("</body>");
out.println("</html>");
}
}
В HttpServlet есть еще три важных метода: методы service(), doGet() и doPost().По умолчанию в сервлете, независимо от того, отправляете ли вы его с помощью get или post, он будет обработан методом service(), а затем для метода doGet или doPost. Когда пользовательский класс сервлета наследует HttpServlet, если вы не переопределяете метод службы, вам нужно только переопределить метод doGet или doPost.
Реализовать пользовательскую загрузку конфигурации ServletContainerInitializer
public class MyServletContainerInitializer implements ServletContextInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
System.out.println("启动加载自定义的MyServletContainerInitializer");
ServletRegistration.Dynamic registration = servletContext.addServlet("servlet2","com.example.servlet.MyServlet2");
registration.setLoadOnStartup(1);
registration.addMapping("/servlet2");
}
}
Глядя на исходный код класса ServletRegistrationBean, мы видим, что этот класс наследует RegistrationBean, а RegistrationBean реализует интерфейс ServletContextInitializer и имеет метод onStartup, поэтому мы можем настроить класс реализации интерфейса ServletContextInitializer для замены ServletRegistrationBean.
ServletContextInitializerЭто интерфейс инициализации, предоставляемый при инициализации контейнера сервлета. Поэтому при инициализации контейнера сервлетов будет получен и запущен метод onStartup во всех экземплярах `FilterRegistrationBean, FilterRegistrationBean, ServletListenerRegistrationBean.
Класс запуска Spring Boot:
@SpringBootApplication
public class SpbGuide3Application {
/**
* 使用代码注册Servlet(不需要@ServletComponentScan注解)
* @return
*/
@Bean
public MyServletContainerInitializer servletContainerInitializer(){
return new MyServletContainerInitializer();
}
public static void main(String[] args) {
SpringApplication.run(SpbGuide3Application.class, args);
}
}
Запустите проект и откройте URL-адрес: http://localhost:8080/servlet2.
Регистрация сервлетов с аннотациями
Ниже приведен список атрибутов @WebServlet.
| Имя свойства | тип | описывать |
|---|---|---|
| name | String | Указание атрибута имени сервлета эквивалентно, если не указано явно, значением сервлета является полное имя класса. |
| value | String[] | Это свойство эквивалентно свойству urlPatterns. Оба свойства нельзя использовать одновременно. |
| urlPatterns | String[] | Задает набор шаблонов сопоставления URL-адресов для сервлетов, эквивалентных тегам. |
| loadOnStartup | int | Указывает порядок загрузки сервлетов, эквивалентный тегам. |
| initParams | WebInitParam[] | Задает набор параметров инициализации сервлета, эквивалентных тегам. |
| asyncSupported | boolean | Объявляет, поддерживает ли сервлет асинхронный режим работы, эквивалентный тегу. |
| description | String | Информация описания сервлета, эквивалентная метке. |
| displayName | String | Отображаемое имя сервлета, обычно используемое с инструментами, эквивалентно метке. |
Как видно из приведенной выше таблицы, атрибуты сервлета, которые можно настроить в файле web.xml, можно настроить и в @WebServlet.
В Spring Boot встроенный контейнер сервлетов регистрирует сервлеты, фильтры и всех прослушивателей спецификации сервлета (таких как прослушиватели HttpSessionListener) путем сканирования аннотаций.
Класс сервлета:
@WebServlet(urlPatterns = "/servlet/web",
description = "用注解声明Servlet",
initParams = {//以下都是druid数据源状态监控的参数
@WebInitParam(name = "allow",value = ""),// IP白名单 (没有配置或者为空,则允许所有访问)
@WebInitParam(name = "deny",value = ""),// IP黑名单 (存在共同时,deny优先于allow)
@WebInitParam(name = "loginUsername",value = "hresh"),// 用户名
@WebInitParam(name = "loginPassword",value = "123456"),// 密码
@WebInitParam(name = "resetEnable",value = "false")// 禁用HTML页面上的“Reset All”功能
})
public class MyWebServlet extends HttpServlet {
private static final long serialVersionUID = -8685285401859800066L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println(">>>>>>>>>>MyWebServlet doGet()<<<<<<<<<<<");
ServletConfig config = getServletConfig();
System.out.println(config.getInitParameter("loginUsername"));
System.out.println(config.getInitParameter("loginPassword"));
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println(">>>>>>>>>>MyWebServlet doPost()<<<<<<<<<<<");
resp.setContentType("text/html");
PrintWriter out = resp.getWriter();
out.println("<html>");
out.println("<head>");
out.println("<title>Hello World</title>");
out.println("</head>");
out.println("<body>");
out.println("<h1>MyServlet2 doPost()</h1>");
out.println("</body>");
out.println("</html>");
}
}
Класс запуска Spring Boot:
@SpringBootApplication
@ServletComponentScan
public class SpbGuide3Application {
public static void main(String[] args) {
SpringApplication.run(SpbGuide3Application.class, args);
}
}
Запустите проект и откройте URL-адрес: http://localhost:8080/servlet/web.
Выходные данные loginUsername и loginPassword — это то, что мы установили в аннотации @WebServlet.
Filter
Аннотировать с помощью @WebFilter
Общие свойства @WebFilter
| Имя свойства | тип | описывать |
|---|---|---|
| filterName | String | Указывает атрибут имени фильтра, который эквивалентен, |
| value | String[] | Это свойство эквивалентно свойству urlPatterns. Оба свойства нельзя использовать одновременно. |
| urlPatterns | String[] | Шаблоны сопоставления URL-адресов, определяющие набор фильтров, эквивалентных тегам. |
| dispatcherTypes | DispatcherType | Указывает режим пересылки фильтра. Конкретные значения включают: ASYNC, ERROR, FORWARD, INCLUDE, REQUEST. |
| initParams | WebInitParam[] | Задает набор параметров инициализации фильтра, эквивалентных меткам. |
| asyncSupported | boolean | Указывает, поддерживает ли фильтр асинхронный режим работы, эквивалентный тегу. |
| description | String | Описание фильтра, эквивалентное метке. |
| displayName | String | Отображаемое имя фильтра, обычно используемое с инструментами, эквивалентно метке. |
При использовании аннотации @WebFilter обнаружено, что в аннотации нет параметра, который может управлять порядком выполнения.Практически установлено, что если вы хотите контролировать порядок выполнения фильтра, вы можетеКонтролируя имя файла фильтраконтролировать. Например: UserLoginFilter.java и ApiLog.java эти два файла фильтра, потому что эти два файла имеютПервая буква А стоит перед У, чтобы фильтр ApiLog выполнялся первым, а затем фильтр UserLoginFilter выполнялся каждый раз при его выполнении, поэтому теперь мы изменим имена двух файлов на Filter0_UserLogin.java и Filter1_ApiLog.java, а затем выполним Filter0_UserLogin, а затем Filter1_ApiLog.
Пример:
Класс фильтра:
@WebFilter(filterName = "MyFilterWithAnnotation",urlPatterns = "/api/*",
initParams = {//以下都是druid数据源状态监控的参数
@WebInitParam(name = "allow", value = ""),// IP白名单 (没有配置或者为空,则允许所有访问)
@WebInitParam(name = "deny", value = ""),// IP黑名单 (存在共同时,deny优先于allow)
@WebInitParam(name = "loginUsername", value = "hresh"),// 用户名
@WebInitParam(name = "loginPassword", value = "123456"),// 密码
@WebInitParam(name = "resetEnable", value = "false")// 禁用HTML页面上的“Reset All”功能
})
public class MyFilterWithAnnotation implements Filter {
private static final Logger logger = LoggerFactory.getLogger(MyFilterWithAnnotation.class);
@Override
public void init(FilterConfig filterConfig) throws ServletException {
logger.info("初始化MyFilterWithAnnotation过滤器:", filterConfig.getFilterName());
System.out.println(filterConfig.getInitParameter("loginUsername"));
System.out.println(filterConfig.getInitParameter("loginPassword"));
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
logger.info("过滤器开始对请求进行预处理:");
HttpServletRequest request = (HttpServletRequest) servletRequest;
String requestUri = request.getRequestURI();
System.out.println("请求的接口为:" + requestUri);
long startTime = System.currentTimeMillis();
//通过 doFilter 方法实现过滤功能
filterChain.doFilter(servletRequest,servletResponse);
// 上面的 doFilter 方法执行结束后用户的请求已经返回
long endTime = System.currentTimeMillis();
System.out.println("该用户的请求已经处理完毕,请求花费的时间为:" + (endTime - startTime));
}
@Override
public void destroy() {
logger.info("销毁过滤器MyFilterWithAnnotation");
}
}
Пользовательские фильтры проверки контроллера
@RestController
@RequestMapping("/api")
public class MyController {
@GetMapping("/hello")
public String getHello() throws InterruptedException {
Thread.sleep(1000);
return "hello";
}
}
Класс запуска Spring Boot:
@SpringBootApplication
@ServletComponentScan
public class SpbGuide3Application {
public static void main(String[] args) {
SpringApplication.run(SpbGuide3Application.class, args);
}
}
Запустите проект и откройте URL-адрес: http://localhost:8080/api/hello.
Веб-сервер отвечает за создание и уничтожение фильтра.Когда веб-приложение запускается, веб-сервер создает экземпляр объекта фильтра и вызывает его метод init для завершения функции инициализации объекта, чтобы подготовиться к перехват последующих пользовательских запросов.Объект фильтра будет создан только один раз, то есть метод init будет выполнен только один раз. С помощью параметров метода init можно получить объект FilterConfig, представляющий текущую информацию о конфигурации фильтра. Точно так же уничтожение объекта Filter выполняется методом destroy, тем самым высвобождая ресурсы, используемые фильтром.
Наследовать HttpFilter
В сочетании с приведенным выше случаем регистрации сервлетов мы не можем не думать, можем ли мы наследовать HttpFilter через пользовательский класс и переписать метод doFilter.Конкретный код выглядит следующим образом:
Класс фильтра:
@Component
public class MyFilter3 extends HttpFilter {
private static final Logger logger = LoggerFactory.getLogger(MyFilterWithAnnotation.class);
@Override
public void init() throws ServletException {
FilterConfig filterConfig = this.getFilterConfig();
logger.info("初始化MyFilter3过滤器:", filterConfig.getFilterName());
}
@Override
public void destroy() {
logger.info("销毁过滤器MyFilterWithAnnotation");
}
@Override
protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
String requestUri = request.getRequestURI();
System.out.println("进入MyFilter3 过滤器");
System.out.println("请求的接口为:" + requestUri);
long startTime = System.currentTimeMillis();
//通过 doFilter 方法实现过滤功能
super.doFilter(request, response, chain);
// 上面的 doFilter 方法执行结束后用户的请求已经返回
long endTime = System.currentTimeMillis();
System.out.println("该用户的请求已经处理完毕,请求花费的时间为:" + (endTime - startTime));
}
}
Класс запуска Spring Boot:
@SpringBootApplication
@ServletComponentScan
public class SpbGuide3Application {
public static void main(String[] args) {
SpringApplication.run(SpbGuide3Application.class, args);
}
}
В отличие от сервлетов, нет необходимости добавлять FilterRegistrationBean в класс запуска Spring Boot.
Запустите проект и откройте URL-адрес: http://localhost:8080/api/hello.
Реализовать интерфейс фильтра
Определите два класса Filter для реализации функции сортировки фильтра.
@Component
public class MyFilter implements Filter {
private static final Logger logger = LoggerFactory.getLogger(MyFilter.class);
@Override
public void init(FilterConfig filterConfig) throws ServletException {
logger.info("初始化MyFilter过滤器:", filterConfig.getFilterName());
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
logger.info("过滤器开始对请求进行预处理:");
HttpServletRequest request = (HttpServletRequest) servletRequest;
String requestUri = request.getRequestURI();
System.out.println("请求的接口为:" + requestUri);
long startTime = System.currentTimeMillis();
//通过 doFilter 方法实现过滤功能
filterChain.doFilter(servletRequest,servletResponse);
// 上面的 doFilter 方法执行结束后用户的请求已经返回
long endTime = System.currentTimeMillis();
System.out.println("该用户的请求已经处理完毕,请求花费的时间为:" + (endTime - startTime));
}
@Override
public void destroy() {
logger.info("销毁过滤器MyFilter");
}
}
Файл MyFilter2 аналогичен приведенному выше коду, только назван по-другому.
Зарегистрируйте пользовательские фильтры в конфигурации
@Configuration
public class MyFilterConfig {
@Autowired
MyFilter myFilter;
@Autowired
MyFilter2 myFilter2;
@Bean
public FilterRegistrationBean<MyFilter> setUpMyFilter(){
FilterRegistrationBean<MyFilter> filterRegistrationBean = new FilterRegistrationBean<>();
//setOrder 方法可以决定 Filter 的执行顺序
filterRegistrationBean.setOrder(1);
filterRegistrationBean.setFilter(myFilter);
filterRegistrationBean.setUrlPatterns(new ArrayList<>(Arrays.asList("/api/*")));
return filterRegistrationBean;
}
@Bean
public FilterRegistrationBean<MyFilter2> setUpMyFilter2(){
FilterRegistrationBean<MyFilter2> filterRegistrationBean = new FilterRegistrationBean<>();
filterRegistrationBean.setOrder(2);
filterRegistrationBean.setFilter(myFilter2);
filterRegistrationBean.setUrlPatterns(new ArrayList<>(Arrays.asList("/api/* ")));
return filterRegistrationBean;
}
}
Зарегистрируйте пользовательский фильтр в конфигурации, и порядок выполнения фильтра можно определить с помощью метода setOrder объекта FilterRegistrationBean.
Класс запуска Spring Boot:
@SpringBootApplication
@ServletComponentScan
public class SpbGuide3Application {
public static void main(String[] args) {
SpringApplication.run(SpbGuide3Application.class, args);
}
}
Запустите проект и откройте URL-адрес: http://localhost:8080/api/hello.
Listener
@WebListener
После сервлета 3.0 нам не нужно настраивать прослушиватель в web.xml, нам просто нужно добавить аннотацию @WebListener, чтобы добиться этого.
Эта аннотация используется для объявления класса слушателем. Класс, аннотированный @WebListener, должен реализовывать хотя бы один из следующих интерфейсов:
| имя интерфейса | эффект |
|---|---|
| ServletContextListener | Используется для мониторинга запуска и завершения работы веб-приложений. |
| ServletContextAttributeListener | Используется для прослушивания изменений атрибутов в области ServletContext (приложение). |
| ServletRequestListener | Используется для прослушивания запросов пользователей |
| ServletRequestAttributeListener | Используется для прослушивания изменений атрибутов в области ServletRequest (запрос). |
| HttpSessionListener | Используется для отслеживания начала и окончания сеанса пользователя. |
| HttpSessionAttributeListener | Используется для отслеживания изменений свойств в области HttpSession (сеанс). |
Класс слушателя:
@WebListener
public class ContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("application started");
System.out.println(sce.getServletContext().getServerInfo());
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("application stopped");
}
}
Класс запуска Spring Boot:
@SpringBootApplication
@ServletComponentScan
public class SpbGuide3Application {
public static void main(String[] args) {
SpringApplication.run(SpbGuide3Application.class, args);
}
}
Запуск проекта приводит к:
Суммировать
Вышеупомянутое содержание суммирует, как добавить Servlet, FIlter и Listener в Spring Boot.Servlet3.0 упоминается много раз.Для объяснения этой части мы обобщим соответствующий контент позже.