欢迎访问昆山宝鼎软件有限公司网站! 设为首页 | 网站地图 | XML | RSS订阅 | 宝鼎邮箱 | 后台管理


新闻资讯

MENU

软件开发知识

不管加没 CAD加密 有加接口

点击: 次  来源:劳务派遣管理系统 时间:2018-04-02

原文出处: 一年一度的夏天

Transaction在Controller层的摸索

一般开拓中事务要求我们放在Service层,但是有些环境,我们大概会要求放在Controller层,你有没有遇到过这样的需求呢?那么放到Controller层事务会生效吗?会发生什么问题呢?下面一起来看看

I、透过现象看本质

第一种环境

  • Controller层代码如下
    @RestController
    @RequestMapping("/city")
    public class CityControllerImpl implements CityController {
    
      @Autowired
      private CityService cityService;
    
      @Override
      @RequestMapping(value = "getCity",method = RequestMethod.GET,produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
        @Transcational
      public BaseResult<City> getCity(@RequestParam("id") Integer id) {
          City one = cityService.getOne(id);
          BaseResult<City> baseResult=new BaseResult<>();
          baseResult.setData(one);
          return baseResult;
      }
    }
  • 运行功效

    Transaction ERROR controller

  • 对的,你没有看错,当Transactional加载Controller层时呈现404异常
  • 第二种环境

  • Controller层代码如下
    @RestController
    @RequestMapping("/city")
    public class CityControllerImpl  {
      @Autowired
      private CityService cityService;
    
      @RequestMapping(value = "getCity",method = RequestMethod.GET,produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
      @Transactional
      public BaseResult<City> getCity(@RequestParam("id") Integer id) {
          City one = cityService.getOne(id);
          BaseResult<City> baseResult=new BaseResult<>();
          baseResult.setData(one);
          return baseResult;
      }
    }
  • 跟上面的区别,就是没有实现CityController接口了,那么我们运行一下,会有什么功效呢?
  • 运行功效如下:
    {
    data: null,
    message: null,
    status: 0
    }
  • 第二种环境居然没有啥问题,那么Transactional是否正常回滚呢?这里谜底我直接汇报各人了,纵然是换成有数据变动的接口,我们的事务是生效的。
  • 下面我为各人看源码表明一下
  • 第三种环境

  • 笔者测试利用支持==JAX-RS 2.0==的 Resteasy 测试,发明是没有这个问题的,各人可以自测一下Jersey是不是存在这个问题,揣度应该没有
  • II、熟悉本质解现象

    1. 区别

    可以看出,我们两个Controller的区别就是一个有实现接口,一个没有实现,为什么不同会这么大呢?

    2. 事务的本质

    我们知道事务是基于署理实现的,今朝Spring中有JDK动态署理和CGLIB署理两种署理,那么跟Spring选择的署理有没有干系呢?我们看一下Spring在署理类的时候选择利用何种署理的源代码。如下:

    @Override
        public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
            if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
                Class<?> targetClass = config.getTargetClass();
                if (targetClass == null) {
                    throw new AopConfigException("TargetSource cannot determine target class: " +
                            "Either an interface or a target is required for proxy creation.");
                }
                if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
                    return new JdkDynamicAopProxy(config);
                }
                return new ObjenesisCglibAopProxy(config);
            }
            else {
                return new JdkDynamicAopProxy(config);
            }
        }

    这是Spring建设署理较量焦点的一段代码,在类 DefaultAopProxyFactory 中,不管加没有加接口,Spring看到了@Transactional注解城市给我们的Controller注册为一个署理工具。留意:Spring并非对所有的Controller城市建设署理类,昆山软件开发,如果我们的Controller没有袒露任何切面,Spring并不会建设一个署理类,这里大概各人会感想奇怪,我们这里打个TAG,文末讲授。

    继承方才的话题,劳务派遣管理系统,第一种环境,由于我们的Controller有接口,所以就走了JDK署理,相反第二种走了Cglib署理。OK, 我们的CityControllerImpl此刻是一个署理类。那么为什么会产生404异常呢?

    springmvc的道理">3. SpringMvc的道理

    为什么Controller酿成署理之后,就会404异常了,必定跟我们的SpringMVC有关,我们看一下SpringMVC的焦点类 AbstractHandlerMethodMapping 这个类可以绑定URL和需要执行处理惩罚器的哪个要领。这个抽象类实现了initializingBean接口,其实主要的注册URL操纵则是通过这个接口的afterPropertiesSet()接口要领来挪用的。然后挪用initHandlerMethods 要领举办绑定URL。要领具体如下:

    protected void initHandlerMethods() {
            if (logger.isDebugEnabled()) {
                logger.debug("Looking for request mappings in application context: " + getApplicationContext());
            }
            String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
                    BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
                    getApplicationContext().getBeanNamesForType(Object.class));
    
            for (String beanName : beanNames) {
                if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
                    Class<?> beanType = null;
                    try {
                        beanType = getApplicationContext().getType(beanName);
                    }
                    catch (Throwable ex) {
                        // An unresolvable bean type, probably from a lazy bean - let's ignore it.
                        if (logger.isDebugEnabled()) {
                            logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);
                        }
                    }
                    if (beanType != null && isHandler(beanType)) {
                        detectHandlerMethods(beanName);
                    }
                }
            }
            handlerMethodsInitialized(getHandlerMethods());
        }