Spring 框架
- Spring Core: 基礎(chǔ),可以說(shuō) Spring 其他所有的功能都需要依賴于該類庫(kù)。主要提供 IoC 依賴注入功能。
- Spring Aspects : Spring集成面向切面框架,該模塊為與AspectJ的集成提供支持闯袒。
- Spring AOP :提供了面向切面的編程實(shí)現(xiàn)妥色。
- Spring JDBC : Java數(shù)據(jù)庫(kù)連接蒲凶。
- Spring JMS :Java消息服務(wù)。
- Spring ORM : 用于支持Hibernate等ORM工具雪猪。
- Spring Web : 為創(chuàng)建Web應(yīng)用程序提供支持。
- Spring Test : 提供了對(duì) JUnit 和 TestNG 測(cè)試的支持起愈。
@RestController vs @Controller
@Controller 返回一個(gè)頁(yè)面
@RestController 返回JSON 或 XML 形式數(shù)據(jù)
@Controller +@ResponseBody 返回JSON 或 XML 形式數(shù)據(jù)
Spring IOC & AOP(控制反轉(zhuǎn)&面向切面編程)
IOC
IoC(Inverse of Control:控制反轉(zhuǎn))是一種設(shè)計(jì)思想只恨,就是 將原本在程序中手動(dòng)創(chuàng)建對(duì)象的控制權(quán),交由Spring框架來(lái)管理告材。 IoC 在其他語(yǔ)言中也有應(yīng)用坤次,并非 Spring 特有。 IoC 容器是 Spring 用來(lái)實(shí)現(xiàn) IoC 的載體斥赋, IoC 容器實(shí)際上就是個(gè)Map(key缰猴,value),Map 中存放的是各種對(duì)象。
將對(duì)象之間的相互依賴關(guān)系交給 IoC 容器來(lái)管理疤剑,并由 IoC 容器完成對(duì)象的注入滑绒。這樣可以很大程度上簡(jiǎn)化應(yīng)用的開(kāi)發(fā)闷堡,把應(yīng)用從復(fù)雜的依賴關(guān)系中解放出來(lái)。 IoC 容器就像是一個(gè)工廠一樣疑故,當(dāng)我們需要?jiǎng)?chuàng)建一個(gè)對(duì)象的時(shí)候杠览,只需要配置好配置文件/注解即可,完全不用考慮對(duì)象是如何被創(chuàng)建出來(lái)的纵势。 在實(shí)際項(xiàng)目中一個(gè) Service 類可能有幾百甚至上千個(gè)類作為它的底層踱阿,假如我們需要實(shí)例化這個(gè) Service,你可能要每次都要搞清這個(gè) Service 所有底層類的構(gòu)造函數(shù)钦铁,這可能會(huì)把人逼瘋软舌。如果利用 IoC 的話,你只需要配置好牛曹,然后在需要的地方引用就行了佛点,這大大增加了項(xiàng)目的可維護(hù)性且降低了開(kāi)發(fā)難度。
Spring 時(shí)代我們一般通過(guò) XML 文件來(lái)配置 Bean黎比,后來(lái)開(kāi)發(fā)人員覺(jué)得 XML 文件來(lái)配置不太好超营,于是 SpringBoot 注解配置就慢慢開(kāi)始流行起來(lái)。
Spring IoC的初始化過(guò)程:
作個(gè)更形象的比喻,如下:
有一臺(tái)電腦阅虫,它上邊有usb接口,可以插各式各樣的鍵盤,控制電腦進(jìn)行文字輸入的不是電腦本身演闭,而是通過(guò)usb接口連接到電腦上的鍵盤來(lái)控制的。(這可以理解成控制反轉(zhuǎn))
那對(duì)于電腦來(lái)說(shuō)颓帝,它依賴了插入usb接口的不同的鍵盤,像機(jī)械鍵盤船响、數(shù)字鍵盤、雷步牌的鍵盤躲履、雙飛燕的鍵盤等等,達(dá)到了輸入文字的功能,插不同的鍵盤會(huì)有不同的用戶體驗(yàn)见间,并且壞了可以及時(shí)的更換。(對(duì)電腦來(lái)說(shuō)這就是依賴注入)
※在開(kāi)發(fā)一個(gè)系統(tǒng)的時(shí)候,我們會(huì)創(chuàng)建很多對(duì)象,通過(guò)Spring的這個(gè)功能,我們?yōu)榱俗屢恍?duì)象可維護(hù)工猜,就把這些對(duì)象提交給Spring創(chuàng)建管理,我們提供配置文件(如xml或java配置類),等我們用到的時(shí)候,Spring工廠就可以提供給我們米诉。
※使用IOC的好處:https://www.zhihu.com/question/23277575/answer/169698662
AOP
AOP(Aspect-Oriented Programming:面向切面編程)能夠?qū)⒛切┡c業(yè)務(wù)無(wú)關(guān),卻為業(yè)務(wù)模塊所共同調(diào)用的邏輯或責(zé)任(例如事務(wù)處理篷帅、日志管理史侣、權(quán)限控制等)封裝起來(lái),便于減少系統(tǒng)的重復(fù)代碼魏身,降低模塊間的耦合度惊橱,并有利于未來(lái)的可拓展性和可維護(hù)性。
Spring AOP就是基于動(dòng)態(tài)代理的箭昵,如果要代理的對(duì)象税朴,實(shí)現(xiàn)了某個(gè)接口,那么Spring AOP會(huì)使用JDK Proxy,去創(chuàng)建代理對(duì)象正林,而對(duì)于沒(méi)有實(shí)現(xiàn)接口的對(duì)象泡一,就無(wú)法使用 JDK Proxy 去進(jìn)行代理了,這時(shí)候Spring AOP會(huì)使用Cglib 觅廓,這時(shí)候Spring AOP會(huì)使用 Cglib 生成一個(gè)被代理對(duì)象的子類來(lái)作為代理鼻忠,如下圖所示:
當(dāng)然你也可以使用 AspectJ ,Spring AOP 已經(jīng)集成了AspectJ ,AspectJ 應(yīng)該算的上是 Java 生態(tài)系統(tǒng)中最完整的 AOP 框架了杈绸。
使用 AOP 之后我們可以把一些通用功能抽象出來(lái)帖蔓,在需要用到的地方直接使用即可,這樣大大簡(jiǎn)化了代碼量瞳脓。我們需要增加新功能時(shí)也方便讨阻,這樣也提高了系統(tǒng)擴(kuò)展性。日志功能篡殷、事務(wù)管理等等場(chǎng)景都用到了 AOP
Spring AOP 和 AspectJ AOP 有什么區(qū)別?
Spring AOP 屬于運(yùn)行時(shí)增強(qiáng)埋涧,而 AspectJ 是編譯時(shí)增強(qiáng)板辽。 Spring AOP 基于代理(Proxying),而 AspectJ 基于字節(jié)碼操作(Bytecode Manipulation)棘催。
Spring AOP 已經(jīng)集成了 AspectJ 劲弦,AspectJ 應(yīng)該算的上是 Java 生態(tài)系統(tǒng)中最完整的 AOP 框架了。AspectJ 相比于 Spring AOP 功能更加強(qiáng)大醇坝,但是 Spring AOP 相對(duì)來(lái)說(shuō)更簡(jiǎn)單邑跪,
如果我們的切面比較少,那么兩者性能差異不大呼猪。但是画畅,當(dāng)切面太多的話,最好選擇 AspectJ 宋距,它比Spring AOP 快很多轴踱。
Spring bean
Spring 中的 bean 的作用域有哪些?
- singleton : 唯一 bean 實(shí)例,Spring 中的 bean 默認(rèn)都是單例的谚赎。
- prototype : 每次請(qǐng)求都會(huì)創(chuàng)建一個(gè)新的 bean 實(shí)例淫僻。
- request : 每一次HTTP請(qǐng)求都會(huì)產(chǎn)生一個(gè)新的bean,該bean僅在當(dāng)前HTTP request內(nèi)有效壶唤。
- session : 每一次HTTP請(qǐng)求都會(huì)產(chǎn)生一個(gè)新的 bean雳灵,該bean僅在當(dāng)前 HTTP session 內(nèi)有效。
- global-session: 全局session作用域闸盔,僅僅在基于portlet的web應(yīng)用中才有意義悯辙,Spring5已經(jīng)沒(méi)有了
Portlet是能夠生成語(yǔ)義代碼(例如:HTML)片段的小型Java Web插件。它們基于portlet容器,可以像servlet一樣處理HTTP請(qǐng)求笑撞。但是岛啸,與 servlet 不同,每個(gè) portlet 都有不同的會(huì)話
Spring 中的單例 bean 的線程安全問(wèn)題了解嗎
大部分時(shí)候我們并沒(méi)有在系統(tǒng)中使用多線程茴肥,所以很少有人會(huì)關(guān)注這個(gè)問(wèn)題坚踩。單例 bean 存在線程問(wèn)題,主要是因?yàn)楫?dāng)多個(gè)線程操作同一個(gè)對(duì)象的時(shí)候瓤狐,對(duì)這個(gè)對(duì)象的非靜態(tài)成員變量的寫操作會(huì)存在線程安全問(wèn)題瞬铸。
常見(jiàn)的有兩種解決辦法:
- 在Bean對(duì)象中盡量避免定義可變的成員變量(不太現(xiàn)實(shí))。
- 在類中定義一個(gè)ThreadLocal成員變量础锐,將需要的可變成員變量保存在 ThreadLocal 中(推薦的一種方式)嗓节。
@Component 和 @Bean 的區(qū)別是什么?
- 作用對(duì)象不同: @Component 注解作用于類皆警,而@Bean注解作用于方法拦宣。
- @Component通常是通過(guò)類路徑掃描來(lái)自動(dòng)偵測(cè)以及自動(dòng)裝配到Spring容器中(我們可以使用 @ComponentScan 注解定義要掃描的路徑從中找出標(biāo)識(shí)了需要裝配的類自動(dòng)裝配到 Spring 的 bean 容器中)。@Bean 注解通常是我們?cè)跇?biāo)有該注解的方法中定義產(chǎn)生這個(gè) bean,@Bean告訴了Spring這是某個(gè)類的實(shí)例信姓,當(dāng)我需要用它的時(shí)候提供給我鸵隧。
- @Bean 注解比 Component 注解的自定義性更強(qiáng),而且很多地方我們只能通過(guò) @Bean 注解來(lái)注冊(cè)bean意推。比如當(dāng)我們引用第三方庫(kù)中的類需要裝配到 Spring容器時(shí)豆瘫,則只能通過(guò) @Bean來(lái)實(shí)現(xiàn)。
@Bean注解使用示例:
@Configuration
public class AppConfig {
@Bean
public TransferService transferService() {
return new TransferServiceImpl();
}
}
上面的代碼相當(dāng)于下面的 xml 配置
<beans>
<bean id="transferService" class="com.acme.TransferServiceImpl"/>
</beans>
下面這個(gè)例子是通過(guò) @Component 無(wú)法實(shí)現(xiàn)的
@Bean
public OneService getService(status) {
case (status) {
when 1:
return new serviceImpl1();
when 2:
return new serviceImpl2();
when 3:
return new serviceImpl3();
}
}
將一個(gè)類聲明為Spring的 bean 的注解有哪些?
我們一般使用 @Autowired 注解自動(dòng)裝配 bean菊值,要想把類標(biāo)識(shí)成可用于 @Autowired 注解自動(dòng)裝配的 bean 的類,采用以下注解可實(shí)現(xiàn):
- @Component :通用的注解外驱,可標(biāo)注任意類為 Spring 組件。如果一個(gè)Bean不知道屬于哪個(gè)層腻窒,可以使用@Component 注解標(biāo)注昵宇。
- @Repository : 對(duì)應(yīng)持久層即 Dao 層,主要用于數(shù)據(jù)庫(kù)相關(guān)操作儿子。
- @Service : 對(duì)應(yīng)服務(wù)層趟薄,主要涉及一些復(fù)雜的邏輯,需要用到 Dao層典徊。
- @Controller : 對(duì)應(yīng) Spring MVC 控制層杭煎,主要用于接受用戶請(qǐng)求并調(diào)用 Service 層返回?cái)?shù)據(jù)給前端頁(yè)面。
Spring 中的 bean 生命周期?
參考文章:
https://yemengying.com/2016/07/14/spring-bean-life-cycle/
https://www.cnblogs.com/zrtqsk/p/3735273.html
- Bean 容器找到配置文件中 Spring Bean 的定義卒落。
- Bean 容器利用 Java Reflection API 創(chuàng)建一個(gè)Bean的實(shí)例羡铲。
- 如果涉及到一些屬性值 利用 set()方法設(shè)置一些屬性值。
- 如果 Bean 實(shí)現(xiàn)了 BeanNameAware 接口儡毕,調(diào)用 setBeanName()方法也切,傳入Bean的名字扑媚。
- 如果 Bean 實(shí)現(xiàn)了 BeanClassLoaderAware 接口,調(diào)用 setBeanClassLoader()方法雷恃,傳入 ClassLoader對(duì)象的實(shí)例疆股。
- 與上面的類似,如果實(shí)現(xiàn)了其他 *.Aware接口倒槐,就調(diào)用相應(yīng)的方法旬痹。
- 如果有和加載這個(gè) Bean 的 Spring 容器相關(guān)的 BeanPostProcessor 對(duì)象,執(zhí)行postProcessBeforeInitialization() 方法
- 如果Bean實(shí)現(xiàn)了InitializingBean接口讨越,執(zhí)行afterPropertiesSet()方法两残。
- 如果 Bean 在配置文件中的定義包含 init-method 屬性,執(zhí)行指定的方法把跨。
- 如果有和加載這個(gè) Bean的 Spring 容器相關(guān)的 BeanPostProcessor 對(duì)象人弓,執(zhí)行postProcessAfterInitialization() 方法
- 當(dāng)要銷毀 Bean 的時(shí)候,如果 Bean 實(shí)現(xiàn)了 DisposableBean 接口着逐,執(zhí)行 destroy() 方法崔赌。
-
當(dāng)要銷毀 Bean 的時(shí)候,如果 Bean 在配置文件中的定義包含 destroy-method 屬性耸别,執(zhí)行指定的方法健芭。
Spring MVC
說(shuō)說(shuō)自己對(duì)于 Spring MVC 了解?
SpringMVC將在專門的SpringMVC文章中講 //TODO
Spring 框架中用到了哪些設(shè)計(jì)模式?
- 工廠設(shè)計(jì)模式 : Spring使用工廠模式通過(guò) BeanFactory太雨、ApplicationContext 創(chuàng)建 bean 對(duì)象。
- 代理設(shè)計(jì)模式 : Spring AOP 功能的實(shí)現(xiàn)魁蒜。
- 單例設(shè)計(jì)模式 : Spring 中的 Bean 默認(rèn)都是單例的囊扳。
- 模板方法模式 : Spring 中 jdbcTemplate、hibernateTemplate 等以 Template 結(jié)尾的對(duì)數(shù)據(jù)庫(kù)操作的類兜看,它們就使用到了模板模式锥咸。
- 包裝器設(shè)計(jì)模式 : 我們的項(xiàng)目需要連接多個(gè)數(shù)據(jù)庫(kù),而且不同的客戶在每次訪問(wèn)中根據(jù)需要會(huì)去訪問(wèn)不同的數(shù)據(jù)庫(kù)细移。這種模式讓我們可以根據(jù)客戶的需求能夠動(dòng)態(tài)切換不同的數(shù)據(jù)源搏予。
- 觀察者模式: Spring 事件驅(qū)動(dòng)模型就是觀察者模式很經(jīng)典的一個(gè)應(yīng)用。
- 適配器模式 :Spring AOP 的增強(qiáng)或通知(Advice)使用到了適配器模式弧轧、spring MVC 中也是用到了適配器模式適配Controller雪侥。
...
Spring 事務(wù)
Spring 管理事務(wù)的方式有幾種?
編程式事務(wù)精绎,在代碼中硬編碼速缨。(不推薦使用)
聲明式事務(wù),在配置文件中配置(推薦使用)
聲明式事務(wù)又分為兩種:
- 基于XML的聲明式事務(wù)
- 基于注解的聲明式事務(wù)
Spring 事務(wù)中的隔離級(jí)別有哪幾種?
TransactionDefinition 接口中定義了五個(gè)表示隔離級(jí)別的常量:
- TransactionDefinition.ISOLATION_DEFAULT: 使用后端數(shù)據(jù)庫(kù)默認(rèn)的隔離級(jí)別代乃,Mysql 默認(rèn)采用的 REPEATABLE_READ隔離級(jí)別 Oracle 默認(rèn)采用的 READ_COMMITTED隔離級(jí)別.
- TransactionDefinition.ISOLATION_READ_UNCOMMITTED: 最低的隔離級(jí)別旬牲,允許讀取尚未提交的數(shù)據(jù)變更,可能會(huì)導(dǎo)致臟讀、幻讀或不可重復(fù)讀
- TransactionDefinition.ISOLATION_READ_COMMITTED: 允許讀取并發(fā)事務(wù)已經(jīng)提交的數(shù)據(jù)原茅,可以阻止臟讀吭历,但是幻讀或不可重復(fù)讀仍有可能發(fā)生
- TransactionDefinition.ISOLATION_REPEATABLE_READ: 對(duì)同一字段的多次讀取結(jié)果都是一致的,除非數(shù)據(jù)是被本身事務(wù)自己所修改擂橘,可以阻止臟讀和不可重復(fù)讀晌区,但幻讀仍有可能發(fā)生。
- TransactionDefinition.ISOLATION_SERIALIZABLE: 最高的隔離級(jí)別贝室,完全服從ACID的隔離級(jí)別契讲。所有的事務(wù)依次逐個(gè)執(zhí)行,這樣事務(wù)之間就完全不可能產(chǎn)生干擾滑频,也就是說(shuō)捡偏,該級(jí)別可以防止臟讀、不可重復(fù)讀以及幻讀峡迷。但是這將嚴(yán)重影響程序的性能银伟。通常情況下也不會(huì)用到該級(jí)別。
Spring 事務(wù)中哪幾種事務(wù)傳播行為?
支持當(dāng)前事務(wù)的情況:
TransactionDefinition.PROPAGATION_REQUIRED: 如果當(dāng)前存在事務(wù)绘搞,則加入該事務(wù)彤避;如果當(dāng)前沒(méi)有事務(wù),則創(chuàng)建一個(gè)新的事務(wù)夯辖。
TransactionDefinition.PROPAGATION_SUPPORTS: 如果當(dāng)前存在事務(wù)琉预,則加入該事務(wù);如果當(dāng)前沒(méi)有事務(wù)蒿褂,則以非事務(wù)的方式繼續(xù)運(yùn)行圆米。
TransactionDefinition.PROPAGATION_MANDATORY: 如果當(dāng)前存在事務(wù),則加入該事務(wù)啄栓;如果當(dāng)前沒(méi)有事務(wù)娄帖,則拋出異常。(mandatory:強(qiáng)制性)
不支持當(dāng)前事務(wù)的情況:
TransactionDefinition.PROPAGATION_REQUIRES_NEW: 創(chuàng)建一個(gè)新的事務(wù)昙楚,如果當(dāng)前存在事務(wù)近速,則把當(dāng)前事務(wù)掛起。
TransactionDefinition.PROPAGATION_NOT_SUPPORTED: 以非事務(wù)方式運(yùn)行堪旧,如果當(dāng)前存在事務(wù)削葱,則把當(dāng)前事務(wù)掛起。
TransactionDefinition.PROPAGATION_NEVER: 以非事務(wù)方式運(yùn)行淳梦,如果當(dāng)前存在事務(wù)佩耳,則拋出異常。
其他情況:
TransactionDefinition.PROPAGATION_NESTED: 如果當(dāng)前存在事務(wù)谭跨,則創(chuàng)建一個(gè)事務(wù)作為當(dāng)前事務(wù)的嵌套事務(wù)來(lái)運(yùn)行干厚;如果當(dāng)前沒(méi)有事務(wù)李滴,則該取值等價(jià)于TransactionDefinition.PROPAGATION_REQUIRED。
@Transactional(rollbackFor = Exception.class)注解了解嗎蛮瞄?
我們知道:Exception分為運(yùn)行時(shí)異常RuntimeException和非運(yùn)行時(shí)異常所坯。事務(wù)管理對(duì)于企業(yè)應(yīng)用來(lái)說(shuō)是至關(guān)重要的,即使出現(xiàn)異常情況挂捅,它也可以保證數(shù)據(jù)的一致性芹助。
當(dāng)@Transactional注解作用于類上時(shí),該類的所有 public 方法將都具有該類型的事務(wù)屬性闲先,同時(shí)状土,我們也可以在方法級(jí)別使用該標(biāo)注來(lái)覆蓋類級(jí)別的定義。如果類或者方法加了這個(gè)注解伺糠,那么這個(gè)類里面的方法拋出異常蒙谓,就會(huì)回滾,數(shù)據(jù)庫(kù)里面的數(shù)據(jù)也會(huì)回滾训桶。
在@Transactional注解中如果不配置rollbackFor屬性,那么事物只會(huì)在遇到RuntimeException的時(shí)候才會(huì)回滾,加上rollbackFor=Exception.class,可以讓事物在遇到非運(yùn)行時(shí)異常時(shí)也回滾累驮。
擴(kuò)展閱讀:《透徹的掌握 Spring 中 @transactional 的使用》
JPA
如何使用JPA在數(shù)據(jù)庫(kù)中非持久化一個(gè)字段?
假如我們有有下面一個(gè)類:
@Data
@Entity
@Table(name = "sys_user")
@ToString(exclude = {"dept", "roles"})
@EqualsAndHashCode(exclude = {"dept", "roles"})
@EntityListeners(AuditingEntityListener.class)
@SQLDelete(sql = "update sys_user" + StatusUtil.SLICE_DELETE)
@Where(clause = StatusUtil.NOT_DELETE)
@Excel("用戶數(shù)據(jù)")
public class User implements Serializable {
//注代碼中使用了lombok
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Excel(value = "用戶ID", type = ExcelType.EXPORT)
private Long id;
@Excel("用戶名")
private String username;
@JsonIgnore
private String password;
@JsonIgnore
private String salt;
@Excel("昵稱")
private String nickname;
private String picture;
@Excel(value = "性別", dict = "USER_SEX")
private Byte sex;
@Excel("手機(jī)號(hào)碼")
private String phone;
@Excel("電子郵箱")
private String email;
@CreatedDate
@Excel("創(chuàng)建時(shí)間")
private Date createDate;
@LastModifiedDate
@Excel("更新時(shí)間")
private Date updateDate;
@Excel("備注")
private String remark;
@Excel(value = "狀態(tài)", dict = "DATA_STATUS")
private Byte status = StatusEnum.OK.getCode();
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "dept_id")
@JsonIgnore
private Dept dept;
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "sys_user_role",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "role_id"))
@JsonIgnore
private Set<Role> roles = new HashSet<>(0);
private String secrect;
}
如果我們想讓secrect這個(gè)字段不被持久化舵揭,也就是不被數(shù)據(jù)庫(kù)存儲(chǔ)怎么辦谤专?我們可以采用下面幾種方法:
static Stringsecrect1; // not persistent because of static
final String secrect2 = “Satish”; // not persistent because of final
transient String secrect3; // not persistent because of transient
@Transient
String secrect4; // not persistent because of @Transient
一般使用后面兩種方式比較多,我個(gè)人使用注解的方式比較多午绳。
擴(kuò)展閱讀:* http://www.cnblogs.com/wmyskxz/p/8820371.html
未完待續(xù)...