小燃儿

小燃儿 查看完整档案

填写现居城市  |  填写毕业院校  |  填写所在公司/组织填写个人主网站
编辑

java developer.
公众号:程序员小燃【i_java_code】

个人动态

小燃儿 回答了问题 · 1月21日

关于数据库商品库存的架构设计问题

库存不会存在某个节点里 分布式是负载均衡请求。库存合理的应该是整体控制的。如果真的按你这么想的,那么每次请求都打在一个节点上用完了 再去二节点和单机也没多大区别的

关注 4 回答 3

小燃儿 回答了问题 · 2020-12-15

Java 中的垃圾回收为什么要分代

确实没人说一定要分代。
分代理论是说大多数对象都是朝生夕亡,而只有少数对象是一直存活的。所以分代后可以便于对不同类型的对象进行管理从而得到自己想要的收益,比如吞吐量或者延迟。

但是分代之后需要解决跨代引用,分区比例等等的问题。解决这些也需要资源。
那么有些场景下没有那么多资源来解决,或者在本身只需要一次运行就可以退出(就不需要对象管理)。那就不需要引入分代。我记得java用的虚拟机里应该是有的,好像是eclipse开发的一个

关注 6 回答 5

小燃儿 回答了问题 · 2020-11-29

解决为什么说NIO能处理更高的并发链接?

图中好处主要是下面
1.nio只保持有限线程来接受请求,有限工作线程处理业务。同时把请求交给工作线程后又可以接受新请求了。所以这个线程一直存活
2.池化的bio每次请求需要创建一个线程,一般不能无限资源会耗尽,而且应该是同步等待返回的。最后回收到线程池

再讲讲你画的bio图 其实不是完全意义上bio的。比如你引入了等待队列,也就是说整个bio把任务交给等待队列就应该结束了。
Nio也不是完全的nio 而是react模型。

本质上的nio应该是只看多路复用 bio是堵塞 不是你画的图

关注 3 回答 3

小燃儿 回答了问题 · 2020-11-29

解决mysql的left join索引只使用join的表的索引?

跟查询条件有关,没什么条件不就要查全表喽,状态这种字段也不适合加索引

关注 3 回答 3

小燃儿 回答了问题 · 2020-11-29

spring boot的状态机StateMachine有啥用?

顾名思义 状态流转相关的 比如订单,支付这种有复杂状态的场景下。用状态机设计模式来简化业务

关注 3 回答 2

小燃儿 发布了文章 · 2020-11-07

springmvc详解(3)请求分发流程之拦截器 HandlerInterceptor

前言

当Servlet接收到请求后会最终调用doDispatch方法后会去找到对应的HandlerMapping,同时也会找到配置的拦截器,最终组成需要的HandlerExecutionChain执行链(这里省略部分代码保留主要功能):

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {

                // 1.确定使用的handler
                HandlerExecutionChain mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }

                // 确定handler的适配器
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

                // 拦截器前置调用
                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }

                // 实际调用
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                // 设置默认视图名
                applyDefaultViewName(processedRequest, mv);
                // 拦截器后置调用
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            // 分发结果
            processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    }

拦截器

在开发中拦截器的使用是比较频繁的,例如用户拦截器,权限拦截器等。sringmvc的拦截器提供了pre,post,after三种时期的方法(调用处理器方法前,调用后,返回数据后),非常方便的进行资源设置,释放等功能。

public interface HandlerInterceptor {
    // 前
    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {

        return true;
    }
    // 中
    default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
            @Nullable ModelAndView modelAndView) throws Exception {
    }
    // 后
    default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
            @Nullable Exception ex) throws Exception {
    }

}

拦截器注册

现在看看拦截器是怎么注册的?spring提供两种方式。

1. 配置类注册EnableWebMvcConfiguration#setConfigurers
通常在开发中我们会使用自己编写实现WebMvcConfigurer接口的方式添加拦截器,并把注册类注册到IOC容器中,EnableWebMvcConfiguration#setConfigurers就会使用@Autowired进行制动注入。
```java
@Autowired(required = false)
public void setConfigurers(List<WebMvcConfigurer> configurers) {
    if (!CollectionUtils.isEmpty(configurers)) {
        this.configurers.addWebMvcConfigurers(configurers);
    }
}
```
最后把注册的拦截器注册到HandlerMapping当中去,(以RequestMappingHandlerMapping为例)代码如下,getInterceptors方法最终会调用所有WebMvcConfigurer实例添加拦截器的方法,从而将我们自定义的拦截器注册到HanderMapping当中去
```java
public RequestMappingHandlerMapping requestMappingHandlerMapping(
            @Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
            @Qualifier("mvcConversionService") FormattingConversionService conversionService,
            @Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {

        RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
        mapping.setOrder(0);
        mapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));
        ……
```
2. 以MappedInterceptor类实例或者子类直接注册到IOC容器中
由于HandlerMapping继承WebApplicationObjectSupport,再创建bean的时候会调用其initApplicationContext方法。而该方法会注册容器中的MappedInterceptor类的bean。
    这样我们就可以使用这些拦截器了。

