java框架之SpringBoot(7)-异常处理
前言
在 SpringBoot 项目中,默认情况下,使用浏览器访问一个不存在的地址会返回如下错误页面:

而当客户端未非浏览器时,错误信息则会以 json 数据返回,如下:

会出现如上效果的原因是 SpringBoot 针对错误消息做了自动配置,对应自动配置类为 org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration 。
自定义错误页
查看错误自动配置类会发现在该类中注册了如下组件:
ErrorPageCustomizer
@Bean
public ErrorPageCustomizer errorPageCustomizer() {
return new ErrorPageCustomizer(this.serverProperties);
}
查看该组件类:
private static class ErrorPageCustomizer implements ErrorPageRegistrar, Ordered {
private final ServerProperties properties;
protected ErrorPageCustomizer(ServerProperties properties) {
this.properties = properties;
}
@Override
public void registerErrorPages(ErrorPageRegistry errorPageRegistry) {
ErrorPage errorPage = new ErrorPage(this.properties.getServletPrefix()
+ this.properties.getError().getPath());
errorPageRegistry.addErrorPages(errorPage);
}
@Override
public int getOrder() {
;
}
}
org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration.ErrorPageCustomizer
在第 10 行的 registerErrorPages 方法中,注册了一个错误页,错误页路径为 this.properties.getError().getPath() ,该值为
@Value("${error.path:/error}")
private String path = "/error";
即,一旦出现了 4xx 或 5xx 错误,该组件就会生效,可用其定制系统发生错误时的转发路径,默认情况下当前请求会转发到 /error 路径。
BasicErrorController
@Bean
@ConditionalOnMissingBean(value = ErrorController.class, search = SearchStrategy.CURRENT)
public BasicErrorController basicErrorController(ErrorAttributes errorAttributes) {
return new BasicErrorController(errorAttributes, this.serverProperties.getError(),
this.errorViewResolvers);
}
查看该组件类:
package org.springframework.boot.autoconfigure.web;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.boot.autoconfigure.web.ErrorProperties.IncludeStacktrace;
import org.springframework.boot.context.embedded.AbstractEmbeddedServletContainerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {
private final ErrorProperties errorProperties;
public BasicErrorController(ErrorAttributes errorAttributes,
ErrorProperties errorProperties) {
this(errorAttributes, errorProperties,
Collections.<ErrorViewResolver>emptyList());
}
public BasicErrorController(ErrorAttributes errorAttributes,
ErrorProperties errorProperties, List<ErrorViewResolver> errorViewResolvers) {
super(errorAttributes, errorViewResolvers);
Assert.notNull(errorProperties, "ErrorProperties must not be null");
this.errorProperties = errorProperties;
}
@Override
public String getErrorPath() {
return this.errorProperties.getPath();
}
@RequestMapping(produces = "text/html")
public ModelAndView errorHtml(HttpServletRequest request,
HttpServletResponse response) {
HttpStatus status = getStatus(request);
Map<String, Object> model = Collections.unmodifiableMap(getErrorAttributes(
request, isIncludeStackTrace(request, MediaType.TEXT_HTML)));
response.setStatus(status.value());
ModelAndView modelAndView = resolveErrorView(request, response, status, model);
return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
}
@RequestMapping
@ResponseBody
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
Map<String, Object> body = getErrorAttributes(request,
isIncludeStackTrace(request, MediaType.ALL));
HttpStatus status = getStatus(request);
return new ResponseEntity<Map<String, Object>>(body, status);
}
protected boolean isIncludeStackTrace(HttpServletRequest request,
MediaType produces) {
IncludeStacktrace include = getErrorProperties().getIncludeStacktrace();
if (include == IncludeStacktrace.ALWAYS) {
return true;
}
if (include == IncludeStacktrace.ON_TRACE_PARAM) {
return getTraceParameter(request);
}
return false;
}
protected ErrorProperties getErrorProperties() {
return this.errorProperties;
}
}
org.springframework.boot.autoconfigure.web.BasicErrorController
可以看到该组件实际上是一个控制器,用来处理路径为配置中定义的 "${server.error.path:${error.path:/error}}" 请求,如果 server.error 和 error.path 都没有配置,则默认处理路径为 /error 的请求。
控制器中有两个响应方法,分别为第 46 行的 errorHtml 方法和第 58 行的 error 方法,它们都是用来处理路径为 /error 的请求,但 errorHtml 方法返回的错误消息是一个 html 页面,而 error 方法是返回的错误消息是一个 json 数据。通过 @RequestMapping 注解中的 produces 属性来区分客户端需要的错误消息类型,即根据客户端的 accept 请求头区分。具体以哪个页面作为错误页则可看到在第 52 行的 resolveErrorView 方法:
protected ModelAndView resolveErrorView(HttpServletRequest request,
HttpServletResponse response, HttpStatus status, Map<String, Object> model) {
for (ErrorViewResolver resolver : this.errorViewResolvers) {
ModelAndView modelAndView = resolver.resolveErrorView(request, status, model);
if (modelAndView != null) {
return modelAndView;
}
}
return null;
}
org.springframework.boot.autoconfigure.web.AbstractErrorController#resolveErrorView
可以看到,该方法时遍历容器中所有的错误视图解析器,如果解析器解析当前请求返回的 modelAndView 不为空,则以该 modelAndView 作为错误页的响应。即:以哪个页面作为错误页是由错误视图解析器的 resolveErrorView 方法的返回值决定。
DefaultErrorViewResolver
@Bean
@ConditionalOnBean(DispatcherServlet.class)
@ConditionalOnMissingBean
public DefaultErrorViewResolver conventionErrorViewResolver() {
return new DefaultErrorViewResolver(this.applicationContext,
this.resourceProperties);
}
这是默认配置的错误视图解析器,查看它的 resolveErrorView 方法:
@Override
public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status,
Map<String, Object> model) {
// 传入字符串形式的状态码
ModelAndView modelAndView = resolve(String.valueOf(status), model);
if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
modelAndView = resolve(SERIES_VIEWS.get(status.series()), model);
}
return modelAndView;
}
private ModelAndView resolve(String viewName, Map<String, Object> model) {
String errorViewName = "error/" + viewName; // 如:error/404
TemplateAvailabilityProvider provider = this.templateAvailabilityProviders
.getProvider(errorViewName, this.applicationContext);
if (provider != null) { // 如果模板引擎解析器可解析则返回模板视图
return new ModelAndView(errorViewName, model);
}
// 模板引擎不可解析时
return resolveResource(errorViewName, model);
}
private ModelAndView resolveResource(String viewName, Map<String, Object> model) {
for (String location : this.resourceProperties.getStaticLocations()) { // 遍历静态资源文件夹
try {
Resource resource = this.applicationContext.getResource(location); // 获取静态资源
resource = resource.createRelative(viewName + ".html"); // 如:error/404.html
if (resource.exists()) { // 判断对应资源是否存在
return new ModelAndView(new HtmlResourceView(resource), model); // 如果存在则返回对应 html 视图
}
}
catch (Exception ex) {
}
}
return null;
}
org.springframework.boot.autoconfigure.web.DefaultErrorViewResolver#resolveErrorView
通过上述代码可以看到,当请求出现错误时,错误视图解析器会在模板路径及静态文件夹路径下寻找以该错误对应状态码命名的 html 页面作为错误响应视图。比如错误代码为 404,那么默认情况下将会寻找在 templates 和 static 等静态资源文件夹下的 error/404.html 页面作为响应页。我们还可以通过使用 4xx.html 和 5xx.html 作为模板页或静态页分别来匹配以 4 开头和 5 开头的错误让其作为该错误的响应页。从 org.springframework.boot.autoconfigure.web.BasicErrorController#errorHtml 方法的返回值可以看到,如果在模板文件夹和静态文件夹下都没有找到对应的错误页,那么将会返回 new ModelAndView("error", model) 对象,而这个 error 视图在错误自动配置类中中已经配置好了:
@Configuration
@ConditionalOnProperty(prefix = "server.error.whitelabel", name = "enabled", matchIfMissing = true)
@Conditional(ErrorTemplateMissingCondition.class)
protected static class WhitelabelErrorViewConfiguration {
private final SpelView defaultErrorView = new SpelView(
"<html><body><h1>Whitelabel Error Page</h1>"
+ "<p>This application has no explicit mapping for /error, so you are seeing this as a fallback.</p>"
+ "<div id='created'>${timestamp}</div>"
+ "<div>There was an unexpected error (type=${error}, status=${status}).</div>"
+ "<div>${message}</div></body></html>");
@Bean(name = "error")
@ConditionalOnMissingBean(name = "error")
public View defaultErrorView() {
return this.defaultErrorView;
}
@Bean
@ConditionalOnMissingBean(BeanNameViewResolver.class)
public BeanNameViewResolver beanNameViewResolver() {
BeanNameViewResolver resolver = new BeanNameViewResolver();
resolver.setOrder(Ordered.LOWEST_PRECEDENCE - );
return resolver;
}
}
org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration.WhitelabelErrorViewConfiguration
DefaultErrorAttributes
@Bean
@ConditionalOnMissingBean(value = ErrorAttributes.class, search = SearchStrategy.CURRENT)
public DefaultErrorAttributes errorAttributes() {
return new DefaultErrorAttributes();
}
如上我们只说明了错误页的显示规则,那错误页的消息又是从何而来呢?回头看到 org.springframework.boot.autoconfigure.web.BasicErrorController#errorHtml 方法:
@RequestMapping(produces = "text/html")
public ModelAndView errorHtml(HttpServletRequest request,
HttpServletResponse response) {
HttpStatus status = getStatus(request);
Map<String, Object> model = Collections.unmodifiableMap(getErrorAttributes(
request, isIncludeStackTrace(request, MediaType.TEXT_HTML)));
response.setStatus(status.value());
ModelAndView modelAndView = resolveErrorView(request, response, status, model);
return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
}
org.springframework.boot.autoconfigure.web.BasicErrorController#errorHtml
可以看到返回的 model 的数据为 getErrorAttributes(request, isIncludeStackTrace(request, MediaType.TEXT_HTML)) 方法的返回值,查看该方法:
protected Map<String, Object> getErrorAttributes(HttpServletRequest request,
boolean includeStackTrace) {
RequestAttributes requestAttributes = new ServletRequestAttributes(request);
return this.errorAttributes.getErrorAttributes(requestAttributes,
includeStackTrace);
}
org.springframework.boot.autoconfigure.web.AbstractErrorController#getErrorAttributes
继续查看 this.errorAttributes.getErrorAttributes(requestAttributes, includeStackTrace) 方法:
@Override
public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes,
boolean includeStackTrace) {
Map<String, Object> errorAttributes = new LinkedHashMap<String, Object>();
errorAttributes.put("timestamp", new Date());
addStatus(errorAttributes, requestAttributes);
addErrorDetails(errorAttributes, requestAttributes, includeStackTrace);
addPath(errorAttributes, requestAttributes);
return errorAttributes;
}
private void addStatus(Map<String, Object> errorAttributes,
RequestAttributes requestAttributes) {
Integer status = getAttribute(requestAttributes,
"javax.servlet.error.status_code");
if (status == null) {
errorAttributes.put();
errorAttributes.put("error", "None");
return;
}
errorAttributes.put("status", status);
try {
errorAttributes.put("error", HttpStatus.valueOf(status).getReasonPhrase());
}
catch (Exception ex) {
errorAttributes.put("error", "Http Status " + status);
}
}
private void addErrorDetails(Map<String, Object> errorAttributes,
RequestAttributes requestAttributes, boolean includeStackTrace) {
Throwable error = getError(requestAttributes);
if (error != null) {
while (error instanceof ServletException && error.getCause() != null) {
error = ((ServletException) error).getCause();
}
errorAttributes.put("exception", error.getClass().getName());
addErrorMessage(errorAttributes, error);
if (includeStackTrace) {
addStackTrace(errorAttributes, error);
}
}
Object message = getAttribute(requestAttributes, "javax.servlet.error.message");
if ((!StringUtils.isEmpty(message) || errorAttributes.get("message") == null)
&& !(error instanceof BindingResult)) {
errorAttributes.put("message",
StringUtils.isEmpty(message) ? "No message available" : message);
}
}
private void addErrorMessage(Map<String, Object> errorAttributes, Throwable error) {
BindingResult result = extractBindingResult(error);
if (result == null) {
errorAttributes.put("message", error.getMessage());
return;
}
) {
errorAttributes.put("errors", result.getAllErrors());
errorAttributes.put("message",
"Validation failed for object='" + result.getObjectName()
+ "'. Error count: " + result.getErrorCount());
}
else {
errorAttributes.put("message", "No errors");
}
}
private void addStackTrace(Map<String, Object> errorAttributes, Throwable error) {
StringWriter stackTrace = new StringWriter();
error.printStackTrace(new PrintWriter(stackTrace));
stackTrace.flush();
errorAttributes.put("trace", stackTrace.toString());
}
private void addPath(Map<String, Object> errorAttributes,
RequestAttributes requestAttributes) {
String path = getAttribute(requestAttributes, "javax.servlet.error.request_uri");
if (path != null) {
errorAttributes.put("path", path);
}
org.springframework.boot.autoconfigure.web.DefaultErrorAttributes#getErrorAttributes
通过上述代码我们可以知道在错误页中我们可以使用如下错误信息:
timestamp:时间戳 status:状态码 error:错误提示 exception:异常对象 message:异常信息 errors:JSR303 数据校验错误信息
自定义错误信息
上述已经描述了我们如何使用自定义的错误页,但是使用的错误信息还依旧是 SpringBoot 默认配置的,如果我们想要自己定制错误信息,则可通过如下方式。
方便下面测试先编写如下异常类及控制器:
package com.springboot.webdev2.ex;
public class MyException extends RuntimeException {
public MyException() {
super("运行期间出异常了");
}
}
com.springboot.webdev2.ex.MyException
package com.springboot.webdev2.controller;
import com.springboot.webdev2.ex.MyException;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class TestController {
@RequestMapping("test")
public void test1(){
throw new MyException();
}
}
com.springboot.webdev2.controller.TestController
方式一:自定义异常处理器
package com.springboot.webdev2.component;
import com.springboot.webdev2.ex.MyException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.HashMap;
import java.util.Map;
@ControllerAdvice
public class MyExceptionHandler {
@ResponseBody
@ExceptionHandler(MyException.class)
public Map<String,Object> handleException(Exception e){
Map<String, Object> map = new HashMap<>();
map.put("code", "myCode");
map.put("msg", "自定义的异常");
return map;
}
}

com.springboot.webdev2.component.MyExceptionHandler
该方式是 SpringMVC 提供的异常处理方式,缺点:使用该方式失去了 SpringBoot 本身的根据客户端的不同自适应响应数据类型的功能。
方式二:转发到错误处理路径
我们已经知道默认情况下出现异常 SpringBoot 会将请求转发到 /error ,那么如果我们通过异常处理器手动转发到该路径,并可手动将我们需要的错误信息放入请求域,我们就可以解决方式一的缺点并且可以在错误页使用我们自己的错误信息了。
package com.springboot.webdev2.component;
import com.springboot.webdev2.ex.MyException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
@ControllerAdvice
public class MyExceptionHandler {
@ExceptionHandler(MyException.class)
public String handleException(Exception e, HttpServletRequest request){
// SpringBoot 默认使用的状态码就是请求域中的 javax.servlet.error.status_code
request.setAttribute();
Map<String, Object> map = new HashMap<>();
map.put("code", "myCode");
map.put("msg", "自定义的异常");
request.setAttribute("ext", map);
return "forward:/error";
}
}
com.springboot.webdev2.component.MyExceptionHandler
<!DOCTYPE html>
<html lang="cn">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>[[${status}]]</h1>
<h2>[[${message}]]</h2>
<!--请求域中取错误信息-->
<h2>[[${ext.msg}]]</h2>
</body>
</html>


templates/error/4xx.html
可以发现,该方式依旧有一个缺点:放入请求域中的数据未被序列化,所以只可在转发到的模板页中取到,而在客户端是非浏览器时是拿不到自定义的错误信息的。
方式三:自定义错误处理控制器
出现异常时 SpringBoot 会将请求转发到 /error ,而处理该请求的控制器为 BaseErrorController ,查看该控制器注册信息我们也可以知道,当我们自己定义一个 org.springframework.boot.autoconfigure.web.ErrorController 组件注册到容器中时,那么默认的 BasicErrorController 就不生效了,所以我们可以在自定义的错误处理控制器中根据我们的需要取到我们合适的信息返回。该方式比较复杂,明显不合适,了解即可,略过。
方式四:自定义ErrorAttributes
通过查看 org.springframework.boot.autoconfigure.web.BasicErrorController 我们已经知道,不管是响应 html 还是 json 错误信息,它们的错误信息都是通过 this.errorAttributes.getErrorAttributes(requestAttributes, includeStackTrace) 方法取到,而 this.errorAttributes 对应的组件实际上在错误自动配置类中已经注册,即 DefaultErrorAttributes ,所以我们可以自定义一个的 org.springframework.boot.autoconfigure.web.ErrorAttributes 组件注册到容器中,重写它的 getErrorAttributes 方法,通过手动取得自定义的错误信息返回即可。
package com.springboot.webdev2.component;
import org.springframework.boot.autoconfigure.web.DefaultErrorAttributes;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import java.util.Map;
@Component
public class MyErrorAttributes extends DefaultErrorAttributes {
@Override
public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes, boolean includeStackTrace) {
Map<String, Object> errorAttributes = super.getErrorAttributes(requestAttributes, includeStackTrace);
errorAttributes.put("ext", requestAttributes.getAttribute("ext", RequestAttributes.SCOPE_REQUEST));
return errorAttributes;
}
}
com.springboot.webdev2.component.MyErrorAttributes
package com.springboot.webdev2.component;
import com.springboot.webdev2.ex.MyException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
@ControllerAdvice
public class MyExceptionHandler {
@ExceptionHandler(MyException.class)
public String handleException(Exception e, HttpServletRequest request){
// SpringBoot 默认使用的状态码就是请求域中的 javax.servlet.error.status_code
request.setAttribute("javax.servlet.error.status_code", 400);
Map<String, Object> map = new HashMap<>();
map.put("code", "myCode");
map.put("msg", "自定义的异常");
request.setAttribute("ext", map);
return "forward:/error";
}
}
com.springboot.webdev2.component.MyExceptionHandler
<!DOCTYPE html>
<html lang="cn">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>[[${status}]]</h1>
<h2>[[${message}]]</h2>
<!--请求域中取错误信息-->
<h2>[[${ext.msg}]]</h2>
</body>
</html>


