Java工程师必会技术自检清单
- 0 前言
- 1 Java web方面
- 1.1 全局异常处理
- 1.2 拦截器写法
- 1.3 自定义注解和切面写法
- 1.4 线程池的使用
- 1.5 跨域过滤器
- 1.6 文件上传与下载
- 1.7 WebSocket基本使用
- 1.8 图片转Base64编码
- 1.9 JSON转化
- 1.10 XML转化
- 1.11 自定义Starter
- 2 消息中间件
- 2.1 Rabbit MQ
- 2.1.1 AMQP协议
- 2.1.2 消息100%投递
- 2.1.3 死信队列
- 2.2 Kafka
- 2.2.1 Kafka与Spring整合
- 2.2.2 Kafka配置详解
- 3 缓存中间件
- 3.1 本地缓存EhCache
- 3.2 分布式缓存Redis
- 3.3 缓存失效及解决方案
- 4 ES基础知识
- 4.1 ELK系统搭建
- 4.2 ES基本增删改查
- 5 数据库基础
- 5.1 主从复制
- 5.2 分库分表
- 5.3 SQL调优
- 6 JVM基础
- 7 前端基础
- 7.1 原生JS发送ajax
0 前言
俗话说:“吾日三省吾身”,经常性地自检有助于查漏补缺,逐步提升技术水平。Java开发所要面对的技术多如牛毛,没有一份明确的清单很难去排查。为解决以上难题,我根据常用和实用两点原则,总结如下技术清单。
愿大家技术进步,与君共勉。
1 Java web方面
1.1 全局异常处理
主要注意的点是http响应码,这涉及到“锅由谁来背”。众所周知,500响应码是系统内部错误。很不幸的是,全局异常处理默认的响应码就是500。当然,锅能不背就不背。大胆的将ResponseStatus设成HttpStatus.OK之后,你会发现大部分请求都回归了200响应码。
当然,错误信息还是要传递出去的。通过在返回JSON添加returnCode字段,就可以将异常信息与正常信息区分出来。
核心代码如下所示:
/**
* 跟定义顺序无关,只是优先处理具体异常
* @author yiqiang
* @date 2020/8/7 10:31
*/
@ControllerAdvice
@Slf4j
@ResponseBody
public class GlobalExceptionHandler {
private static final String COMMON_EXCEPTION="COMMON EXCEPTION: ";
private static final String SERIOUS_EXCEPTION="SERIOUS_EXCEPTION: ";
private static final String UNKNOWN_EXCEPTION="UNKNOWN_EXCEPTION: ";
@ExceptionHandler(value = {CommonException.class})
@ResponseStatus(value = HttpStatus.OK)
public String commonHandler(CommonException e) throws JsonProcessingException {
log.error(e.getMessage());
e.printStackTrace();
return ReturnDTO.failToJson(COMMON_EXCEPTION + e.getMessage(),null);
}
@ExceptionHandler(value = {SeriousException.class})
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
public String seriousHandler(SeriousException e) throws JsonProcessingException {
log.error(e.getMessage());
e.printStackTrace();
return ReturnDTO.failToJson(SERIOUS_EXCEPTION + e.getMessage(),null);
}
@ExceptionHandler(value = {Exception.class})
@ResponseStatus(value = HttpStatus.OK)
public String unknownHandler(Exception e) throws JsonProcessingException {
log.error(e.getMessage());
e.printStackTrace();
return ReturnDTO.failToJson(UNKNOWN_EXCEPTION + "PLEASE CONNECT TO MANAGER",null);
}
}
个人强烈建议,不要将异常堆栈信息直接返回给前端,而应该返回自定义的“帮助信息”。毕竟,返回堆栈信息一方面不安全;另一方面,返回的信息对不是程序员的客户没有任何帮助。
具体做法也很简单:实现建立异常枚举类和自定义异常,直接抛出自定义异常即可。
1.2 拦截器写法
拦截器对于绝大多数Java工程师而言,也是必备技能。废话不多说,步骤如下。
- 继承HandlerInterceptor,并作为component传递给Spring容器
@Component
@Slf4j
public class PtzRoleInterceptor implements HandlerInterceptor{
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
log.info("PtzRoleInterceptor 成功拦截 ==> [{}]",httpServletRequest.getRequestURL());
String roleIds = httpServletRequest.getHeader("roleIds");
log.info("roleIds {}",roleIds);
if(roleIds==null){
log.info("roleIds为空");
}
boolean role=true;
if(!StringUtils.isEmpty(roleIds)){
for (String roleId : roleIds.split(",")) {
if (ConstantUtil.PTZ_CONTROLLER_ROLE_ID.equals(roleId)) {
role = false;
}
}
}
if(role){
//信息返回
httpServletResponse.setCharacterEncoding("UTF-8");
httpServletResponse.setContentType("text/html;charset=utf-8");
try(PrintWriter writer = httpServletResponse.getWriter()){
JSONObject jsonObject=new JSONObject();
jsonObject.put("returnCode",1);
jsonObject.put("returnMsg","权限不足.如有需要,请联系配置管理员");
writer.print(JSONObject.toJSONString(jsonObject,true));
}catch (Exception e){
e.printStackTrace();
log.error("出现异常"+e.getMessage());
}
log.info("PtzRoleInterceptor 校验失败 ==> [{}],当前用户角色ID为{}",httpServletRequest.getRequestURL(),roleIds);
return false;
}else {
log.info("PtzRoleInterceptor 校验成功 ==> [{}],当前用户角色ID为{}",httpServletRequest.getRequestURL(),roleIds);
return true;
}
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
}
- 对拦截器进行配置
/**
* @author yiqiang
* @date 2020/6/19 15:19
*/
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
@Resource
private PtzRoleInterceptor ptzRoleInterceptor;
@Resource
private PtzControlInterceptor ptzControlInterceptor;
@Override
public void addInterceptors(InterceptorRegistry interceptorRegistry) {
interceptorRegistry.addInterceptor(ptzRoleInterceptor).
addPathPatterns("/realTimeStream/lockChannel",
"/realTimeStream/ptzCtrl",
"/realTimeStream/showLockChannel",
"/realTimeStream/unlockChannel",
"/realTimeStream/seizeChannel");
interceptorRegistry.addInterceptor(ptzControlInterceptor).
addPathPatterns("/realTimeStream/ptzCtrl");
}
}
1.3 自定义注解和切面写法
自定义注解类
import java.lang.annotation.*;
@Inherited
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Timeout {
long value() default 1000;
}
切面类的编写
/**
* @author yiqiang
* @date 2020/8/12 13:38
*/
@Aspect
@Slf4j
@Component
public class TimeoutAspect {
@Pointcut(value = "@annotation(com.gosun.pcms.aop.Timeout)")
public void pointCut() {
}
@Around(value = "pointCut()")
public Object timeOut(ProceedingJoinPoint joinPoint) throws Exception {
log.info("正在进入切面");
Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
Timeout timeOut = method.getDeclaredAnnotation(Timeout.class);
int i = 0;
ExecutorService executor = Executors.newCachedThreadPool();
Future future = executor.submit(() -> {
log.info("正在执行");
try {
return joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
return ReturnDTO.fail("操作失败");
}
});
long t = System.currentTimeMillis();
while (true) {
Thread.sleep(1000);
log.info("第{}次执行", i++);
if (System.currentTimeMillis() - t >= timeOut.value()) {
return ReturnDTO.fail("超时,TimeOut:"+timeOut.value());
}
if (future.isDone()) {
return future.get();
}
}
}
}
1.4 线程池的使用
声明executor,然后使用executor的submit或者execute方法创建线程。前者针对callable接口,后者针对runable接口。
ExecutorService executor = Executors.newCachedThreadPool();
Future future = executor.submit(() -> {
//do something
});
if(future.isDone()){
log.info(future.get())
}
1.5 跨域过滤器
偶尔会遇到跨域问题,只要在启动类添加如下配置即可
private CorsConfiguration buildConfig() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedOrigin("*");
corsConfiguration.addAllowedHeader("*");
corsConfiguration.addAllowedMethod("*");
return corsConfiguration;
}
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", buildConfig());
return new CorsFilter(source);
}
1.6 文件上传与下载
1.7 WebSocket基本使用
1.8 图片转Base64编码
1.9 JSON转化
1.10 XML转化
1.11 自定义Starter
2 消息中间件
2.1 Rabbit MQ
2.1.1 AMQP协议
2.1.2 消息100%投递
2.1.3 死信队列
2.2 Kafka
2.2.1 Kafka与Spring整合
2.2.2 Kafka配置详解
3 缓存中间件
3.1 本地缓存EhCache
3.2 分布式缓存Redis
3.3 缓存失效及解决方案
4 ES基础知识
4.1 ELK系统搭建
4.2 ES基本增删改查
5 数据库基础
5.1 主从复制
5.2 分库分表
5.3 SQL调优
6 JVM基础
7 前端基础
7.1 原生JS发送ajax
<script>
var xmlHttp;
if(window.XMLHttpRequest){
xmlHttp = new XMLHttpRequest();
}else{
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
request.open("GET", "http://localhost:8080/test",true);
request.send(null);
request.onreadystatechange = function() {
if (request.readyState==4) {
if (request.status==200) {
var data = JSON.parse(request.responseText);
if (data.success) {
document.getElementById("XXX").innerHTML = data.msg;
} else {
document.getElementById("XXX").innerHTML = "出现错误:" + data.msg;
}
} else {
alert("发生错误:" + request.status);
}
}
}
</script>