总结

最后以一个流程图直观总结一下其过程:
image.png

查看原文

赞 0 收藏 0 评论 0

小燃儿 发布了文章 · 2020-10-28

SpringMVC详解(2)-请求分发流程之处理器 HandlerMapping

上篇文章《SpringMVC详解-怎么接收请求》已经提到过了,当Servlet接收到请求后会最终调用doDispatch方法(这里省略部分代码保留主要功能):

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {

        // 1.确定使用的handler
        mappedHandler = getHandler(processedRequest);
        if (mappedHandler == null) {
          noHandlerFound(processedRequest, response);
          return;
        }

        // 确定handler的适配器
        HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

        // 拦截器前置调用
        if (!mappedHandler.applyPreHandle(processedRequest, response)) {
          return;
        }

        // 实际调用
        mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
        // 设置默认视图名
        applyDefaultViewName(processedRequest, mv);
        // 拦截器后置调用
        mappedHandler.applyPostHandle(processedRequest, response, mv);
      // 分发结果
      processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
  }

1 处理器 HandlerMapping

请求分发完毕后就需要去进行处理,这就是我们本文所要讲的重点:HandlerMapping。如果使用过springmvc进行开发过的同学都知道,handler通常指的就是业务上我们在Controller层中编写的带有@RequestMapping的方法,在SpringBoot中为了方便开发,框架还提供了@GetMapping,@PostMaping等注解来简化配置。而这些所有方法为构成一个HandlerMapping。

2 如何转化HandlerMapping?

我们目光可以聚焦到EnableWebMvcConfiguration#requestMappingHandlerMapping方法,它会去创建RequestMappingHandlerMapping对象(就是转化上面提到注解的类)。这个对象实现了InitializingBean接口,所以最终会调用其afterPropertiesSet方法:

@Override
  public void afterPropertiesSet() {
         // 初始化handler
    initHandlerMethods();
  }
  
protected void initHandlerMethods() {
    for (String beanName : getCandidateBeanNames()) {
      if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
        processCandidateBean(beanName);
      }
    }
    handlerMethodsInitialized(getHandlerMethods());
}

其中的processCandidateBean会去解析带有@Controller注解的类。我们简单的看一下代码:

if (beanType != null && isHandler(beanType)) {
      detectHandlerMethods(beanName);
    }
    
@Override
  protected boolean isHandler(Class<?> beanType) {
    return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
        AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}

isHandler方法会找到带有@Controller注解或@RequestMapping的类,detectHandlerMethods方法会对整个类进行处理,解析带有@RequestMapping注解的方法并注册到MappingRegistry中去。

3 如何注册HandlerMapping?

最后看一下框架是怎么将handlers注册到控制器DispatcherServlet中去的。

DispatcherServlet的父类中添加了一个ContextRefreshListener监听器,当容器完全启动后,会调用其onRefresh方法,而正是这个方法,去注册了handers。

@Override
  protected void onRefresh(ApplicationContext context) {
    initStrategies(context);
  }

  protected void initStrategies(ApplicationContext context) {
    initMultipartResolver(context);
    initLocaleResolver(context);
    initThemeResolver(context);
    // 注册handler
    initHandlerMappings(context);
    initHandlerAdapters(context);
    initHandlerExceptionResolvers(context);
    initRequestToViewNameTranslator(context);
    initViewResolvers(context);
    initFlashMapManager(context);
  }

可以看到控制器初始化了非常多的东西,这里我们只关注initHandlerMappings方法,后面的文章会讲到

initHandlerAdapters,initHandlerExceptionResolvers等。

具体代码如下(省略部分代码):

private void initHandlerMappings(ApplicationContext context) {
    this.handlerMappings = null;

    if (this.detectAllHandlerMappings) { // true
      Map<String, HandlerMapping> matchingBeans =
          BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);

      this.handlerMappings = new ArrayList<>(matchingBeans.values());
    }
    else {
        HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
        this.handlerMappings = Collections.singletonList(hm);
    }

由以上代码可以知道,它会去Spring IOC容器中获取HandlerMapping类型的所有Bean设置为控制器的Handler,而上文说的RequestMappingHandlerMapping就是其中一个Bean。与此同时我们发现,也可以通过自己手写一个HandlerMapping实现类从而注册到控制器当中去。

最后用流程图总结一下这个过程:
image.png

查看原文

赞 0 收藏 0 评论 0

小燃儿 发布了文章 · 2020-10-21

SpringMVC详解(1)-怎么接收请求

在学习springmvc怎么接收请求前,我们需要先讲一讲servlet。它其实java提供的一个Web组件。而我们比较熟知的Tomcat容器就是一种Servlet容器的实现,顾名思义就是用来管理servlet的。

