美文网首页
SpringBoot 注入请求公用参数(线程安全)

SpringBoot 注入请求公用参数(线程安全)

作者: BeRicher | 来源:发表于2018-12-24 17:07 被阅读0次

介绍

Spring 中我们可以使用如下方式注入当前请求的信息,而不会产生线程安全问题。

public class DemoController {
    @Autowired
    private HttpServletRequest httpServletRequest;
}

其主要实现方式为: 将当前请求的信息放入ThreadLocal中。并且会创建一个HttpServletRequest的代理对象,每次使用时通过该代理对象获取ThreadLocal中的信息

我们项目中通常也存在这样的需求,前端将一些公用的参数放在请求头中传递到后台,后台使用时无需再次查询数据库,可以直接获取。

原理介绍

我们通过ThreadLocal存储请求信息。
通过BeanFactoryPostProcessor定义一个动态创建的bean(调用时才创建bean)。
通过ObjectFactory生产动态创建的bean,获取ThreadLocal中的信息返回。

ThreadLocal

ThreadLocal用于隔离每个线程中的变量,使用者可以将当前线程的变量存储,其他线程无法获取。这非常适用于我们web请求中,某次请求的信息存储。但注意,如果在服务器端处理时,开启了新的线程,threadLocal获取信息时则获取不到,可以提现获取并存储在final中供新的线程使用。

BeanFactoryPostProcessor&&BeanPostProcessor

BeanFactoryPostProcessor(定义bean的扩展点)和BeanPostProcessor(注入bean的扩展点)作为Spring提供的操作bean的扩展点,被各大组件广泛应用。
如mybatis的扫描组件MapperScannerConfigurer便实现的BeanDefinitionRegistryPostProcessor(继承的BeanFactoryPostProcessor),用于将mybatis注解所标注的类,定义成spring bean,等待spring容器的初始化。

其中BeanFactoryPostProcessor可以拦截到定义bean、但是还没注入到容器时的端点,可以在此时增加自定义的一些组件,如创建一个自定义注解,将其标注的类定义成Spring的Bean。

其中BeanPostProcessor可以拦截到bean注入时的端点,可以在bean注入前与注入后针对要注入的对象做一些处理。

ObjectFactory

BeanFactoryPostProcessor中的postProcessBeanFactory方法,允许我们额外添加bean的定义。
其中ConfigurableListableBeanFactory存在registerResolvableDependency方法允许我们定义的bean不直接加入容器,而是在调用时才创建bean,此方法需要传递生成bean的工厂类。

ObjectFactory允许在每次调用时返回一些目标对象的新实例。我们可以在其getObject方法中,每次都动态返回一个对象。

实现

WebContextFacade

public class WebContextFacade {
    private static ThreadLocal<RequestContext> requestContextThreadLocal = new ThreadLocal<>();

    public static RequestContext getRequestContext() {
        RequestContext requestContext = requestContextThreadLocal.get();
        return requestContext == null ? new RequestContextConcrete() : requestContext;
    }

    public static void setRequestContext(RequestContext requestContext) {
        requestContextThreadLocal.set(requestContext);
    }

    public static void removeRequestContext() {
        requestContextThreadLocal.remove();
    }
}

RequestContext

ObjectFactory要求必须定义接口。

public interface RequestContext {
    String getIp();

    void setIp(String ip);

    String getUri();

    void setUri(String uri);
}

RequestContextConcrete

RequestContextConcrete为真实生成的对象。

Data
public class RequestContextConcrete implements RequestContext {
    private String ip;
    private String uri;
}

ContextInterceptor

通过拦截器将请求对象放入ThreadLocal中,供ObjectFactory调用。

public class ContextInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response, Object handler) {
        setRequestContext(request);
        return true;
    }

    private void setRequestContext(HttpServletRequest request) {
        RequestContext requestContext = WebContextFacade.getRequestContext();
        requestContext.setIp(NetworkUtil.getIp(request));
        requestContext.setUri(request.getRequestURI());
        WebContextFacade.setRequestContext(requestContext);
    }

    @Override
    public void afterCompletion(HttpServletRequest request,
                                HttpServletResponse response, Object handler, Exception ex) {
        WebContextFacade.removeRequestContext();
    }
}

RequestContextBeanFactoryPostProcessor&&RequestContextObjectFactory

Spring启动时,定义动态装配bean。
请求时,通过ObjectFactory的getObject()动态获取当前请求的信息,由拦截器放入到ThreadLocal中。

public class RequestContextBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        beanFactory.registerResolvableDependency(RequestContext.class, new RequestContextObjectFactory());
    }

    public static class RequestContextObjectFactory implements ObjectFactory<RequestContext>, Serializable {

        @Override
        public RequestContext getObject() {
            return WebContextFacade.getRequestContext();
        }
        @Override
        public String toString() {
            return WebContextFacade.getRequestContext().toString();
        }
    }

}

快速开发框架
高质量图片压缩工具

相关文章

网友评论

      本文标题:SpringBoot 注入请求公用参数(线程安全)

      本文链接:https://www.haomeiwen.com/subject/akuzkqtx.html