有很多错误由于需要是多线程是才会发生,导致经常在开发时很难发现,
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这个对象里使用缓存之类的东西造成的.