templates/error/4xx.html
java框架之SpringBoot(7)-异常处理的更多相关文章
- java框架之SpringBoot(1)-入门
简介 Spring Boot 用来简化 Spring 应用开发,约定大于配置,去繁从简,just run 就能创建一个独立的.产品级别的应用. 背景: J2EE 笨重的开发.繁多的配置.低下的开发效率 ...
- java框架之SpringBoot(3)-日志
市面上的日志框架 日志抽象层 日志实现 JCL(Jakarta Commons Logging).SLF4J(Simple Logging Facade For Java).JBoss-Logging ...
- java框架之SpringBoot(4)-资源映射&thymeleaf
资源映射 静态资源映射 查看 SpringMVC 的自动配置类,里面有一个配置静态资源映射的方法: @Override public void addResourceHandlers(Resource ...
- java框架之SpringBoot(5)-SpringMVC的自动配置
本篇文章内容详细可参考官方文档第 29 节. SpringMVC介绍 SpringBoot 非常适合 Web 应用程序开发.可以使用嵌入式 Tomcat,Jetty,Undertow 或 Netty ...
- java框架之SpringBoot(14)-任务
使用 maven 创建 SpringBoot 项目,引入 Web 场景启动器. 异步任务 1.编写异步服务类,注册到 IoC 容器: package zze.springboot.task.servi ...
- java框架之SpringBoot(15)-安全及整合SpringSecurity
SpringSecurity介绍 Spring Security 是针对 Spring 项目的安全框架,也是 Spring Boot 底层安全模块默认的技术选型.它可以实现强大的 Web 安全控制.对 ...
- java框架之SpringBoot(16)-分布式及整合Dubbo
前言 分布式应用 在分布式系统中,国内常用 Zookeeper + Dubbo 组合,而 SpringBoot 推荐使用 Spring 提供的分布式一站式解决方案 Spring + SpringBoo ...
- 【java框架】SpringBoot(5)--SpringBoot整合分布式Dubbo+Zookeeper
1.理论概述 1.1.分布式 分布式系统是若干独立计算机的集合,这些计算机对于用户来讲就像单个系统. 由多个系统集成成一个整体,提供多个功能,组合成一个板块,用户在使用上看起来是一个服务.(比如淘宝网 ...
- java框架之SpringBoot(2)-配置
规范 SpringBoot 使用一个全局的配置文件,配置文件名固定为 application.properties 或 application.yml .比如我们要配置程序启动使用的端口号,如下: s ...
随机推荐
- [druid]大数据挑战——如何使用Druid实现数据聚合
-- 知道你为什么惧组件很多的一些开源软件? 因为缺乏阅读能力. 最近我接手了druid+kafka+elk一套等日志系统. 但是我对druid很陌生, 周旋了几天, 官网文档快速开始照着做了下. 看 ...
- python环境与PyDev IDE配置
工具eclipse:我目前用是的Eclipse oxygen.历史版本可参考:https://wiki.eclipse.org/Older_Versions_Of_EclipsePython:http ...
- atom汉化
Atom 是 Github 专门为程序员推出的一个跨平台文本编辑器. 推荐一下 Atom官方网站https://atom.io/ GitHub 以后肯定会通过官方模块把 Atom 和 GitHub 进 ...
- Ubuntu apt 使用代理
. . . . . 由于一台 Ubuntu 主机无法直接连接外网,但同内网的另一台 Windows 主机可以连接外网,于是在 Win 主机上搭建了代理服务,使 Ubuntu 主机通过代理更新系统. 记 ...
- 用反卷积(Deconvnet)可视化理解卷积神经网络还有使用tensorboard
『cs231n』卷积神经网络的可视化与进一步理解 深度学习小白——卷积神经网络可视化(二) TensorBoard--TensorFlow可视化 原文地址:http://blog.csdn.net/h ...
- 通过灰度线性映射增强图像对比度实现PS中的色阶
通过灰度线性映射增强图像对比度 Halcon中如何通过灰度线性映射增强图片对比度呢?不急,我先讲点其他的. 如果你用过Photoshop,那么想必对增强图像的对比度很熟悉.在Photoshop中,我们 ...
- netMarketing类库: 类库说明
这个类库是作者工作中使用的私人类库,本类库适用于自动化行业的软件工程师使用.如果大家在使用中有任何疑问和建议欢迎联系作者, 或者在页面留言. (一) 引用类库 本类库的环境为.net framewor ...
- 02Spark的左连接
两个文件,一个是用户的数据,一个是交易的数据. 用户: 交易: 流程如下: 分为以下几个步骤: (1)分别读取user文件和transform文件,并转为两个RDD. * (2)对上面两个RDD执行m ...
- JAVA并发理论与实践
JDK5.0中更灵活.更具可伸缩性的锁定机制 流行的原子 非阻塞算法简介
- Linux 环境变量_006
***Linux 环境变量指系统运行程序或命令的能快速找到其位置等其它功能,不用输入复杂命令.以$PATH环境变量为例子, $PATH决定了shell指定寻找命令或程序的路径,比较执行ls命令,如果没 ...