Spring Boot注解学习之@ServletComponentScan及扩展

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: Spring Boot注解学习之@ServletComponentScan及扩展

@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 注解,然后会在 Spring 容器中注册  ServletComponentRegisteringPostProcessor,是个  BeanFactoryPostProcessor,会去解析扫描出来的类是不是有 @WebServlet、@WebListener、@WebFilter 这三种注解,有的话会把这三种类型的类转换成  ServletRegistrationBean、FilterRegistrationBean 或者ServletListenerRegistrationBean,然后让 Spring 容器去解析。


在 SpringBootApplication 上使用@ServletComponentScan 注解后,Servlet、Filter、Listener 可以直接通过@WebServlet、@WebFilter、@WebListener 注解自动注册,无需其他代码。


上述内容即为@ServletComponentScan 注解的学习归纳,接下来我们主要介绍如何在 Spring Boot 中添加 Servlet、Filter、Listener。


刚才有提到 @WebServlet、@WebFilter、@WebListener 这三个注解,其实是 Servlet3.0 中的新增的注解。


Servlet 3.0 作为 Java EE 6 规范体系中一员,随着 Java EE 6 规范一起发布。该版本在前一版本(Servlet 2.5)的基础上提供了若干新特性用于简化 Web 应用的开发和部署。其中有几项特性的引入让开发者感到非常兴奋,同时也获得了 Java 社区的一片赞誉之声:

  1. 异步处理支持:在 Servlet3.0之前,Servlet 线程需要一直阻塞,直到业务处理完毕才能再输出响应,最后才结束 Servlet 线程。而 Servlet3.0 中,在接收到请求之后,Servlet 线程可以将耗时的操作委托给另一个线程来完成,自己在不生成响应的情况下返回至容器。针对业务处理较耗时的情况,这将大大减少服务器资源的占用,并且提高并发处理速度。
  2. 新增的注解支持:该版本新增了若干注解,用于简化 Servlet、过滤器(Filter)和监听器(Listener)的声明,这使得 web.xml 部署描述文件从该版本开始不再是必选的了。
  3. 可插性支持:熟悉 Struts2 的开发者一定会对其通过插件的方式与包括 Spring 在内的各种常用框架的整合特性记忆犹新。将相应的插件封装成 JAR 包并放在类路径下,Struts2 运行时便能自动加载这些插件。现在 Servlet3.0 提供了类似的特性,开发者可以通过插件的方式很方便的扩充已有 Web 应用的功能,而不需要修改原有的应用。

关于 Servlet3.0 新特性的介绍可以参考:Servlet 3.0 新特性详解


Servlet


在 Servlet3.0 之前,我们只能从 web.xml 文件中配置 Servlet。web.xml 是 Java EE 中可选择用来描述应用部署的文件,使得 Servlet 容器可以加载部署应用,该文件可以用于声明 Servlet,Servlet 的访问映射,配置监听器等信息,描述外部资源。

例如,Tomcat 在启动部署一个 Web 应用的时候,会在初始化阶段加载 web.xml 文件,进而加载 Servlet,加载 Servlet 与 Api 的映射关系,最终才能对外提供服务。在这个阶段,我们每次开发新的功能,增加新的 Servlet 都需要修改 web.xml 文件,配置起来比较繁琐。


在 Servlet3.0 之后,创建 Servlet 可以有以下方法:

  • 首先自定义类继承 HttpServlet,然后代码注册通过  ServletRegistrationBean 获得控制。也可以通过实现  ServletContextInitializer 接口直接实现;
  • 在 SpringBootApplication 上使用@ServletComponentScan 注解后,Servlet可以直接通过 @WebServlet 注解自动注册,无需其他代码。


通过代码注册Servlet


Spring Boot 启动类中声明 ServletRegistrationBean。


servlet 类:

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");
    }
}
复制代码


先定义一个 Servlet,重写 service 实现自己的业务逻辑。


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);
    }
}
复制代码


然后通过@Bean 注解往 Spring 容器中注入一个  ServletRegistrationBean 类型的 bean 实例,并且实例化一个自定义的 Servlet 作为参数,这样就将自定义的 Servlet 加入 Tomcat 中了。


运行该项目,打开网址: http://localhost:8080/servlet

image.png


刚才有提到还可以通过实现  ServletContextInitializer 接口直接实现,如下所示:

Servlet 类:


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()方法,在 servlet 中默认情况下,无论你是 get 还是 post 提交过来,都会经过 service()方法来处理,然后转向到 doGet 或 doPost 方法。当自定义的 servlet 类继承 HttpServlet 时,如果不覆盖重写 service 方法,就只需要重写 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");
    }
}
复制代码


image.png 

通过查看 ServletRegistrationBean 类的源码可知,该类继承了  RegistrationBean,而  RegistrationBean  又实现了 ServletContextInitializer 这个接口,并且有一个 onStartup 方法,所以我们可以自定义 ServletContextInitializer 接口实现类,来替代 ServletRegistrationBean。


