public abstract class BaseController { @Autowired protected HttpSession httpSession; @Autowired protected HttpServletRequest request; }
public abstract class BaseController1 { protected HttpServletRequest request; protected HttpServletResponse response; protected HttpSession httpSession; @ModelAttribute public void init(HttpServletRequest request, HttpServletResponse response, HttpSession httpSession) { this.request = request; this.response = response; this.httpSession = httpSession; } }
线程安全测试
@RequestMapping("/test")@RestControllerpublic class TestController extends BaseController { @GetMapping("/1") public void test1() throws InterruptedException {// System.out.println("thread.id=" + Thread.currentThread().getId());// System.out.println("thread.name=" + Thread.currentThread().getName());// ServletRequestAttributes servletRequestAttributes =// ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes());//// HttpServletRequest httpServletRequest = servletRequestAttributes.getRequest(); TimeUnit.SECONDS.sleep(10);// System.out.println("base.request=" + request); System.out.println("base.request.name=" + request.getParameter("name")); } @GetMapping("/2") public void test2() throws InterruptedException {// System.out.println("thread.id=" + Thread.currentThread().getId());// System.out.println("thread.name=" + Thread.currentThread().getName());// ServletRequestAttributes servletRequestAttributes =// ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes());//// HttpServletRequest httpServletRequest = servletRequestAttributes.getRequest();// System.out.println("base.request=" + request); System.out.println("base.request.name=" + request.getParameter("name")); } @InitBinder public void initBinder(WebDataBinder binder) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); binder.registerCustomEditor(Date.class, new CustomDateEditor(sdf, true)); } }
通过JUC的CountDownLatch,模拟同一时刻100个并发请求。
public class Test { public static void main(String[] args) { CountDownLatch start = new CountDownLatch(1); CountDownLatch end = new CountDownLatch(100); CustomThreadPoolExecutor customThreadPoolExecutor = new CustomThreadPoolExecutor( 100, 100, 0L, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(100) ); for (int i = 0; i < 100; i++) { final int finalName = i; CustomThreadPoolExecutor.CustomTask task = new CustomThreadPoolExecutor.CustomTask( new Runnable() { @Override public void run() { try { start.await(); HttpUtil.get("http://localhost:8081/test/2?name=" + finalName); } catch (Exception ex) { ex.printStackTrace(); } finally { end.countDown(); } } } , "success"); customThreadPoolExecutor.submit(task); } start.countDown(); try { end.await(); } catch (InterruptedException ex) { ex.printStackTrace(); } customThreadPoolExecutor.shutdown(); } }
通过观看base.request.name的值并没有null值和存在值重复的现象,很肯定的说@Autowired注入的HttpServletRequest不存在线程安全问题。
base.request.name=78 base.request.name=20 base.request.name=76 base.request.name=49 base.request.name=82 base.request.name=12 base.request.name=80 base.request.name=91 base.request.name=92 base.request.name=30 base.request.name=28 base.request.name=36 base.request.name=41 base.request.name=73 base.request.name=29 base.request.name=2 base.request.name=81 base.request.name=43 base.request.name=35 base.request.name=22 base.request.name=6 base.request.name=27 base.request.name=17 base.request.name=70 base.request.name=65 base.request.name=84 base.request.name=14 base.request.name=54 base.request.name=67 base.request.name=19 base.request.name=21 base.request.name=66 base.request.name=11 base.request.name=53 base.request.name=9 base.request.name=72 base.request.name=64 base.request.name=0 base.request.name=44 base.request.name=89 base.request.name=77 base.request.name=48 base.request.name=1 base.request.name=8 base.request.name=74 base.request.name=46 base.request.name=88 base.request.name=26 base.request.name=24 base.request.name=62 base.request.name=61 base.request.name=51 base.request.name=96 base.request.name=33 base.request.name=45 base.request.name=5 base.request.name=95 base.request.name=68 base.request.name=60 base.request.name=56 base.request.name=42 base.request.name=57 base.request.name=10 base.request.name=55 base.request.name=90 base.request.name=47 base.request.name=97 base.request.name=40 base.request.name=85 base.request.name=86 base.request.name=69 base.request.name=98 base.request.name=13 base.request.name=32 base.request.name=37 base.request.name=4 base.request.name=23 base.request.name=50 base.request.name=38 base.request.name=59 base.request.name=99 base.request.name=71 base.request.name=25 base.request.name=58 base.request.name=34 base.request.name=7 base.request.name=93 base.request.name=31 base.request.name=3 base.request.name=39 base.request.name=75 base.request.name=94 base.request.name=83 base.request.name=63 base.request.name=79 base.request.name=16 base.request.name=52 base.request.name=15 base.request.name=87 base.request.name=18
很明显发现base.request.name的值存在null或者重复的现象,说明通过@ModelAttribute注入的HttpServletRequest存在线程安全问题。
base.request.name=97base.request.name=59base.request.name=63base.request.name=14base.request.name=82base.request.name=49base.request.name=86base.request.name=13base.request.name=99base.request.name=29base.request.name=45base.request.name=85base.request.name=8base.request.name=35base.request.name=69base.request.name=70base.request.name=16base.request.name=21base.request.name=74base.request.name=20base.request.name=34base.request.name=23base.request.name=96base.request.name=19base.request.name=67base.request.name=15base.request.name=27base.request.name=43base.request.name=39base.request.name=47base.request.name=87base.request.name=71base.request.name=41base.request.name=38base.request.name=nullbase.request.name=31base.request.name=32base.request.name=76base.request.name=55base.request.name=75base.request.name=93base.request.name=nullbase.request.name=56base.request.name=1base.request.name=18base.request.name=89base.request.name=65base.request.name=10base.request.name=78base.request.name=nullbase.request.name=80base.request.name=24base.request.name=88base.request.name=88base.request.name=44base.request.name=53base.request.name=58base.request.name=61base.request.name=60base.request.name=37base.request.name=92base.request.name=42base.request.name=11base.request.name=68base.request.name=72base.request.name=91base.request.name=79base.request.name=33base.request.name=66base.request.name=54base.request.name=40base.request.name=94base.request.name=46base.request.name=83base.request.name=17base.request.name=64base.request.name=26base.request.name=90base.request.name=7base.request.name=62base.request.name=57base.request.name=73base.request.name=98base.request.name=30base.request.name=6base.request.name=2base.request.name=28base.request.name=5base.request.name=95base.request.name=9base.request.name=3base.request.name=51base.request.name=4base.request.name=52base.request.name=12base.request.name=25base.request.name=36base.request.name=84base.request.name=81base.request.name=50
源码分析
1.在Spring容器初始化中,refresh()方法会调用postProcessBeanFactory(beanFactory);
。它是个模板方法,在BeanDefinition被装载后(所有BeanDefinition被加载,但是没有bean被实例化),提供一个修改beanFactory容器的入口。这里还是贴下AbstractApplicationContext中的refresh()方法吧。
@Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // 1.Prepare this context for refreshing. prepareRefresh(); // 2.Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // 3.Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // 4.Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // 5.Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // 6.Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // 7.Initialize message source for this context. initMessageSource(); // 8.Initialize event multicaster for this context. initApplicationEventMulticaster(); // 9.Initialize other special beans in specific context subclasses. onRefresh(); //10. Check for listener beans and register them. registerListeners(); // 11.Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); //12. Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } }
2.由于postProcessBeanFactory是模板方法,它会被子类AbstractRefreshableWebApplicationContext重写。在AbstractRefreshableWebApplicationContext的postProcessBeanFactory()做以下几件事情。
1.注册ServletContextAwareProcessor。
2.注册需要忽略的依赖接口ServletContextAware
、ServletConfigAware
。
3.注册Web应用的作用域和环境配置信息。
@Override protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext, this.servletConfig)); beanFactory.ignoreDependencyInterface(ServletContextAware.class); beanFactory.ignoreDependencyInterface(ServletConfigAware.class); WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext); WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, this.servletContext, this.servletConfig); }
WebApplicationContextUtils中的registerWebApplicationScopes(),beanFactory注册了request,application,session,globalSession作用域,也注册了需要解决的依赖:ServletRequest、ServletResponse、HttpSession、WebRequest。
public static void registerWebApplicationScopes(ConfigurableListableBeanFactory beanFactory, ServletContext sc) { beanFactory.registerScope(WebApplicationContext.SCOPE_REQUEST, new RequestScope()); beanFactory.registerScope(WebApplicationContext.SCOPE_SESSION, new SessionScope(false)); beanFactory.registerScope(WebApplicationContext.SCOPE_GLOBAL_SESSION, new SessionScope(true)); if (sc != null) { ServletContextScope appScope = new ServletContextScope(sc); beanFactory.registerScope(WebApplicationContext.SCOPE_APPLICATION, appScope); // Register as ServletContext attribute, for ContextCleanupListener to detect it. sc.setAttribute(ServletContextScope.class.getName(), appScope); } beanFactory.registerResolvableDependency(ServletRequest.class, new RequestObjectFactory()); beanFactory.registerResolvableDependency(ServletResponse.class, new ResponseObjectFactory()); beanFactory.registerResolvableDependency(HttpSession.class, new SessionObjectFactory()); beanFactory.registerResolvableDependency(WebRequest.class, new WebRequestObjectFactory()); if (jsfPresent) { FacesDependencyRegistrar.registerFacesDependencies(beanFactory); } }
作者:cmazxiaoma
链接:https://www.jianshu.com/p/672a8ecada6c