Java基礎
1、JRE和JDK和JVM的區別?
JRE是java程序運行標准環境;JDK包含JRE它是java程序開發最小環境;JVM是Java虛擬機,java程序需要在虛擬機上運行,不同平台有不同JVM,實現java程序跨平台。
2、javase,javaee,javame的區別?
java分為三個版本,標准版javase,企業版javaee,微型版javame
3、JDK和SDK的區別?
SDK(Software Development Kit)軟件開發工具包,相當廣泛的名詞是一系列文件的組合;JDK(JAVA Development Kit)JAVA開發工具包,針對java開發的。
4、Java八大基本數據類型?
數據類型 | 所占字節 |
byte | 1字節,8比特位 |
short | 2字節,16位 |
int | 4字節,32位 |
long | 8字節,64位 |
float | 4字節,32位 |
double | 8字節,64位 |
boolean | true/false |
char | 2字節,16位 |
5、面向對象的三個特征?
封裝、繼承、多態。
封裝:對象屬性私有,對外提供一個公共訪問的方法。好處是提高代碼安全性,實現代碼組件化。
繼承:子類繼承父類,無需重新編寫實現代碼功能的擴展。
多態:父類的引用指向子類的對象。多態存在的三個必要條件(繼承,重寫,父類的引用指向子類的對象)
6、什么是父類的引用指向子類的對象?
即聲明的是父類,實際指向的是子類的一個對象。
優點可以用這幾個關鍵詞來概括:多態、動態鏈接,向上轉型
7、Java中訪問修飾符?
private:同一類中可見
dufault:同一包下可見
protected:同包及其子包下可見
public:對所有類可見
8、&,&&,|,|| 的區別?
9、Java中運算符分為幾種?
分為算數運算符和邏輯運算符。
10、面向對象的優點?
優點:易維護、易復用、易擴展,由於面向對象有封裝、繼承、多態性的特性,可以設計出低耦合的系統,使系統更加靈活、更加易於維護
缺點:性能比面向過程低
11、面向對象的五大基本原則?
①單一職責原則:類的功能要單一。
②開放封閉原則:一個模塊對於擴展是開放的對於修改是封閉的。
③里式替換原則:子類可以替換父類出現在父類能夠出現的任何地方。
④依賴倒置原則:高層次的模塊不應該依賴於低層次的模塊,他們都應該依賴於抽象。抽象不應該依賴於具體實現,具體實現應該依賴於抽象。
⑤接口分離原則:設計時采用多個與特定客戶類有關的接口比采用一個通用的接口要好。
12、抽象類和接口的區別?
抽象類是用來捕捉子類的通用特性的。接口是抽象方法的集合。
從設計層面來說,抽象類是對類的抽象,是一種模板設計,接口是行為的抽象,是一種行為的規范。
相同點:
①接口和抽象類都不能實例化
②都位於繼承的頂端,用於被其他類實現或繼承
③都包含抽象方法,其子類都必須覆寫這些抽象方法
不同點:
①抽象類可以有構造器,接口不可以
②子類用extends繼承,用implements實現
③一個類只能繼承有個抽象類,但可以實現多個接口
13、局部變量和成員變量的區別?
①局部變量定義在方法中,成員變量定義在類內部方法外部。
②成員變量隨着對象的創建而創建隨着對象的消失而消失,存儲在堆內存中;局部變量當方法被調用時存儲在棧內存中,方法調用完自動釋放。
14、重載和重寫的區別?
構造器不能被重寫,但可以被重載。
重載:方法名相同,參數列表不同。
重寫:發生在父子類中,方法名和參數列表必須相同。
15、“==” 和 equals的區別?
equals比較的是值相同;“==”當比較的是基本數據類型比較的是值,當是引用類型比較的是內存地址。
16、Java獲取反射的三種方法?
1.通過new對象實現反射機制 2.通過路徑實現反射機制 3.通過類名實現反射機制
//方式一(通過建立對象) Student stu = new Student(); Class classobj1 = stu.getClass(); System.out.println(classobj1.getName()); //方式二(所在通過路徑-相對路徑) Class classobj2 = Class.forName("fanshe.Student"); System.out.println(classobj2.getName()); //方式三(通過類名) Class classobj3 = Student.class; System.out.println(classobj3.getName());
17、Java集合中有序無序的概念。
有序、無序是指在進行插入操作時,插入位置的順序性。先插的位置在前,后插的位置在后,則為有序,反之無序
集合 | 有序無序 |
實現List接口的 | ArrayList、LinkedList有序 |
實現Set接口的 | HashSet無序、TreeSet有序 |
實現Map | HashMap無序,TreeMap有序 |
18、有關HashMap
HashMap數組初始長度為16,HashMap只允許一個key為null,允許多個value為null。
HashMap擴容分為兩步:第一步把數組長度變為原來的兩倍,第二步采用頭插法,把舊的數組的元素插入到新數組中。
HashMap大小為什么是2的冪次方:避免造成浪費和不隨機等為題。
為什么負載因子0.75:負載因子過大空間利用率上升但是時間效率下降了,過小則反之。
19、Map.put(k,v)實現原理:先將k,v封裝到node對象中(節點),然后根據k的hashCode()方法得出hash值,
然后通過哈希表函數將hash值轉為數組下標,如果下標位置沒有任何元素九八node添加上去,
如果對應的下標位置上有鏈表,則拿k對每個鏈表上的節點k進行equals如果返回true則節點上的value將會被覆蓋,如果返回都是false則在鏈表末尾添加新的節點。
Spring框架面試題
1、什么是Spring
Spring是一種輕量級框架,一般說的Spring框架就是指Spring Framework,它是很多模塊的集合,這些模塊分別是:核心容器、數據訪問/集成、Web、AOP(面向切面編程)、工具、消息和測試模塊。
Spring的6個特征:
核心技術:依賴注入(DI),AOP,事件(Events),資源,i18n,驗證,數據綁定,類型轉換,SpEL。
測試:模擬對象,TestContext框架,Spring MVC測試,WebTestClient。
數據訪問:事務,DAO支持,JDBC,ORM,編組XML。
Web支持:Spring MVC和Spring WebFlux Web框架。
集成:遠程處理,JMS,JCA,JMX,電子郵件,任務,調度,緩存。
語言:Kotlin,Groovy,動態語言。
2、談談對Spring IOC和AOP的理解?
IOC(控制反轉,依賴注入)是一種設計思想,就是將原本在程序中手動創建的對象的控制權,交給Spring容器管理。將對象之間的依賴交給IOC容器
管理,由IOC容器完成對象的注入,簡化應用開發,需要創建一個對象時,只需要配置好文件/注解即可。降低代碼之間的耦合度
增加了項目的可維護性,降低了開發難度。
AOP面向切面編程,可以通過預編譯方式和運行期間動態代理,在不修改源碼的情況下給程序動態擴展功能的一種技術。
如果要代理的對象實現了某個接口,則Spring AOP就會用JDK動態代理去創建代理對象,沒有實現接口的對象,無法使用JDK
動態代理,轉而使用CGlib動態代理生成一個被代理對象的子類來作為代理。
3、Spring中bean的生命周期?
初始化--》使用--》銷毀
4、Spring框架中用到了哪些設計模式?
- 工廠設計模式:Spring使用工廠模式通過BeanFactory和ApplicationContext創建bean對象。
- 代理設計模式:Spring AOP功能的實現。
- 單例設計模式:Spring中的bean默認都是單例的。
- 模板方法模式:Spring中的jdbcTemplate、hibernateTemplate等以Template結尾的對數據庫操作的類,它們就使用到了模板模式。
- 包裝器設計模式:我們的項目需要連接多個數據庫,而且不同的客戶在每次訪問中根據需要會去訪問不同的數據庫。這種模式讓我們可以根據客戶的需求能夠動態切換不同的數據源。
- 觀察者模式:Spring事件驅動模型就是觀察者模式很經典的一個應用。
- 適配器模式:Spring AOP的增強或通知(Advice)使用到了適配器模式、Spring MVC中也是用到了適配器模式適配Controller。
5、Spring的事務管理方式有幾種?
編程式事務:編程方式管理事務,極大靈活性,難維護。
聲明式事務:可以將業務代碼和事務管理分離,用注解和xml配置來管理事務。
6、spring注入bean的幾種方式?
構造器,setter方法,注解
SpringMVC框架面試題
1、說說對SpringMVC的了解
MVC是一種設計模式,在SpringMVC下主要可以把后端項目分為,Controller控制層,Service業務層,Dao持久層和實體類。
2、Spring MVC的工作原理?
- 客戶端(瀏覽器)發送請求,直接請求到DispatcherServlet。
- DispatcherServlet根據請求信息調用HandlerMapping,解析請求對應的Handler。
- 解析到對應的Handler(也就是我們平常說的Controller控制器)。
- HandlerAdapter會根據Handler來調用真正的處理器來處理請求和執行相對應的業務邏輯。
- 處理器處理完業務后,會返回一個ModelAndView對象,Model是返回的數據對象,View是邏輯上的View。
- ViewResolver會根據邏輯View去查找實際的View。
- DispatcherServlet把返回的Model傳給View(視圖渲染)。
- 把View返回給請求者(瀏覽器)。
3、Spring MVC中的主要組件
前端控制器DispatcherServlet
處理映射器HandlerMapping
處理適配器HandlerAdapter
處理器Handler(控制器)
視圖解析器ViewResolver
試圖View(jsp)
4、Spring MVC怎樣設定重定向和轉發?
forward、redirect
5、如何開啟注解處理器和適配器?
配置文件中添加<mvc:annotation-driven>實現
MyBatis框架面試題
1、${} 和 #{} 的區別?
#傳入的參數都會當成一個字符串,會對傳入的數據加上引號,$傳入的參數是直接顯示生成在sql中的被當作一個對象,
#方式 它底層采用預編譯方式PerparedStatement,很大程度上能夠防止sql注入問題,$方式 底層使用Statement,無法
方式sql注入問題。在傳數據庫名/表名/字段名時使用$。
2、MyBatis是如何將sql執行結果封裝為目標對象並返回的?
mybatis有兩種映射,一種是resultType,另一種是resultMap。
resultType也叫自動映射,對象屬性和數據表字段名必須一致才能映射成功,不一致時可以在sql中取列別名。
resultMap也叫手動映射,可以手動將實體類屬性和數據表字段一一對應。
3、如何開啟MyBatis的延遲加載?
延遲加載又叫做按需加載,就是當用到需要的數據的時候才會執行查詢操作,減輕了數據庫的壓力。
mybatis的延遲加載功能默認是關閉的,需要在xml文件中通過setting標簽來開啟延遲加載功能。
lazyLoadingEanbled:全局性設置懶加載,true
aggressiveLazyLoading:false
<settings> <setting name ="aggressiveLazyLoading" value="false"/> <!--開啟延遲加載--> <setting name="lazyLoadingEnabled" value="true"/> </settings>
4、MyBatis支持延遲加載的原理?
使用CGLIB創建目標對象的代理對象,然后調用目標方法時,進入攔截器方法。比如調用對象的get方法獲取對象屬性時,
攔截器的invoke()方法發現get方法獲取的是nulll值,然后就會單獨發送事先保存好的查sql,把之前需要的屬性查詢出來,
然后用set方法賦值,於是在前一個對象屬性就有值了。最后調用對象的.getB().getName()方法。
5、JDBC編程的不足有哪些,mybatis是如何解決的?
JDBC創建數據庫的連接,頻繁釋放造成系統資源浪費影響了性能,而且sql語句寫在代碼中不易維護。
mybatis將sql語句與java代碼分離,能夠將數據庫記錄封裝成java對象,mybatis能夠自動將sql結果有哪個映射到java對象。
6、MyBatis的一級緩存和二級緩存?
mybatis一級緩存指的是SQLSession,一級緩存的作用域是SQLSession。
mybatis默認開啟一級緩存,在同一個sqlSession中執行相同的sql查詢時,第一次會查詢數據庫,並寫在緩存中。
第二次會直接從緩存中取,當兩次查詢之間發生增刪改的操作時,則sqlSession的緩存會被清空。每次查詢前去緩存中找,
如果找不到再去查數據庫。mybatis的內部緩存使用的是HashMap,key值為hashCode+statementId+sql語句,value值為
查詢出來的結果集映射成的java對象。
mybatis默認沒有開啟二級緩存,二級緩存指的是mapper映射文件,作用域是同一個namespace下。
開啟二級緩存:
cacheEnabled設置為true;對應的mapper.xml文件中需要使用<cache/>標簽
二級緩存,第一次查詢信息會存放在mapper對應的二級緩存中,第二次相同的sql查詢會去對應的二級緩存內取結果,
如果調用了相同的namespace下的增刪改sql並執行了commit操作,此時緩存會被清除。
7、MyBatis編程步驟是什么?
- 創建SqlSessionFactory工廠(Mybatis工廠)
- 通過工廠創建SqlSession.
- SqlSession執行數據庫操作.
- Session.commit()提交事務.
- Session.close()關閉會話.
SpringBoot框架面試題
1、什么是SpringBoot?
用來簡化Spring應用的初始搭建以及開發過程,使用特定的方式來進行配置,properties或yml文件。
創建獨立的Spring引用程序main方法運行。
嵌入式tomcat無需部署war文件
簡化了maven配置
starter自動化配置
2、SpringBoot自動配置原理?
SpringBoot遵循“約定優於配置”的原則。
在入口類中使用@SpringBootApplication注解來啟動整個應用,它是SpringBoot的核心注解,
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication { }
上面注解里最主要的就是@EnableAutoConfiguration(開啟自動裝配)
再進入@EnableAutoConfiguration的源碼:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(EnableAutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { }
可以看到@EnableAutoConfiguration注解內部使用了@Import注解來完成導入配置的功能。
EnableAutoConfigurationImportSelector它使用SpringFactoriesLoader.loadFactoryNames方法,用來掃描具有
MEAT-INF/spring.factories文件的jar包,它的里面都是自動配置類,SpringBoot會根據這些自動配置類去自動配置環境。
spiringboot項目啟動時,會執行selectImport方法,找到自動配置類對相應的class然后將所有自動配置類加載到Spring容器中
spring.factories文件是key,value形式的。
3、SpringBoo核心配置文件是什么?
bootstrap(.yml或.properties),由ApplicationContext加載比application文件優先加載,且里面的屬性不能被覆蓋。
application(.yml或.properties),用於spring boot項目的自動化配置。
SSM框架和SpringBoot框架的區別
1、SpringBoot、Spring MVC、Spirngde區別?
SpringFrame
SpringFramework 最重要的特征是依賴注入。所有 SpringModules 不是依賴注入就是 IOC 控制反轉。
當我們恰當的使用 DI 或者是 IOC 的時候,我們可以開發松耦合應用。松耦合應用的單元測試可以很容易的進行。
SpringMVC
Spring MVC 提供了一種分離式的方法來開發 Web 應用。通過運用像 DispatcherServelet,MoudlAndView 和 ViewResolver 等一些簡單的概念,開發 Web 應用將會變的非常簡單。
SpringBoot
Spring 和 SpringMVC 的問題在於需要配置大量的參數,Spring Boot 通過一個自動配置和啟動的項來目解決這個問題。
為了更快的構建產品就緒應用程序,Spring Boot 提供了一些非功能性特征。
MySQL數據庫面試題
1、什么是sql?
結構化查詢語言,是一種數據庫查詢語言。用於存儲數據、查詢、更新、和管理關系型數據庫。
2、什么是MySQL?
它是企業級開發中非常常用的,開源的關系型數據庫。
3、數據庫的三大范式?
第一范式:每列保存原子性,每個列都不可以再拆分。
第二范式:在第一范式的基礎上,第二范式確保表中的每列都和主鍵相關,而不能只與主鍵的一部分相關,這是針對聯合主鍵
而言,也就是說每一個數據表中只能保存一中數據。
第三范式:在第二范式的基礎上,非主鍵只能依賴於主鍵,不依賴於其他非主鍵。就是說確保每列都和主鍵列直接相關,而不是
間接相關,避免冗余列。
4、MySQL有關權限的表有哪幾個?
- user權限表:記錄允許連接到服務器的用戶帳號信息,里面的權限是全局級的。
- db權限表:記錄各個帳號在各個數據庫上的操作權限。
- table_priv權限表:記錄數據表級的操作權限。
- columns_priv權限表:記錄數據列級的操作權限。
- host權限表:配合db權限表對給定主機上數據庫級操作權限作更細致的控制。這個權限表不受GRANT和REVOKE語句的影響。
5、mysql分為哪幾種數據類型?
- 整數類型
- 小數類型
- 字符串類型
- 枚舉類型
- 日期和時間類型
6、MySQL常用存儲引擎有哪些?
- Innodb引擎:Innodb引擎提供了對數據庫ACID事務的支持。並且還提供了行級鎖和外鍵的約束。它的設計的目標就是處理大數據容量的數據庫系統。
- MyIASM引擎(原本Mysql的默認引擎):不提供事務的支持,也不支持行級鎖和外鍵。
- MEMORY引擎:所有的數據都在內存中,數據的處理速度快,但是安全性不高。
7、MySQL存儲引擎MyISAM和InnoDB的區別?
- InnoDB索引是聚簇索引,MyISAM索引是非聚簇索引。
- InnoDB的主鍵索引的葉子節點存儲着行數據,因此主鍵索引非常高效。
- MyISAM索引的葉子節點存儲的是行數據地址,需要再尋址一次才能得到數據。
- InnoDB非主鍵索引的葉子節點存儲的是主鍵和其他帶索引的列數據,因此查詢時做到覆蓋索引會非常高效。
存儲引擎的選擇:
如果沒有特別的需求,使用默認的Innodb
即可。
MyISAM:以讀寫插入為主的應用程序,比如博客系統、新聞門戶網站。
Innodb:更新(刪除)操作頻率也高,或者要保證數據的完整性;並發量高,支持事務和外鍵。比如OA自動化辦公系統。
8、什么是索引?
索引通俗的說相當於目錄,它們包含着對數據表里所有記錄的引用指針;
索引的數據結構是B樹及其變種B+樹。
優點:大大增加檢索速度提高系統性能
缺點:創建索引耗費時間,對表中進行增加、刪除修改操作時,索引也需要動態維護。降低增刪改的執行效率。
9、索引有哪幾種類型?
- 主鍵索引
- 唯一索引
- 普通索引
- 全文索引
10、事務的四個屬性ACID?
1、原子性(atomicity)
事務是原子性操作,由一系列動作組成,事務的原子性確保動作要么全部完成,要么完全不起作用
2、一致性(consistency)
一旦所有事務動作完成,事務就要被提交。數據和資源處於一種滿足業務規則的一致性狀態中
3、隔離性(isolation)
可能多個事務會同時處理相同的數據,因此每個事務都應該與其他事務隔離開來,防止數據損壞
4、持久性(durability)
事務一旦完成,無論系統發生什么錯誤,結果都不會受到影響。通常情況下,事務的結果被寫到持久化存儲器中
11、什么是不可重復讀?幻讀?臟讀?
不可重復讀:兩次查詢的數據不一致
幻讀:兩次查詢的記錄條數不一致
臟讀:數據在更新時讀取到了錯誤的數據,未更新成功,讀取到了更新后的數據,事務出錯回滾了。
12、事務的隔離級別?
- READ-UNCOMMITTED(讀取未提交): 最低的隔離級別,允許讀取尚未提交的數據變更,可能會導致臟讀、幻讀或不可重復讀。
- READ-COMMITTED(讀取已提交): 允許讀取並發事務已經提交的數據,可以阻止臟讀,但是幻讀或不可重復讀仍有可能發生。
- REPEATABLE-READ(可重復讀): 對同一字段的多次讀取結果都是一致的,除非數據是被本身事務自己所修改,可以阻止臟讀和不可重復讀,但幻讀仍有可能發生。
- SERIALIZABLE(可串行化): 最高的隔離級別,完全服從ACID的隔離級別。所有的事務依次逐個執行,這樣事務之間就完全不可能產生干擾,也就是說,該級別可以防止臟讀、不可重復讀以及幻讀。
有關Redis面試題
1、Redis支持的數據類型?
1.String字符串
格式:set key value
2.Hash(哈希)
格式:hmset name key1 value1 key2 value2...
3.List(列表)
Redis 列表是簡單的字符串列表,按照插入順序排序。你可以添加一個元素到列表的頭部(左邊)或者尾部(右邊)
格式: lpush name value
在 key 對應 list 的頭部添加字符串元素
格式: rpush name value
在 key 對應 list 的尾部添加字符串元素
格式: lrem name index
key 對應 list 中刪除 count 個和 value 相同的元素
格式: llen name
返回 key 對應 list 的長度
4.Set(集合)
格式: sadd name value
Redis的Set是string類型的無序集合。
格式: zadd name score value
Redis zset 和 set 一樣也是string類型元素的集合,且不允許重復的成員。
2、什么是緩存穿透,緩存擊穿,緩存雪崩?
- 緩存穿透:在緩存系統中,首先是按照key去緩存查詢,如果不存在則去數據庫中查詢,如果惡意大量的去查詢不存在的key,會造成數據庫的壓力,緩存失去了原本的意義,這就是緩存穿透。
- 緩存擊穿:高並發訪問熱點key,這時候可能會出現緩存擊穿。
- 緩存雪崩:當緩存服務器重啟或者說某一時間內大量key過期了,這樣所有請求會直接訪問數據庫,給數據庫造成很大壓力。
3、如果解決緩存穿透,緩存擊穿,緩存雪崩?
緩存穿透:采用布隆過濾器,將所有可能存在的數據hash到一個足夠大的bitmap中,
一個一定不存在的數據會被這個bitmap攔截;或者將查詢為空的數據進行緩存,
設置一個短的過期時間。
緩存擊穿:業界比較常用的做法,是使用mutex。簡單地來說,就是在緩存失效的時候(判斷拿出來的值為空),
不是立即去load db,而是先使用緩存工具的某些帶成功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)
去set一個mutex key,當操作返回成功時,再進行load db的操作並回設緩存;否則,就重試整個get緩存的方法。
緩存雪崩: 那就要在存儲的時候,設置有效時間的時候,在失效時間的基礎上增加一個Random隨機值,不讓大量key
在同一時間過期。
Shiro框架面試題
1、什么是shiro?
Shiro是一個強大易用的java安全框架,提供了認證、授權、加密、會話管理、與web集成、緩存等功能,對於任何一個應用程序,都可以提供全面的安全服務,相比其他安全框架,shiro要簡單的多。
2、shiro的核心組件Subject、SecurityManager、Realms
1,Subject:主體,代表了當前用戶,與當前應用交互的所有東西都是Subject,如人、爬蟲、機器人等。所有Subject都綁定
SecurityManager,與Subject所有的交互都會委托給SecurityManager,它才是實際的執行者。
2,SecurityManager:安全管理器,所有有關安全的操作都會與SecurityManager交互,並且它管理所有的Subject,
它是shiro的核心。
3,Realm:域,shiro從Realm中獲取安全數據(如用戶、角色、權限),當SecurityManager驗證用戶身份時,那么它
需要從Realm獲取相應的用戶進行比較以確定用戶身份是否合法。也需要從Realm中得到用戶相應的角色、權限進行
驗證用戶是否有權限進行操作。可以把Realm看成DataSource,即安全數據源。
3、shiro框架的優點
- 簡單的身份驗證,支持多種數據源
- 對角色的簡單授權,支持細粒度的授權(方法)
- 支持一級緩存,以提升應用程序的性能
- 內置基於POJO的企業會話管理,適用於web及非web環境
- 非常簡單的API加密
- 不跟任何框架綁定,可以獨立運行
4、shiro主要的四個組件?
SecurityManager、Authenticator、Authorizer、Session Manager
5、shiro功能細分為?
1. Authentication:身份認證/登錄(賬號密碼驗證)。
2. Authorization:授權,即角色或者權限驗證。
3. Session Manager:會話管理,用戶登錄后的session相關管理。
4. Cryptography:加密,密碼加密等。
5. Web Support:Web支持,集成Web環境。
6. Caching:緩存,用戶信息、角色、權限等緩存到如redis等緩存中。
7. Concurrency:多線程並發驗證,在一個線程中開啟另一個線程,可以把權限自動傳播過去。
8. Testing:測試支持;
9. Run As:允許一個用戶假裝為另一個用戶(如果他們允許)的身份進行訪問。
10. Remember Me:記住我,登錄后,下次再來的話不用登錄了。
6、shiro的四種權限控制方式?
- url 級別權限控制
- 方法注解權限控制
- 代碼級別權限控制
- 頁面標簽權限控制
7、springboot整合shiro流程?
1,pom.xml文件中添加依賴
<!-- shiro --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>${spring.shiro.version}</version> </dependency>
2,創建對應的用戶、角色、權限實體類
3,自定義Realm用於查詢用戶的角色和權限信息,保存到權限管理器
import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.StringUtils;
//繼承AuthorizingRealm類 public class CustomRealm extends AuthorizingRealm {
//注入查詢數據的service層 @Autowired private LoginService loginService; /** * @MethodName doGetAuthorizationInfo * @Description 權限配置類 * @Param [principalCollection] * @Return AuthorizationInfo * @Author WangShiLin */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { //獲取登錄用戶名 String name = (String) principalCollection.getPrimaryPrincipal(); //查詢用戶名稱 User user = loginService.getUserByName(name); //添加角色和權限 SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); for (Role role : user.getRoles()) { //添加角色 simpleAuthorizationInfo.addRole(role.getRoleName()); //添加權限 for (Permissions permissions : role.getPermissions()) { simpleAuthorizationInfo.addStringPermission(permissions.getPermissionsName()); } } return simpleAuthorizationInfo; } /** * @MethodName doGetAuthenticationInfo * @Description 認證配置類 * @Param [authenticationToken] * @Return AuthenticationInfo * @Author WangShiLin */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { if (StringUtils.isEmpty(authenticationToken.getPrincipal())) { return null; } //獲取用戶信息 String name = authenticationToken.getPrincipal().toString(); User user = loginService.getUserByName(name); if (user == null) { //這里返回后會報出對應異常 return null; } else { //這里驗證authenticationToken和simpleAuthenticationInfo的信息 SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(name, user.getPassword().toString(), getName()); return simpleAuthenticationInfo; } } }
4,新建配置一個配置類,如ShiroConfig.java,把CustomRealm和SecurityManager等注入到spring容器中。
import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.HashMap; import java.util.Map; @Configuration public class shiroConfig { @Bean @ConditionalOnMissingBean public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() { DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator(); defaultAAP.setProxyTargetClass(true); return defaultAAP; } //將自己的驗證方式加入容器 @Bean public CustomRealm myShiroRealm() { CustomRealm customRealm = new CustomRealm(); return customRealm; } //權限管理,配置主要是Realm的管理認證 @Bean public SecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(myShiroRealm()); return securityManager; } //Filter工廠,設置對應的過濾條件和跳轉條件 @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); Map<String, String> map = new HashMap<>(); //登出 map.put("/logout", "logout"); //對所有用戶認證 map.put("/**", "authc"); //登錄 shiroFilterFactoryBean.setLoginUrl("/login"); //首頁 shiroFilterFactoryBean.setSuccessUrl("/index"); //錯誤頁面,認證不通過跳轉 shiroFilterFactoryBean.setUnauthorizedUrl("/error"); shiroFilterFactoryBean.setFilterChainDefinitionMap(map); return shiroFilterFactoryBean; } @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return authorizationAttributeSourceAdvisor; } }
5、控制器類中
@RequiresPermissions("add"):表示驗證權限
@RequiresRoles("admin"):表示驗證角色
Lucene全文檢索工具包的基本使用
1、什么是全文檢索?
將非結構化數據中的一部分信息提取出來,重新組織,使其變得有一定結構,然后對此有一定結構的數據進行搜索,
從而達到搜索相對較快的目的。這部分從非結構化數據中提取出然后重新組織的信息,我們稱之索引。
這種先建立索引,再對索引進行搜索的過程就叫全文檢索(Full-text Search)。
2、lucene內部結構是什么?
索引(Index): 在Lucene中一個索引是放在一個文件夾中的。 如上圖,同一文件夾中的所有的文件構成一個Lucene索引。
段(Segment): 一個索引可以包含多個段,段與段之間是獨立的,添加新文檔可以生成新的段,不同的段可以合並。
segments.gen和segments_X是段的元數據文件,也即它們保存了段的屬性信息。
文檔(Document): 文檔是我們建索引的基本單位,不同的文檔是保存在不同的段中的,一個段可以包含多篇文檔。
新添加的文檔是單獨保存在一個新生成的段中,隨着段的合並,不同的文檔合並到同一個段中。
域(Field):
一篇文檔包含不同類型的信息,可以分開索引,比如標題,時間,正文,作者等,都可以保存在不同的域里。 不同域的索引方式可以不同,在真正解析域的存儲的時候,我們會詳細解讀。
詞(Term):
詞是索引的最小單位,是經過詞法分析和語言處理后的字符串。
3、IK分詞器的原理?
本質上是詞典分詞,在內存中初始化一個詞典,然后在分詞過程中逐個讀取字符,和字典中的字符相匹配,
把文檔中的所有詞語拆分出來的過程
4、solr和lucene之間的區別?
lucene是全文檢索工具包
solr是單獨運行的全文檢索服務器。
手寫幾種排序?
1、冒泡排序
public static void main(String[] args) { int arr[] = {8, 5, 3, 2, 4}; //冒泡 for (int i = 0; i < arr.length; i++) { //外層循環,遍歷次數 for (int j = 0; j < arr.length - i - 1; j++) { //內層循環,升序(如果前一個值比后一個值大,則交換) //內層循環一次,獲取一個最大值 if (arr[j] > arr[j + 1]) { int temp = arr[j + 1]; arr[j + 1] = arr[j]; arr[j] = temp; } } } }
2、選擇排序
public static void main(String[] args) { int arr[] = {6, 5, 3, 2, 4}; //選擇 for (int i = 0; i < arr.length; i++) { //默認第一個是最小的。 int min = arr[i]; //記錄最小的下標 int index = i; //通過與后面的數據進行比較得出,最小值和下標 for (int j = i + 1; j < arr.length; j++) { if (min > arr[j]) { min = arr[j]; index = j; } } //然后將最小值與本次循環的,開始值交換 int temp = arr[i]; arr[i] = min; arr[index] = temp; //說明:將i前面的數據看成一個排好的隊列,i后面的看成一個無序隊列。每次只需要找無需的最小值,做替換 } }
3、插入排序
public static void main(String[] args) { int arr[] = {7, 5, 3, 2, 4}; //插入排序 for (int i = 1; i < arr.length; i++) { //外層循環,從第二個開始比較 for (int j = i; j > 0; j--) { //內存循環,與前面排好序的數據比較,如果后面的數據小於前面的則交換 if (arr[j] < arr[j - 1]) { int temp = arr[j - 1]; arr[j - 1] = arr[j]; arr[j] = temp; } else { //如果不小於,說明插入完畢,退出內層循環 break; } } } }
4、希爾排序
public static void main(String[] args) { int arr[] = {7, 5, 3, 2, 4}; //希爾排序(插入排序變種版) for (int i = arr.length / 2; i > 0; i /= 2) { //i層循環控制步長 for (int j = i; j < arr.length; j++) { //j控制無序端的起始位置 for (int k = j; k > 0 && k - i >= 0; k -= i) { if (arr[k] < arr[k - i]) { int temp = arr[k - i]; arr[k - i] = arr[k]; arr[k] = temp; } else { break; } } } //j,k為插入排序,不過步長為i } }
5、快速排序
public static void main(String[] args) { int arr[] = {7, 5, 3, 2, 4, 1, 8, 9, 6}; //快速排序 int low = 0; int high = arr.length - 1; quickSort(arr, low, high); } public static void quickSort(int[] arr, int low, int high) { //如果指針在同一位置(只有一個數據時),退出 if (high - low < 1) { return; } //標記,從高指針開始,還是低指針(默認高指針) boolean flag = true; //記錄指針的其實位置 int start = low; int end = high; //默認中間值為低指針的第一個值 int midValue = arr[low]; while (true) { //高指針移動 if (flag) { //如果列表右方的數據大於中間值,則向左移動 if (arr[high] > midValue) { high--; } else if (arr[high] < midValue) { //如果小於,則覆蓋最開始的低指針值,並且移動低指針,標志位改成從低指針開始移動 arr[low] = arr[high]; low++; flag = false; } } else { //如果低指針數據小於中間值,則低指針向右移動 if (arr[low] < midValue) { low++; } else if (arr[low] > midValue) { //如果低指針的值大於中間值,則覆蓋高指針停留時的數據,並向左移動高指針。切換為高指針移動 arr[high] = arr[low]; high--; flag = true; } } //當兩個指針的位置相同時,則找到了中間值的位置,並退出循環 if (low == high) { arr[low] = midValue; break; } } //然后出現有,中間值左邊的小於中間值。右邊的大於中間值。 //然后在對左右兩邊的列表在進行快速排序 quickSort(arr, start, low -1); quickSort(arr, low + 1, end); }
6、歸並排序
public static void main(String[] args) { int arr[] = {7, 5, 3, 2, 4, 1,6}; //歸並排序 int start = 0; int end = arr.length - 1; mergeSort(arr, start, end); } public static void mergeSort(int[] arr, int start, int end) { //判斷拆分的不為最小單位 if (end - start > 0) { //再一次拆分,知道拆成一個一個的數據 mergeSort(arr, start, (start + end) / 2); mergeSort(arr, (start + end) / 2 + 1, end); //記錄開始/結束位置 int left = start; int right = (start + end) / 2 + 1; //記錄每個小單位的排序結果 int index = 0; int[] result = new int[end - start + 1]; //如果查分后的兩塊數據,都還存在 while (left <= (start + end) / 2 && right <= end) { //比較兩塊數據的大小,然后賦值,並且移動下標 if (arr[left] <= arr[right]) { result[index] = arr[left]; left++; } else { result[index] = arr[right]; right++; } //移動單位記錄的下標 index++; } //當某一塊數據不存在了時 while (left <= (start + end) / 2 || right <= end) { //直接賦值到記錄下標 if (left <= (start + end) / 2) { result[index] = arr[left]; left++; } else { result[index] = arr[right]; right++; } index++; } //最后將新的數據賦值給原來的列表,並且是對應分塊后的下標。 for (int i = start; i <= end; i++) { arr[i] = result[i - start]; } } }
說說對三個項目的了解