概述
问题描述
在项目中加入Spring Security
后,运行web项目,报错:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'springSecurityFilterChain' available
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:816)
at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1288)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:207)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1115)
at org.springframework.web.filter.DelegatingFilterProxy.initDelegate(DelegatingFilterProxy.java:338)
at org.springframework.web.filter.DelegatingFilterProxy.initFilterBean(DelegatingFilterProxy.java:243)
at org.springframework.web.filter.GenericFilterBean.init(GenericFilterBean.java:239)
at org.apache.catalina.core.ApplicationFilterConfig.initFilter(ApplicationFilterConfig.java:270)
at org.apache.catalina.core.ApplicationFilterConfig.getFilter(ApplicationFilterConfig.java:251)
at org.apache.catalina.core.ApplicationFilterConfig.<init>(ApplicationFilterConfig.java:102)
at org.apache.catalina.core.StandardContext.filterStart(StandardContext.java:4566)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5203)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:717)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:690)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:706)
at org.apache.catalina.startup.HostConfig.manageApp(HostConfig.java:1727)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:288)
at java.management/com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:809)
at java.management/com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801)
at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:459)
at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:408)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:288)
at java.management/com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:809)
at java.management/com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801)
at java.management/com.sun.jmx.remote.security.MBeanServerAccessController.invoke(MBeanServerAccessController.java:468)
at java.management.rmi/javax.management.remote.rmi.RMIConnectionImpl.doOperation(RMIConnectionImpl.java:1466)
at java.management.rmi/javax.management.remote.rmi.RMIConnectionImpl$PrivilegedOperation.run(RMIConnectionImpl.java:1307)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:691)
at java.management.rmi/javax.management.remote.rmi.RMIConnectionImpl.doPrivilegedOperation(RMIConnectionImpl.java:1406)
at java.management.rmi/javax.management.remote.rmi.RMIConnectionImpl.invoke(RMIConnectionImpl.java:827)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
at java.rmi/sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:359)
at java.rmi/sun.rmi.transport.Transport$1.run(Transport.java:200)
at java.rmi/sun.rmi.transport.Transport$1.run(Transport.java:197)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:691)
at java.rmi/sun.rmi.transport.Transport.serviceCall(Transport.java:196)
at java.rmi/sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:587)
at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:828)
at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:705)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:704)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630)
at java.base/java.lang.Thread.run(Thread.java:832)
错误分析
搭建Spring Security
时,创建了一个基于注解的自定义配置类WebAppSecurityConfig
(该类必须被扫描,否则将不起作用!)。
但是在原本的项目中,存在两个IOC容器。
ContextLoaderListener负责了Spring IOC
容器的初始化,主要扫描的类是XxxxService
、XxxxMapper
类等。
DispatcherServlet负责了SpringMVC IOC
容器的初始化,主要负责XxxxHandler
、view-controller
。
而在项目中为了让Spring Security可以针对浏览器的请求进行权限控制
,所以需要让SpringMVC来扫描WebAppSecurityConfig配置类
。
但是在项目启动过程中报错未找到bean,和三大组件的启动顺序相关:
- 首先:ContextLoaderListener初始化,创建Spring的IOC容器
- 其次:Delegating初始化,查找IOC容器,查找bean
- 最后:DispatcherServlet初始化,创建SpringMVC的IOC容器
通过debug发现,按照官方源码的顺序,DelegatingFilterProxy
类中只能查找到Spring IOC
,但是我们的Spring Security
只存在于SpringMVC IOC
中,所以必然会报错。
首先进入initFilterBean()
方法初始化我们设置的bean
,通过findWebApplicationContext
方法会调用WebApplicationContextUtils
工具类获取得到WebApplicationContext
即Spring的IOC容器
,但是第一次查找并没有找到该bean,此时的delegate
为null
,初始化失败。doFilter()
会在第一次请求
的时候继续找IOC容器,由于项目并未启动
,所以无法执行doFilter()
,因此会始终找不到我们需要的bean,整个项目启动以未找到bean的错误告终,报错No bean named 'springSecurityFilterChain' available
@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) {
WebApplicationContext wac = findWebApplicationContext();
if (wac == null) {
throw new IllegalStateException("No WebApplicationContext found: " +
"no ContextLoaderListener or DispatcherServlet registered?");
}
delegateToUse = initDelegate(wac);
}
this.delegate = delegateToUse;
}
}
@Nullable
public static WebApplicationContext getWebApplicationContext(ServletContext sc) {
return getWebApplicationContext(sc, WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
}
解决措施
方式一:将Spring的IOC容器和SpringMVC的IOC容器合二为一
在web.xml
文件中做如下修改:
注释ContextLoaderListener
的相关设置
<!-- <context-param>-->
<!-- <param-name>contextConfigLocation</param-name>-->
<!-- <param-value>classpath:spring-persist-*.xml</param-value>-->
<!-- </context-param>-->
<!-- <listener>-->
<!-- <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>-->
<!-- </listener>-->
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>
通过debug发现,首先进入initFilterBean
方法,然后在doFilter
方法中初始化我们的filter成功!原因是在同一个IOC容器中可以找到对应delegate。
方式二:修改源代码
首先新建一个与源代码相同的包,并创建一个类名相同的类,做出如下修改:
initFilterBean
@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);
}*/
}
}
}
doFilter
@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 sc = this.getServletContext();
// 2. 拼接SringMVC将IOC容器存到ServletContext域的容器中
String servletName = "springDispatcherServlet";
String attrName = FrameworkServlet.SERVLET_CONTEXT_PREFIX + servletName;
// 3. 根据attrName从ServletContext域中获取IOC容器对象
WebApplicationContext wac = (WebApplicationContext) sc.getAttribute(attrName);
if (wac == null) {
throw new IllegalStateException("No WebApplicationContext found: " +
"no ContextLoaderListener or DispatcherServlet registered?");
}
delegateToUse = initDelegate(wac);
}
this.delegate = delegateToUse;
}
}
最后
以上就是纯情路灯为你收集整理的报错解决:No-bean-named-springSecurityFilterChain-available问题描述错误分析解决措施方式二:修改源代码的全部内容,希望文章能够帮你解决报错解决:No-bean-named-springSecurityFilterChain-available问题描述错误分析解决措施方式二:修改源代码所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复