Web项目加入SpringSecurity后抛异常NoSuchBeanDefinitionException

一、先让我们来看看异常信息

抛异常

二、在解决异常前我们先需要知道的事情

1、Web组件加载顺序

首先是:ContextLoaderListener初始化,创建Spring的IOC容器
其次是:DelegatingFilterProxy初始化,查找IOC容器,查找Bean
最后是:DispatcherServlet初始化,创建SpringMVC的IOC容器

2、有两个IOC容器

① Spring的IOC容器

Spring的IOC容器装配的 Bean 有 Mapper 和 Service ,因此当 SpringSecurity 加载到 Spring 的IOC容器时只对 Mapper 和 Service 生效
在这里插入图片描述

②SpringMVC的IOC容器

SpringMVC的IOC容器中装配的 Bean 有 Controller,所以当 SpringSecurity 加载到SpringMVC的IOC容器时就可以对 Controller 请求生效。(这也正是我们想要的效果,所以该web项目采用的SpringMVC的IOC容器)
在这里插入图片描述

3、DelegatingFilterProxy初始化后查找Bean的机制

在这里插入图片描述

三、问题分析

ContextLoaderListener 初始化后,springSecurityFilterChain 就在 ContextLoaderListener 创建的 Spring的IOC 容器中查找所需要的 bean,但是我们没有在 ContextLoaderListener 的 IOC 容 器中扫描 SpringSecurity 的配置类,所以 springSecurityFilterChain 对应的 bean 找不到。
在这里插入图片描述

四、解决思路

方案一:把两个IOC容器合二为一

不再使用 ContextLoaderListener ,而是让 DispatcherServlet 扫描所有的 spring 配置文件。

	<servlet>
		<servlet-name>springDispatcherServlet</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:spring-*.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>
		<servlet-mapping>
		<servlet-name>springDispatcherServlet</servlet-name>
		<!-- url-pattern配置方式1: /表示拦截所有请求-->
		<!-- <url-pattern>/</url-pattern> -->
		
		<!-- url-pattern配置方式2:使用扩展名
			优点:1、xxx.css、xxx.png、xxx.js等静态资源不经过SpringMVC
				2、可以实现伪静态效果。即表面看上去是访问一个HTML这样一个静态页面,实际上是静JAVA代码经过运算的
				
				伪静态优点:
					1、给黑客入侵增加难度
					2、有利于SEO优化。(即百度、谷歌等搜索引擎更容易找我们的项目)
			缺点:违背了RestFul风格
		-->
		<url-pattern>*.html</url-pattern>

		<!-- 为什么要另外配置json扩展名呢? -->
		<!-- 如果一个ajax请求扩展名html,但实际服务器给浏览器返回的数据是json数据,会出现406错误 -->
		<!-- 为了能让Ajax请求能顺利拿到Json中得数据,配置一个.JSON的扩展名 -->
		<url-pattern>*.json</url-pattern>
	</servlet-mapping>


	<filter>
		<filter-name>springSecurityFilterChain</filter-name>
		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>springSecurityFilterChain</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

方案二:改源码

创建同名 org.springframework.web.filter 的包名,创建同名 DelegatingFilterProxy 类 ,将源码复制粘贴到里面,再进行修改。

第一处:初始化时,直接跳过查找IOC容器的环节

    @Override
    protected void initFilterBean() throws ServletException {
        synchronized (this.delegateMonitor) {
            if (this.delegate == null) {
                // If no target bean name specified, use filter name.
                if (this.targetBeanName == null) {
                    this.targetBeanName = getFilterName();
                }
                // Fetch Spring root application context and initialize the delegate early,
                // if possible. If the root application context will be started after this
                // filter proxy, we'll have to resort to lazy initialization.
                
                // 修改的第一处
//                WebApplicationContext wac = findWebApplicationContext();
//                if (wac != null) {
//                    this.delegate = initDelegate(wac);
//                }
            }
        }
    }

第二处:第一次请求的时候直接找SpringMVC的IOC容器

@Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {

        // Lazily initialize the delegate if necessary.
        Filter delegateToUse = this.delegate;
        if (delegateToUse == null) {
            synchronized (this.delegateMonitor) {
                delegateToUse = this.delegate;
                if (delegateToUse == null) {

                    // 把原来的查找IOC容器的代码注释掉
                    // WebApplicationContext wac = findWebApplicationContext();


                    // 按照自己的需要重新编写
                    // 1、获取 ServletContext 对象
                    ServletContext servletContext = this.getServletContext();

                    // 2、拼接 SpringMVC将IOC容器存入ServletContext域的时候使用的属性名
                    String servletName = "springDispatcherServlet";

                    String attrName = FrameworkServlet.SERVLET_CONTEXT_PREFIX + servletName;

                    // 3、根据 attrName 从 ServletContext 域中获取IOC容器对象
                    WebApplicationContext wac = (WebApplicationContext) servletContext.getAttribute(attrName);
                    
                    if (wac == null) {
                        throw new IllegalStateException("No WebApplicationContext found: " +
                                "no ContextLoaderListener or DispatcherServlet registered?");
                    }
                    delegateToUse = initDelegate(wac);
                }
                this.delegate = delegateToUse;
            }
        }

        // Let the delegate perform the actual doFilter operation.
        invokeDelegate(delegateToUse, request, response, filterChain);
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值