Servlet中自动注入sping IOC容器中bean失败原因分析
在用Spring + Mybatis + servlet 执行web项目时, 在Servlet类中用@Autowired 自动注入Spring容器中已经配置的bean时,出现空指针异常
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bdI1bT1h-1595920757535)(C:\Users\yangchx\AppData\Roaming\Typora\typora-user-images\1595917994291.png)]
- 空指针异常
java.lang.NullPointerException
at com.itheima.web.UserServlet.doGet(UserServlet.java:47)
at com.itheima.web.UserServlet.doPost(UserServlet.java:40)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:647)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)
-
异常原因分析 :
-
Spring 配置文件没有注入bean, 没有扫描到该Servlet类
-
检查完所有配置文件, 确定IOC容器中已经存在了bean
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p1P9vNLu-1595920757536)(C:\Users\yangchx\AppData\Roaming\Typora\typora-user-images\1595917819990.png)]
-
-
客户端请求的Servlet 和Spring 容器中的Servlet不是同一对象,导致请求的Servlet不能从Spring容器中直接获取对象
- tomcat启动后先加载web.xml文件, web.xml中配置的servlet 、filter、listenner三种javaee规范的类 , 加载顺序为 : listenner > filter > servlet
- spring的初始化类为org.springframework.web.context.ContextLoaderListener就是一listenner,在Spring加载完时, 所有配置bean和标记类都会被注入到IOC容器中, 所以只要在Servlet类上标记@Controller ,它就会被Spring容器管理, 由于这里没有使用SpringMVC 配置文件中并没有配置DispatcherServlet , 在客户端请求服务器时 , tomcat 会根据第一次请求 , 自动实例化一个Servlet 对象,这个对象相当于是Tomcat 新建(new) 出来的 ,跟IOC容器中的对象并不是同一个, 所以并不被Spring管理,用@Autowired自动注入会失败.
-
-
解决方法 :
- 在tomcat创建Servlet对象时, 初始化方法中,手动将该类注入到Sring 容器中
@Override public void init() { SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this); }
-
spring-web在version2.5.1的时候,在包org.springframework.web.context.support下加入了一个工具类叫SpringBeanAutowiringSupport,主要用来对Spring Web Application上下文的类提供@Autowired注入功能
-
SpringBeanAutowiringSupport源码分析
/ ** *处理给定目标对象的{@code @Autowired}注入, *基于当前的Web应用程序上下文。 * <p>拟作为代表使用。 * @param定位要处理的目标对象 * @see org.springframework.web.context.ContextLoader#getCurrentWebApplicationContext() * / public static void processInjectionBasedOnCurrentContext(Object target){ Assert.notNull(target, “Target对象不能为null” ); // 通过类加载器获取当前WebApplicationContext 对象 WebApplicationContext cc = ContextLoader.getCurrentWebApplicationContext(); if (cc!= null ){ //创建AutowiredAnnotationBeanPostProcessor ,用来解决目标类的@Autowired注入能力 AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor(); bpp.setBeanFactory(cc.getAutowireCapableBeanFactory()); bpp.processInjection(目标); } 其他 { if (logger.isDebugEnabled()){ logger.debug(“当前WebApplicationContext不可用于处理” + ClassUtils.getShortName(target.getClass())+ “:” + “确保在Spring Web应用程序中构建此类。无需注入即可继续执行。” ); } } }
-
<context:component-scan> 标签
-
Spring会默认生成注册AutowiredAnnotationBeanPostProcessor类来帮助解析@Autowired @Value 等标签.
-
Spring会默认生成注册AutowiredAnnotationBeanPostProcessor类来帮助解析@Autowired @Value 等标签.