ServletContextInitializer是 Servlet 容器初始化的时候,提供的初始化接口。所以,Servlet 容器初始化会获取并触发所有的`FilterRegistrationBean、FilterRegistrationBean、ServletListenerRegistrationBean 实例中 onStartup 方法


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);
    }
}
复制代码


运行该项目,打开网址: http://localhost:8080/servlet2

image.png


使用注解注册Servlet


下面是@WebServlet 的属性列表。

属性名 类型 描述
name String 指定Servlet的name属性,等价于,如果没有显示指定,则该Servlet的取值即为类的全限定名。
value String[] 该属性等价于urlPatterns属性。两个属性不能同时使用。
urlPatterns String[] 指定一组Servlet的URL匹配模式,等价于标签。
loadOnStartup int 指定 Servlet 的加载顺序,等价于 标签。
initParams WebInitParam[] 指定一组 Servlet 初始化参数,等价于标签。
asyncSupported boolean 声明 Servlet 是否支持异步操作模式,等价于 标签。
description String 该 Servlet 的描述信息,等价于 标签。
displayName String 该 Servlet 的显示名,通常配合工具使用,等价于 标签。


从上表可见,web.xml 可以配置的 servlet 属性,在@WebServlet 中都可以配置。

在 Spring Boot 中,嵌入式 Servlet 容器通过扫描注解的方式注册 Servlet、Filter 和 Servlet 规范的所有监听器(如  HttpSessionListener 监听器)。


Servlet 类:

@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);
    }
}
复制代码


运行该项目,打开网址: http://localhost:8080/servlet/web


image.png

其中输出的 loginUsername 和 loginPassword 是我们在@WebServlet 注解中设置的。


Filter


使用@WebFilter 注解


@WebFilter 的常用属性

属性名 类型 描述
filterName String 指定过滤器的name属性,等价于,
value String[] 该属性等价于urlPatterns属性。两个属性不能同时使用。
urlPatterns String[] 指定一组过滤器的URL匹配模式,等价于标签。
dispatcherTypes DispatcherType 指定过滤器的转发模式,具体取值包括:ASYNC、ERROR、FORWARD、INCLUDE、REQUEST。
initParams WebInitParam[] 指定一组过滤器初始化参数,等价于标签。
asyncSupported boolean 声明过滤器是否支持异步操作模式,等价于 标签。
description String 该过滤器的描述信息,等价于 标签。
displayName String 该过滤器的显示名,通常配合工具使用,等价于 标签。


使用@WebFilter 注解的时候发现注解里面没有提供可以控制执行顺序的参数 , 通过实践发现如果想要控制 filter的执行顺序可以通过控制 filter 的文件名来控制 。比如:UserLoginFilter.java 和 ApiLog.java 这两个过滤器文件,因为这两个文件的首字母A排U之前,导致每次执行的时候都是先执行 ApiLog 过滤器再执行 UserLoginFilter 过滤器,所以我们现在修改两个文件的名称分别为 Filter0_UserLogin.java 和 Filter1_ApiLog.java,就会先执行 Filter0_UserLogin 再执行 Filter1_ApiLog。


示例:


Filter 类:

@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");
    }
}
复制代码


自定义 Controller 验证过滤器


@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);
    }
}
复制代码


运行该项目,打开网址: http://localhost:8080/api/hello


image.png


Filter 的创建和销毁由 Web 服务器负责,Web 应用程序启动时,Web 服务器将创建 Filter 的实例对象,并调用其 init 方法,完成对象的初始化功能,从而为后续的用户请求做好拦截的准备工作,filter 对象只会创建一次,即 init 方法只会执行一次。通过 init 方法的参数,可获得代表当前 filter 配置信息的 FilterConfig 对象。同样 Filter 对象的销毁由 destroy 方法执行,进而释放过滤器使用的资源。


继承 HttpFilter


结合上述注册 Servlet 的案例,我们不由得想到是否可以通过自定义类继承 HttpFilter,重写 doFilter方法,具体代码如下:

Filter 类:

@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);
    }
}
复制代码


与 servlet 不同,不需要在 Spring Boot 启动类中添加 FilterRegistrationBean

运行该项目,打开网址: http://localhost:8080/api/hello


image.png


实现 Filter 接口


定义两个 Filter 类,用来实现 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;
    }
}
复制代码


在配置中注册自定义的过滤器,通过 FilterRegistrationBean 的 setOrder 方法可以决定 Filter 的执行顺序。


Spring Boot 启动类:

@SpringBootApplication
@ServletComponentScan
public class SpbGuide3Application {
    public static void main(String[] args) {
        SpringApplication.run(SpbGuide3Application.class, args);
    }
}
复制代码


运行该项目,打开网址: http://localhost:8080/api/hello


image.png


Listener


@WebListener


在 servlet3.0 以后,我们可以不用再 web.xml 里面配置 listener,只需要加上@WebListener 注解就可以实现。


该注解用于将类声明为监听器,被 @WebListener 标注的类必须实现以下至少一个接口 :

接口名称 作用
ServletContextListener 用于监听Web应用的启动和关闭
ServletContextAttributeListener 用于监听ServletContext范围(application)内属性的改变
ServletRequestListener 用于监听用户请求
ServletRequestAttributeListener 用于监听ServletRequest范围(request)内属性的改变
HttpSessionListener 用于监听用户Session的开始和结束
HttpSessionAttributeListener 用于监听HttpSession范围(session)内属性的改变


Listener 类:


@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);
    }
}
复制代码


运行该项目,结果为:


image.png


总结


上述内容总结归纳了关于如何在 Spring Boot 中添加 Servlet、FIlter 和 Listener,其中多次提到 Servlet3.0,关于该部分的讲解,后续我们再做相关内容总结。



目录
相关文章
|
20天前
|
前端开发 Java Spring
Spring MVC核心:深入理解@RequestMapping注解
在Spring MVC框架中,`@RequestMapping`注解是实现请求映射的核心,它将HTTP请求映射到控制器的处理方法上。本文将深入探讨`@RequestMapping`注解的各个方面,包括其注解的使用方法、如何与Spring MVC的其他组件协同工作,以及在实际开发中的应用案例。
36 4
|
20天前
|
前端开发 Java 开发者
Spring MVC中的请求映射:@RequestMapping注解深度解析
在Spring MVC框架中,`@RequestMapping`注解是实现请求映射的关键,它将HTTP请求映射到相应的处理器方法上。本文将深入探讨`@RequestMapping`注解的工作原理、使用方法以及最佳实践,为开发者提供一份详尽的技术干货。
59 2
|
20天前
|
前端开发 Java Spring
探索Spring MVC:@Controller注解的全面解析
在Spring MVC框架中,`@Controller`注解是构建Web应用程序的基石之一。它不仅简化了控制器的定义,还提供了一种优雅的方式来处理HTTP请求。本文将全面解析`@Controller`注解,包括其定义、用法、以及在Spring MVC中的作用。
40 2
|
23天前
|
消息中间件 Java 数据库
解密Spring Boot:深入理解条件装配与条件注解
Spring Boot中的条件装配与条件注解提供了强大的工具,使得应用程序可以根据不同的条件动态装配Bean,从而实现灵活的配置和管理。通过合理使用这些条件注解,开发者可以根据实际需求动态调整应用的行为,提升代码的可维护性和可扩展性。希望本文能够帮助你深入理解Spring Boot中的条件装配与条件注解,在实际开发中更好地应用这些功能。
27 2
|
20天前
|
前端开发 Java 开发者
Spring MVC中的控制器:@Controller注解全解析
在Spring MVC框架中,`@Controller`注解是构建Web应用程序控制层的核心。它不仅简化了控制器的定义,还提供了灵活的请求映射和处理机制。本文将深入探讨`@Controller`注解的用法、特点以及在实际开发中的应用。
52 0
|
Java 程序员 网络安全
spring4.1.8扩展实战之二:Aware接口揭秘
Aware.java是个没有定义任何方法的接口,拥有众多子接口,在spring源码中有多处都在使用这些子接口完成各种场景下的回调操作,当业务有需要时,我们只需创建类来实现相关接口,再声明为bean,就可以被spring容器主动回调
287 0
spring4.1.8扩展实战之二:Aware接口揭秘
|
2月前
|
人工智能 自然语言处理 前端开发
SpringBoot + 通义千问 + 自定义React组件:支持EventStream数据解析的技术实践
【10月更文挑战第7天】在现代Web开发中,集成多种技术栈以实现复杂的功能需求已成为常态。本文将详细介绍如何使用SpringBoot作为后端框架,结合阿里巴巴的通义千问(一个强大的自然语言处理服务),并通过自定义React组件来支持服务器发送事件(SSE, Server-Sent Events)的EventStream数据解析。这一组合不仅能够实现高效的实时通信,还能利用AI技术提升用户体验。
221 2
|
2天前
|
NoSQL Java Redis
Spring Boot 自动配置机制:从原理到自定义
Spring Boot 的自动配置机制通过 `spring.factories` 文件和 `@EnableAutoConfiguration` 注解,根据类路径中的依赖和条件注解自动配置所需的 Bean,大大简化了开发过程。本文深入探讨了自动配置的原理、条件化配置、自定义自动配置以及实际应用案例,帮助开发者更好地理解和利用这一强大特性。
30 14
|
24天前
|
缓存 IDE Java
SpringBoot入门(7)- 配置热部署devtools工具
SpringBoot入门(7)- 配置热部署devtools工具
42 1
SpringBoot入门(7)- 配置热部署devtools工具
|
1月前
|
缓存 IDE Java
SpringBoot入门(7)- 配置热部署devtools工具
SpringBoot入门(7)- 配置热部署devtools工具
43 2
 SpringBoot入门(7)- 配置热部署devtools工具