javascript
Shiro切入Spring的方式
在springMVC中要使用shiro,一般都遵循下面的配置:
applicationContext-shiro.xml
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager" /> .....</bean>web.xml
<listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener><!-- Spring MVC Servlet --> <servlet><servlet-name>springServlet</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>classpath*:/spring/springmvc-common.xml</param-value></init-param><load-on-startup>1</load-on-startup> </servlet> <servlet-mapping><servlet-name>springServlet</servlet-name><url-pattern>/</url-pattern> </servlet-mapping><filter><filter-name>shiroFilter</filter-name><filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class><init-param><param-name>targetFilterLifecycle</param-name><param-value>true</param-value></init-param> </filter> <filter-mapping><filter-name>shiroFilter</filter-name><url-pattern>/*</url-pattern> </filter-mapping>看到這樣的配置,難免有些疑問:
1. 這三者是怎樣融合到一起的
2. 為什么那個bean的id和filter的name保持一樣
3. 為什么配置的bean是一個factoryBean而不是普通的bean
帶著這樣的疑問,跟蹤下啟動過程。正向跟蹤,即在refresh方法中開展,鎖定getBean方法,通過name來追蹤shiroFilter,發現關系比較亂,而且費時。于是鎖定ShiroFilterFactoryBean的getObject方法,反向追蹤,獲得了下面的序列圖:
重點就是:
配置的監聽器啟動容器的初始化,完成上面ShiroFilterFactoryBean的創建和維護
servlet觸發過濾器Filter的init方法,在initDelegate方法中會getBean(),這個getBean最終會轉移到ShiroFilterFactoryBean的getObject方法
上面的問題第二個看源碼很容易解決。
在調用DelegatingFilterProxy初始化方法時:
@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);}}} }protected final String getFilterName() {return (this.filterConfig != null ? this.filterConfig.getFilterName() : this.beanName); }protected Filter initDelegate(WebApplicationContext wac) throws ServletException {Filter delegate = wac.getBean(getTargetBeanName(), Filter.class);if (isTargetFilterLifecycle()) {delegate.init(getFilterConfig());}return delegate; }獲取的這個名字將用來追尋之前spring維護的那個bean,并且還要通過它來拿到一個Filter實例。
那么為什么配置的那個bean是一個factoryBean而不是一個普通的bean?
首先shiro是基于過濾器來實現的,配置一個filter是必要的。
但是和spring在一起使用,就要讓spirng來管理一些filter依賴的bean,例如安全管理器,還有自己實現的認證和鑒權服務之類。如果shiro直接寫一個過濾器,那spring的容器早就啟動完了,你這些東西怎么注入。
所以只有提前初始化那些過濾器需要的東西,讓他們依附于某個特定類,通過約定來在過濾器的初始化中獲取(這個就是前面提到的相同的name)。
那么這個bean可以是普通的bean嗎?答案是不可以,畢竟shiro要的這個bean得是一個filter。普通的bean只能獲取到它本身的實例,要獲取filter那么它必須實現Filter接口。但是創建filter的方法你掌控不了了,spring會通過反射來創建對象,怎么創建是它說了算,自定義的創建是不可能了。
這時候你就只能使用FactoyBean了。
這個FactoryBean當然得交給shiro實現。不過filter的創建方式自由了,我們可以通過getBean來獲取這個filter,另一邊的filter配置還有必要嗎?
當然必要。因為這個filter怎么創建不重要,切入web的生命周期才重要,這個配置是一個規范。所以spring引入了這么一個類: DelegatingFilterProxy。就是一個代理類,自定義的實現轉移到factoryBean了,這里就是公共類,主要操作是在容器中追蹤并獲取之前的factoryBean,并在初始化方法中獲取需要的filter。
Spring容器的啟動依靠監聽器,而filter是在監聽器之后,依靠servlet規范,在listener,filter和servlet三者之間尋找契機,并形成一個共榮圈,Spring把這種關系處理得非常妙!
轉載于:https://www.cnblogs.com/lucare/p/8679131.html
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的Shiro切入Spring的方式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: spring mvc4 笔记
- 下一篇: JAVA知识积累 JSP第一篇【JSP介