yangyang12138

导航

一个request引发的bug

有很多错误由于需要是多线程是才会发生,导致经常在开发时很难发现,

import java.lang.reflect.ParameterizedType;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.context.support.WebApplicationContextUtils;

import com.google.gson.Gson;
import com.qdyq.common.criteria.Criteria;
import com.qdyq.common.orm.BaseEntity;
import com.qdyq.common.orm.BaseMapper;
import com.qdyq.common.orm.MySqlSessionFactoryBean;

import qdyq.api.service.CommonService;
import qdyq.api.service.exception.MapperNameErrorException;

@Service("common")
@SuppressWarnings({ "rawtypes", "unchecked" })
public class CommonServiceImpl implements CommonService {

    //private String entityName;
    private ThreadLocal<String> entityNameSafe = new ThreadLocal<String>();

    @Autowired
    private MySqlSessionFactoryBean sqlSessionFactory;

    public List list(Criteria criteria) {
        BaseMapper mapper = getMapper();
        System.out.println(mapper.getClass().getName());
        return mapper.getBy(criteria);
    }

    public BaseEntity getById(Long id) {
        BaseMapper mapper = getMapper();
        return mapper.getById(id);
    }

    public int save(String entityJson) {
        BaseMapper mapper = getMapper();
        BaseEntity entity = (BaseEntity) new Gson().fromJson(entityJson, this.getEntityClass());
        
        if(entity==null){
            System.err.println(Thread.currentThread().getId() + entityJson + this.entityNameSafe.get());
        }
        
        if (entity.getId() == null)
            return mapper.insert(entity);
        else
            return mapper.update(entity);
    }

    public int save(BaseEntity entity) {
        
        this.setEntityName(entity.getClass().getSimpleName());
        
        BaseMapper mapper = getMapper();
        
        System.err.println(mapper.getClass().getName() + Thread.currentThread().getId());

        
        if (entity.getId() == null)
            return mapper.insert(entity);
        else
            return mapper.update(entity);
    }

    public int countBy(Criteria criteria) {
        BaseMapper mapper = getMapper();
        return mapper.countBy(criteria);
    }

    public void setEntityName(String entityName) {
        this.entityNameSafe.set(entityName);
        //this.entityName = entityName;
    }

    public Class getEntityClass() {
        String daoName = this.sqlSessionFactory.getPkgName() + "." + this.entityNameSafe.get() + "Dao";
        Class daoInterface;
        try {
            daoInterface = Class.forName(daoName);
            return (Class) ((ParameterizedType) daoInterface.getGenericInterfaces()[0]).getActualTypeArguments()[0];
        } catch (ClassNotFoundException e) {
            throw new MapperNameErrorException("无效的entityName" + daoName, e, daoName);
        } catch (Exception e) {
            throw new MapperNameErrorException("未注册的entityName" + daoName, e, daoName);
        }
    }

    public List listAss(Criteria criteria) {
        BaseMapper mapper = getMapper();
        return mapper.getAssBy(criteria);
    }

    public List all() {
        BaseMapper mapper = getMapper();
        return mapper.findAll();
    }

    public int delete(Object[] id) {
        BaseMapper mapper = getMapper();
        return mapper.deleteByIds(id);
    }

    protected BaseMapper getMapper(){
        String daoName = this.sqlSessionFactory.getPkgName() + "." + this.entityNameSafe.get() + "Dao";
        Class daoInterface;
        System.err.println(daoName + Thread.currentThread().getId());
        HttpServletRequest request = null;
        try{
            daoInterface = Class.forName(daoName);
            request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(request.getSession().getServletContext());
            return (BaseMapper) context.getBean(daoInterface);
        }catch(Exception e){
            throw new RuntimeException("无法获取到mapper:" + this.entityNameSafe.get());
        }
        
    }

    public int saveAll(List<BaseEntity> list) {
        BaseMapper mapper = getMapper();
        return mapper.insertAll(list);
    }
    
    public int removeBy(Criteria criteria){
        BaseMapper mapper = getMapper();
        return mapper.removeBy(criteria);
    }
    
    public static void main(String[] args){
        //String json = "{id:485,lastTraceDate:'2017-11-09T10:20:37.293Z'}";
        
    }
}

这是一个公共的类,能满足一般的增删改查功能,通过参数,entityName来确定具体使用哪个mapper来完成任务,mapper只要有一个公共的基类,就可以实现上述

功能,但是由于网络请求是多线程的,所以及易导致entityName的冲突,因为原先的entityName只是一个本地字符串,而springmvc的对象又是单例的,这就导致错误的发生

ThreadLocal<String>是一种解决办法,第二种办法是把该对象配置成原型模式,也可以避免错误的发生.


既然类成员变量容易导致错误,哪么request会不会导致错误,因为这次错误引起了思考果断做了测试,
@ResponseBody
    @RequestMapping(value = "/save/{entityName}", method = RequestMethod.POST)
    public Object save(@PathVariable String entityName, String entityJson,HttpServletRequest request) {
        this.getService().setEntityName(entityName);
        System.err.println("============================");
        System.err.println(request.hashCode());
        System.err.println(this.request.hashCode());
        System.err.println(this.request.getParameter("entityJson"));
        System.err.println(this.request.getClass().getName());
        System.err.println(request.getClass().getName());
        System.err.println("============================");
        return getService().save(entityJson);
    }

this.request是通过@Autowired注入的,打印结果到显示this.request始终是一个对象,但是它的获取结果却始终都是正确的,因为它并不是一个对象而是一个代理,也就是说它是线程安全的.但是作为参数传进来的request就不是线程安全的,如果你把它保存在一个成员变量里,后果是很严重的,因为使用过期的request会导致现有request参数的丢失,具体原因还不太清楚,感觉应该是request这个对象里使用缓存之类的东西造成的.

posted on 2017-11-09 21:37  杨杨09265  阅读(229)  评论(0编辑  收藏  举报