Java面试题精选—Spring 统一功能处理

精选2020年大厂高频Java面试真题集锦(含答案),面试一路开挂 本文涵盖了阿里巴巴、腾讯、字节跳动、京东、华为等大厂的Java面试真题,不管你是要面试大厂还是普通的互联网公司,这些面试题对你肯定是有帮助的,毕竟大厂一定是行业的发展方向标杆,很多公司的面试官同样会研究大厂的面试题。 与此同时,今年算法面试一定是会被问的,而算法不是光靠背面试题就有用的,它是需要数学逻辑思维的,因此,小编会在文末为大家准备一份非常优质的算法学习手册,重点在于学习思维方法,话不多说,直接开始上精选的大厂面试真题! 由于文章篇幅限制,不可能将所有面试题以文字形式把大厂面试题展示出来,本篇为 阅读详情

本篇文章我们来讲解如何使用 Spring 拦截器来处理项目中常用的统一功能处理:

  • 统一用户登录权限验证

  • 统一异常处理

  • 统一数据格式返回

这时有人可能反问到,不是可以用Spring AOP来对这些功能进行处理吗?那我还要去学拦截器干嘛?

还是一句话:“存在即合理”,Spring拦截器的存在能弥补一些Spring AOP的不足,且听我细细道来。

1. 用户登录权限验证

这里我将分别使用三种方式实现这个登录权限验证的功能,从而引出为什么要使用 Spring 拦截器。

1.1 最原始的登录验证

在学习Spring AOP之前,也就是Servlet的时代我们会在所有需要登录验证的路由方法下添加登录验证的业务:

HttpSession session = req.getSession(false);
if (session == null) {
    resp.setContentType("text/html;charset=utf-8");
    resp.getWriter().write("用户未登陆,请登录!!");
    return;
}
User user = (User) session.getAttribute("user");
if (user == null) {
    resp.setContentType("text/html;charset=utf-8");
    resp.getWriter().write("用户未登陆,请登录!!");
    return;
}

1.2 Spring AOP实现

学习了Spring AOP后,我们就想着使用切面来消除冗余代码。

编写UserController类,类中目前有两个方法login()和getall()

@RestController
@RequestMapping("/user")
public class UserController {

    @RequestMapping("/login")
    public String login(HttpServletRequest request, String username) {
        //验证账密的伪代码
        System.out.println("进行账号密码验证");
        //存储session
        HttpSession session = request.getSession(true);
        session.setAttribute("user", username);
        return "登录成功";
    }

    @RequestMapping("/getall")
    public String getAll() {
        return "查询用户信息";
    }
}

编写Aspect类:

@Component
@Aspect
public class LoginAspect {
    @Pointcut("execution(* com.chenshu.intercept_demo.controller..*.*(..))")
    private void myPointcut(){};

    @Around("myPointcut()")
    public String loginValidate(ProceedingJoinPoint joinPoint) throws Throwable {
        HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
        HttpSession session = request.getSession(false);
        if (session == null) {
            return "用户未登陆,请登录!!";
        }
        String username = (String) session.getAttribute("user");
        if (username == null) {
            return "用户未登陆,请登录!!";
        }
        return (String) joinPoint.proceed();
    }
}

第一个弊端: 获取Session的过程中我们发现,需要使用九牛二虎之力才能成功在切面中取到HttpServletRequest对象。

首先通过RequestContextHolder.getRequestAttributes()方法获取到当前线程的请求上下文,然后通过强制类型转换为ServletRequestAttributes类型,再通过getRequest()方法获取到HttpServletRequest对象。

HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();

第二个弊端: 一般情况下Usercontroller类中会存放所有用户相关的业务,有些业务可能不需要身份权限验证,比如登录、注册业务,下面的登录业务中就遇到了这种状况:

图片

这种状况有两种解决方案,一种是编写更复杂的切点表达式,一种是将不需要登录权限验证的业务另外分包。

这里我就使用分包来解决上面的问题:

图片

validate包下的UserController1类:

@RestController
@RequestMapping("/user1")
public class UserController1 {

    @RequestMapping("/getall")
    public String getAll() {
        return "查询用户信息";
    }
}

novalidate包下的UserController2类:

