为了账号安全,请及时绑定邮箱和手机立即绑定

SpringBoot入门(五)——自定义配置

标签:
SpringBoot
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.注册需要忽略的依赖接口ServletContextAwareServletConfigAware
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);
    }
  1. 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


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消