| | 1 | |
| | 2 | == 1.java类中EnvUtils使用优化 == |
| | 3 | |
| | 4 | |
| | 5 | 在servlet处理请求时,EnvFilter中会调用EnvUtils.getEnv()实例化Env,并对实例进行参数设置,这些都是必要步骤,在非Controller类或jsp中使用EnvUtils.getEnv()可能会跳过这些步骤,从而造成程序出错。 |
| | 6 | |
| | 7 | {{{ |
| | 8 | //evn过滤器代码 org.gelivable.web.EnvFilter : 62 |
| | 9 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) |
| | 10 | throws IOException, ServletException { |
| | 11 | ...... |
| | 12 | Env env = EnvUtils.getEnv(); |
| | 13 | envMap.put(env, ""); |
| | 14 | try { |
| | 15 | //下面三个变量只有servlet处理请求时才会被实例化 |
| | 16 | env.setRequest((HttpServletRequest) request); |
| | 17 | env.setResponse((HttpServletResponse) response); |
| | 18 | env.setServletContext(servletContext); |
| | 19 | |
| | 20 | request.setAttribute("env", env); |
| | 21 | |
| | 22 | chain.doFilter(request, response); |
| | 23 | |
| | 24 | } |
| | 25 | ...... |
| | 26 | } |
| | 27 | }}} |
| | 28 | 在聚超值项目中,entity类(service偶尔也会出现)中经常会用env获取service或dao类实例的代码。 |
| | 29 | |
| | 30 | {{{ |
| | 31 | public class User { |
| | 32 | ...... |
| | 33 | public UserPurchasingInfo getInfo() { |
| | 34 | ...... |
| | 35 | info = EnvUtils.getEnv().getBean(GeliDao.class).find(UserPurchasingInfo.class, this.userId); |
| | 36 | //或者 info = EnvUtils.getEnv().getBean(UserPurchasingInfoService.class).find(this.userId); |
| | 37 | ...... |
| | 38 | } |
| | 39 | ...... |
| | 40 | } |
| | 41 | }}} |
| | 42 | 这种代码至少在三种情况下调用会出现NullPointerException错误:[[BR]] |
| | 43 | 1.在单元测试中被调用时[[BR]] |
| | 44 | 2.不依赖于servlet的定时任务中被调用时[[BR]] |
| | 45 | 3.在处理servlet的请求时,通过代码发起的新线程中简单的调用EnvUtils.getEnv().getBean(xxx.class)获取某个类的实例会和1、2点一样出现错误。[[BR]][[BR]] |
| | 46 | |
| | 47 | '''原因'''[[BR]] |
| | 48 | 1、2点的错误原因比较明显,原因是在代码执行的时候没有经过过滤器,EnvUtils.getEnv()只是把Env实例化了,env对象里的servletContext参数是空的,没有进行设置,调用的时候就报空指针异常了。[[BR]] |
| | 49 | 第3点报错的原因是因为EnvUtils.getEnv()用到了线程单例(代码如下),当线程第一次调用时threadLocal会实例一个Env对象,之后这个线程再调用都只返回这个对象,而且只能被这个线程调用,当在处理请求过程中如果新启了线程再调用EnvUtils.getEnv()就会得到一个新的Env实例,这个实例和1、2一样,没有设置env对象里的servletContext参数,调用env.getBean(xxx.class)的时候就会报空指针异常。 |
| | 50 | {{{ |
| | 51 | public class EnvUtils { |
| | 52 | public static Env getEnv() { |
| | 53 | return threadLocal.get(); |
| | 54 | } |
| | 55 | |
| | 56 | public static void removeEnv() { |
| | 57 | threadLocal.remove(); |
| | 58 | } |
| | 59 | |
| | 60 | //通过EnvUtils获取的Env实例是线程单例的,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。 |
| | 61 | private static final ThreadLocal<Env> threadLocal = new ThreadLocal<Env>() { |
| | 62 | @Override |
| | 63 | protected Env initialValue() { |
| | 64 | return new Env(); |
| | 65 | } |
| | 66 | }; |
| | 67 | |
| | 68 | } |
| | 69 | }}} |
| | 70 | '''优化方法'''[[BR]] |
| | 71 | 将非Controller的java类中的EnvUtils.getEnv().getBean(GeliDao.class)改为GeliUtils.getDao(),[[BR]] |
| | 72 | 将EnvUtils.getEnv().getBean(xxxService.class)改为SpringCtxUtils.getBean(xxxService.class)。(SpringCtxUtils在cn.pconline.best.util目录下) |