@RestController
@RequestMapping("user2")
public class UserController2 {
    @RequestMapping("/login")
    public String login(HttpServletRequest request, String username) {
        //伪代码
        System.out.println("进行账号密码验证");
        //存储session
        HttpSession session = request.getSession(true);
        session.setAttribute("user", username);
        return "登录成功";
    }

}

修改切点表达式:只对validate包内的所有类进行身份权限验证

@Pointcut("execution(* com.chenshu.intercept_demo.controller.validate.*.*(..))")
private void myPointcut(){};

此时问题就得以解决了:用户成功在登录后查询到用户信息

图片

一顿操作下来问题虽然解决了,但是想必大家写完不禁感慨道:这也太麻烦了吧!

别担心,你遇到的问题前人一定早就遇到了,官方给出了解决方案 —— Spring 拦截器

1.3 Spring 拦截器

对于以上问题,Spring中提供了具体的实现拦截器:HandlerInterceptor,对比与Spring AOP,拦截器在Spring Web项目中可以更方便使用请求和响应的包装类,并且可以更方便地配置拦截规则,的如果用户访问的路由中有拦截器的话,就会先走拦截器,拦截器会决定是否放行给Controller:

图片

拦截器的实现分为以下两个步骤:

  • 创建自定义拦截器:实现HandlerInterceptor接口并重写preHandle(执行具体方法之前的预处理)方法。

  • 配置拦截器并添加拦截规则:将自定义拦截器加入WebMvcConfigurer的addInterceptors方法中。

接下来我将对拦截器的实现进行代码演示:

1、先把刚刚在controller包中拆分的UserController还原回来,并稍作修改:

@RestController
@RequestMapping("/user")
public class UserController {

    @RequestMapping("/login")
    public String login(HttpServletRequest request, String username, String password) {
        //1. 非空判断,这里直接使用Spring提供的工具方法来实现
        if (StringUtils.hasLength(username) && StringUtils.hasLength(password)) {
            //2. 验证密码是否正确
            if ("admin".equals(username) && "admin".equals(password)) {
                HttpSession session = request.getSession(true);
                session.setAttribute("user", "admin");
                return "登录成功!";
            } else {
                return "用户名或密码输入错误,登录失败!";
            }
        }
        return "用户名或密码为空,请重新输入!";
    }
    
    @RequestMapping("/reg")
    public String reg() {
        return "用户进行注册操作!";
    }

    @RequestMapping("/getall")
    public String getAll() {
        return "查询用户信息成功!";
    }
}
1.3.1 创建自定义拦截器
  • 新建一个interceptors包

  • 在interceptors包中创建LoginInterceptor类(自行命名),并实现HandlerInterceptor接口(成为拦截器)

  • 重写preHandle(),在其中编写用户登录权限验证的功能

  • 如果preHandle()方法返回true就继续执行后续业务,返回false则相反

public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session = request.getSession(false);
        if (session != null && session.getAttribute("user") != null) {
            return true;
        }
        response.setStatus(401);
        return false;
    }
}
1.3.2 配置拦截器并添加拦截规则:
  • 创建一个config包

  • 在config包中创建一个InterceptorConfig类(自行命名),并实现WebMvcConfigurer接口(成为Mvc配置类)

  • 添加五大类注解,使其在项目启动时就生效,这里使用@Configuration

  • 重写addInterceptors(),在其中使用registry对象的addInterceptor的方法将自定义拦截器加入到系统配置信息中

  • 添加拦截规则:通过addInterceptor()返回的对象调用addPathPatterns()拦截规则

  • 排除拦截规则:通过addPathPatterns()返回的对象调用excludePathPatterns()排除拦截规则

