欲實現路由驗證,寫了一個Hibernate
攔截器,在對數據庫進行操做以前對菜單進行校驗,若是存在,則拋出異常,終止保存操做的執行;若是不存在,則繼續執行。攔截器代碼以下:html
package com.mengyunzhi.measurement.interceptor; import com.mengyunzhi.measurement.Service.WebAppMenuService; import com.mengyunzhi.measurement.entity.WebAppMenu; import org.hibernate.EmptyInterceptor; import org.hibernate.type.Type; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataIntegrityViolationException; import java.io.Serializable; /** * 繼承自Hibernate提供的EmptyInterceptor攔截器 * 在進行數據庫操做以前進行處理,判斷該路由是否惟一 * 抽象菜單:則不能相同 * 其子菜單:若是不屬於同一父菜單,能夠相同 */ public class WebAppMenuInterceptor extends EmptyInterceptor { @Autowired private WebAppMenuService webAppMenuService; /** * 重寫OnSave方法 */ @Override public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) { // 若是是菜單實體 if (entity instanceof WebAppMenu) { // 則執行路由驗證 this.validateWebAppMenuRoute((WebAppMenu) entity); } // 調用父類的onSave方法,不太明白這個方法設計返回boolean值是爲什麼? return super.onSave(entity, id, state, propertyNames, types); } /** * 路由驗證 */ private void validateWebAppMenuRoute(WebAppMenu webAppMenu) { // 初始化變量 Boolean isAbstract = webAppMenu.isAbstract(); String routeName = webAppMenu.getRouteName(); Boolean isExist; // 若是是抽象菜單 if (isAbstract){ // 根據路由名和抽象爲true,判斷是否存在 isExist = webAppMenuService.existsByRouteNameAndIsAbstractIsTrue(routeName); } else { // 非抽象,判斷其同一父菜單下是否有相同菜單 isExist = webAppMenuService.existsByRouteNameAndParentWebAppMenu(routeName, (WebAppMenu) webAppMenu.getParentWebAppMenu()); } if (isExist) { throw new DataIntegrityViolationException("該路由非法"); } } }
可是測試一直通不過,報錯:沒法加載上下文。java
打印了一下webAppMenuService
的值,發現全都是null
;果真,這個東西沒注入進來。web
看來@Autowired
並非萬能的,纔有了下文對Spring IOC
的學習。spring
系統的學習一下,不要像之前同樣去StackOverflow
查答案,而後照搬別人的解決方案,感受如今對Spring
的瞭解仍是太少了。數據庫
IOC
:控制反轉,你們都熟知的定義這裏就再也不贅述了。segmentfault
其實,Spring
負責幫咱們管理對象,就像我在關於接口的代碼複用中實現的手動注入對象的代碼同樣。架構
/** * 鳥 */ public class Bird extends Animal implements Volitant { private Volitant volitant; public void setVolitant(Volitant volitant) { this.volitant = volitant; } @Override public void fly() { this.volitant.fly(); } } /** * 蝙蝠 */ public class Bat extends Animal implements Volitant { private Volitant volitant; public void setVolitant(Volitant volitant) { this.volitant = volitant; } @Override public void fly() { this.volitant.fly(); } } public class Main { public static void main(String[] args) { Volitant volitant = new VolitantImpl(); Bird bird = new Bird(); bird.setVolitant(volitant); bird.fly(); Bat bat = new Bat(); bat.setVolitant(volitant); bat.fly(); } }
上述代碼是手動對一個類中須要設置的對象進行注入,其實Spring IOC
就是一個幫助咱們管理和注入對象的角色。maven
說到對象的管理,不得不提一個「容器」的概念,這個容器裝的是對象。ide
固然,這裏的容器是咱們的想象的一個概念,它的術語名稱你們可能會很熟悉——「上下文」,只是上下文是一種更高級的容器罷了。學習
創建一個maven
項目,依賴引入spring-context
,咱們以一個實際的Spring
使用來學習IOC
。
這是Spring
的七個模塊,在IBM
的開發者學習文檔中找到的。大體看了一下介紹,core
是Spring
的核心庫,有各類功能,context
調用核心庫中的方法,對外提供容器以及其餘功能。
BeanFactory
是一個最基礎的容器接口,提供了從容器中獲取對象,判斷容器中是否有某對象的方法。只有最基礎的容器功能,如今已經不建議使用。
public interface BeanFactory { String FACTORY_BEAN_PREFIX = "&"; Object getBean(String var1) throws BeansException; <T> T getBean(String var1, @Nullable Class<T> var2) throws BeansException; Object getBean(String var1, Object... var2) throws BeansException; <T> T getBean(Class<T> var1) throws BeansException; <T> T getBean(Class<T> var1, Object... var2) throws BeansException; boolean containsBean(String var1); boolean isSingleton(String var1) throws NoSuchBeanDefinitionException; boolean isPrototype(String var1) throws NoSuchBeanDefinitionException; boolean isTypeMatch(String var1, ResolvableType var2) throws NoSuchBeanDefinitionException; boolean isTypeMatch(String var1, @Nullable Class<?> var2) throws NoSuchBeanDefinitionException; @Nullable Class<?> getType(String var1) throws NoSuchBeanDefinitionException; String[] getAliases(String var1); }
既然咱們要從容器中拿對象?那咱們就須要告訴IOC
容器,你的容器中應該有什麼對象,而且各個對象之間的依賴關係,總不能我寫的每一個類都實例化到容器中吧?因此誕生了xml
配置文件。相似下面這樣。
一個bean
,id
是什麼,對應的是哪一個類,而且這個對象依賴於其餘的什麼對象。
看了一下,有個刪除線,不建議使用,也就沒有過深的研究,咱們投入上下文的懷抱。
ApplicationContext
固然也支持xml
配置,其實現類爲ClassPathXmlApplicationContext
。
由於本人更喜歡註解,這裏深刻學習一下註解的配置方式。
模擬項目中的真實架構,Service
接口,依賴,爲其注入實現。
TestService
接口:
package com.mengyunzhi.spring.service; public interface TestService { }
TestService
實現:
package com.mengyunzhi.spring.service; import org.springframework.stereotype.Component; @Component public class TestServiceImpl implements TestService { @Override public String toString() { return "我是TestServiceImpl, TestService的實現"; } }
你問這裏爲何加的是@Component
註解而不是@Service
,其實這幾個註解,不管是@Service
、@Repository
、@Controller
實際上都是聲明一個組件,讓其被Spring
所管理,這麼設計,不過是讓人們看代碼上的註解時,就能清晰地瞭解這個類的職責。
注意,這裏加的@Component
告訴Spring
,你爲我管理這個類。而後還有一個註解,@ComponentScan
就是表示我要掃描哪一個包下的有@Component
註解的類,並對其進行管理。
主方法:
package com.mengyunzhi.spring; import com.mengyunzhi.spring.service.TestService; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.ComponentScan; @ComponentScan("com.mengyunzhi.spring") public class Application { public static void main(String []args) { ApplicationContext context = new AnnotationConfigApplicationContext(Application.class); TestService testService = context.getBean(TestService.class); System.out.println(testService); } }
新建一個基於註解的上下文,而後獲取TestService
這個類的實例,同時打印。注意,這裏的@ComponentScan
必定要加,不加會報錯。(我感受這種方式比xml
配置簡單多了,@
幾下就完成了配置)
運行結果:
突發奇想,咱們修改一下toString
方法的代碼,相信你看到代碼就知道我想幹什麼了。
package com.mengyunzhi.spring.service; import org.springframework.stereotype.Component; @Component public class TestServiceImpl implements TestService { @Override public String toString() { return "我是TestServiceImpl, TestService的實現\n" + "個人內存地址是:" + super.toString(); } }
修改主方法代碼,如今咱們從上下文中獲取兩個TestService
的實例。
package com.mengyunzhi.spring; import com.mengyunzhi.spring.service.TestService; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.ComponentScan; @ComponentScan("com.mengyunzhi.spring") public class Application { public static void main(String []args) { ApplicationContext context = new AnnotationConfigApplicationContext(Application.class); TestService testService1 = context.getBean(TestService.class); System.out.println(testService1); TestService testService2 = context.getBean(TestService.class); System.out.println(testService2); } }
咱們發現這兩個對象的內存地址是相同的,說明這兩個對象引用的是同一塊內存。
這就涉及到Bean
對象的Scope
屬性,默認的Scope
屬性是Singleton
,單例,全容器共享這個實例。
若是想使用多例,就將其Scope
屬性設置爲Prototype
。
@Component @Scope("prototype") public class TestServiceImpl implements TestService { @Override public String toString() { return "我是TestServiceImpl, TestService的實現\n" + "個人內存地址是:" + super.toString(); } }
在TestServiceImpl
上加上@Scope
註解,注意這裏的prototype
必定要是小寫的。
再次運行:
兩個內存地址不一樣,這是兩個獨立的對象。
咱們本身寫的類,咱們想將其加入到容器中,咱們能夠將其加上@Component
註解將其添加到容器中,可是若是咱們想把某個第三方庫中的對象添加到容器中怎麼辦呢?咱們可不能去改動依賴開源庫的代碼。
package com.mengyunzhi.spring; public class OtherService { public void show() { System.out.println("我是一個第三方的庫"); } }
package com.mengyunzhi.spring; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan("com.mengyunzhi.spring") public class Application { public static void main(String []args) { ApplicationContext context = new AnnotationConfigApplicationContext(Application.class); OtherService otherService = context.getBean(OtherService.class); otherService.show(); } @Bean OtherService getOtherService() { return new OtherService(); } }
@Configuration
:表示這是一個Spring
的配置類,而後就能夠在其中配置相關Bean
,經過@Bean
註解,會將其標註的方法的返回值對象加入到容器中。
運行結果:
Spring IOC
其實就是一個容器,咱們常說的啓動一個Spring
項目其實就是所謂的加載容器(上下文)。
路漫漫其修遠兮,讓咱們一塊兒探索
Spring
的本質!