• [技术干货] SpringBootWeb 篇-入门了解 Apache POI 使用方法 -转载
     1.0 Apache POI 概述         Apache POI 是一个处理 Miscrosoft Office 各种文件格式的开源项目。简单来说就是,开源使用 POI 在 Java 程序中对 Miscrosoft Office 各种文件进行读写操作。          一般情况下, POI 都是用于操作 Excel 文件。           2.0 使用 Apache POI 读写 Excel 文件         2.1 写入 Excel 文件         首先添加 Apache POI 依赖:  <dependency>     <groupId>org.apache.poi</groupId>     <artifactId>poi</artifactId>     <version>3.16</version> </dependency> <dependency>     <groupId>org.apache.poi</groupId>     <artifactId>poi-ooxml</artifactId>     <version>3.16</version> </dependency> 具体用到的方法:          1)创建 XSSFWorkbook对象:          使用 XSSFWorkbook 创建 XLSX 格式的 Excel 文件。  Workbook workbook = new XSSFWorkbook();         2)创建工作表对象:  Sheet sheet = workbook.createSheet("Sheet1"); // 创建新的工作表         3)创建行和单元格对象:  Row row = sheet.createRow(0); // 创建新的行 Cell cell = row.createCell(0); // 创建新的单元格         4)设置具体的值:  cell.setCellValue("Hello, World!");//在单元格中写入的内容         5)将在内存创建的 Excel 的文件保存到磁盘中:  FileOutputStream file = new FileOutputStream("example.xlsx"); workbook.write(file);         2.2 写入 Excel 文件代码演示 import org.apache.poi.xssf.usermodel.XSSFCell; import org.apache.poi.xssf.usermodel.XSSFRow; import org.apache.poi.xssf.usermodel.XSSFSheet; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest;   import java.io.File; import java.io.FileOutputStream; import java.io.IOException;   @SpringBootTest(classes = {demo.demo2.class})   public class demo2 {       /**      * 写入 Excel 文件操作      */     public void write() throws IOException {           //创建 XSSFWorkbook 对象         XSSFWorkbook workbook = new XSSFWorkbook();         //创建 Sheet 对象,指定名称 sheet1         XSSFSheet sheet = workbook.createSheet("sheet1");         //创建行,创建第二行的行对象         XSSFRow row = sheet.createRow(1);         //创建单元格,创建第二格单元格对象         XSSFCell cell = row.createCell(1);         //设置具体的值         cell.setCellValue("姓名");           //创建单元格,创建第三格单元格对象         XSSFCell cell1 = row.createCell(2);         //设置具体的值         cell1.setCellValue("性别");           //创建第三行对象         XSSFRow row1 = sheet.createRow(2);         //创建第二格对象         XSSFCell cell2 = row1.createCell(1);         cell2.setCellValue("小板");         //创建第三格对象         XSSFCell cell3 = row1.createCell(2);         cell3.setCellValue("男");           //创建第四行对象         XSSFRow row2 = sheet.createRow(3);         //创建第二格对象         XSSFCell cell4 = row2.createCell(1);         //设置具体的值         cell4.setCellValue("童童");         //创建第三格对象         XSSFCell cell5 = row2.createCell(2);         cell5.setCellValue("女");           //将该 Excel 从内存中放到磁盘中         //首先创建存放的文件         FileOutputStream out = new FileOutputStream(new File("D:\\software\\code\\example.xlsx"));         workbook.write(out);           //最后需要关闭资源         out.close();         workbook.close();       }     @Test     public void test11() throws IOException {         write();     } }  运行结果:           2.3 读取 Excel 文件         首先需要添加 POI 库的依赖,在前面写入 Excel 文件中已经添加了,这里就没有必要继续添加依赖了。  <dependency>     <groupId>org.apache.poi</groupId>     <artifactId>poi</artifactId>     <version>3.16</version> </dependency> <dependency>     <groupId>org.apache.poi</groupId>     <artifactId>poi-ooxml</artifactId>     <version>3.16</version> </dependency> 相关的方法:          1)指定读取的 Excel 文件:          通过 new File("路径") Flie 对象作为参数来指定要读取的 Excel 文件。  FileInputStream file = new FileInputStream(new File("sample.xlsx")); Workbook workbook = WorkbookFactory.create(file);         2)获取 Sheet 对象:          根据索引或者名字来获取指定的 Sheet 对象。  //通过名字来获取 XSSFSheet sheet1 = workbook.getSheet("指定名字来获取"); //通过索引来获取 XSSFSheet sheet2 = workbook.getSheetAt(0);         3)获取 Row 对象          根据索引来获取 Row 对象。  XSSFRow row = sheet.getRow(1);         获取 Sheet 最后一行的行数。  int lastRowNum = sheet.getLastRowNum();         4)获取单元格对象          根据索引来获取 Cell 对象。  XSSFCell cell = row.getCell(1);         5)获取到单元格中的值          通过 cell.getStringCellValue() 方法来获取单元格中的值。  String stringCellValue = cell.getStringCellValue();         2.4 读取 Excel 文件代码演示     public void read() throws Exception {         //创建字节输入流文件对象         File file = new File("D:\\software\\code\\example.xlsx");         FileInputStream in = new FileInputStream(file);           //再进一步封装         XSSFWorkbook workbook = new XSSFWorkbook(in);         //获取 Sheet 对象,根据索引获取         XSSFSheet sheet = workbook.getSheetAt(0);           //获取Row对象         //获取最后一行的行数         int lastRowNum = sheet.getLastRowNum();         for (int i = 1; i <= lastRowNum ; i++) {             XSSFRow row = sheet.getRow(i);             String stringCellValue1 = row.getCell(1).getStringCellValue();             String stringCellValue2 = row.getCell(2).getStringCellValue();             System.out.println(stringCellValue1 + " " + stringCellValue2);         }                  //最后关闭资源         workbook.close();         in.close();     }       @Test     public void test12() throws Exception {         read();     }  运行结果:  ————————————————                              版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。                          原文链接:https://blog.csdn.net/Tingfeng__/article/details/139752796 
  • [技术干货] SpringMVC:@ResponseBody注解与HttpServletResponse对象
    在Spring MVC框架中,@ResponseBody注解和HttpServletResponse对象都扮演着将处理结果发送回客户端的重要角色,但它们的使用方式和目的有所不同。@ResponseBody@ResponseBody注解用于将方法的返回值绑定到web响应体(response body)上。当你使用@ResponseBody注解一个方法的返回值时,Spring会自动选择一个合适的HttpMessageConverter,将返回值转换为对应的格式(如JSON、XML等),并写入HTTP响应体中。这通常用于RESTful Web服务中,当你需要直接返回数据(如JSON或XML)给客户端时。使用示例@RestController public class MyController { @GetMapping("/greeting") @ResponseBody // 通常与@RestController一起使用时可以省略 public String greeting() { return "Hello, World!"; } // 使用@RestController时,下面的@ResponseBody可以省略 @GetMapping("/jsonGreeting") public MyResponseObject jsonGreeting() { MyResponseObject response = new MyResponseObject(); response.setMessage("Hello in JSON"); return response; // Spring将自动使用HttpMessageConverter转换为JSON } }HttpServletResponseHttpServletResponse是Servlet API的一部分,它代表了Servlet对客户端的响应。你可以通过它来直接控制HTTP响应的各个方面,包括状态码、响应头以及响应体。当你需要更细粒度的控制响应时(比如设置特定的响应头、发送二进制文件等),HttpServletResponse就显得非常有用。使用示例@Controller public class MyServletController { @GetMapping("/customResponse") public void customResponse(HttpServletResponse response) throws IOException { response.setContentType("text/plain"); response.setCharacterEncoding("UTF-8"); response.getWriter().write("Custom response using HttpServletResponse"); } @GetMapping("/fileDownload") public void fileDownload(HttpServletResponse response) throws IOException { // 设置响应头 response.setContentType("application/pdf"); response.setHeader("Content-Disposition", "attachment; filename=\"example.pdf\""); // 假设你有一个获取文件输入流的方法 InputStream inputStream = getFileAsStream("path/to/example.pdf"); // 使用ServletOutputStream将文件内容写入响应 ServletOutputStream outputStream = response.getOutputStream(); IOUtils.copy(inputStream, outputStream); // 使用Apache Commons IO库来复制流 outputStream.flush(); } }总结@ResponseBody主要用于将方法的返回值自动转换为JSON、XML等格式,并写入HTTP响应体中,适用于RESTful Web服务。HttpServletResponse提供了对HTTP响应的细粒度控制,适用于需要直接操作响应头、响应体等场景。在Spring MVC中,@RestController注解已经隐式地为所有处理方法的返回值应用了@ResponseBody注解,因此在@RestController注解的控制器中,你可以省略@ResponseBody。转载自https://www.cnblogs.com/cydestiny/p/18308254
  • [技术干货] ApplicationContext 详细介绍
    一、概述ApplicationContext是Spring框架中的一个核心接口,它扩展了BeanFactory接口,并提供了更全面的功能。ApplicationContext不仅包含了BeanFactory的所有功能,还添加了国际化支持、资源访问、事件传播、以及更高级的容器特性,如自动装配和生命周期管理等。它是Spring应用中的核心容器,负责管理和配置应用中的对象(称为beans)。二、主要功能Bean工厂:作为BeanFactory的扩展,ApplicationContext提供了更丰富的Bean管理功能。它可以自动检测并注册Bean定义,管理Bean的生命周期,支持依赖注入等。国际化支持:通过MessageSource接口的实现,ApplicationContext支持国际化的消息资源。这允许开发者根据用户的语言环境提供不同语言的消息。资源访问:ApplicationContext提供了对资源的访问能力,如文件、URL等。这通过Resource接口和ResourceLoader接口实现,使得访问外部资源变得简单。事件传播:ApplicationContext实现了ApplicationEventPublisher接口,允许发布事件到注册的监听器。这是实现松耦合组件之间通信的一种有效方式。环境抽象:ApplicationContext提供了对环境的抽象,包括配置文件和程序化配置。这允许开发者在不同环境下(如开发、测试、生产)灵活地配置应用。Web支持:对于Web应用,Spring提供了WebApplicationContext接口,它是ApplicationContext的扩展,提供了对Web环境的支持,如请求处理、会话管理等。三、Bean的生命周期在ApplicationContext中,Bean的生命周期包括以下几个阶段:实例化:首先,Spring容器会实例化Bean。属性设置:然后,Spring容器会将Bean的依赖关系(通过构造器注入或setter方法注入)注入到Bean中。BeanNameAware**和**BeanFactoryAware`接口:如果Bean实现了这些接口,Spring容器会调用相应的方法,将Bean的名称和BeanFactory传递给Bean。**BeanPostProcessor接口**:Spring容器会调用实现了BeanPostProcessor接口的Bean的postProcessBeforeInitialization`方法,在Bean的初始化之前进行处理。初始化:如果Bean实现了InitializingBean接口或在其配置中指定了init-method,Spring容器会调用相应的方法来初始化Bean。**BeanPostProcessor接口(续)**:在Bean初始化之后,Spring容器会调用实现了BeanPostProcessor接口的Bean的postProcessAfterInitialization`方法。使用:Bean现在已准备好被应用使用。销毁:如果Bean实现了DisposableBean接口或在其配置中指定了destroy-method,当容器关闭时,Spring容器会调用相应的方法来销毁Bean。四、配置方式ApplicationContext的配置可以通过多种方式实现,包括基于XML的配置文件、基于注解的配置(如@Component、@Autowired等)、基于Java的配置(通过@Configuration和@Bean注解)以及混合使用这些方式。五、总结ApplicationContext是Spring框架中功能强大的核心容器,它扩展了BeanFactory接口,并提供了更全面的功能。通过ApplicationContext,开发者可以轻松地管理应用中的对象(Bean),并利用Spring提供的各种特性,如依赖注入、国际化支持、事件传播等,来构建松耦合、可扩展的应用。转载自https://www.cnblogs.com/kongyang/p/18309195
  • [技术干货] 一文分清SaaS、Pass和LaaS及代表性应用
    前言在云计算的浪潮中,SaaS(Software as a Service)、Pass(Passport as a Service,虽然并非传统云计算服务模式,但在此文中我们假设其为一个基于认证或通行证的服务模式)和LaaS(Location as a Service)作为三大服务模式,各自在不同的领域发挥着重要作用。本文将为您详细解析这三种服务模式,并介绍其代表性应用。一、SaaS(Software as a Service)SaaS,即软件即服务,是一种通过互联网提供软件应用的方式。用户无需购买、安装和维护软件,只需按需租用服务,通过云端访问和使用软件。SaaS的特点包括:使用先进的应用程序、无需购买和维护软硬件、只为自己使用的功能付费、无需安装客户端软件、轻松增强员工移动性等。代表性应用:SalesForce:作为CRM(客户关系管理)领域的领军企业,SalesForce提供全面的销售、市场营销和客户服务解决方案,帮助企业实现客户关系的数字化转型。Slack:作为一款团队协作工具,Slack通过即时通讯、文件共享等功能,帮助团队成员进行高效的沟通和协作,提升团队整体效率。ERP软件:SaaS模式下的ERP软件,如金蝶云、用友云等,可以帮助企业实现精细化管理,降低运营成本,提高工作效率。二、Pass(Passport as a Service)虽然Pass并非传统云计算服务模式,但我们可以将其理解为一种基于认证或通行证的服务模式。Pass旨在提供安全、便捷的认证和通行证服务,使用户能够轻松访问和使用各种资源和服务。代表性应用:数字身份证:在数字化时代,数字身份证成为了一种新型的Pass服务。通过数字身份证,用户可以安全、便捷地进行身份验证,享受各种线上服务。企业通行证:在企业内部,Pass服务可以为企业提供统一的通行证解决方案。员工可以通过企业通行证轻松访问公司内部的各种资源和服务,提高工作效率。三、LaaS(Location as a Service)LaaS,即位置即服务,是一种基于位置信息的服务模式。通过获取和利用位置数据,LaaS可以为用户提供与地理位置相关的各种服务,如导航、位置跟踪、位置分析等。代表性应用:导航应用:如高德地图、百度地图等导航应用,通过LaaS服务获取用户的位置信息,为用户提供精准的导航服务。物流跟踪:在物流领域,LaaS服务可以帮助企业实时跟踪货物的位置信息,提高物流效率和服务质量。位置分析:通过LaaS服务获取的位置数据,企业可以进行深入的位置分析,了解用户的活动轨迹、消费习惯等信息,为精准营销和产品优化提供有力支持。总结SaaS、Pass和LaaS作为云计算的三大服务模式,各自在不同的领域发挥着重要作用。通过了解这些服务模式和其代表性应用,我们可以更好地把握云计算的发展趋势和应用前景。
  • [技术干货] 使用@RequestParam和@RequestBody在Spring MVC中处理HTTP请求
    知识点在Spring MVC中,处理HTTP请求时,我们经常需要提取请求的不同部分,如查询参数、路径变量、请求头以及请求体。@RequestParam和@RequestBody是两个常用的注解,用于帮助我们从HTTP请求中抽取这些信息。尽管它们通常用于不同的场景,但Spring允许我们在同一个方法中使用它们,以满足复杂的数据处理需求。 @RequestParam@RequestParam注解通常用于从请求URL的查询参数中提取数据。当客户端发起一个GET请求,并在URL后附带查询参数时,我们可以使用@RequestParam来捕获这些参数的值。 @RequestBody@RequestBody注解则用于处理请求体中的数据。这在客户端发送POST、PUT或PATCH请求,并带有请求体(例如JSON或XML格式的数据)时非常有用。Spring会使用HTTP消息转换器(如MappingJackson2HttpMessageConverter)将请求体的内容转换为对应的Java对象。在同一方法中使用@RequestParam和@RequestBody在某些情况下,我们可能希望在一个请求中同时处理查询参数和请求体数据。虽然这在RESTful API设计中可能不是最佳实践,但在某些特定的应用场景中,这样的需求是合理的。Spring MVC允许我们在同一个方法中同时使用@RequestParam和@RequestBody。下面是一个示例,展示了如何在Spring MVC控制器中同时使用这两个注解:import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/api") public class MyController { @PostMapping("/submit") public String submitData( @RequestParam("name") String name, @RequestBody MyData myData) { // 使用name变量处理查询参数 // 使用myData对象处理请求体中的数据 // 示例逻辑:打印接收到的数据 System.out.println("Name from query parameter: " + name); System.out.println("Data from request body: " + myData); // 返回响应 return "Data submitted successfully"; } // 定义与请求体数据对应的Java类 static class MyData { private String field1; private int field2; // 省略getter和setter方法... @Override public String toString() { return "MyData{" + "field1='" + field1 + '\'' + ", field2=" + field2 + '}'; } } }在这个例子中,我们定义了一个submitData方法,它接受一个名为name的查询参数和一个MyData类型的请求体对象。当客户端发送一个POST请求到/api/submit,并带有查询参数name和请求体中的JSON数据时,Spring MVC会自动将这些数据绑定到对应的方法参数上。注意事项当同时使用@RequestParam和@RequestBody时,确保客户端的请求格式正确,即同时包含查询参数和请求体。如果请求体中的数据无法映射到指定的Java对象,Spring MVC会抛出异常。因此,确保请求体的格式与Java对象的字段匹配,并且使用了正确的HTTP消息转换器。在设计RESTful API时,通常建议将不同的数据类型放在不同的请求部分中,以保持API的一致性和清晰度。例如,查询参数通常用于过滤或分页,而请求体则用于发送完整的资源表示。通过合理使用@RequestParam和@RequestBody注解,我们可以轻松地处理各种复杂的HTTP请求,并在Spring MVC控制器中实现灵活的数据绑定和验证。
  • [技术干货] 【监控】spring actuator源码速读-转载
     1.前言 版本:spring-boot-starter-actuator  2.6.3  阅读源码一定要带着疑问去阅读,这个疑问就是你阅读的主线,不然在浩如烟海的源码里面很容易迷路。我们当前的疑问是什么?之前我们已经聊过spring actuator的使用了:  Spring Boot 监控_springboot 监控-CSDN博客  本文要搞清楚的两个问题在于:  EndPoint是怎么被注入IOC又怎么暴露出去能通过HTTP访问到的?  EndPoint是怎么实现监控能力的?  2.先搂一眼EndPoint 首先我们找一个EndPoint来看看,此处以HealthEndPoint为例。点看源码我们可以看到这个EndPoint被@EndPoint注解所注释,id为health。然后其中的2个方法被@ReadOperation所注释:   这里其实猜都能猜到被@EndPoint注解,然后被注解的类被归类为EndPoint,然后被集中暴露出去,变成可访问的。  3.EndPoint如何被注入 我们是通过stater来引入actuator的,Spring Boot体系内如何注入stater的?那肯定是通过autoConfiguration来的撒。点进actuator的配置文件也可以看到:   于是我们来到spring-boot-starter-actuator来看看,看看它的spring.factories里面注入了些什么:  见名知意了,这些众多的XXXautoConfiguration是拿来做什么的就不必多说了吧,health、metrics......分门别类,各种EndPoint的autoConfiguration。   我们来看看HealthEndpointAutoConfiguration里面做了什么:  其实就是加载了HealthEndpointConfiguration、ReactiveHealthEndpointConfiguration、HealthEndpointWebExtensionConfiguration、HealthEndpointReactiveWebExtensionConfiguration这几个类  @Configuration(     proxyBeanMethods = false ) @ConditionalOnAvailableEndpoint(     endpoint = HealthEndpoint.class ) @EnableConfigurationProperties({HealthEndpointProperties.class}) @Import({HealthEndpointConfiguration.class, ReactiveHealthEndpointConfiguration.class, HealthEndpointWebExtensionConfiguration.class, HealthEndpointReactiveWebExtensionConfiguration.class}) public class HealthEndpointAutoConfiguration {     public HealthEndpointAutoConfiguration() {     } } 我们先看HealthEndpointConfiguration,它里面向IOC中注入了health的Registry,HealthContributorRegistry中注册了HealthContributor类型的实体。   我们随便打开一个health的EndPoint,发现它都继承同一个父类:   而这个父类实现了HealthContributor接口:   所以其实就是在将注册器注入IOC的时候,就将所有属于该类型的EndPoint注册到注册器中了。  4.EndPoint如何被暴露 4.1.如何通过http暴露 在SpringBoot体系中,谁来负责http请求?那当然是SpringMVC的DispatcherServlet。把path和对应处理的类注册到DispatcherServlet中就行了。spring actuator就是这样对外通过HTTP的方式暴露EndPoint的。  回到spring.factories,找ManagementContextAutoConfiguration,这个类中完成了通过HTTP的方式来暴露EndPoint的过程:   这个类的代码并不多,我们去掉不要的部分,把和对外暴露EndPoint相关的代码保留,来读一下:  @ManagementContextConfiguration(     proxyBeanMethods = false ) @ConditionalOnWebApplication(     type = Type.SERVLET )//只能在Web环境中生效 public class ServletEndpointManagementContextConfiguration {     public ServletEndpointManagementContextConfiguration() {     } ​     @Configuration(         proxyBeanMethods = false     )     @ConditionalOnClass({DispatcherServlet.class})//当SpringMVC存在时向IOC中注入     public static class WebMvcServletEndpointManagementContextConfiguration {         public WebMvcServletEndpointManagementContextConfiguration() {         }         @Bean         public ServletEndpointRegistrar servletEndpointRegistrar(WebEndpointProperties properties, ServletEndpointsSupplier servletEndpointsSupplier, DispatcherServletPath dispatcherServletPath) {             return new ServletEndpointRegistrar(dispatcherServletPath.getRelativePath(properties.getBasePath()), servletEndpointsSupplier.getEndpoints());//核心的一步,将EndPoint和对于的Path注册给DispatcherServlet         }     } }  最后就是开头我们看见的在HealthEndPoint被@ReadOperation注解的方法,就相当于@RequetMapping,拿来处理读请求的。  4.2.如何通过jmx暴露 jmx的对外暴露更简单。直接找JmxEndpointAutoConfiguration:   进去整个逻辑一目了然,去扫Jmx的EndPoint然后注册进mBeanServer里:   5.EndPoint是怎么实现监控能力的 spring actuator获取各种监控的值是怎么获取到的?  内置指标获取: Spring Boot 提供了一些内置的监控指标获取器,用于获取常见的监控数据,比如 JVM 内存使用情况、系统负载、数据库连接池状态等。这些指标获取器会周期性地获取数据,并将其暴露为 Actuator 端点,以便外部系统或者工具可以通过相应的接口来获取。例如,MemoryHealthIndicator 获取 JVM 内存使用情况,DataSourceHealthIndicator 获取数据库连接池状态等。  自定义指标获取: 除了内置的指标获取器外,开发者还可以通过实现 HealthIndicator 接口来自定义监控指标获取器,用于获取应用程序特定的监控数据。HealthIndicator 接口定义了一个 health() 方法,用于返回健康状态信息。开发者可以在 health() 方法中编写自定义的监控逻辑,比如检查某个依赖服务的可用性、计算某个指标的值等。  JMX 获取: Spring Actuator 还可以通过 Java Management Extensions (JMX) API 来获取一些系统级的监控数据,比如 JVM 运行时信息、操作系统信息等。Spring Actuator 中的一些监控指标获取器会使用 JMX API 来获取数据,然后将其暴露为 Actuator 端点。例如,JvmInfoContributor 使用 JMX API 来获取 JVM 运行时信息。  系统调用获取: 有些监控数据可能需要通过系统调用来获取,比如获取操作系统的 CPU 使用率、磁盘使用情况等。Spring Actuator 中的一些监控指标获取器会使用系统调用来获取这些数据,然后将其暴露为 Actuator 端点。  6.知道这些的意义是什么 本文是作者java监控系列文章的第三篇,之前两篇文章我们着重讲了java监控的基石——JMX。  【JMX】JAVA监控的基石-CSDN博客  详解tomcat中的jmx监控-CSDN博客  在spring actuator里面我们知道了目前市面上一个成熟的框架是如何通过http、JMX等不同方式来对外暴露监控能力的。基本上走到这里我们就已经对JAVA整个的监控技术体系最核心的部分有了认识了。作为监控框架来说核心点有哪些?无非是:  获取数据  对外暴露口子  监控的核心肯定是怎么获取数据以及如何将获取的数据暴露出去,只要这两点搞定了,后面的对接各种可视化平台就很好办了。所有知道为啥这篇文章为啥要关心spring actuator这些地方了吧,主要是看看实现思想。 ————————————————                              版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。                          原文链接:https://blog.csdn.net/Joker_ZJN/article/details/136154345 
  • [专题汇总] 快进来宝子们,二月份干货汇总来了。
     大家好,二月的合集又来了,本次涵盖了java,linux,spirng诸多内容供大家学习。 1.Vue中的$nextTick有什么作用?【转】 https://bbs.huaweicloud.com/forum/thread-02109144317537776031-1-1.html  2.基于Python的地图绘制教程【转】 https://bbs.huaweicloud.com/forum/thread-0240144319944756030-1-1.html  3.hcache 介绍(1)--Ehcache 功能特性 【转】 https://bbs.huaweicloud.com/forum/thread-0268144320319800023-1-1.html  4.Java 操作 XML(1)--DOM 方式处理 XML【转】 https://bbs.huaweicloud.com/forum/thread-02109144320463478034-1-1.html  5.Java操作XML(3)--StAX方式处理XML 【转】 https://bbs.huaweicloud.com/forum/thread-0240144320615655031-1-1.html  6.Java操作XML(4)--使用woodstox处理XML【转】 https://bbs.huaweicloud.com/forum/thread-0270144320690142025-1-1.html  7.Java操作XML(5)--使用JDOM处理XML【转】 https://bbs.huaweicloud.com/forum/thread-0240144320817429032-1-1.html  8.Java操作XML(6)--使用dom4j处理XML【转】 https://bbs.huaweicloud.com/forum/thread-02101144320893290020-1-1.html  9.基于原生Go语言开发一个博客系统【转】 https://bbs.huaweicloud.com/forum/thread-0269144558086016006-1-1.html  10.详解Golang如何使用Debug库优化代码【转】 https://bbs.huaweicloud.com/forum/thread-0225144558556243005-1-1.html  11.Python3中的指针你了解吗【转】 https://bbs.huaweicloud.com/forum/thread-0274144558593208011-1-1.html  12.Python绘图实现坐标轴共享与复用详解【转】 https://bbs.huaweicloud.com/forum/thread-02104144558721772010-1-1.html  13.使用Golang开发一个简易版shell【转】 https://bbs.huaweicloud.com/forum/thread-0226144559187797004-1-1.html  14.python导入其它py文件的实现步骤【转】 https://bbs.huaweicloud.com/forum/thread-0284144559442791006-1-1.html  15.Python property函数的具体使用【转】 https://bbs.huaweicloud.com/forum/thread-0259144559705810008-1-1.html  16.正则表达式的神奇世界之表达、匹配和提取全解析【转】 https://bbs.huaweicloud.com/forum/thread-02104144569438876012-1-1.html  17.正则去除中括号(符号)及里面包含的内容(最新推荐)【转】 https://bbs.huaweicloud.com/forum/thread-0226144569604407008-1-1.html  18.Javaweb项目启动Tomcat常见的报错解决方案【转】 https://bbs.huaweicloud.com/forum/thread-0274144571948983013-1-1.html  19.SpringBoot+Vue前后端分离实现审核功能的示例【转】 https://bbs.huaweicloud.com/forum/thread-0269144572025869007-1-1.html  20.Java中Collections.sort()排序方法举例详解【转】 https://bbs.huaweicloud.com/forum/thread-0269144572170830008-1-1.html  21.Java中回调函数 (callback) 及其实际应用场景【转】 https://bbs.huaweicloud.com/forum/thread-0259144572260172012-1-1.html  22.Redis实现商品秒杀的示例代码【转】 https://bbs.huaweicloud.com/forum/thread-0274144574350854015-1-1.html 
  • [技术干货] CORS就是跨域吗【转】
    首先,跨域的域是什么?跨域的英文是:Cross-Origin。Origin 中文含义为:起源,源头,出生地。在跨域中,"域"指的是一个 Web 资源(比如网页、脚本、图片等)的源头。包括该资源的协议、主机名、端口号。在同源策略中,如果两个资源的域相同,则它们属于同一域,可以自由进行交互和共享数据。反之,如果两个资源的域不同,就会出现跨域问题。这时就需要特殊的方式来处理,如跨域资源共享(CORS)。那什么是同源策略?同源策略(Same-Origin Policy)是浏览器中的一项安全机制,用于保护用户的隐私和安全。它限制了一个网页或者脚本只能从同一个源加载的资源进行访问,而不能访问其他来源的资源。这样做可以防止恶意网站利用用户身份信息进行跨站请求伪造(CSRF)攻击,保护用户的数据安全。什么是跨站请求伪造?跨站请求伪造(CSRF,Cross-Site Request Forgery)是一种网络攻击方式。在 CSRF 攻击中,攻击者利用已认证的用户身份(例如用户在银行网站上登录后的会话信息)来伪造请求,以执行未经授权的操作。举个例子:我登录了银行网站,浏览器根据我的登录信息生成了一个会话令牌,也就是 session token。但是这个令牌被而恶意网站给拿到了,它拿着我的 token 去服务器发送请求。就可以把我银行卡里的 29 块八毛五全部转走。但是如果有同源策略的限制,恶意网站就无法直接发送请求到银行。我的 29 块八毛五就可以保住。因为恶意网站的域名与银行网站的域名不同,浏览器会阻止这种抢劫行为。什么是跨域资源共享(CORS)?为了防止被面试官笑话,这里一定要知道:跨域资源共享(CORS,Cross-Origin Resource Sharing)是一种用来解决由于浏览器的同源策略而导致的跨域请求问题的一种机制。浏览器将 CORS 请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。什么是简单请求?只要同时满足以下两大条件,就属于简单请求。(1)请求方法是以下三种方法之一: - HEAD - GET - POST (2)HTTP的头信息不超出以下几种字段: - Accept - Accept-Language - Content-Language - Last-Event-ID - Content-Type:只限于三个值:application/x-www-form-urlencoded、multipart/form-data、text/plain简单请求的工作流程如下:1. 浏览器在请求中增加一个 Origin 头部字段,其中包含当前页面的源信息(协议、主机、端口)。2. 服务器在收到这个请求后,会根据请求中的 Origin 头部信息来判断是否允许该请求。3. 如果服务器允许该请求,会在响应头部中包含一个 Access-Control-Allow-Origin 头部,"*"表示允许所有来源。4. 浏览器在收到响应后,决定是否允许页面访问该资源。什么是非简单请求?不是简单请求的,就是非简单请求。非简单请求它非简单在哪?或者说:它非简单又能怎么样?非简单请求在发起正式请求之前,会先发起一个预检请求。什么是预检请求?预检请求是用于在实际的跨域请求之前进行探测和验证,以确保服务器能够正确处理,预防跨域请求可能会引发的安全性问题。一句话就是:我去前面探探路!只有得到服务器的肯定答复,浏览器才会发出正式的 XMLHttpRequest 请求,否则就报错。实际 java 开发中的 CORS 解决跨域配置长这样:@Configuration public class CorsConfig implements WebMvcConfigurer {     @Override     public void addCorsMappings(CorsRegistry registry) {         // 允许所有的URL路径都可以跨域访问         registry.addMapping("/**")             // 允许所有来源(即允许任何域名)的请求跨域访问             .allowedOrigins("*")             // 允许发送身份验证信息(如cookies、HTTP身份验证或客户端SSL证明)             .allowCredentials(true)             // 允许跨域请求的HTTP方法,包括GET、POST、PUT、DELETE和OPTIONS。             .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")             // 预检请求(OPTIONS请求)的有效期,单位为秒             .maxAge(3600);     } }还有别的方式可以解决跨域问题吗?有的。使用 Nginx 部署为同一域。什么意思呢?就是说 Nginx 作为唯一域,代理所有服务端,在客户端眼里,只有 Nginx 这一个域,也就不存在跨域问题,由 Nginx 拿到请求再分发给对应服务器。这里我们就不再展开。转载自https://www.cnblogs.com/cosimo/p/18023596
  • [技术干货] Spring Cloud与Spring的区别
    前言随着微服务架构的兴起,Spring Cloud逐渐崭露头角,成为了构建分布式系统的重要工具。对于很多初次接触Spring Cloud的开发者来说,可能不太清楚它与传统的Spring框架有何区别。本文旨在探讨Spring Cloud与Spring之间的主要差异。一、Spring框架简介Spring是一个开源的Java平台,它提供了一套全面的编程和配置模型,用于构建企业级应用程序。Spring的核心特性包括依赖注入(DI)、面向切面编程(AOP)、数据访问抽象等。通过Spring,开发者可以更加高效、简洁地开发应用程序。二、Spring Cloud简介Spring Cloud是基于Spring Boot的一套微服务工具集,它提供了一整套微服务解决方案,包括服务发现、配置管理、熔断器、负载均衡等。Spring Cloud的目标是让微服务架构的搭建变得更加简单、快速和可靠。三、Spring Cloud与Spring的主要区别关注点不同:Spring框架主要关注的是应用程序的本身,提供了一系列的基础功能,如依赖注入、事务管理等。而Spring Cloud则更加关注微服务架构中的各个组件和服务之间的通信与协作,为构建分布式系统提供了丰富的工具和解决方案。组件和服务的集成:Spring Cloud集成了许多优秀的开源项目,如Netflix的Eureka、Hystrix、Zuul等,这些组件共同构成了微服务架构的核心部分。而Spring本身并不包含这些组件,需要开发者自行集成。服务治理:Spring Cloud提供了强大的服务治理功能,包括服务发现、配置管理、熔断器、负载均衡等。这些功能使得微服务架构更加健壮、可靠和易于维护。相比之下,Spring本身并不提供这些服务治理功能。部署和扩展性:Spring Cloud的设计初衷就是为了支持快速部署和横向扩展,它允许开发者将应用程序拆分成多个独立的服务,每个服务都可以独立部署和扩展。而传统的Spring应用程序通常需要整体部署,扩展性相对较差。与Spring Boot的整合:Spring Cloud是建立在Spring Boot基础之上的,因此它充分利用了Spring Boot的优点,如自动配置、快速启动等。这使得开发者在构建微服务应用程序时,可以更加高效、简洁地开发、部署和维护。四、总结综上所述,Spring Cloud与Spring的主要区别在于它们的关注点、组件和服务的集成、服务治理、部署和扩展性以及与Spring Boot的整合。Spring Cloud作为一套微服务工具集,为构建分布式系统提供了丰富的解决方案,使得开发者可以更加轻松地应对复杂的业务需求。然而,在使用Spring Cloud时,也需要关注其复杂性、学习曲线和潜在的兼容性问题。因此,在选择使用Spring Cloud还是传统的Spring框架时,需要根据项目的具体需求和团队的实际情况进行权衡。
  • [技术干货] 多线程事务回滚的处理【转】
     转自 : https://blog.csdn.net/weixin_43225491/article/details/117705686背景介绍  1.最近有一个大数据量插入的操作入库的业务场景,需要先做一些其他修改操作,然后在执行插入操作,由于插入数据可能会很多,用到多线程去拆分数据并行处理来提高响应时间,如果有一个线程执行失败,则全部回滚.  2.在spring中可以使用@Transactional注解去控制事务,使出现异常时会进行回滚,在多线程中,这个注解则不会生效,如果主线程需要先执行一些修改数据库的操作,当子线程在进行处理出现异常时,主线程修改的数据则不会回滚,导致数据错误。 3.下面用一个简单示例演示多线程事务. 公用的类和方法  /**      * 平均拆分list方法.      * @param source      * @param n      * @param <T>      * @return      */     public static <T> List<List<T>> averageAssign(List<T> source,int n){         List<List<T>> result=new ArrayList<List<T>>();         int remaider=source.size()%n;          int number=source.size()/n;          int offset=0;//偏移量         for(int i=0;i<n;i++){             List<T> value=null;             if(remaider>0){                 value=source.subList(i*number+offset, (i+1)*number+offset+1);                 remaider--;                 offset++;             }else{                 value=source.subList(i*number+offset, (i+1)*number+offset);             }             result.add(value);         }         return result;     }      1     2     3     4     5     6     7     8     9     10     11     12     13     14     15     16     17     18     19     20     21     22     23     24     25  /**  线程池配置  * @version V1.0  * @since 2021-06-08 15:39  */ public class ExecutorConfig {     private static int maxPoolSize = Runtime.getRuntime().availableProcessors();     private volatile static ExecutorService executorService;     public static ExecutorService getThreadPool() {         if (executorService == null){             synchronized (ExecutorConfig.class){                 if (executorService == null){                     executorService =  newThreadPool();                 }             }         }         return executorService;     }      private static  ExecutorService newThreadPool(){         int queueSize = 500;         int corePool = Math.min(5, maxPoolSize);         return new ThreadPoolExecutor(corePool, maxPoolSize, 10000L, TimeUnit.MILLISECONDS,             new LinkedBlockingQueue<>(queueSize),new ThreadPoolExecutor.AbortPolicy());     }     private ExecutorConfig(){} }      1     2     3     4     5     6     7     8     9     10     11     12     13     14     15     16     17     18     19     20     21     22     23     24     25     26  /** 获取sqlSession  * @author 86182  * @version V1.0  * @since 2021-06-03 15:08  */ @Component public class SqlContext {     @Resource     private SqlSessionTemplate sqlSessionTemplate;      public SqlSession getSqlSession(){         SqlSessionFactory sqlSessionFactory = sqlSessionTemplate.getSqlSessionFactory();         return sqlSessionFactory.openSession();     } }      1     2     3     4     5     6     7     8     9     10     11     12     13     14     15  示例事务不成功操作    /**      * 测试多线程事务.      * @param employeeDOList      */     @Override     @Transactional     public void saveThread(List<EmployeeDO> employeeDOList) {         try {             //先做删除操作,如果子线程出现异常,此操作不会回滚             this.getBaseMapper().delete(null);             //获取线程池             ExecutorService service = ExecutorConfig.getThreadPool();             //拆分数据,拆分5份             List<List<EmployeeDO>> lists=averageAssign(employeeDOList,2);             //执行的线程             Thread []threadArray = new Thread[lists.size()];             //监控子线程执行完毕,再执行主线程,要不然会导致主线程关闭,子线程也会随着关闭             CountDownLatch countDownLatch = new CountDownLatch(lists.size());             AtomicBoolean atomicBoolean = new AtomicBoolean(false);             for (int i =0;i<lists.size();i++){                 List<EmployeeDO> list  = lists.get(i);                 threadArray[i] =  new Thread(() -> {                     try {                         if (!atomicBoolean.get()){                             throw new ServiceException("001","出现异常");                         }                         //批量添加,mybatisPlus中自带的batch方法                         this.saveBatch(list);                     }finally {                         countDownLatch.countDown();                     }                 });             }             for (int i = 0; i <lists.size(); i++){                 service.execute(threadArray[i]);             }             //当子线程执行完毕时,主线程再往下执行             countDownLatch.await();             System.out.println("添加完毕");         }catch (Exception e){             log.info("error",e);             throw new ServiceException("002","出现异常");         }     }      1     2     3     4     5     6     7     8     9     10     11     12     13     14     15     16     17     18     19     20     21     22     23     24     25     26     27     28     29     30     31     32     33     34     35     36     37     38     39     40     41     42     43     44     45  数据库中存在一条数据: 在这里插入图片描述  //测试用例 @RunWith(SpringRunner.class) @SpringBootTest(classes = { ThreadTest01.class, MainApplication.class}) public class ThreadTest01 {      @Resource     private EmployeeBO employeeBO;      /**      *   测试多线程事务.      * @throws InterruptedException      */     @Test     public  void MoreThreadTest2() throws InterruptedException {         int size = 10;         List<EmployeeDO> employeeDOList = new ArrayList<>(size);         for (int i = 0; i<size;i++){             EmployeeDO employeeDO = new EmployeeDO();             employeeDO.setEmployeeName("lol"+i);             employeeDO.setAge(18);             employeeDO.setGender(1);             employeeDO.setIdNumber(i+"XX");             employeeDO.setCreatTime(Calendar.getInstance().getTime());             employeeDOList.add(employeeDO);         }         try {             employeeBO.saveThread(employeeDOList);             System.out.println("添加成功");         }catch (Exception e){             e.printStackTrace();         }     } }      1     2     3     4     5     6     7     8     9     10     11     12     13     14     15     16     17     18     19     20     21     22     23     24     25     26     27     28     29     30     31     32     33  测试结果: 在这里插入图片描述 在这里插入图片描述 可以发现子线程组执行时,有一个线程执行失败,其他线程也会抛出异常,但是主线程中执行的删除操作,没有回滚,Transactional注解没有生效. 使用sqlSession控制手动提交事务   @Resource    SqlContext sqlContext;  /**      * 测试多线程事务.      * @param employeeDOList      */     @Override     public void saveThread(List<EmployeeDO> employeeDOList) throws SQLException {         // 获取数据库连接,获取会话(内部自有事务)         SqlSession sqlSession = sqlContext.getSqlSession();         Connection connection = sqlSession.getConnection();         try {             // 设置手动提交             connection.setAutoCommit(false);             //获取mapper             EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);             //先做删除操作             employeeMapper.delete(null);             //获取执行器             ExecutorService service = ExecutorConfig.getThreadPool();             List<Callable<Integer>> callableList  = new ArrayList<>();             //拆分list             List<List<EmployeeDO>> lists=averageAssign(employeeDOList, 5);             AtomicBoolean atomicBoolean = new AtomicBoolean(true);             for (int i =0;i<lists.size();i++){                 if (i==lists.size()-1){                     atomicBoolean.set(false);                 }                 List<EmployeeDO> list  = lists.get(i);                 //使用返回结果的callable去执行,                 Callable<Integer> callable = () -> {                     if (!atomicBoolean.get()){                         throw new ServiceException("001","出现异常");                     }                   return employeeMapper.saveBatch(list);                 };                 callableList.add(callable);             }             //执行子线程            List<Future<Integer>> futures = service.invokeAll(callableList);             for (Future<Integer> future:futures) {             //如果有一个执行不成功,则全部回滚                 if (future.get()<=0){                     connection.rollback();                      return;                 }             }             connection.commit();             System.out.println("添加完毕");         }catch (Exception e){             connection.rollback();             log.info("error",e);             throw new ServiceException("002","出现异常");         }finally {              connection.close();          }     }              1     2     3     4     5     6     7     8     9     10     11     12     13     14     15     16     17     18     19     20     21     22     23     24     25     26     27     28     29     30     31     32     33     34     35     36     37     38     39     40     41     42     43     44     45     46     47     48     49     50     51     52     53     54     55     56     57     58     59    // sql    <insert id="saveBatch" parameterType="List">     INSERT INTO     employee (employee_id,age,employee_name,birth_date,gender,id_number,creat_time,update_time,status)     values         <foreach collection="list" item="item" index="index" separator=",">         (         #{item.employeeId},         #{item.age},         #{item.employeeName},         #{item.birthDate},         #{item.gender},         #{item.idNumber},         #{item.creatTime},         #{item.updateTime},         #{item.status}             )         </foreach>     </insert>      1     2     3     4     5     6     7     8     9     10     11     12     13     14     15     16     17     18     19  数据库中一条数据: 在这里插入图片描述 测试结果:抛出异常, 在这里插入图片描述 删除操作的数据回滚了,数据库中的数据依旧存在,说明事务成功了. 在这里插入图片描述  成功操作示例:   @Resource     SqlContext sqlContext;     /**      * 测试多线程事务.      * @param employeeDOList      */     @Override     public void saveThread(List<EmployeeDO> employeeDOList) throws SQLException {         // 获取数据库连接,获取会话(内部自有事务)         SqlSession sqlSession = sqlContext.getSqlSession();         Connection connection = sqlSession.getConnection();         try {             // 设置手动提交             connection.setAutoCommit(false);             EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);             //先做删除操作             employeeMapper.delete(null);             ExecutorService service = ExecutorConfig.getThreadPool();             List<Callable<Integer>> callableList  = new ArrayList<>();             List<List<EmployeeDO>> lists=averageAssign(employeeDOList, 5);             for (int i =0;i<lists.size();i++){                 List<EmployeeDO> list  = lists.get(i);                 Callable<Integer> callable = () -> employeeMapper.saveBatch(list);                 callableList.add(callable);             }             //执行子线程            List<Future<Integer>> futures = service.invokeAll(callableList);             for (Future<Integer> future:futures) {                 if (future.get()<=0){                     connection.rollback();                      return;                 }             }             connection.commit();             System.out.println("添加完毕");         }catch (Exception e){             connection.rollback();             log.info("error",e);             throw new ServiceException("002","出现异常");            // throw new ServiceException(ExceptionCodeEnum.EMPLOYEE_SAVE_OR_UPDATE_ERROR);         }     }      1     2     3     4     5     6     7     8     9     10     11     12     13     14     15     16     17     18     19     20     21     22     23     24     25     26     27     28     29     30     31     32     33     34     35     36     37     38     39     40     41     42  测试结果: blog.csdnimg.cn/20210608165840483.png)  数据库中数据: 删除的删除了,添加的添加成功了,测试成功. 在这里插入图片描述 ————————————————                              版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。                          原文链接:https://blog.csdn.net/weixin_43225491/article/details/117705686 
  • [技术干货] 40 个 SpringBoot 常用注解:让生产力爆表!【转】
    企业开发项目SpringBoot已经是必备框架了,其中注解是开发中的小工具,用好了开发效率大大提升,当然用错了也会引入缺陷。一、Spring Web MVC与Spring Bean注解Spring Web MVC注解@RequestMapping@RequestMapping注解的主要用途是将Web请求与请求处理类中的方法进行映射。Spring MVC和Spring WebFlux都通过RquestMappingHandlerMapping和RequestMappingHndlerAdapter两个类来提供对@RequestMapping注解的支持。@RequestMapping注解对请求处理类中的请求处理方法进行标注;@RequestMapping注解拥有以下的六个配置属性:value:映射的请求URL或者其别名method:兼容HTTP的方法名params:根据HTTP参数的存在、缺省或值对请求进行过滤header:根据HTTP Header的存在、缺省或值对请求进行过滤consume:设定在HTTP请求正文中允许使用的媒体类型product:在HTTP响应体中允许使用的媒体类型提示:在使用@RequestMapping之前,请求处理类还需要使用@Controller或@RestController进行标记,公众 号Java精选,回复java面试,获取面试资料,支持在线刷题。下面是使用@RequestMapping的两个示例:@RequestMapping还可以对类进行标记,这样类中的处理方法在映射请求路径时,会自动将类上@RequestMapping设置的value拼接到方法中映射路径之前,如下:@RequestBody@RequestBody在处理请求方法的参数列表中使用,它可以将请求主体中的参数绑定到一个对象中,请求主体参数是通过HttpMessageConverter传递的,根据请求主体中的参数名与对象的属性名进行匹配并绑定值。此外,还可以通过@Valid注解对请求主体中的参数进行校验。下面是一个使用@RequestBody的示例:@GetMapping@GetMapping注解用于处理HTTP GET请求,并将请求映射到具体的处理方法中。具体来说,@GetMapping是一个组合注解,它相当于是@RequestMapping(method=RequestMethod.GET)的快捷方式。下面是@GetMapping的一个使用示例:@PostMapping@PostMapping注解用于处理HTTP POST请求,并将请求映射到具体的处理方法中。@PostMapping与@GetMapping一样,也是一个组合注解,它相当于是@RequestMapping(method=HttpMethod.POST)的快捷方式。下面是使用@PostMapping的一个示例:@PutMapping@PutMapping注解用于处理HTTP PUT请求,并将请求映射到具体的处理方法中,@PutMapping是一个组合注解,相当于是@RequestMapping(method=HttpMethod.PUT)的快捷方式。下面是使用@PutMapping的一个示例:@DeleteMapping@DeleteMapping注解用于处理HTTP DELETE请求,并将请求映射到删除方法中。@DeleteMapping是一个组合注解,它相当于是@RequestMapping(method=HttpMethod.DELETE)的快捷方式。下面是使用@DeleteMapping的一个示例:@PatchMapping@PatchMapping注解用于处理HTTP PATCH请求,并将请求映射到对应的处理方法中。@PatchMapping相当于是@RequestMapping(method=HttpMethod.PATCH)的快捷方式。下面是一个简单的示例:@ControllerAdvice@ControllerAdvice是@Component注解的一个延伸注解,Spring会自动扫描并检测被@ControllerAdvice所标注的类。@ControllerAdvice需要和@ExceptionHandler、@InitBinder以及@ModelAttribute注解搭配使用,主要是用来处理控制器所抛出的异常信息。首先,我们需要定义一个被@ControllerAdvice所标注的类,在该类中,定义一个用于处理具体异常的方法,并使用@ExceptionHandler注解进行标记。此外,在有必要的时候,可以使用@InitBinder在类中进行全局的配置,还可以使用@ModelAttribute配置与视图相关的参数。使用@ControllerAdvice注解,就可以快速的创建统一的,自定义的异常处理类。下面是一个使用@ControllerAdvice的示例代码:@ResponseBody@ResponseBody会自动将控制器中方法的返回值写入到HTTP响应中。特别的,@ResponseBody注解只能用在被@Controller注解标记的类中。如果在被@RestController标记的类中,则方法不需要使用@ResponseBody注解进行标注。@RestController相当于是@Controller和@ResponseBody的组合注解。下面是使用该注解的一个示例@ExceptionHandler@ExceptionHander注解用于标注处理特定类型异常类所抛出异常的方法。当控制器中的方法抛出异常时,Spring会自动捕获异常,并将捕获的异常信息传递给被@ExceptionHandler标注的方法。面试宝典:https://www.yoodb.com下面是使用该注解的一个示例:@ResponseStatus@ResponseStatus注解可以标注请求处理方法。使用此注解,可以指定响应所需要的HTTP STATUS。特别地,我们可以使用HttpStauts类对该注解的value属性进行赋值。下面是使用@ResponseStatus注解的一个示例:@PathVariable@PathVariable注解是将方法中的参数绑定到请求URI中的模板变量上。可以通过@RequestMapping注解来指定URI的模板变量,然后使用@PathVariable注解将方法中的参数绑定到模板变量上。特别地,@PathVariable注解允许我们使用value或name属性来给参数取一个别名。下面是使用此注解的一个示例:模板变量名需要使用{ }进行包裹,如果方法的参数名与URI模板变量名一致,则在@PathVariable中就可以省略别名的定义。下面是一个简写的示例:提示:如果参数是一个非必须的,可选的项,则可以在@PathVariable中设置require = false@RequestParam@RequestParam注解用于将方法的参数与Web请求的传递的参数进行绑定。使用@RequestParam可以轻松的访问HTTP请求参数的值。下面是使用该注解的代码示例:该注解的其他属性配置与@PathVariable的配置相同,特别的,如果传递的参数为空,还可以通过defaultValue设置一个默认值。示例代码如下:@Controller@Controller是@Component注解的一个延伸,Spring 会自动扫描并配置被该注解标注的类。此注解用于标注Spring MVC的控制器。下面是使用此注解的示例代码:@RestController@RestController是在Spring 4.0开始引入的,这是一个特定的控制器注解。此注解相当于@Controller和@ResponseBody的快捷方式。当使用此注解时,不需要再在方法上使用@ResponseBody注解。下面是使用此注解的示例代码:@ModelAttribute通过此注解,可以通过模型索引名称来访问已经存在于控制器中的model。下面是使用此注解的一个简单示例:与@PathVariable和@RequestParam注解一样,如果参数名与模型具有相同的名字,则不必指定索引名称,简写示例如下:特别地,如果使用@ModelAttribute对方法进行标注,Spring会将方法的返回值绑定到具体的Model上。示例如下:在Spring调用具体的处理方法之前,被@ModelAttribute注解标注的所有方法都将被执行。@CrossOrigin@CrossOrigin注解将为请求处理类或请求处理方法提供跨域调用支持。如果我们将此注解标注类,那么类中的所有方法都将获得支持跨域的能力。使用此注解的好处是可以微调跨域行为。使用此注解的示例如下:@InitBinder@InitBinder注解用于标注初始化WebDataBinider 的方法,该方法用于对Http请求传递的表单数据进行处理,如时间格式化、字符串处理等。下面是使用此注解的示例:二、Spring Bean注解在本小节中,主要列举与Spring Bean相关的4个注解以及它们的使用方式。@ComponentScan@ComponentScan注解用于配置Spring需要扫描的被组件注解注释的类所在的包。可以通过配置其basePackages属性或者value属性来配置需要扫描的包路径。面试宝典:https://www.yoodb.comvalue属性是basePackages的别名。此注解的用法如下:@Component@Component注解用于标注一个普通的组件类,它没有明确的业务范围,只是通知Spring被此注解的类需要被纳入到Spring Bean容器中并进行管理。此注解的使用示例如下:@Service@Service注解是@Component的一个延伸(特例),它用于标注业务逻辑类。与@Component注解一样,被此注解标注的类,会自动被Spring所管理。下面是使用@Service注解的示例:@Repository@Repository注解也是@Component注解的延伸,与@Component注解一样,被此注解标注的类会被Spring自动管理起来,@Repository注解用于标注DAO层的数据持久化类。此注解的用法如下:三、Spring Dependency Inject与Bean Scops注解Spring DI注解@DependsOn@DependsOn注解可以配置Spring IoC容器在初始化一个Bean之前,先初始化其他的Bean对象。下面是此注解使用示例代码:@Bean@Bean注解主要的作用是告知Spring,被此注解所标注的类将需要纳入到Bean管理工厂中。@Bean注解的用法很简单,在这里,着重介绍@Bean注解中initMethod和destroyMethod的用法。示例如下:Scops注解@Scope@Scope注解可以用来定义@Component标注的类的作用范围以及@Bean所标记的类的作用范围。@Scope所限定的作用范围有:singleton、prototype、request、session、globalSession或者其他的自定义范围。这里以prototype为例子进行讲解。当一个Spring Bean被声明为prototype(原型模式)时,在每次需要使用到该类的时候,Spring IoC容器都会初始化一个新的改类的实例。在定义一个Bean时,可以设置Bean的scope属性为prototype:scope=“prototype”,也可以使用@Scope注解设置,如下:@Scope(value=ConfigurableBeanFactory.SCOPE_PROPTOTYPE)下面将给出两种不同的方式来使用@Scope注解,示例代码如下:@Scope 单例模式当@Scope的作用范围设置成Singleton时,被此注解所标注的类只会被Spring IoC容器初始化一次。在默认情况下,Spring IoC容器所初始化的类实例都为singleton。同样的原理,此情形也有两种配置方式,示例代码如下:四、容器配置注解@Autowired@Autowired注解用于标记Spring将要解析和注入的依赖项。此注解可以作用在构造函数、字段和setter方法上。作用于构造函数下面是@Autowired注解标注构造函数的使用示例:作用于setter方法下面是@Autowired注解标注setter方法的示例代码:作用于字段@Autowired注解标注字段是最简单的,只需要在对应的字段上加入此注解即可,示例代码如下:@Primary当系统中需要配置多个具有相同类型的bean时,@Primary可以定义这些Bean的优先级。下面将给出一个实例代码来说明这一特性:输出结果:thisissendDingDingmethodmessage.@PostConstruct与@PreDestroy值得注意的是,这两个注解不属于Spring,它们是源于JSR-250中的两个注解,位于common-annotations.jar中。@PostConstruct注解用于标注在Bean被Spring初始化之前需要执行的方法。@PreDestroy注解用于标注Bean被销毁前需要执行的方法。下面是具体的示例代码:@Qualifier当系统中存在同一类型的多个Bean时,@Autowired在进行依赖注入的时候就不知道该选择哪一个实现类进行注入。此时,我们可以使用@Qualifier注解来微调,帮助@Autowired选择正确的依赖项。另外,推荐公众 号Java精选,回复java面试,获取资料。下面是一个关于此注解的代码示例:五、Spring Boot注解@SpringBootApplication@SpringBootApplication注解是一个快捷的配置注解,在被它标注的类中,可以定义一个或多个Bean,并自动触发自动配置Bean和自动扫描组件。此注解相当于@Configuration、@EnableAutoConfiguration和@ComponentScan的组合。在Spring Boot应用程序的主类中,就使用了此注解。示例代码如下:@SpringBootApplication publicclassApplication{ publicstaticvoidmain(String[]args){ SpringApplication.run(Application.class,args); } }@EnableAutoConfiguration@EnableAutoConfiguration注解用于通知Spring,根据当前类路径下引入的依赖包,自动配置与这些依赖包相关的配置项。@ConditionalOnClass与@ConditionalOnMissingClass这两个注解属于类条件注解,它们根据是否存在某个类作为判断依据来决定是否要执行某些配置。下面是一个简单的示例代码:@Configuration @ConditionalOnClass(DataSource.class) classMySQLAutoConfiguration{ //... }@ConditionalOnBean与@ConditionalOnMissingBean这两个注解属于对象条件注解,根据是否存在某个对象作为依据来决定是否要执行某些配置方法。示例代码如下:@Bean @ConditionalOnBean(name="dataSource") LocalContainerEntityManagerFactoryBeanentityManagerFactory(){ //... } @Bean @ConditionalOnMissingBean publicMyBeanmyBean(){ //... }@ConditionalOnProperty@ConditionalOnProperty注解会根据Spring配置文件中的配置项是否满足配置要求,从而决定是否要执行被其标注的方法。示例代码如下:@Bean @ConditionalOnProperty(name="alipay",havingValue="on") Alipayalipay(){ returnnewAlipay(); }@ConditionalOnResource此注解用于检测当某个配置文件存在使,则触发被其标注的方法,下面是使用此注解的代码示例:@ConditionalOnResource(resources="classpath:website.properties") PropertiesaddWebsiteProperties(){ //... }@ConditionalOnWebApplication与@ConditionalOnNotWebApplication这两个注解用于判断当前的应用程序是否是Web应用程序。如果当前应用是Web应用程序,则使用Spring WebApplicationContext,并定义其会话的生命周期。下面是一个简单的示例:@ConditionalOnWebApplication HealthCheckControllerhealthCheckController(){ //... }@ConditionalExpression此注解可以让我们控制更细粒度的基于表达式的配置条件限制。当表达式满足某个条件或者表达式为真的时候,将会执行被此注解标注的方法。@Bean @ConditionalException("${localstore}&&${local=='true'}") LocalFileStorestore(){ //... }@Conditional@Conditional注解可以控制更为复杂的配置条件。在Spring内置的条件控制注解不满足应用需求的时候,可以使用此注解定义自定义的控制条件,以达到自定义的要求。下面是使用该注解的简单示例:@Conditioanl(CustomConditioanl.class) CustomPropertiesaddCustomProperties(){ //... }总结本次课程总结了Spring Boot中常见的各类型注解的使用方式,让大家能够统一的对Spring Boot常用注解有一个全面的了解。
  • [技术干货] 定时任务之-------Elastic-Job
    Elastic-Job 是一个基于 Quartz 的分布式任务调度框架,它提供了弹性扩容、任务分片、失效转移等特性,适用于大规模分布式任务调度场景。本文将介绍 Elastic-Job 的主要特点、使用方法以及在实际项目中的应用场景和优势。 Elastic-Job 的主要特点 分布式任务调度:Elastic-Job 支持将任务分布到多个节点上执行,实现任务的高可用性和负载均衡。任务调度中心负责管理任务和分发任务到各个执行节点,保证任务的可靠执行。 弹性扩容和缩容:Elastic-Job 具有弹性伸缩的能力,可以根据任务负载自动增加或减少执行节点的数量,以应对不同规模的任务调度需求。 失败转移和错过执行策略:当任务执行失败或错过执行时间时,Elastic-Job 可以根据配置的策略进行相应的处理,如重试执行、跳过执行或报警通知等。 实时监控和统计:Elastic-Job 提供了丰富的监控和统计功能,可以实时查看任务的执行情况、节点的状态和任务的执行日志,方便进行故障排查和性能优化。 任务分片和并行执行:Elastic-Job 支持将一个任务分成多个独立的子任务进行并行执行,提高任务执行的效率和吞吐量。 使用 Elastic-Job 进行分布式任务调度 定义任务:编写一个实现 ElasticJob 接口的任务类,实现具体的任务逻辑。  public class MySimpleJob implements SimpleJob {      @Override     public void execute(ShardingContext shardingContext) {         System.out.println(String.format("------ Thread ID: %s, Sharding Item: %s, Time: %s ------",                 Thread.currentThread().getId(), shardingContext.getShardingItem(), new SimpleDateFormat("HH:mm:ss").format(new Date())));     } } 配置任务:通过配置文件或编程方式指定任务的调度策略、触发器、任务参数等信息。 xml <!-- 配置任务调度中心 --> <bean id="jobScheduler" class="io.elasticjob.lite.spring.job.SpringJobScheduler">     <constructor-arg ref="registryCenter" />     <constructor-arg>         <bean class="io.elasticjob.lite.job.config.JobCoreConfiguration" c:init-method="init" p:name="mySimpleJob" p:shardingTotalCount="3" p:cron="0/5 * * * * ?" />     </constructor-arg>     <constructor-arg>         <bean class="io.elasticjob.lite.job.config.simple.SimpleJobConfiguration" c:init-method="init" p:jobClass="com.example.MySimpleJob" />     </constructor-arg> </bean> 注册任务:将任务注册到任务调度中心,包括任务的名称、描述和执行类等信息。 @Bean(initMethod = "init") public ZookeeperRegistryCenter registryCenter() {     ZookeeperConfiguration zookeeperConfiguration = new ZookeeperConfiguration("localhost:2181", "elastic-job-example");     return new ZookeeperRegistryCenter(zookeeperConfiguration); }  @Bean public MySimpleJob mySimpleJob() {     return new MySimpleJob(); } @Bean public JobScheduler jobScheduler(final MySimpleJob simpleJob, final ZookeeperRegistryCenter regCenter) {     return new SpringJobScheduler(simpleJob, regCenter, getLiteJobConfiguration(simpleJob.getClass(), "0/5 * * * * ?", 3, "0=Beijing,1=Shanghai", "simpleJob")); } private LiteJobConfiguration getLiteJobConfiguration(final Class<? extends SimpleJob> jobClass, final String cron, final int shardingTotalCount, final String shardingItemParameters, final String jobParameter) {     return LiteJobConfiguration.newBuilder(new SimpleJobConfiguration(JobCoreConfiguration.newBuilder(             jobClass.getName(), cron, shardingTotalCount).jobParameter(jobParameter).shardingItemParameters(shardingItemParameters).build(), jobClass.getCanonicalName())).overwrite(true).build(); } 启动任务调度中心:启动任务调度中心,它负责管理任务和分发任务到执行节点。 启动执行节点:启动执行节点,它负责接收任务调度中心分发的任务,并执行任务逻辑。 Elastic-Job 在实际项目中的应用场景 Elastic-Job 在许多实际项目中都有广泛的应用场景,特别适用于大规模分布式任务调度和处理场景。例如电商网站的订单处理、金融系统的批量处理、数据仓库的数据同步等。 Elastic-Job 的优势和局限性 优势:Elastic-Job 提供了灵活的分布式任务调度能力,简化了任务调度的开发和管理,提高了任务执行的可靠性和效率。 局限性:对于一些特定的任务调度需求,可能需要深入了解 Elastic-Job 的特性和配置,学习曲线较陡。 
  • [技术干货] 定时任务之-------Quartz
     一、Quartz 的概述 什么是 Quartz? Quartz 是一个开源的 Java 定时任务调度框架,由 Terracotta 公司开发并于2009年成为 Apache 顶级项目。它允许开发人员在 Java 应用程序中创建和管理各种类型的定时任务,并提供了丰富的功能和配置选项。 Quartz 的主要特点 灵活的任务调度:Quartz 支持基于日历的任务调度,可以按照指定的时间表执行任务,如每天、每周或每月。它还支持 cron 表达式,允许更灵活地定义任务执行时间。 分布式任务调度:Quartz 提供了集群支持,可以在多个节点上运行任务调度程序。这样可以实现高可用性和负载均衡,确保任务的可靠执行。 错过执行策略:当一个任务错过了预定的执行时间时,Quartz 可以根据配置的策略来处理。常见的策略包括立即执行、延迟执行或忽略该次执行。 任务持久化:Quartz 支持将任务信息持久化到数据库中,以便在应用程序重启后能够恢复任务状态。这对于长时间运行的任务或需要跟踪任务执行历史的场景非常有用。 监听器和触发器:Quartz 提供了丰富的监听器和触发器机制,可以在任务执行前后进行各种操作,如记录日志、发送通知或触发其他任务。 二、使用 Quartz 进行定时任务调度 创建 Job 类 在使用 Quartz 进行任务调度时,首先需要创建一个实现 org.quartz.Job 接口的 Job 类。该类定义了具体任务的执行逻辑,可以根据业务需求编写相应的逻辑代码。 创建 Trigger 对象 Trigger 对象用于指定任务的执行时间和频率。Quartz 提供了两种常用的 Trigger 实现:SimpleTrigger 和 CronTrigger。SimpleTrigger 允许指定任务的开始时间和重复间隔时间,而 CronTrigger 则可以使用 cron 表达式更灵活地定义任务执行时间。 创建 Scheduler 对象 Scheduler 是 Quartz 的核心组件,负责管理和调度任务。通过 StdSchedulerFactory 可以获取一个 Scheduler 实例,用于注册和启动任务。 将 Job 和 Trigger 注册到 Scheduler 中 使用 scheduler.scheduleJob() 方法将 Job 和 Trigger 关联起来,并注册到 Scheduler 中。 启动 Scheduler 调用 scheduler.start() 方法启动任务调度器,开始执行任务。 三、Quartz 在实际项目中的应用场景 Quartz 在许多实际项目中都有广泛的应用场景,以下是一些常见的应用场景: 数据清理和备份:定时清理数据库中的过期数据或执行数据备份操作。 缓存刷新:定时刷新缓存,保证数据的时效性。 报表生成:定时生成各类报表,如每日销售报表、月度统计报表等。 邮件发送:定时发送邮件,如每日汇总邮件、生日祝福邮件等。 系统监控:定时检查系统状态,如服务器性能监控、日志文件分析等。 四、Quartz 的优势和局限性 优势: 灵活性:Quartz 提供了丰富的配置选项和高级特性,可以满足各种复杂的任务调度需求。 可靠性:Quartz 支持任务的持久化和集群部署,保证任务执行的可靠性和高可用性。 扩展性:Quartz 提供了丰富的 API 和插件机制,可以方便地扩展和自定义功能。 局限性: 学习成本:Quartz 的学习曲线较陡,需要一定的时间和经验来熟悉其概念和使用方法。 复杂性:在处理一些复杂的任务调度需求时,可能需要编写复杂的代码逻辑和配置文件。 
  • [技术干货] 定时任务之-------Spring Task
    Spring Task 是一个强大而灵活的框架,能够帮助开发人员轻松地管理和调度定时任务。无论是通过注解驱动还是编程式方式,Spring Task 提供了简单易用的接口和功能,使得定时任务的实现变得简单而高效。本文将介绍 Spring Task 的基本用法,并探讨一些高级特性,以帮助读者更好地理解和应用该框架。  一、使用注解驱动的 Spring Task 启用注解驱动的 Spring Task 要使用注解驱动的 Spring Task,我们需要在 Spring 配置文件中启用相应的功能。可以通过在配置文件中添加 <task:annotation-driven /> 来启用注解驱动的 Spring Task。这样一来,我们就可以在需要执行定时任务的方法上使用 @Scheduled 注解来标记。 定时任务的基本配置 在使用注解驱动的 Spring Task 时,我们可以使用 @Scheduled 注解来标记一个方法作为定时任务。该注解支持多种配置方式,包括 cron 表达式、固定延迟和固定频率。 cron 表达式:可以指定任务执行的时间表达式,以便更加精确地控制任务的执行时机。 @Scheduled(cron = "0 0 1 * * ?") public void runAtOneAM() {     // 执行具体的定时任务逻辑 } 固定延迟:表示任务执行完成后固定延迟多久再次执行。 @Scheduled(fixedDelay = 5000) public void runEveryFiveSeconds() {     // 每隔 5 秒执行一次任务 } 固定频率:表示任务开始执行后固定频率执行。 @Scheduled(fixedRate = 5000) public void runEveryFiveSeconds() {     // 每隔 5 秒执行一次任务 } 通过简单的注解配置,我们可以方便地实现各种定时任务,并根据需求灵活地调整任务的执行方式。 二、使用编程式的 Spring Task 除了使用注解驱动的方式,Spring Task 还提供了编程式的方式来管理和调度定时任务。我们可以通过实现 Runnable 接口或继承 TimerTask 类来定义定时任务,并将它们注册到 TaskScheduler 中。 以下是一个使用编程式方式实现定时任务的示例代码: public class MyTask implements Runnable {       @Override     public void run() {         // 执行具体的定时任务逻辑     } }  ScheduledExecutorService executor = Executors.newScheduledThreadPool(1); TaskScheduler scheduler = new ConcurrentTaskScheduler(executor); scheduler.scheduleAtFixedRate(new MyTask(), 5000); 在这个示例中,我们创建了一个 ScheduledExecutorService 线程池,并使用它来创建 ConcurrentTaskScheduler 对象。然后,我们调用 scheduleAtFixedRate 方法来注册定时任务,指定了任务对象和重复执行的时间间隔。 通过编程式的方式,我们可以更加灵活地管理和调度定时任务,尤其适用于一些复杂的场景。 三、高级特性 除了基本的用法之外,Spring Task 还提供了一些高级特性,以帮助开发人员更好地管理和调度定时任务。 异步执行:Spring Task 支持在单独的线程中执行定时任务,从而不阻塞应用程序的其他操作。 并发控制:如果一个任务的执行时间超过了下一次执行的间隔,Spring Task 可以灵活地处理并发执行的情况,确保任务按照预期的间隔执行。 定时任务监听:通过实现 TaskScheduler 的接口,我们可以监听定时任务的执行情况,并进行相应的处理。 这些高级特性使得 Spring Task 更加强大和灵活,能够满足各种复杂的定时任务需求。 
  • [技术干货] Spring高手之路-@Autowired和@Resource注解异同点-转载
     概述 @Autowired 和 @Resource 是在 Java 开发中用于实现依赖注入的注解。  @Autowired 是 Spring 框架提供的注解,用于自动装配(自动注入)依赖。通过在需要注入依赖的字段、构造方法或者方法上使用 @Autowired 注解,Spring 容器会自动寻找匹配类型的 Bean,并将其注入到被注解的位置。如果存在多个满足条件的 Bean,则可以使用 @Qualifier 注解指定具体的 Bean。  @Resource 是 JavaEE 规范提供的注解,也可用于依赖注入。它可以根据名称或者类型进行依赖注入。当使用名称进行依赖注入时,@Resource 注解会根据指定的名称去查找匹配的 Bean 进行注入;当使用类型进行依赖注入时,@Resource 注解会使用类型去查找匹配的 Bean 进行注入。  相同点 1.都可以实现依赖注入 通过注解将需要的Bean自动注入到目标类中。  2.都可以用于注入任意类型的Bean 包括类、接口、原始类型、数组等。  3.都支持通过名称、类型匹配进行注入 @Autowired 注解默认按照类型匹配,而 @Resource 注解默认按照名称匹配  @Autowired private Bean beanA; @Resource private Bean beanB; 在Spring容器中这两个注解功能基本是等价的,都可以将bean注入到对应的字段中。  不同点 虽然功能上看起来基本相同还是存在区别的下面从几个不同方面分析  1.来源不同。 @Autowired 是 Spring 框架提供的注解。  @Resource 是 JavaEE(现在的 JakartaEE)规范中定义的注解。  2.包含的属性不同 @Autowired 只包含一个参数:required,表示是否开启自动注入,默认是true。  @Resource 包含七个参数,其中最重要的两个参数是:name 和 type。  3.匹配方式(装配顺序)不同。 @Autowired 默认先按照类型进行自动装配,再是根据名称的方式。意思就是先在Spring容器中找以Bean为类型的Bean实例,如果找不到或者找到多个bean,则会进一步通过字段名称来找。当有多个同类型的 Bean 存在时,也可以通过 @Qualifier 注解指定具体的 Bean。  @Component public class UserService {     @Autowired     @Qualifier("userRepository")//如果有多个同类型的Bean,可以使用@Qualifier注解指定具体的Bean     private UserRepository userRepository;     // ... }  @Resource 和@Autowired恰好相反,先是按照名称方式,然后再是按照类型方式;当然,我们也可以通过注解中的参数显示指定通过哪种方式。如果有多个同名的Bean,可以使用@Resource注解的name属性指定具体的Bean  默认使用  @Component public class UserService {     @Resource//不指定任何属性     private UserRepository userRepository;     // ... }  指定name  @Component public class UserService {     @Resource(name = "userRepository")//使用name属性指定具体的Bean     private UserRepository userRepository;     // ... }  指定type  @Component public class UserService {     @Resource(type = UserRepository.class)//使用type属性指定Bean类型     private UserRepository userRepository;     // ... }  指定name和type  @Component public class UserService {     @Resource(type = "UserRepository.class",name = "userRepository")//使用type属性指定Bean类型,name指定Bean名称     private UserRepository userRepository;     // ... }  4.支持的注入对象类型不同 @Autowired 可以注入任何类型的对象,只要 Spring 容器中存在该类型的 Bean。  @Resource 注解可以用于注入 JNDI 名称(JNDI名称可以是任何字符串,但通常使用具有描述性的名称来标识资源。在应用程序中,可以使用JNDI名称来查找和绑定对象)或者默认按照名称匹配的 Bean  5.应用地方不同 @Autowired能够用在:构造器、方法、参数、成员变量和注解上  @Resource能用在:类、成员变量和方法上。 ———————————————— 版权声明:本文为CSDN博主「mi9688」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/qq_62262918/article/details/135291547 
总条数:189 到第 页
上滑加载中