最早学习java的时候我相信每个人都学习过Servlet的知识,我们只需要继承一个HttpServlet,然后在WEB-INF目录下的web.xml文件中配置Servlet的访问对外路径,最后启动Tomcat服务器。我们就可以在浏览器中访问该Servlet了。

不过在springboot中提供了更简单的实现,代码如下:

@WebServlet(urlPatterns = "/self_servlet/*")
public class MvcTest extends HttpServlet {

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("I received a req:"+req.getMethod()+"-"+req.getServletPath()+"-"+req.getRequestURI());
    }
}

最后只需要将该类注册到容器当中去就可以了。当然注册Servlet,Springboot提供了两种方式:
1.使用@WebServlet注解,再通过@ServletComponentScan 扫描该类,会自动注册。
2.@Bean的方式也提供注册方法:

  @Bean
    public ServletRegistrationBean<MvcTest> registrationInit(){
        ServletRegistrationBean<MvcTest> registrationBean =new ServletRegistrationBean<>();
        registrationBean.addUrlMappings("/self_servlet/*");
        registrationBean.setServlet(new MvcTest());
        return registrationBean;
    }

最后我们请求测试一下http://localhost:8080/self_servlet/dsadsd,控制台中就会打印出相应的信息。

I received a req:GET-/self_servlet-/self_servlet/dsadsd

以上的学习我们就知道了,只要向spring注册一个ServletBean就可以拦截到请求,然后再方法内做处理。没错!springboot也是这么做的!这里我们简单的引出此类,后续详解。

这个类就是DispatcherServlet,它就是HttpServlet的一个子类。容器会调用service方法,最终调用DispatcherServlet#doService的方法,代码如下:

@Override
    protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
                      // …… 忽略代码,只保留主要逻辑
        try {
            doDispatch(request, response);
        }
        // ……
       }

可以看到的是MVC的控制器再接收到请求后会调用doDispatch方法进行一个分发操作。我们很容易想到在这个方法里会进行最终的方法调用和结果返回。具体实现留在后面文章。

最后我们来看一下这个DispatcherServlet怎么注册的:DispatcherServletAutoConfiguration

protected static class DispatcherServletRegistrationConfiguration {

        @Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
        @ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
        public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,
                WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
            DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
                    webMvcProperties.getServlet().getPath());
            registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
            registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
            multipartConfig.ifAvailable(registration::setMultipartConfig);
            return registration;
        }

    }

DispatcherServletRegistrationBean是ServletRegistrationBean的一个子类,所以我们知道,springMVC的前端控制器Servlet是通过上文提到的第二种方式实现的。至于为什么使用DispatcherServletRegistrationBean而不直接用ServletRegistrationBean,是为了重写url_pattern相关的方法不允许中途被修改。

查看原文

赞 0 收藏 0 评论 0

小燃儿 赞了问题 · 2020-10-15

netty 服务端tcp长连接运行一段时间后,不读取设备端报文,超过60秒超时断开设备重连正常

image.png

如图上图所示:
119.126.106.X是客户端地址
172.18.221.X是服务端地址
客户端发送了5次心跳(Len=40),服务端只是响应了ACK(Len=0),通过后台日志分析,ChannelRead方法并未读取到客户端心跳报文,导致超时60秒(netty初始化服务设置),服务端断开长连接,等几分钟设备重连后,能正常运行,几小时后周而复始### 问题描述

### 问题出现的环境背景及自己尝试过哪些方法

### 相关代码
粘贴代码文本(请勿用截图)

### 你期待的结果是什么?实际看到的错误信息又是什么?

关注 1 回答 0

小燃儿 回答了问题 · 2020-10-13

Spring的循环依赖中的三级缓存

代码中写的是一个lamda表达式,即ObjectFactory

//3.将创建完的bean添加到三级缓存
             this.addSingletonFactory(beanName, () -> {
               ** return this.getEarlyBeanReference(beanName, mbd, bean);**
            });

1.循环引用时,会调用三级缓存getEarlyBeanReference:

//如果bean不是AOP代理,则本方法直接返回bean。
    protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
        Object exposedObject = bean;
        if (!mbd.isSynthetic() && this.hasInstantiationAwareBeanPostProcessors()) {
            Iterator var5 = this.getBeanPostProcessors().iterator();

            while(var5.hasNext()) {
                BeanPostProcessor bp = (BeanPostProcessor)var5.next();
                if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                    SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor)bp;
                    exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
                }
            }
        }
        return exposedObject;
    }

2.可以看到mbd只用来判断是否需要进行BeanPostProcessor操作,但是方法最终返回的是bean。
所以说三级缓存真正想保存的其实是bean对象。但是由于Aop存在,有时需要返回代理对象而不是原始bean,所以会调用ibp.getEarlyBeanReference获取代理后的对象

关注 3 回答 2

认证与成就

  • 获得 8 次点赞
  • 获得 3 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 3 枚铜徽章

擅长技能
编辑

(゚∀゚ )
暂时没有

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2020-07-12
个人主页被 458 人浏览