springboot处理参数再转发请求_SpringBoot 注入请求公用参数(线程安全)

介绍

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 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, Serializable {

@Override

public RequestContext getObject() {

return WebContextFacade.getRequestContext();

}

@Override

public String toString() {

return WebContextFacade.getRequestContext().toString();

}

}

}

你可能感兴趣的:(springboot处理参数再转发请求_SpringBoot 注入请求公用参数(线程安全))