Springmvc中在controller注入request会有线程安全问题吗

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Springmvc中在controller注入request会有线程安全问题吗相关的知识,希望对你有一定的参考价值。

servlet是单例的,而tomcat则是在多个线程中调用servlet的处理方法。因此如果servlet存在实例对象,那么就会引出线程安全的问题。而springmvc允许在controller类中通过@Autowired配置request、response以及requestcontext等实例对象。这种配置方法是否线程安全?答案是——这种配置方法是线程安全的,request、response以及requestcontext在使用时不需要进行同步。而根据spring的默认规则,controller对于beanfactory而言是单例的。即controller只有一个,controller中的request等实例对象也只有一个。然而tomcat依旧会以多线程的方式访问controller。这种做法似乎并不能保证线程安全。我们如何理解这一矛盾?
在解释controller线程安全这一问题之前需要首先了解如下的一些问题和概念:

1.servlet的request域的问题:request域是javaweb的基础概念,他指的是从发起http请求到返回响应的这一段时间内,存在一个httprequest对象对应于http请求。以上的表述是没有问题的,然而有些人“自作主张”的将之前的表述换成了其他的描述方式:(1):request对象的生命周期以发起http请求开始,当http请求返回时结束;(2):用户发送一个请求的时候,request被创建,当用户关闭请求的时候,request会消亡。以上两种表述的主要错误在于混淆了http请求和request对象这两个概念。tomcat在接收到http请求的时候并不会创建一个request对象,即request对象并不是一个http请求的实例。只是request对象“恰巧”拥有了http请求中的所有参数而已。request对象在tomcat发起处理线程的时候就被创建,只有当处理线程终止的时候request才会被销毁。我们可以创建一个servlet类,并在doget和dopost方法上面打上断点。你会发现如果是同一个进程,即便发起多次访问,request对象的id始终不变。读者可以亲自尝试,用以验证本人说法的真伪。

2.Threadlocal类:该对象包含两个关键函数:set(Object obj)和get()。这两个函数与调用该函数的线程相关,set方法将某一对象“注入”到当前线程中,而get方法则是从当前线程中获取对象。

3.InvocationHandler接口:这是springmvc保证request对象线程安全的核心。通过实现该接口,开发者能够在Java对象方法执行时进行干预,搭配Threadlocal就能够实现线程安全。

下面将通过例子介绍springmvc如何保证request对象线程安全:

Httprequest接口:

public interface HttpRequest
public void service();


HttpRequestImpl类:对httprequest接口的具体实现,为了区别不同的HttpRequestImpl对象,本人为HttpRequestImpl设置了一个Double对象,如果不设置该对象,其默认为null

public class HttpRequestImpl implements HttpRequest
public Double d;
@Override
public void service()
System.out.println("do some serivce, random value is "+d);




ThreadLocalTest类:负责向ThreadLocal设置对象和获取对象,本人设置ThreadLocal对象为static,因此ThreadLocalTest类中只能有一个ThreadLocal对象。

public class ThreadLocalTest
public static ThreadLocal<HttpRequest> local=new ThreadLocal<HttpRequest>();
public static void set(HttpRequest f)
if(get()==null)
System.out.println("ThreadLocal is null");
local.set(f);


public static HttpRequest get()
return local.get();



Factory类:该类是一个工厂类并且是单例模式,主要负责向ThreadLocalTest对象中设置和获取对象

public class Factory
private static Factory factory=new Factory();
private Factory()


public static Factory getInstance()
return factory;

public HttpRequest getObject()
return (HttpRequest)ThreadLocalTest.get();

public void setObject(HttpRequest request)
ThreadLocalTest.set(request);



Delegate类:该类实现了InvocationHandler接口,并实现了invoke方法

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class Delegate implements InvocationHandler
private Factory factory;

public Factory getFactory()
return factory;


public void setFactory(Factory factory)
this.factory = factory;


@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable
return method.invoke(this.factory.getObject(), args);


参考技术A controller默认是单例 需要的数据都从方法参数传递 只要不定义类变量 就安全 放心吧

@Controller
@RequestMapping("/fui")
public class FuiController extends SpringController

这么定义的话就是单例

@Controller
@Scope("prototype")
@RequestMapping("/fui")
public class FuiController extends SpringController
每次都创建

@Controller
@Scope("session")
@RequestMapping("/fui")
public class FuiController extends SpringController
一个会话创建一个

解决非controller使用@Autowired注解注入为null问题

在SpringMVC框架中,我们经常要使用@Autowired注解注入Service或者Mapper接口,我们也知道,在controller层中注入service接口,在service层中注入其它的service接口或者mapper接口都是可以的,但是如果我们要在我们自己封装的Utils工具类中或者非controller普通类中使用@Autowired注解注入Service或者Mapper接口,直接注入是不可能的,因为Utils使用了静态的方法,我们是无法直接使用非静态接口的,当我们遇到这样的问题,我们就要想办法解决了。
@Autowired注解的方法:

@Component
public class TestUtils {
    @Autowired
    private ItemService itemService;

    @Autowired
    private ItemMapper itemMapper;

    public static TestUtils testUtils;

    @PostConstruct
    public void init() {
        testUtils = this;
    }
}

//utils工具类中使用service和mapper接口的方法例子,用‘testUtils.xxx.方法‘ 就可以了

public static void test(Item record){
    testUtils.itemMapper.insert(record);
    testUtils.itemService.queryAll();
}

如果以上不能解决,只能直接用 dao层:
ApplicationContext context = new ClassPathXmlApplicationContext(‘classpath:spring/applicationContext-*.xml‘);
FcglEsHouseMapper sfMapper = context.getBean(FcglEsHouseMapper.class);

然后直接调mybatis接口,不推荐此种,违背springmvc

以上是关于Springmvc中在controller注入request会有线程安全问题吗的主要内容,如果未能解决你的问题,请参考以下文章

springMVC @RestController和@Controller的区别

springMVC @RestController和@Controller的区别

SpringMVC的 几个注解

SpringMVC怎么注入HttpServletResponse

解决非controller使用@Autowired注解注入为null问题

在Controller注入Service报错的解决方法