@Configuration
public class MyMvcConfigurer implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor())
                .addPathPatterns("/**") // 拦截所有请求
                .excludePathPatterns("/user/login") //排除不拦截的路径
                .excludePathPatterns("/user/reg"); // 排除不拦截的路径
    }
}
1.3.3 【引入】拦截规则的通配符
  • /*: 匹配单个路径级别,如/admin、/user、/product

  • /**: 匹配多个路径级别,包括子路径,如/admin、/admin/user、/admin/user/edit

  • /* + 文件后缀名: 某路径下所有某后缀名的文件,如/*.html

运行并测试:

成功拦截user/getall,并将状态码设置为401:

图片

不拦截user/reg,执行到UserController业务代码:

图片

不拦截user/login,执行到UserController业务代码:

图片

1.3.4 拦截器源码分析

项目启动后服务器接收到第一个HTTP请求时,可以在console中查看到初始化DispatcherServlet(前端控制器)的日志信息:

2024-04-22 00:41:58.898  INFO 45311 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2024-04-22 00:41:58.898  INFO 45311 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'

DispatcherServlet的解释:当客户端发送一个HTTP请求到服务器时,请求首先到达 Servlet 容器。然后会到达DispatcherServlet(前端控制器),它是一个Servlet,负责拦截所有的请求并将请求分发给相应的Controller。

而所有请求到达DispatcherServlet的时候,会调用一个doDispatch()方法,它的源码如下(重点看标注了注解的地方):

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;
    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

    try {
        try {
            ModelAndView mv = null;
            Exception dispatchException = null;

            try {
                processedRequest = this.checkMultipart(request);
                multipartRequestParsed = processedRequest != request;
                mappedHandler = this.getHandler(processedRequest);
                if (mappedHandler == null) {
                    this.noHandlerFound(processedRequest, response);
                    return;
                }
                //获取HandlerAdapter执行处理器,这是用来执行Controller中的业务的
                HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
                String method = request.getMethod();
                boolean isGet = HttpMethod.GET.matches(method);
                if (isGet || HttpMethod.HEAD.matches(method)) {
                    long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                    if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
                        return;
                    }
                }
                //调用预处理
                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }
                //通过HandlerAdapter执行Controller中的业务
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }
                //调用后处理
                this.applyDefaultViewName(processedRequest, mv);
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            } catch (Exception var20) {
                dispatchException = var20;
            } catch (Throwable var21) {
                dispatchException = new NestedServletException("Handler dispatch failed", var21);
            }

            this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
        } catch (Exception var22) {
            this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
        } catch (Throwable var23) {
            this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
        }

    } finally {
        if (asyncManager.isConcurrentHandlingStarted()) {
            if (mappedHandler != null) {
                mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
            }
        } else if (multipartRequestParsed) {
            this.cleanupMultipart(processedRequest);
        }

    }
}

提取重点内容:

//调用预处理
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
    return;
}
//通过HandlerAdapter执行Controller中的业务
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
    return;
}
//调用后处理
this.applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);

根据源码我们发现在预处理的时候,如果返回的值为false就直接就直接return,不继续走Controller层的业务;返回的值为true之后,才会通过HandlerAdapter执行Controller中的业务。

点进applyPreHandle(processedRequest, response)方法的源码中查看一下它做了什么事:

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
    for(int i = 0; i < this.interceptorList.size(); this.interceptorIndex = i++) {
        HandlerInterceptor interceptor = (HandlerInterceptor)this.interceptorList.get(i);
        if (!interceptor.preHandle(request, response, this.handler)) {
            this.triggerAfterCompletion(request, response, (Exception)null);
            return false;
        }
    }

    return true;
}

这个方法位于HandlerExecutionChain这个类中,拉到最上面,可以看到该类有一个属性interceptorList,也就是拦截器列表,我们在WebMvcConfigurer中添加的拦截器都在这里面:

private final List<HandlerInterceptor> interceptorList;

然后我们再回头看applyPreHandle(processedRequest, response)方法,就能知道它到底做了什么事了:无非就是遍历拦截器列表,并且调用它们的preHandle()方法,只要有一个拦截器的preHandle()方法返回false,那就都别玩,直接返回false给到doDispatcher()方法中,不继续执行Controller层的代码。

2. 统一异常处理

Spring 框架的统一异常处理是基于 Spring MVC 模块的特性来实现的。

2.1 统一异常处理是什么

统一异常处理是通过@ControllerAdvice+@ExceptionHandler这两个注解来实现的。

  • @ControllerAdvice 注解: @ControllerAdvice 注解是 Spring MVC 提供的一种特殊类型的控制器,它可以用于定义全局的控制器通知。被 @ControllerAdvice 注解标记的类中的方法可以拦截应用程序中抛出的异常。

  • @ExceptionHandler 注解: @ExceptionHandler 注解用于标记方法,指示该方法应该处理特定类型的异常。当被标记的方法抛出对应类型的异常时,Spring MVC 将调用该方法来处理异常,并返回适当的响应。

2.2 统一异常处理的使用

在讲解使用前,我先举个例子来描述一下为什么要使用统一异常处理。

在/reg路由中构造一个算数异常:

@RequestMapping("/reg")
public String reg() {
    int ret = 10/0;
    return "用户进行注册操作!";
}

当我们在浏览器中访问该路由时,发现状态码直接变成401,并显示该页面不存在,这样太过于暴力,对于程序猿来说倒还好,但是用户看到这么一串信息直接就懵逼了:

图片

但是通过统一异常的处理,在后端程序抛异常的时候,@ControllerAdvice标注的类中的方法可以拦截应用程序中抛出的异常;该类中的方法又需要使用一个@ExceptionHandler 注解来对相应的异常进行处理,在方法中,后端程序猿可以返回自定义的错误响应(如错误码+错误信息)给前端,前端的程序猿就可以合理地根据响应,将合适的页面展现给用户。

扯了这么多,接下来我就来演示下如何实现统一异常的处理,并返回自定义的错误响应给前端:

1、首先在interceptors包新建了一个ExceptionAdvice类,并在类上添加了一个@ControllerAdvice使该类中的方法可以拦截到异常,然后又在类上添加了一个@ResponseBody的注解表示返回非视图对象:

@ResponseBody
@ControllerAdvice
public class ExceptionAdvice {
    
}

2、编写方法,并在方法上添加@ExceptionHandler注解标识拦截哪种异常:

@ControllerAdvice
public class ExceptionAdvice {
    @ExceptionHandler(Exception.class)
    public HashMap<String, Object> ExceptionHandler() {
        HashMap<String, Object> retMap = new HashMap<>();
        return retMap;
    }
}

由于我想返回一个自定义的Json对象给前端,因此上面的代码中我返回一个Map对象,由Spring MVC来帮我自动转换为Json对象并返回。

3、此时我们需要往map对象中添加你想要返回的键值对,这里我想返回一个-1错误码以及异常信息给前端:

@ControllerAdvice
public class ExceptionAdvice {
    @ExceptionHandler(Exception.class)
    public HashMap<String, Object> ExceptionHandler(Exception e) {
        HashMap<String, Object> retMap = new HashMap<>();
        retMap.put("code", "-1");
        retMap.put("msg", e.getMessage());
        return retMap;
    }
}

由于我想要在返回的Json对象中添加异常信息的键值对,而@ExceptionHandler注解修饰的方法可以直接在参数中获取到异常对象,这里我直接通过异常对象获取异常信息并返回。

此时再通过浏览器访问后端,发现显示了下面信息:

图片

2.3 粒度细化

前面说过@ExceptionHandler 标记的方法会处理特定类型的异常。

因此可以再编写一个方法并在该注解的参数中传入一个ArithmeticException.class,从而使拦截的异常粒度细化:

这里将错误码设为-2,表示算数异常的错误码:

@ExceptionHandler(ArithmeticException.class)
public HashMap<String, Object> ArithmeticExceptionHandler(ArithmeticException e) {
    HashMap<String, Object> retMap = new HashMap<>();
    retMap.put("code", "-2");
    retMap.put("msg", e.getMessage());
    return retMap;
}

测试发现,这次算数异常的处理优先被刚刚编写的方法所拦截:

图片

在构造一个空指针异常来测试:

@RequestMapping("/reg")
public String reg() {
    Object obj = null;
    obj.hashCode();
    return "用户进行注册操作!";
}

此时不是算数异常,就被最初写的ExceptionHandler方法拦截了

图片

3. 统一数据返回格式

为什么要统一数据返回格式呢?其实和异常处理一样,是为了降低和前端程序猿的沟通成本,后端程序猿将所有响应都分装成一个固定格式的Json对象,前端程序猿可以通过Json对象中的状态码来辨别是正常的响应还是异常的响应。

统一异常处理是通过@ControllerAdvice注解 + 实现ResponseBodyAdvice接口来实现的

  • @ControllerAdvice注解就不多说了,统一异常处理中已经提到

  • 编写 ResponseBodyAdvice 实现类:创建一个实现 ResponseBodyAdvice 接口的类,该类可以在响应返回给客户端之前进行处理。

3.1 实现统一数据返回格式处理

前面我们通过统一的异常处理,将异常的响应信息的状态码设为负数,这里我们就可以将正常响应信息的状态码设为正数,并且在Json对象中新增一个body的属性来填写响应的具体内容。

1、在interceptors包中新增一个ResponseAdvice类,给该类添加@ControllerAdvice注解并且该类需实现ResponseBodyAdvice接口

注意事项:实现ResponseBodyAdvice接口后默认返回非视图对象,因此这里不需要添加@ResponseBody注解

@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
    
}

2、必须实现接口中的support()和beforeBodyWrite()方法

  • support()方法: 用于确定Controller层返回的类型是否需要后续的beforeBodyWrite()对其进行封装,如果返回true,就继续走beforeBodyWrite()方法,返回false,则直接返回Controller层原生的数据格式

  • beforeBodyWrite()方法: 在这里可以编写你具体要讲响应分装成什么形式的逻辑代码。里面有一个body参数,是用来获取到Controller中返回的对象的。

了解了两个方法是做什么的就可以开始编写代码了:

@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {

    //对所有请求都进行封装
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }

    //处理统一数据返回格式的逻辑
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        HashMap<String, Object> retMap = new HashMap<>();
        retMap.put("code", "200");
        retMap.put("msg", "");
        retMap.put("body", body);
        return retMap;
    }
}

3.2 【引入】响应嵌套问题

当我们编写完代码,满心欢喜的进行测试/reg路由后,发现前面统一异常处理的错误响应信息被嵌套在统一数据返回格式的响应的body属性中了:

图片

出现这种结果显然不符合我们的预期,这时supports()方法就派上用场了,修改方法:

//只封装非HashMap对象
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
    return !returnType.getParameterType().equals(HashMap.class);
}

在写程序前,我们可以提前约定好,将所有响应信息都使用HashMap进行返回,这样我们就可以使用上面的代码,在封装响应前先判断是否为已经封装好的响应信息,如果是,就不继续嵌套封装。

成功解决响应嵌套问题:

图片

3.3 【引入】Controller层返回String时的异常

此时我们在测试一下,如果Controller层返回的类型非HashMap,会将响应封装成什么样式:

访问/login路由,我们惊奇的发现返回的是错误响应信息,并且错误信息是"java.util.HashMap cannot be cast to java.lang.String"

图片

这个问题在初次遇到的时候困扰了我很久,这是怎么回事呢?原因如下:

在Controller中添加了@ResponseBody之后,Spring就会根据返回的类型选择合适的消息转化器进行转换。

在前面的Spring MVC中讲过返回String本质上是返回一个html页面,而不是一个Json对象。如果要返回一个Json对象的话,会使用内置的Jackson框架进行处理。

由于返回String比较特殊,当Controller层返回一个String的时候,就会使用StringHttpMessageConverter这个消息转化器进行处理,但是我们进行统一数据返回格式处理的业务后,在将响应返回给前端之前,会对Controller进行拦截,然后将其分装成一个HashMap对象后返回,返回响应后,StringHttpMessageConverter一看,咋返回的不是个String,然后就将HashMap对象进行强行转换为String,于是就抛出了"java.util.HashMap cannot be cast to java.lang.String"的异常。

解决方案:修改beforeBodyWrite()方法,当body为String的时候,自行调用Jackson的 ObjectMapper对象进行特殊处理,并返回给前端。

//处理统一数据返回格式的逻辑
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
    HashMap<String, Object> retMap = new HashMap<>();
    retMap.put("code", "200");
    retMap.put("msg", "");
    retMap.put("body", body);

    if (body instanceof String) {
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            return objectMapper.writeValueAsString(retMap);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }
    return retMap;
}

成功获取到封装好的响应信息:

图片

4. 总结

本篇文章中讲解了三种统一功能的实现:用户登录权限验证、统一异常处理、统一数据返回格式。

对上面三种统一功能详细讲解了:

  • 如何使用拦截器实现用户登录权限验证

  • 通过Spring MVC中@ControllerAdvice和@ExceptionHandler实现统一异常处理

  • 通过@ControllerAdvice和ResponseBodyAdvice接口实现统一数据返回格式的处理

面试题精选 singletonObjects:一级缓存,存放完全初始化好的 Bean 的集合,从这个集合中取出来的 Bean 可以立马返回 earlySingletonObjects:二级缓存,存放创建好但没有初始化属性的 Bean 的集合,它用来解决循环依赖 singletonFactories:三级缓存,存放单实例 Bean 工厂的集合 singletonsCurrentlyInCreation:存放正在被创建的 Bean 的集合。关联创建时,检查“正在被创建的 Bean”中是否有即将注入的 Bean。 阅读详情

相关推荐

银河麒麟V10SP1系统盘瘦身指南:如何制作一个只含必需软件的轻量级安装镜像

本文详细介绍了银河麒麟V10SP1系统盘瘦身的完整流程,从依赖分析到最小化镜像构建,帮助用户创建只含必需软件的轻量级安装镜像。通过智能包筛选、KickStart定制和性能优化,显著减少ISO体积和部署时间,适用于服务器批量部署等场景。

weixin_26765287的博客 404

springboot3整合SpringSecurity实现登录校验与权限认证(万字超详细讲解)

用户提交登录请求Spring Security 将请求交给 UsernamePasswordAuthenticationFilter 过滤器处理。UsernamePasswordAuthenticationFilter 获取请求中的用户名和密码,并生成一个 AuthenticationToken 对象,将其交给 AuthenticationManager 进行认证。

2301_78646673的博客 3万+

RAGFlow源码解析-2、从源码构架ragflow v0.25.0镜像

本文详细记录了从零开始构建自定义RAGFlow镜像lt/ragflow:0.25.0的全过程。首先介绍了环境准备,包括检查Node.js、Docker版本和项目结构。然后详细说明了构建步骤:编译前端、创建自定义Dockerfile、构建镜像和验证。接着讲解了docker-compose配置修改要点,包括网络定义、MySQL依赖处理等。最后总结了部署过程中遇到的五个关键问题及解决方案,包括networks未定义、MySQL服务依赖、tiktoken文件缺失、数据库配置错误和端口映射问题。通过完整的操作流程和问

liutao841204的专栏 364

Springboot整合shiro:实现用户登录和权限验证

Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。当然类型大家也可以使用spring security;因为我平时开发的项目都是中小型的,所以使用shiro对于业务来说已经够用了,那么下面是我整理的整合记录; 一、什么是Shiro 这里大家需要先认识一下它的三个重要的组件; 1.1、Subject 即当前的操作用户,就是当前登录的用户; 1.2、SecurityManager 该组件是用来管理所有的操作用户的安全操作 1.3、Realm 该组件需要我们自定义,主

猪大肠的博客 7276

AI一键生成带货短视频:电商直播的未来

随着电子商务行业的快速发展,直播带货已成为一种极为有效的营销手段。然而,传统的人工直播方式存在诸多局限性,如人力成本高、主播培训周期长等问题。为了解决这些挑战,一种基于AI技术的一键生成带货视频解决方案应运而生——这就是“Anchor Crafter”。这项创新技术不仅能够自动创建高质量的带货视频,而且还能实现人物与商品之间的自然交互,极大地提升了用户体验。

这里汇聚了前沿的技术分享与实用的开发技巧,带你探索从创意到企业的技术创业之路。 2204

三、参考比较法之城市建成区提取——提取

在正式开始提取之前,还是要继续啰嗦一下,其实基于夜间灯光数据建成区提取,可以分为好几个层次,基于县一级、市一级、省一级,很多论文的做法都是以市一级为准,也就是统计好市一级城市的每一年的统计年鉴中的建成区面积作为备用。有人可能会问为什么不以县一级,由于县一级的统计数据一般存在缺失,很难形成一个长时间序列的数据,所以一般采取的是市一级。前文也进行建议,如果确实要基于县一级进行提取可以参考前面理论介绍那篇文章中的相关建议。那么当我们确定阈值后如何将城市建成区提取出来?

端木宛白的博客家园 5401

15. Spring 统一功能处理

本篇文章中讲解了三种统一功能的实现:用户登录权限验证、统一异常处理、统一数据返回格式;引出了拦截器的使用方法以及如何通过注解来实现响应信息的封装。

干脆面la的博客 1091

C语言设计模式全面解析:从理论到28个实战示例

本文系统介绍了23种经典设计模式在C语言中的实现方法。通过结构体、函数指针等技巧,C语言可以优雅地模拟面向对象特性,实现代码复用与解耦。文章分类详解了创建型、结构型、行为型三大类模式,并提供了28个完整代码示例,包括单例、策略、观察者等核心模式的精简实现。这些模式在嵌入式系统、操作系统内核等场景中广泛应用,如状态机、事件驱动等。学习C语言设计模式的关键在于理解模式意图,掌握函数指针和结构体组合等技巧,并在实际项目中灵活运用。本文为C语言开发者提供了全面的设计模式参考指南。

数字人生 364

解决springmvc因版本造成的路径通配问题

Invalid mapping pattern detected: /**/swagger-ui/** No more pattern data allowed after {*...} or ** pattern element 解决路径通配问题

SuperLvMoTong的博客 5814

高通蓝牙学习-- 关于Android 蓝牙的介绍

本文详细介绍了AOSP蓝牙架构及高通QCA芯片的蓝牙功能实现方案。主要内容包括:1)AOSP蓝牙架构在不同Android版本中的层次结构,重点分析BTHIDL层和libbt-vendor关键组件;2)QCA芯片的引导流程说明;3)两种获取libbt-vendor源码的方式(Google官方源和国内镜像源);4)针对QCA6595/6696/66x8/67x7芯片的具体实现方案,包括固件文件定义、厂商事件处理和补丁下载流程;5)蓝牙调试方法,包括logcat日志分析、HSU和Ellisys硬件跟踪技术。文章为

专业开发者的博客,一个从事Android WiFi和蓝牙的工程师 495

《囚徒健身》 六艺,十式

文章目录《囚徒健身》 六艺,十式1、六艺,十式2、优缺点3、手臂和上半身 《囚徒健身》 六艺,十式 1、六艺,十式 1、俯卧撑: 2、深蹲: 3、举腿: 4、引体向上: 5、桥: 6、倒立撑: 十式就是每个动作分拆为十个不同的动作等级,进行训练 每一式里面,又根据做的组数和个数,分成三层,初级,中级,高级 依次循序渐进,每完成上一个动作的高级,则向下一个动作前进 2、优缺点 优点 : 六个动作都是很常见的,日常的动作,不太需要依赖什么器械,随时随地都可以练习,很方便 这些动作可以覆盖到全身大部分

简简单单Onlinezuozuo 4564

拦截器中addInterceptor和excludePathPatterns里的/* /**是什么

/** /**匹配/后面所有资源,包括/ a/**会匹配/a/下的所有路径 /* /*匹配一级路径,即a/的后面不能再有别的/,只能是a/x,a/xx,a/xxx,不能是a/x/xx a/* 仅可匹配一级路径如a/b、a/c不能匹配a/b/c和更多级路径 ...

z2431435的博客 2794

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

Spring Boot面试题精选汇总

Spring Boot面试题精选汇总

感谢阅读 1744

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

精选2025年最新97道Java面试题:spring+Redis+JVM+mysql全在这里了

包含但不限于(分布式架构、高并发、Jvm性能调优、Spring,MyBatis,Nginx源码分析,Redis,ActiveMQ、、Mycat、Netty、Kafka、Mysql、Zookeeper、Tomcat、Docker、Dubbo、Nginx等知识点解析)也有各大互联网公司人才,可以交流讨论共同进步。2、一张自增表里面总共有 7 条数据,删除了最后 2 条数据,重启 mysql 数据库,又插入了一条数据,此时 id 是几?7、mysql 的内连接、左连接、右连接有什么区别?

x1ao_fe1的博客 3029

2021全网最全最详细的Spring面试题精选合集

1. Spring基础 1.1 什么是 spring? Spring是一个轻量级Java开发框架,最早有Rod Johnson创建,目的是为了解决企业级应用开发的业务逻辑层和其他各层的耦合问题。它是一个分层的JavaSE/JavaEE full-stack(一站式)轻量级开源框架,为开发Java应用程序提供全面的基础架构支持。Spring负责基础架构,因此Java开发者可以专注于应用程序的开发。 Spring最根本的使命是解决企业级应用开发的复杂性,即简化Java开发。 Spring可以做很多事情,

Java烂猪皮 6558

精选2024年最新97道Java面试题:spring+Redis+JVM+mysql全在这里了

您的支持是我不断前行的动力,我将更加勤勉地分享高质量内容。如果您喜欢我的分享,欢迎关注,期待与您共同进步。祝愿每位同仁都能在面试中脱颖而出,顺利斩获心仪的offer!

x1ao_fe1的博客 1601

100道中高级Java面试题整理

JavaEE面试题整理一、Java基础篇二、JVM篇三、Tomcat篇四、MyBatis篇五、Spring篇六、SpringMVC面试题整理七、Redis篇八、Mongodb篇九、MQ篇十、Shiro篇十一、搜索引擎篇十二、Nginx篇十三、SpringBoot篇十四、Dubbo篇 一、Java基础篇 1、JAVA中的几种基本数据类型是什么,各自占用多少字节? 浮点类型:float(4字节)、do...

ThisLX的博客 2万+

2025年Java大厂面试题精选

*答案:**全面支持虚拟线程,AOT编译优化,GraalVM原生镜像支持,响应式编程增强。**答案:**减少类型转换代码,增强可读性,编译时类型安全,支持穷尽性检查。**答案:**查询模式分析,自动创建和删除索引,索引使用效果评估,持续优化。**答案:**统一查询语言,智能存储选择,跨模型事务支持,统一安全模型。**答案:**数据完整性保证,审计追踪支持,时间旅行查询,合规性增强。**答案:**故障预测模型,自动修复策略,性能异常检测,资源自动调优。

maoyi216的博客 397

Spring面试题集锦(精选)

点击上方“Coder编程”,选择“置顶公众号” 技术文章第一时间送达! 以下来自网络收集,找不到原文出处。此次主要为了面试收集,希望对大家有所帮助~~~~ Spring面试题集锦(精选)1.什么是Spring? Spring是一个开源的Java EE开发框架。Spring框架的核心功能可以应用在任何Java应用程序中,但对Java EE平台上的Web应用程序有更好的扩展性。Spring...

Coder编程的博客 1961

100道Java中高级面试题汇总+详细拆解

出自:Java面试题精选1-10期【10期】Redis 面试常见问答【09期】说说hashCode() 和 equals() 之间的关系?【08期】说说Object类下面有几种方法呢?【...

Java知音 7001

java面试题精选

一、tomcat运行原理: 1. Tomcat需要main方法启动,创建实例:server。 2. Tomcat需要监听本机上的某个端口,主要有8005,8043,8080。 3. Tomcat需要抓取此端口(8080)上来自客户端的链接并获得请求调用的方法与参数。 4. Tomcat需要根据请求调用的方法,动态地加载方法所在的类,完成类的实例化并通过该实例获得需要的方法,最终将请求传...

jiankang66的博客 390

牛客网精选1000道Java 面试题附答案汇总

不论是技术经理还是架构师,没有绝对地说哪条路是对还是错,适合自己才是最重要。小公司的可能没有架构师这个概念,大公司的架构师职位又不是那么容易拿下。但不管怎样,不断去学习新的技术,提升自己的层次是很有必要的,无论你在哪一家公司,过硬的技术水平才能吃得开。Ps:由于篇幅限制,笔记无法全部为大家展示出来,就以截图主要内容的形式让大家参考啦,需要完整的朋友可以点击下方名片获取。

m0_50180963的博客 779

java最新20个精选热点面试题,包含详细解释和超全面答案

IoC(Inversion of Control,控制反转)是一种设计思想,指的是将对象的创建、生命周期管理等控制权交给容器(如Spring容器),而非传统的由对象自身控制。DI(Dependency Injection,依赖注入)是IoC的具体实现方式,即组件不直接创建依赖对象,而是由外部容器在运行期间将依赖对象注入到组件中。这是因为hashCode()被广泛应用于基于散列的数据结构(如HashSet、HashMap)中,如果两个equals的对象hashCode不同,可能会导致数据结构的行为出现异常。

man2017的博客 1045

Spring 面试题和答案

史上最全 69 道 Spring 面试题和答案 Java技术栈 Java技术栈 微信号 javastack 功能介绍 专注分享Java技术干货,包括多线程、JVM、Spring Boot、Spring Cloud、Intellij IDEA、Dubbo、Zookeeper、Redis、架构设计、微服务、消息队列、Git、面试题、程序员攻略、最新动态等。 2018-08-06 Jav...

java_2017_csdn的博客 1929
上一篇: Java八股文最新整理
下一篇: 3万字80道Java基础经典面试题总结(2025修订版)
Javaaaaaaaaaaa13
博客等级 码龄2年 381粉丝 53原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值