欧美free性护士vide0shd,老熟女,一区二区三区,久久久久夜夜夜精品国产,久久久久久综合网天天,欧美成人护士h版

首頁綜合 正文
目錄

柚子快報(bào)邀請(qǐng)碼778899分享:【開發(fā)框架和中間件】

柚子快報(bào)邀請(qǐng)碼778899分享:【開發(fā)框架和中間件】

http://yzkb.51969.com/

開發(fā)框架相關(guān)

# 10.1 Spring

# 什么是Spring框架?

Spring是一種輕量級(jí)框架,旨在提高開發(fā)人員的開發(fā)效率以及系統(tǒng)的可維護(hù)性。

我們一般說的Spring框架就是Spring Framework,它是很多模塊的集合,使用這些模塊可以很方便地協(xié)助我們進(jìn)行開發(fā)。這些模塊是核心容器、數(shù)據(jù)訪問/集成、Web、AOP(面向切面編程)、工具、消息和測試模塊。比如Core Container中的Core組件是Spring所有組件的核心,Beans組件和Context組件是實(shí)現(xiàn)IOC和DI的基礎(chǔ),AOP組件用來實(shí)現(xiàn)面向切面編程。

Spring官網(wǎng)列出的Spring的6個(gè)特征:

核心技術(shù):依賴注入(DI),AOP,事件(Events),資源,i18n,驗(yàn)證,數(shù)據(jù)綁定,類型轉(zhuǎn)換,SpEL。測試:模擬對(duì)象,TestContext框架,Spring MVC測試,WebTestClient。數(shù)據(jù)訪問:事務(wù),DAO支持,JDBC,ORM,編組XML。Web支持:Spring MVC和Spring WebFlux Web框架。集成:遠(yuǎn)程處理,JMS,JCA,JMX,電子郵件,任務(wù),調(diào)度,緩存。語言:Kotlin,Groovy,動(dòng)態(tài)語言。

# 列舉一些重要的Spring模塊?

下圖對(duì)應(yīng)的是Spring 4.x的版本,目前最新的5.x版本中Web模塊的Portlet組件已經(jīng)被廢棄掉,同時(shí)增加了用于異步響應(yīng)式處理的WebFlux組件。

Spring Core:基礎(chǔ),可以說Spring其他所有的功能都依賴于該類庫。主要提供IOC和DI功能。Spring Aspects:該模塊為與AspectJ的集成提供支持。Spring AOP:提供面向切面的編程實(shí)現(xiàn)。Spring JDBC:Java數(shù)據(jù)庫連接。Spring JMS:Java消息服務(wù)。Spring ORM:用于支持Hibernate等ORM工具。Spring Web:為創(chuàng)建Web應(yīng)用程序提供支持。Spring Test:提供了對(duì)JUnit和TestNG測試的支持。

# 什么是IOC? 如何實(shí)現(xiàn)的?

IOC(Inversion Of Controll,控制反轉(zhuǎn))是一種設(shè)計(jì)思想,就是將原本在程序中手動(dòng)創(chuàng)建對(duì)象的控制權(quán),交給IOC容器來管理,并由IOC容器完成對(duì)象的注入。這樣可以很大程度上簡化應(yīng)用的開發(fā),把應(yīng)用從復(fù)雜的依賴關(guān)系中解放出來。IOC容器就像是一個(gè)工廠一樣,當(dāng)我們需要?jiǎng)?chuàng)建一個(gè)對(duì)象的時(shí)候,只需要配置好配置文件/注解即可,完全不用考慮對(duì)象是如何被創(chuàng)建出來的。

Spring 中的 IoC 的實(shí)現(xiàn)原理就是工廠模式加反射機(jī)制。

示例:

interface Fruit {

public abstract void eat();

}

class Apple implements Fruit {

public void eat(){

System.out.println("Apple");

}

}

class Orange implements Fruit {

public void eat(){

System.out.println("Orange");

}

}

class Factory {

public static Fruit getInstance(String ClassName) {

Fruit f=null;

try {

f=(Fruit)Class.forName(ClassName).newInstance();

} catch (Exception e) {

e.printStackTrace();

}

return f;

}

}

class Client {

public static void main(String[] a) {

Fruit f=Factory.getInstance("io.github.dunwu.spring.Apple");

if(f!=null){

f.eat();

}

}

}

# 什么是AOP? 有哪些AOP的概念?

AOP(Aspect-Oriented Programming,面向切面編程)能夠?qū)⒛切┡c業(yè)務(wù)無關(guān),卻為業(yè)務(wù)模塊所共同調(diào)用的邏輯或責(zé)任(例如事務(wù)處理、日志管理、權(quán)限控制等)封裝起來,便于減少系統(tǒng)的重復(fù)代碼,降低模塊間的耦合度,并有利于未來的可擴(kuò)展性和可維護(hù)性。

Spring AOP是基于動(dòng)態(tài)代理的,如果要代理的對(duì)象實(shí)現(xiàn)了某個(gè)接口,那么Spring AOP就會(huì)使用JDK動(dòng)態(tài)代理去創(chuàng)建代理對(duì)象;而對(duì)于沒有實(shí)現(xiàn)接口的對(duì)象,就無法使用JDK動(dòng)態(tài)代理,轉(zhuǎn)而使用CGlib動(dòng)態(tài)代理生成一個(gè)被代理對(duì)象的子類來作為代理。

當(dāng)然也可以使用AspectJ,Spring AOP中已經(jīng)集成了AspectJ,AspectJ應(yīng)該算得上是Java生態(tài)系統(tǒng)中最完整的AOP框架了。使用AOP之后我們可以把一些通用功能抽象出來,在需要用到的地方直接使用即可,這樣可以大大簡化代碼量。我們需要增加新功能也方便,提高了系統(tǒng)的擴(kuò)展性。日志功能、事務(wù)管理和權(quán)限管理等場景都用到了AOP。

AOP包含的幾個(gè)概念

Jointpoint(連接點(diǎn)):具體的切面點(diǎn)點(diǎn)抽象概念,可以是在字段、方法上,Spring中具體表現(xiàn)形式是PointCut(切入點(diǎn)),僅作用在方法上。Advice(通知): 在連接點(diǎn)進(jìn)行的具體操作,如何進(jìn)行增強(qiáng)處理的,分為前置、后置、異常、最終、環(huán)繞五種情況。目標(biāo)對(duì)象:被AOP框架進(jìn)行增強(qiáng)處理的對(duì)象,也被稱為被增強(qiáng)的對(duì)象。AOP代理:AOP框架創(chuàng)建的對(duì)象,簡單的說,代理就是對(duì)目標(biāo)對(duì)象的加強(qiáng)。Spring中的AOP代理可以是JDK動(dòng)態(tài)代理,也可以是CGLIB代理。Weaving(織入):將增強(qiáng)處理添加到目標(biāo)對(duì)象中,創(chuàng)建一個(gè)被增強(qiáng)的對(duì)象的過程

總結(jié)為一句話就是:在目標(biāo)對(duì)象(target object)的某些方法(jointpoint)添加不同種類的操作(通知、增強(qiáng)操處理),最后通過某些方法(weaving、織入操作)實(shí)現(xiàn)一個(gè)新的代理目標(biāo)對(duì)象。

# AOP 有哪些應(yīng)用場景?

舉幾個(gè)例子:

記錄日志(調(diào)用方法后記錄日志)監(jiān)控性能(統(tǒng)計(jì)方法運(yùn)行時(shí)間)權(quán)限控制(調(diào)用方法前校驗(yàn)是否有權(quán)限)事務(wù)管理(調(diào)用方法前開啟事務(wù),調(diào)用方法后提交關(guān)閉事務(wù) )緩存優(yōu)化(第一次調(diào)用查詢數(shù)據(jù)庫,將查詢結(jié)果放入內(nèi)存對(duì)象, 第二次調(diào)用,直接從內(nèi)存對(duì)象返回,不需要查詢數(shù)據(jù)庫 )

# 有哪些AOP Advice通知的類型?

特定 JoinPoint 處的 Aspect 所采取的動(dòng)作稱為 Advice。Spring AOP 使用一個(gè) Advice 作為攔截器,在 JoinPoint “周圍”維護(hù)一系列的攔截器。

前置通知(Before advice) : 這些類型的 Advice 在 joinpoint 方法之前執(zhí)行,并使用 @Before 注解標(biāo)記進(jìn)行配置。后置通知(After advice) :這些類型的 Advice 在連接點(diǎn)方法之后執(zhí)行,無論方法退出是正常還是異常返回,并使用 @After 注解標(biāo)記進(jìn)行配置。返回后通知(After return advice) :這些類型的 Advice 在連接點(diǎn)方法正常執(zhí)行后執(zhí)行,并使用@AfterReturning 注解標(biāo)記進(jìn)行配置。環(huán)繞通知(Around advice) :這些類型的 Advice 在連接點(diǎn)之前和之后執(zhí)行,并使用 @Around 注解標(biāo)記進(jìn)行配置。拋出異常后通知(After throwing advice) :僅在 joinpoint 方法通過拋出異常退出并使用 @AfterThrowing 注解標(biāo)記配置時(shí)執(zhí)行。

# AOP 有哪些實(shí)現(xiàn)方式?

實(shí)現(xiàn) AOP 的技術(shù),主要分為兩大類:

靜態(tài)代理 - 指使用 AOP 框架提供的命令進(jìn)行編譯,從而在編譯階段就可生成 AOP 代理類,因此也稱為編譯時(shí)增強(qiáng);

編譯時(shí)編織(特殊編譯器實(shí)現(xiàn))類加載時(shí)編織(特殊的類加載器實(shí)現(xiàn))。動(dòng)態(tài)代理 - 在運(yùn)行時(shí)在內(nèi)存中“臨時(shí)”生成 AOP 動(dòng)態(tài)代理類,因此也被稱為運(yùn)行時(shí)增強(qiáng)。

JDK 動(dòng)態(tài)代理

JDK Proxy 是 Java 語言自帶的功能,無需通過加載第三方類實(shí)現(xiàn);Java 對(duì) JDK Proxy 提供了穩(wěn)定的支持,并且會(huì)持續(xù)的升級(jí)和更新,Java 8 版本中的 JDK Proxy 性能相比于之前版本提升了很多;JDK Proxy 是通過攔截器加反射的方式實(shí)現(xiàn)的;JDK Proxy 只能代理實(shí)現(xiàn)接口的類;JDK Proxy 實(shí)現(xiàn)和調(diào)用起來比較簡單;CGLIB

CGLib 是第三方提供的工具,基于 ASM 實(shí)現(xiàn)的,性能比較高;CGLib 無需通過接口來實(shí)現(xiàn),它是針對(duì)類實(shí)現(xiàn)代理,主要是對(duì)指定的類生成一個(gè)子類,它是通過實(shí)現(xiàn)子類的方式來完成調(diào)用的。

# 談?wù)勀銓?duì)CGLib的理解?

JDK 動(dòng)態(tài)代理機(jī)制只能代理實(shí)現(xiàn)接口的類,一般沒有實(shí)現(xiàn)接口的類不能進(jìn)行代理。使用 CGLib 實(shí)現(xiàn)動(dòng)態(tài)代理,完全不受代理類必須實(shí)現(xiàn)接口的限制。

CGLib 的原理是對(duì)指定目標(biāo)類生成一個(gè)子類,并覆蓋其中方法實(shí)現(xiàn)增強(qiáng),但因?yàn)椴捎玫氖抢^承,所以不能對(duì) final 修飾的類進(jìn)行代理。

舉例:

public class CGLibDemo {

// 需要?jiǎng)討B(tài)代理的實(shí)際對(duì)象

static class Sister {

public void sing() {

System.out.println("I am Jinsha, a little sister.");

}

}

static class CGLibProxy implements MethodInterceptor {

private Object target;

public Object getInstance(Object target){

this.target = target;

Enhancer enhancer = new Enhancer();

// 設(shè)置父類為實(shí)例類

enhancer.setSuperclass(this.target.getClass());

// 回調(diào)方法

enhancer.setCallback(this);

// 創(chuàng)建代理對(duì)象

return enhancer.create();

}

@Override

public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

System.out.println("introduce yourself...");

Object result = methodProxy.invokeSuper(o,objects);

System.out.println("score...");

return result;

}

}

public static void main(String[] args) {

CGLibProxy cgLibProxy = new CGLibProxy();

//獲取動(dòng)態(tài)代理類實(shí)例

Sister proxySister = (Sister) cgLibProxy.getInstance(new Sister());

System.out.println("CGLib Dynamic object name: " + proxySister.getClass().getName());

proxySister.sing();

}

}

CGLib 的調(diào)用流程就是通過調(diào)用攔截器的 intercept 方法來實(shí)現(xiàn)對(duì)被代理類的調(diào)用。而攔截邏輯可以寫在 intercept 方法的 invokeSuper(o, objects);的前后實(shí)現(xiàn)攔截。

# 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ì)來說更簡單。

如果我們的切面比較少,那么兩者性能差異不大。但是,當(dāng)切面太多的話,最好選擇AspectJ,它比SpringAOP快很多。

# 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)沒有了。Portlet是能夠生成語義代碼(例如HTML)片段的小型Java Web插件。它們基于Portlet容器,可以像Servlet一樣處理HTTP請(qǐng)求。但是與Servlet不同,每個(gè)Portlet都有不同的會(huì)話。

# Spring中的單例bean的線程安全問題了解嗎?

大部分時(shí)候我們并沒有在系統(tǒng)中使用多線程,所以很少有人會(huì)關(guān)注這個(gè)問題。單例bean存在線程問題,主要是因?yàn)楫?dāng)多個(gè)線程操作同一個(gè)對(duì)象的時(shí)候,對(duì)這個(gè)對(duì)象的非靜態(tài)成員變量的寫操作會(huì)存在線程安全問題。

有兩種常見的解決方案:

1.在bean對(duì)象中盡量避免定義可變的成員變量(不太現(xiàn)實(shí))。

2.在類中定義一個(gè)ThreadLocal成員變量,將需要的可變成員變量保存在ThreadLocal中(推薦的一種方式)。

# Spring中的bean生命周期?

Bean的完整生命周期經(jīng)歷了各種方法調(diào)用,這些方法可以劃分為以下幾類:

Bean自身的方法: 這個(gè)包括了Bean本身調(diào)用的方法和通過配置文件中的init-method和destroy-method指定的方法Bean級(jí)生命周期接口方法: 這個(gè)包括了BeanNameAware、BeanFactoryAware、ApplicationContextAware;當(dāng)然也包括InitializingBean和DiposableBean這些接口的方法(可以被@PostConstruct和@PreDestroy注解替代)容器級(jí)生命周期接口方法: 這個(gè)包括了InstantiationAwareBeanPostProcessor 和 BeanPostProcessor 這兩個(gè)接口實(shí)現(xiàn),一般稱它們的實(shí)現(xiàn)類為“后處理器”。工廠后處理器接口方法: 這個(gè)包括了AspectJWeavingEnabler, ConfigurationClassPostProcessor, CustomAutowireConfigurer等等非常有用的工廠后處理器接口的方法。工廠后處理器也是容器級(jí)的。在應(yīng)用上下文裝配配置文件之后立即調(diào)用。

具體而言,流程如下

如果 BeanFactoryPostProcessor 和 Bean 關(guān)聯(lián), 則調(diào)用postProcessBeanFactory方法.(即首先嘗試從Bean工廠中獲取Bean)如果 InstantiationAwareBeanPostProcessor 和 Bean 關(guān)聯(lián),則調(diào)用postProcessBeforeInstantiation方法根據(jù)配置情況調(diào)用 Bean 構(gòu)造方法實(shí)例化 Bean。利用依賴注入完成 Bean 中所有屬性值的配置注入。如果 InstantiationAwareBeanPostProcessor 和 Bean 關(guān)聯(lián),則調(diào)用postProcessAfterInstantiation方法和postProcessProperties調(diào)用xxxAware接口 (上圖只是給了幾個(gè)例子)

第一類Aware接口

如果 Bean 實(shí)現(xiàn)了 BeanNameAware 接口,則 Spring 調(diào)用 Bean 的 setBeanName() 方法傳入當(dāng)前 Bean 的 id 值。如果 Bean 實(shí)現(xiàn)了 BeanClassLoaderAware 接口,則 Spring 調(diào)用 setBeanClassLoader() 方法傳入classLoader的引用。如果 Bean 實(shí)現(xiàn)了 BeanFactoryAware 接口,則 Spring 調(diào)用 setBeanFactory() 方法傳入當(dāng)前工廠實(shí)例的引用。第二類Aware接口

如果 Bean 實(shí)現(xiàn)了 EnvironmentAware 接口,則 Spring 調(diào)用 setEnvironment() 方法傳入當(dāng)前 Environment 實(shí)例的引用。如果 Bean 實(shí)現(xiàn)了 EmbeddedValueResolverAware 接口,則 Spring 調(diào)用 setEmbeddedValueResolver() 方法傳入當(dāng)前 StringValueResolver 實(shí)例的引用。如果 Bean 實(shí)現(xiàn)了 ApplicationContextAware 接口,則 Spring 調(diào)用 setApplicationContext() 方法傳入當(dāng)前 ApplicationContext 實(shí)例的引用。...如果 BeanPostProcessor 和 Bean 關(guān)聯(lián),則 Spring 將調(diào)用該接口的預(yù)初始化方法 postProcessBeforeInitialzation() 對(duì) Bean 進(jìn)行加工操作,此處非常重要,Spring 的 AOP 就是利用它實(shí)現(xiàn)的。如果 Bean 實(shí)現(xiàn)了 InitializingBean 接口,則 Spring 將調(diào)用 afterPropertiesSet() 方法。(或者有執(zhí)行@PostConstruct注解的方法)如果在配置文件中通過 init-method 屬性指定了初始化方法,則調(diào)用該初始化方法。如果 BeanPostProcessor 和 Bean 關(guān)聯(lián),則 Spring 將調(diào)用該接口的初始化方法 postProcessAfterInitialization()。此時(shí),Bean 已經(jīng)可以被應(yīng)用系統(tǒng)使用了。如果在 中指定了該 Bean 的作用范圍為 scope="singleton",則將該 Bean 放入 Spring IoC 的緩存池中,將觸發(fā) Spring 對(duì)該 Bean 的生命周期管理;如果在 中指定了該 Bean 的作用范圍為 scope="prototype",則將該 Bean 交給調(diào)用者,調(diào)用者管理該 Bean 的生命周期,Spring 不再管理該 Bean。如果 Bean 實(shí)現(xiàn)了 DisposableBean 接口,則 Spring 會(huì)調(diào)用 destory() 方法將 Spring 中的 Bean 銷毀;(或者有執(zhí)行@PreDestroy注解的方法)如果在配置文件中通過 destory-method 屬性指定了 Bean 的銷毀方法,則 Spring 將調(diào)用該方法對(duì) Bean 進(jìn)行銷毀。

# 說說自己對(duì)于Spring MVC的了解?

MVC是一種設(shè)計(jì)模式,Spring MVC是一款很優(yōu)秀的MVC框架。Spring MVC可以幫助我們進(jìn)行更簡潔的Web層的開發(fā),并且它天生與Spring框架集成。Spring MVC下我們一般把后端項(xiàng)目分為Service層(處理業(yè)務(wù))、Dao層(數(shù)據(jù)庫操作)、Entity層(實(shí)體類)、Controller層(控制層,返回?cái)?shù)據(jù)給前臺(tái)頁面)。

# Spring MVC的工作原理了解嘛?

流程說明:

1.客戶端(瀏覽器)發(fā)送請(qǐng)求,直接請(qǐng)求到DispatcherServlet。

2.DispatcherServlet根據(jù)請(qǐng)求信息調(diào)用HandlerMapping,解析請(qǐng)求對(duì)應(yīng)的Handler。

3.解析到對(duì)應(yīng)的Handler(也就是我們平常說的Controller控制器)。

4.HandlerAdapter會(huì)根據(jù)Handler來調(diào)用真正的處理器來處理請(qǐng)求和執(zhí)行相對(duì)應(yīng)的業(yè)務(wù)邏輯。

5.處理器處理完業(yè)務(wù)后,會(huì)返回一個(gè)ModelAndView對(duì)象,Model是返回的數(shù)據(jù)對(duì)象,View是邏輯上的View。

6.ViewResolver會(huì)根據(jù)邏輯View去查找實(shí)際的View。

7.DispatcherServlet把返回的Model傳給View(視圖渲染)。

8.把View返回給請(qǐng)求者(瀏覽器)。

# Spring框架中用到了哪些設(shè)計(jì)模式?

舉幾個(gè)例子

1.工廠設(shè)計(jì)模式:Spring使用工廠模式通過BeanFactory和ApplicationContext創(chuàng)建bean對(duì)象。

2.代理設(shè)計(jì)模式:Spring AOP功能的實(shí)現(xiàn)。

3.單例設(shè)計(jì)模式:Spring中的bean默認(rèn)都是單例的。

4.模板方法模式:Spring中的jdbcTemplate、hibernateTemplate等以Template結(jié)尾的對(duì)數(shù)據(jù)庫操作的類,它們就使用到了模板模式。

5.包裝器設(shè)計(jì)模式:我們的項(xiàng)目需要連接多個(gè)數(shù)據(jù)庫,而且不同的客戶在每次訪問中根據(jù)需要會(huì)去訪問不同的數(shù)據(jù)庫。這種模式讓我們可以根據(jù)客戶的需求能夠動(dòng)態(tài)切換不同的數(shù)據(jù)源。

6.觀察者模式:Spring事件驅(qū)動(dòng)模型就是觀察者模式很經(jīng)典的一個(gè)應(yīng)用。

7.適配器模式:Spring AOP的增強(qiáng)或通知(Advice)使用到了適配器模式、Spring MVC中也是用到了適配器模式適配Controller。

# @Component和@Bean的區(qū)別是什么?

1.作用對(duì)象不同。@Component注解作用于類,而@Bean注解作用于方法。

2.@Component注解通常是通過類路徑掃描來自動(dòng)偵測以及自動(dòng)裝配到Spring容器中(我們可以使用@ComponentScan注解定義要掃描的路徑)。@Bean注解通常是在標(biāo)有該注解的方法中定義產(chǎn)生這個(gè)bean,告訴Spring這是某個(gè)類的實(shí)例,當(dāng)我需要用它的時(shí)候還給我。

3.@Bean注解比@Component注解的自定義性更強(qiáng),而且很多地方只能通過@Bean注解來注冊(cè)bean。比如當(dāng)引用第三方庫的類需要裝配到Spring容器的時(shí)候,就只能通過@Bean注解來實(shí)現(xiàn)。

@Bean注解的使用示例:

@Configuration

public class AppConfig {

@Bean

public TransferService transferService() {

return new TransferServiceImpl();

}

}

上面的代碼相當(dāng)于下面的XML配置:

下面這個(gè)例子是無法通過@Component注解實(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。而想要把一個(gè)類標(biāo)識(shí)為可以用@Autowired注解自動(dòng)裝配的bean,可以采用以下的注解實(shí)現(xiàn):

1.@Component注解。通用的注解,可標(biāo)注任意類為Spring組件。如果一個(gè)Bean不知道屬于哪一個(gè)層,可以使用@Component注解標(biāo)注。

2.@Repository注解。對(duì)應(yīng)持久層,即Dao層,主要用于數(shù)據(jù)庫相關(guān)操作。

3.@Service注解。對(duì)應(yīng)服務(wù)層,即Service層,主要涉及一些復(fù)雜的邏輯,需要用到Dao層(注入)。

4.@Controller注解。對(duì)應(yīng)Spring MVC的控制層,即Controller層,主要用于接受用戶請(qǐng)求并調(diào)用Service層的方法返回?cái)?shù)據(jù)給前端頁面。

# Spring事務(wù)管理的方式有幾種?

1.編程式事務(wù):在代碼中硬編碼(不推薦使用)。

2.聲明式事務(wù):在配置文件中配置(推薦使用),分為基于XML的聲明式事務(wù)和基于注解的聲明式事務(wù)。

# Spring事務(wù)中的隔離級(jí)別有哪幾種?

在TransactionDefinition接口中定義了五個(gè)表示隔離級(jí)別的常量:

ISOLATION_DEFAULT:使用后端數(shù)據(jù)庫默認(rèn)的隔離級(jí)別,Mysql默認(rèn)采用的REPEATABLE_READ隔離級(jí)別;Oracle默認(rèn)采用的READ_COMMITTED隔離級(jí)別。

ISOLATION_READ_UNCOMMITTED:最低的隔離級(jí)別,允許讀取尚未提交的數(shù)據(jù)變更,可能會(huì)導(dǎo)致臟讀、幻讀或不可重復(fù)讀。

ISOLATION_READ_COMMITTED:允許讀取并發(fā)事務(wù)已經(jīng)提交的數(shù)據(jù),可以阻止臟讀,但是幻讀或不可重復(fù)讀仍有可能發(fā)生

ISOLATION_REPEATABLE_READ:對(duì)同一字段的多次讀取結(jié)果都是一致的,除非數(shù)據(jù)是被本身事務(wù)自己所修改,可以阻止臟讀和不可重復(fù)讀,但幻讀仍有可能發(fā)生。

ISOLATION_SERIALIZABLE:最高的隔離級(jí)別,完全服從ACID的隔離級(jí)別。所有的事務(wù)依次逐個(gè)執(zhí)行,這樣事務(wù)之間就完全不可能產(chǎn)生干擾,也就是說,該級(jí)別可以防止臟讀、不可重復(fù)讀以及幻讀。但是這將嚴(yán)重影響程序的性能。通常情況下也不會(huì)用到該級(jí)別。

# Spring事務(wù)中有哪幾種事務(wù)傳播行為?

在TransactionDefinition接口中定義了7個(gè)表示事務(wù)傳播行為的常量。

支持當(dāng)前事務(wù)的情況:

PROPAGATION_REQUIRED:如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒有事務(wù),則創(chuàng)建一個(gè)新的事務(wù)。

PROPAGATION_SUPPORTS: 如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒有事務(wù),則以非事務(wù)的方式繼續(xù)運(yùn)行。

PROPAGATION_MANDATORY: 如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒有事務(wù),則拋出異常。(mandatory:強(qiáng)制性)。

不支持當(dāng)前事務(wù)的情況:

PROPAGATION_REQUIRES_NEW: 創(chuàng)建一個(gè)新的事務(wù),如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起。

PROPAGATION_NOT_SUPPORTED: 以非事務(wù)方式運(yùn)行,如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起。

PROPAGATION_NEVER: 以非事務(wù)方式運(yùn)行,如果當(dāng)前存在事務(wù),則拋出異常。

其他情況:

PROPAGATION_NESTED: 如果當(dāng)前存在事務(wù),則創(chuàng)建一個(gè)事務(wù)作為當(dāng)前事務(wù)的嵌套事務(wù)來運(yùn)行;如果當(dāng)前沒有事務(wù),則該取值等價(jià)于PROPAGATION_REQUIRED。

# Bean Factory和ApplicationContext有什么區(qū)別?

ApplicationContex提供了一種解析文本消息的方法,一種加載文件資源(如圖像)的通用方法,它們可以將事件發(fā)布到注冊(cè)為偵聽器的bean。此外,可以在應(yīng)用程序上下文中以聲明方式處理容器中的容器或容器上的操作,這些操作必須以編程方式與Bean Factory一起處理。ApplicationContext實(shí)現(xiàn)MessageSource,一個(gè)用于獲取本地化消息的接口,實(shí)際的實(shí)現(xiàn)是可插入的。

# 如何定義bean的范圍?

在Spring中定義一個(gè)時(shí),我們也可以為bean聲明一個(gè)范圍。它可以通過bean定義中的scope屬性定義。例如,當(dāng)Spring每次需要生成一個(gè)新的bean實(shí)例時(shí),bean'sscope屬性就是原型。另一方面,當(dāng)每次需要Spring都必須返回相同的bean實(shí)例時(shí),bean scope屬性必須設(shè)置為singleton。

# 可以通過多少種方式完成依賴注入?

通常,依賴注入可以通過三種方式完成,即:

構(gòu)造函數(shù)注入setter 注入接口注入

# 10.2 Spring Boot

# 什么是SpringBoot?

Spring Boot 是 Spring 開源組織下的子項(xiàng)目,是 Spring 組件一站式解決方案,主要是簡化了使用 Spring 的難度,簡省了繁重的配置,提供了各種啟動(dòng)器,開發(fā)者能快速上手。

用來簡化Spring應(yīng)用的初始搭建以及開發(fā)過程,使用特定的方式來進(jìn)行配置創(chuàng)建獨(dú)立的Spring引用程序main方法運(yùn)行嵌入的tomcat無需部署war文件簡化maven配置自動(dòng)配置Spring添加對(duì)應(yīng)的功能starter自動(dòng)化配置SpringBoot來簡化Spring應(yīng)用開發(fā),約定大于配置,去繁化簡

# 為什么使用SpringBoot?

獨(dú)立運(yùn)行

Spring Boot 而且內(nèi)嵌了各種 servlet 容器,Tomcat、Jetty 等,現(xiàn)在不再需要打成war 包部署到容器中,Spring Boot 只要打成一個(gè)可執(zhí)行的 jar 包就能獨(dú)立運(yùn)行,所有的依賴包都在一個(gè) jar 包內(nèi)。

簡化配置

spring-boot-starter-web 啟動(dòng)器自動(dòng)依賴其他組件,簡少了 maven 的配置。

自動(dòng)配置

Spring Boot 能根據(jù)當(dāng)前類路徑下的類、jar 包來自動(dòng)配置 bean,如添加一個(gè) spring

boot-starter-web 啟動(dòng)器就能擁有 web 的功能,無需其他配置。

無代碼生成和XML配置

Spring Boot 配置過程中無代碼生成,也無需 XML 配置文件就能完成所有配置工作,這一切都是借助于條件注解完成的,這也是 Spring4.x 的核心功能之一。

應(yīng)用監(jiān)控

Spring Boot 提供一系列端點(diǎn)可以監(jiān)控服務(wù)及應(yīng)用,做健康檢測。

# Spring、Spring MVC和SpringBoot有什么區(qū)別?

Spring

Spring最重要的特征是依賴注入。所有Spring Modules不是依賴注入就是IOC控制反轉(zhuǎn)。

當(dāng)我們恰當(dāng)?shù)氖褂肈I或者是IOC的時(shí)候,可以開發(fā)松耦合應(yīng)用。

Spring MVC

Spring MVC提供了一種分離式的方法來開發(fā)Web應(yīng)用。通過運(yùn)用像DispatcherServelet,ModelAndView 和 ViewResolver 等一些簡單的概念,開發(fā) Web 應(yīng)用將會(huì)變的非常簡單。

SpringBoot

Spring和Spring MVC的問題在于需要配置大量的參數(shù)。

SpringBoot通過一個(gè)自動(dòng)配置和啟動(dòng)的項(xiàng)來解決這個(gè)問題。

# SpringBoot自動(dòng)配置的原理?

在Spring程序main方法中,添加@SpringBootApplication或者@EnableAutoConfiguration會(huì)自動(dòng)去maven中讀取每個(gè)starter中的spring.factories文件,該文件里配置了所有需要被創(chuàng)建的Spring容器中的bean

# Spring Boot的核心注解是哪些?他主由哪幾個(gè)注解組成的?

啟動(dòng)類上面的注解是@SpringBootApplication,他也是SpringBoot的核心注解,主要組合包含了以下3個(gè)注解:

@SpringBootConfiguration:組合了@Configuration注解,實(shí)現(xiàn)配置文件的功能;@EnableAutoConfiguration:打開自動(dòng)配置的功能,也可以關(guān)閉某個(gè)自動(dòng)配置的選項(xiàng),如關(guān)閉數(shù)據(jù)源自動(dòng)配置的功能:@SpringBootApplication(exclude={DataSourceAutoConfiguration.class});@ComponentScan:Spring組件掃描。

# SpringBoot的核心配置文件有哪幾個(gè)?他們的區(qū)別是什么?

SpringBoot的核心配置文件是application和bootstrap配置文件。

application配置文件這個(gè)容易理解,主要用于Spring Boot項(xiàng)目的自動(dòng)化配置。

bootstrap配置文件有以下幾個(gè)應(yīng)用場景:

使用Spring Cloud Config配置中心時(shí),這時(shí)需要在bootstrap配置文件中添加連接到配置中心的配置屬性來加載外部配置中心的配置信息;一些固定的不能被覆蓋的屬性;一些加密/解密的場景;

# 什么是Spring Boot Starter?有哪些常用的?

和自動(dòng)配置一樣,Spring Boot Starter的目的也是簡化配置,而Spring Boot Starter解決的是依賴管理配置復(fù)雜的問題,有了它,當(dāng)我需要構(gòu)建一個(gè)Web應(yīng)用程序時(shí),不必再遍歷所有的依賴包,一個(gè)一個(gè)地添加到項(xiàng)目的依賴管理中,而是只需要一個(gè)配置spring-boot-starter-web, 同理,如果想引入持久化功能,可以配置spring-boot-starter-data-jpa:

org.springframework.boot

spring-boot-starter-web

Spring Boot 也提供了其它的啟動(dòng)器項(xiàng)目包括,包括用于開發(fā)特定類型應(yīng)用程序的典型依賴項(xiàng)。

spring-boot-starter-web-services - SOAP Web Services

spring-boot-starter-web - Web 和 RESTful 應(yīng)用程序

spring-boot-starter-test - 單元測試和集成測試

spring-boot-starter-jdbc - 傳統(tǒng)的 JDBC

spring-boot-starter-hateoas - 為服務(wù)添加 HATEOAS 功能

spring-boot-starter-security - 使用 SpringSecurity 進(jìn)行身份驗(yàn)證和授權(quán)

spring-boot-starter-data-jpa - 帶有 Hibernate 的 Spring Data JPA

spring-boot-starter-data-rest - 使用 Spring Data REST 公布簡單的 REST 服務(wù)

# spring-boot-starter-parent有什么作用?

我們知道,新建一個(gè)SpringBoot項(xiàng)目,默認(rèn)都是有parent的,這個(gè)parent就是spring-boot-starter-parent,spring-boot-starter-parent主要有如下作用:

定義了Java編譯版本使用UTF-8格式編碼繼承自spring-boor-dependencies,這里面定義了依賴的版本,也正是因?yàn)槔^承了這個(gè)依賴,所以我們?cè)趯懸蕾嚂r(shí)才不需要寫版本號(hào)執(zhí)行打包操作的配置自動(dòng)化的資源過濾自動(dòng)化的插件配置

# 如何自定義Spring Boot Starter?

實(shí)現(xiàn)功能添加Properties

@Data

@ConfigurationProperties(prefix = "com.pdai")

public class DemoProperties {

private String version;

private String name;

}

添加AutoConfiguration

@Configuration

@EnableConfigurationProperties(DemoProperties.class)

public class DemoAutoConfiguration {

@Bean

public com.pdai.demo.module.DemoModule demoModule(DemoProperties properties){

com.pdai.demo.module.DemoModule demoModule = new com.pdai.demo.module.DemoModule();

demoModule.setName(properties.getName());

demoModule.setVersion(properties.getVersion());

return demoModule;

}

}

添加spring.factory

在META-INF下創(chuàng)建spring.factory文件

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\

com.pdai.demospringbootstarter.DemoAutoConfiguration

# 為什么需要spring-boot-maven-plugin?

spring-boot-maven-plugin提供了一些像jar一樣打包或者運(yùn)行應(yīng)用程序的命令。

spring-boot:run 運(yùn)行SpringBoot應(yīng)用程序;spring-boot:repackage 重新打包你的jar包或者是war包使其可執(zhí)行spring-boot:start和spring-boot:stop管理Spring Boot應(yīng)用程序的生命周期spring-boot:build-info生成執(zhí)行器可以使用的構(gòu)造信息

# SpringBoot 打成jar和普通的jar有什么區(qū)別?

Spring Boot 項(xiàng)目最終打包成的 jar 是可執(zhí)行 jar ,這種 jar 可以直接通過java -jar xxx.jar命令來運(yùn)行,這種 jar 不可以作為普通的 jar 被其他項(xiàng)目依賴,即使依賴了也無法使用其中的類。

Spring Boot 的 jar 無法被其他項(xiàng)目依賴,主要還是他和普通 jar 的結(jié)構(gòu)不同。普通的 jar 包,解壓后直接就是包名,包里就是我們的代碼,而 Spring Boot 打包成的可執(zhí)行 jar 解壓后,在 \BOOT-INF\classes目錄下才是我們的代碼,因此無法被直接引用。如果非要引用,可以在 pom.xml 文件中增加配置,將 Spring Boot 項(xiàng)目打包成兩個(gè) jar ,一個(gè)可執(zhí)行,一個(gè)可引用。

# 如何使用Spring Boot實(shí)現(xiàn)異常處理?

Spring提供了一種使用ControllerAdvice處理異常的非常有用的方法。通過實(shí)現(xiàn)一個(gè)ControlerAdvice類,來處理控制類拋出的所有異常。

# SpringBoot 實(shí)現(xiàn)熱部署有哪幾種方式?

主要有兩種方式:

Spring LoadedSpring-boot-devtools

# Spring Boot中的監(jiān)視器是什么?

Spring boot actuator是spring啟動(dòng)框架中的重要功能之一。Spring boot監(jiān)視器可幫助您訪問生產(chǎn)環(huán)境中正在運(yùn)行的應(yīng)用程序的當(dāng)前狀態(tài)。

有幾個(gè)指標(biāo)必須在生產(chǎn)環(huán)境中進(jìn)行檢查和監(jiān)控。即使一些外部應(yīng)用程序可能正在使用這些服務(wù)來向相關(guān)人員觸發(fā)警報(bào)消息。監(jiān)視器模塊公開了一組可直接作為HTTP URL訪問的REST端點(diǎn)來檢查狀態(tài)。

# Spring Boot 可以兼容老 Spring 項(xiàng)目嗎?

可以兼容,使用 @ImportResource 注解導(dǎo)入老 Spring 項(xiàng)目配置文件。

# 10.3 Spring Security

# 什么是Spring Security?核心功能?

Spring Security是基于Spring的安全框架.它提供全面的安全性解決方案,同時(shí)在Web請(qǐng)求級(jí)別和調(diào)用級(jí)別確認(rèn)和授權(quán).在Spring Framework基礎(chǔ)上,Spring Security充分利用了依賴注入(DI)和面向切面編程(AOP)功能,為應(yīng)用系統(tǒng)提供聲明式的安全訪問控制功能,建曬了為企業(yè)安全控制編寫大量重復(fù)代碼的工作,是一個(gè)輕量級(jí)的安全框架,并且很好集成Spring MVC

spring security 的核心功能主要包括:

認(rèn)證(Authentication):指的是驗(yàn)證某個(gè)用戶是否為系統(tǒng)中的合法主體,也就是說用戶能否訪問該系統(tǒng)。授權(quán)(Authorization):指的是驗(yàn)證某個(gè)用戶是否有權(quán)限執(zhí)行某個(gè)操作攻擊防護(hù):指的是防止偽造身份

# Spring Security的原理?

簡單談?wù)勂渲械囊c(diǎn)

基于Filter技術(shù)實(shí)現(xiàn)?

首先SpringSecurity是基于Filter技術(shù)實(shí)現(xiàn)的。Spring通過DelegatingFilterProxy建立Web容器和Spring ApplicationContext的聯(lián)系,而SpringSecurity使用FilterChainProxy 注冊(cè)SecurityFilterChain。

認(rèn)證模塊的實(shí)現(xiàn)?

SecurityContextHolder(用于存儲(chǔ)授權(quán)信息)

手動(dòng)授權(quán)的例子(SecurityContextHolder.getContext().setAuthentication(authentication)這種授權(quán)方式多線程不安全):

SecurityContext context = SecurityContextHolder.createEmptyContext();

Authentication authentication =

new TestingAuthenticationToken("username", "password", "ROLE_USER");

context.setAuthentication(authentication);

SecurityContextHolder.setContext(context);

除了手動(dòng)授權(quán)外,SpringSecurity通過AuthenticationManager和ProviderManager進(jìn)行授權(quán)。其中AuthenticationProvider代表不同的認(rèn)證機(jī)制(最常用的賬號(hào)/密碼)。

授權(quán)模塊的實(shí)現(xiàn)?

認(rèn)證完成之后,SpringSecurity通過AccessDecisionManager 完成授權(quán)操作。除了全局的授權(quán)配置之外,也可以通過@PreAuthorize, @PreFilter, @PostAuthorize , @PostFilter注解實(shí)現(xiàn)方法級(jí)別的權(quán)限控制。

# Spring Security基于用戶名和密碼的認(rèn)證模式流程?

請(qǐng)求的用戶名密碼可以通過表單登錄,基礎(chǔ)認(rèn)證,數(shù)字認(rèn)證三種方式從HttpServletRequest中獲得,用于認(rèn)證的數(shù)據(jù)源策略有內(nèi)存,數(shù)據(jù)庫,ldap,自定義等。

攔截未授權(quán)的請(qǐng)求,重定向到登錄頁面

表單登錄的過程,進(jìn)行賬號(hào)密碼認(rèn)證

# 10.4 MyBatis

# 10.5 JPA

# 10.6 日志框架

# 什么是日志系統(tǒng)和日志門面?分別有哪些框架?

日志系統(tǒng)是具體的日志框架,日志門面是不提供日志的具體實(shí)現(xiàn),而是在運(yùn)行時(shí)動(dòng)態(tài)的綁定日志實(shí)現(xiàn)組件來工作,是一種外觀模式。

日志系統(tǒng)

java.util.logging (JUL),JDK1.4 開始,通過 java.util.logging 提供日志功能。雖然是官方自帶的log lib,JUL的使用確不廣泛。Log4j,Log4j 是 apache 的一個(gè)開源項(xiàng)目,創(chuàng)始人 Ceki Gulcu。Log4j 應(yīng)該說是 Java 領(lǐng)域資格最老,應(yīng)用最廣的日志工具。Log4j 是高度可配置的,并可通過在運(yùn)行時(shí)的外部文件配置。它根據(jù)記錄的優(yōu)先級(jí)別,并提供機(jī)制,以指示記錄信息到許多的目的地,諸如:數(shù)據(jù)庫,文件,控制臺(tái),UNIX 系統(tǒng)日志等。Log4j 的短板在于性能,在Logback 和 Log4j2 出來之后,Log4j的使用也減少了。Logback,Logback 是由 log4j 創(chuàng)始人 Ceki Gulcu 設(shè)計(jì)的又一個(gè)開源日志組件,是作為 Log4j 的繼承者來開發(fā)的,提供了性能更好的實(shí)現(xiàn),異步 logger,F(xiàn)ilter等更多的特性。Log4j2,維護(hù) Log4j 的人為了性能又搞出了 Log4j2。Log4j2 和 Log4j1.x 并不兼容,設(shè)計(jì)上很大程度上模仿了 SLF4J/Logback,性能上也獲得了很大的提升。Log4j2 也做了 Facade/Implementation 分離的設(shè)計(jì),分成了 log4j-api 和 log4j-core。 日志門面

common-logging,common-logging 是 apache 的一個(gè)開源項(xiàng)目。也稱Jakarta Commons Logging,縮寫 JCL。slf4j, 全稱為 Simple Logging Facade for Java,即 java 簡單日志門面。作者又是 Ceki Gulcu!這位大神寫了 Log4j、Logback 和 slf4j。類似于 Common-Logging,slf4j 是對(duì)不同日志框架提供的一個(gè) API 封裝,可以在部署的時(shí)候不修改任何配置即可接入一種日志實(shí)現(xiàn)方案。但是,slf4j 在編譯時(shí)靜態(tài)綁定真正的 Log 庫。使用 SLF4J 時(shí),如果你需要使用某一種日志實(shí)現(xiàn),那么你必須選擇正確的 SLF4J 的 jar 包的集合(各種橋接包)。

# 日志庫中使用橋接模式解決什么問題?

什么是橋接呢?

假如你正在開發(fā)應(yīng)用程序所調(diào)用的組件當(dāng)中已經(jīng)使用了 common-logging,這時(shí)你需要 jcl-over-slf4j.jar 把日志信息輸出重定向到 slf4j-api,slf4j-api 再去調(diào)用 slf4j 實(shí)際依賴的日志組件。這個(gè)過程稱為橋接。

從圖中應(yīng)該可以看出,無論你的老項(xiàng)目中使用的是 common-logging 或是直接使用 log4j、java.util.logging,都可以使用對(duì)應(yīng)的橋接 jar 包來解決兼容問題。

slf4j 兼容 common-logging

org.slf4j

jcl-over-slf4j

1.7.12

slf4j 兼容 log4j

org.slf4j

log4j-over-slf4j

1.7.12

# 在日志配置時(shí)會(huì)考慮哪些點(diǎn)?

支持日志路徑,日志level等配置日志控制配置通過application.yml下發(fā)按天生成日志,當(dāng)天的日志>50MB回滾最多保存10天日志生成的日志中Pattern自定義Pattern中添加用戶自定義的MDC字段,比如用戶信息(當(dāng)前日志是由哪個(gè)用戶的請(qǐng)求產(chǎn)生),request信息。此種方式可以通過AOP切面控制,在MDC中添加requestID,在spring-logback.xml中配置Pattern。根據(jù)不同的運(yùn)行環(huán)境設(shè)置Profile - dev,test,product對(duì)控制臺(tái),Err和全量日志分別配置對(duì)第三方包路徑日志控制

# 對(duì)Java日志組件選型的建議?

slf4j已經(jīng)成為了Java日志組件的明星選手,可以完美替代JCL,使用JCL橋接庫也能完美兼容一切使用JCL作為日志門面的類庫,現(xiàn)在的新系統(tǒng)已經(jīng)沒有不使用slf4j作為日志API的理由了。

日志記錄服務(wù)方面,log4j在功能上輸于logback和log4j2,在性能方面log4j2則全面超越log4j和logback。所以新系統(tǒng)應(yīng)該在logback和log4j2中做出選擇,對(duì)于性能有很高要求的系統(tǒng),應(yīng)優(yōu)先考慮log4j2。

# 對(duì)日志架構(gòu)使用比較好的實(shí)踐?

說幾個(gè)點(diǎn):

總是使用Log Facade,而不是具體Log Implementation只添加一個(gè) Log Implementation依賴具體的日志實(shí)現(xiàn)依賴應(yīng)該設(shè)置為optional和使用runtime scope如果有必要, 排除依賴的第三方庫中的Log Impementation依賴避免為不會(huì)輸出的log付出代價(jià)日志格式中最好不要使用行號(hào),函數(shù)名等字段

# 對(duì)現(xiàn)有系統(tǒng)日志架構(gòu)的改造建議?

如果現(xiàn)有系統(tǒng)使用JCL作為日志門面,又確實(shí)面臨著JCL的ClassLoader機(jī)制帶來的問題,完全可以引入slf4j并通過橋接庫將JCL api輸出的日志橋接至slf4j,再通過適配庫適配至現(xiàn)有的日志輸出服務(wù)(如log4j)。

這樣做不需要任何代碼級(jí)的改造,就可以解決JCL的ClassLoader帶來的問題,但沒有辦法享受日志模板等slf4j的api帶來的優(yōu)點(diǎn)。不過之后在現(xiàn)系統(tǒng)上開發(fā)的新功能就可以使用slf4j的api了,老代碼也可以分批進(jìn)行改造。

如果現(xiàn)有系統(tǒng)使用JCL作為日志門面,又頭疼JCL不支持logback和log4j2等新的日志服務(wù),也可以通過橋接庫以slf4j替代JCL,但同樣無法直接享受slf4j api的優(yōu)點(diǎn)。

如果想要使用slf4j的api,那么就不得不進(jìn)行代碼改造了,當(dāng)然改造也可以參考1中提到的方式逐步進(jìn)行。

如果現(xiàn)系統(tǒng)面臨著log4j的性能問題,可以使用Apache Logging提供的log4j到log4j2的橋接庫log4j-1.2-api,把通過log4j api輸出的日志橋接至log4j2。這樣可以最快地使用上log4j2的先進(jìn)性能,但組件中缺失了slf4j,對(duì)后續(xù)進(jìn)行日志架構(gòu)改造的靈活性有影響。另一種辦法是先把log4j橋接至slf4j,再使用slf4j到log4j2的適配庫。這樣做稍微麻煩了一點(diǎn),但可以逐步將系統(tǒng)中的日志輸出標(biāo)準(zhǔn)化為使用slf4j的api,為后面的工作打好基礎(chǔ)。

# 10.7 Tomcat

# Tomcat 整體架構(gòu)的設(shè)計(jì)?

Server: 表示服務(wù)器,它提供了一種優(yōu)雅的方式來啟動(dòng)和停止整個(gè)系統(tǒng),不必單獨(dú)啟停連接器和容器;它是Tomcat構(gòu)成的頂級(jí)構(gòu)成元素,所有一切均包含在Server中; Service: 表示服務(wù),Server可以運(yùn)行多個(gè)服務(wù)。比如一個(gè)Tomcat里面可運(yùn)行訂單服務(wù)、支付服務(wù)、用戶服務(wù)等等;Server的實(shí)現(xiàn)類StandardServer可以包含一個(gè)到多個(gè)Services, Service的實(shí)現(xiàn)類為StandardService調(diào)用了容器(Container)接口,其實(shí)是調(diào)用了Servlet Engine(引擎),而且StandardService類中也指明了該Service歸屬的Server; Container: 表示容器,可以看做Servlet容器;引擎(Engine)、主機(jī)(Host)、上下文(Context)和Wraper均繼承自Container接口,所以它們都是容器。

Engine -- 引擎Host -- 主機(jī)Context -- 上下文Wrapper -- 包裝器 Connector: 表示連接器, 它將Service和Container連接起來,首先它需要注冊(cè)到一個(gè)Service,它的作用就是把來自客戶端的請(qǐng)求轉(zhuǎn)發(fā)到Container(容器),這就是它為什么稱作連接器, 它支持的協(xié)議如下:

支持AJP協(xié)議支持Http協(xié)議支持Https協(xié)議 Service內(nèi)部還有各種支撐組件,下面簡單羅列一下這些組件

Manager -- 管理器,用于管理會(huì)話SessionLogger -- 日志器,用于管理日志Loader -- 加載器,和類加載有關(guān),只會(huì)開放給Context所使用Pipeline -- 管道組件,配合Valve實(shí)現(xiàn)過濾器功能Valve -- 閥門組件,配合Pipeline實(shí)現(xiàn)過濾器功能Realm -- 認(rèn)證授權(quán)組件

# Tomcat 一個(gè)請(qǐng)求的處理流程?

假設(shè)來自客戶的請(qǐng)求為:http://localhost:8080/test/index.jsp 請(qǐng)求被發(fā)送到本機(jī)端口8080,被在那里偵聽的Coyote HTTP/1.1 Connector,然后

Connector把該請(qǐng)求交給它所在的Service的Engine來處理,并等待Engine的回應(yīng)Engine獲得請(qǐng)求localhost:8080/test/index.jsp,匹配它所有虛擬主機(jī)HostEngine匹配到名為localhost的Host(即使匹配不到也把請(qǐng)求交給該Host處理,因?yàn)樵揌ost被定義為該Engine的默認(rèn)主機(jī))localhost Host獲得請(qǐng)求/test/index.jsp,匹配它所擁有的所有ContextHost匹配到路徑為/test的Context(如果匹配不到就把該請(qǐng)求交給路徑名為""的Context去處理)path="/test"的Context獲得請(qǐng)求/index.jsp,在它的mapping table中尋找對(duì)應(yīng)的servletContext匹配到URL PATTERN為*.jsp的servlet,對(duì)應(yīng)于JspServlet類,構(gòu)造HttpServletRequest對(duì)象和HttpServletResponse對(duì)象,作為參數(shù)調(diào)用JspServlet的doGet或doPost方法Context把執(zhí)行完了之后的HttpServletResponse對(duì)象返回給HostHost把HttpServletResponse對(duì)象返回給EngineEngine把HttpServletResponse對(duì)象返回給ConnectorConnector把HttpServletResponse對(duì)象返回給客戶browser

# Tomcat 中類加載機(jī)制?

在Bootstrap中我們可以看到有如下三個(gè)classloader

ClassLoader commonLoader = null;

ClassLoader catalinaLoader = null;

ClassLoader sharedLoader = null;

為什么要設(shè)計(jì)多個(gè)類加載器?

如果所有的類都使用一個(gè)類加載器來加載,會(huì)出現(xiàn)什么問題呢?

假如我們自己編寫一個(gè)類java.util.Object,它的實(shí)現(xiàn)可能有一定的危險(xiǎn)性或者隱藏的bug。而我們知道Java自帶的核心類里面也有java.util.Object,如果JVM啟動(dòng)的時(shí)候先行加載的是我們自己編寫的java.util.Object,那么就有可能出現(xiàn)安全問題!

所以,Sun(后被Oracle收購)采用了另外一種方式來保證最基本的、也是最核心的功能不會(huì)被破壞。你猜的沒錯(cuò),那就是雙親委派模式!

什么是雙親委派模型?

雙親委派模型解決了類錯(cuò)亂加載的問題,也設(shè)計(jì)得非常精妙。

雙親委派模式對(duì)類加載器定義了層級(jí),每個(gè)類加載器都有一個(gè)父類加載器。在一個(gè)類需要加載的時(shí)候,首先委派給父類加載器來加載,而父類加載器又委派給祖父類加載器來加載,以此類推。如果父類及上面的類加載器都加載不了,那么由當(dāng)前類加載器來加載,并將被加載的類緩存起來。

所以上述類是這么加載的

Java自帶的核心類 -- 由啟動(dòng)類加載器加載 Java支持的可擴(kuò)展類 -- 由擴(kuò)展類加載器加載 我們自己編寫的類 -- 默認(rèn)由應(yīng)用程序類加載器或其子類加載 為什么Tomcat的類加載器也不是雙親委派模型?

Java默認(rèn)的類加載機(jī)制是通過雙親委派模型來實(shí)現(xiàn)的,而Tomcat實(shí)現(xiàn)的方式又和雙親委派模型有所區(qū)別。

原因在于一個(gè)Tomcat容器允許同時(shí)運(yùn)行多個(gè)Web程序,每個(gè)Web程序依賴的類又必須是相互隔離的。因此,如果Tomcat使用雙親委派模式來加載類的話,將導(dǎo)致Web程序依賴的類變?yōu)楣蚕淼摹?/p>

舉個(gè)例子,假如我們有兩個(gè)Web程序,一個(gè)依賴A庫的1.0版本,另一個(gè)依賴A庫的2.0版本,他們都使用了類xxx.xx.Clazz,其實(shí)現(xiàn)的邏輯因類庫版本的不同而結(jié)構(gòu)完全不同。那么這兩個(gè)Web程序的其中一個(gè)必然因?yàn)榧虞d的Clazz不是所使用的Clazz而出現(xiàn)問題!而這對(duì)于開發(fā)來說是非常致命的!

# Tomcat Container設(shè)計(jì)?

我們看下幾個(gè)Container之間的關(guān)系:

從上圖上,我們也可以看出Container頂層也是基于Lifecycle的組件設(shè)計(jì)的。

在設(shè)計(jì)Container組件層次組件時(shí),上述4個(gè)組件分別做什么的呢?為什么要四種組件呢?

Engine - 表示整個(gè)catalina的servlet引擎,多數(shù)情況下包含一個(gè)或多個(gè)子容器,這些子容器要么是Host,要么是Context實(shí)現(xiàn),或者是其他自定義組。

Host - 表示包含多個(gè)Context的虛擬主機(jī)的。

Context — 表示一個(gè)ServletContext,表示一個(gè)webapp,它通常包含一個(gè)或多個(gè)wrapper。

Wrapper - 表示一個(gè)servlet定義的(如果servlet本身實(shí)現(xiàn)了SingleThreadModel,則可能支持多個(gè)servlet實(shí)例)。

結(jié)合整體的框架圖中上述組件部分,我們看下包含了什么?

很明顯,除了四個(gè)組件的嵌套關(guān)系,Container中還包含了Realm,Cluster,Listeners, Pipleline等支持組件。

這一點(diǎn),還可以通過相關(guān)注釋可以看出:

**Loader** - Class loader to use for integrating new Java classes for this Container into the JVM in which Catalina is running.

**Logger** - Implementation of the log() method signatures of the ServletContext interface.

**Manager** - Manager for the pool of Sessions associated with this Container.

**Realm** - Read-only interface to a security domain, for authenticating user identities and their corresponding roles.

**Resources** - JNDI directory context enabling access to static resources, enabling custom linkages to existing server components when Catalina is embedded in a larger server.

# Tomcat LifeCycle機(jī)制?

Server及其它組件

Server后續(xù)組件生命周期及初始化

Server的依賴結(jié)構(gòu)

public interface Lifecycle {

/** 第1類:針對(duì)監(jiān)聽器 **/

// 添加監(jiān)聽器

public void addLifecycleListener(LifecycleListener listener);

// 獲取所以監(jiān)聽器

public LifecycleListener[] findLifecycleListeners();

// 移除某個(gè)監(jiān)聽器

public void removeLifecycleListener(LifecycleListener listener);

/** 第2類:針對(duì)控制流程 **/

// 初始化方法

public void init() throws LifecycleException;

// 啟動(dòng)方法

public void start() throws LifecycleException;

// 停止方法,和start對(duì)應(yīng)

public void stop() throws LifecycleException;

// 銷毀方法,和init對(duì)應(yīng)

public void destroy() throws LifecycleException;

/** 第3類:針對(duì)狀態(tài) **/

// 獲取生命周期狀態(tài)

public LifecycleState getState();

// 獲取字符串類型的生命周期狀態(tài)

public String getStateName();

}

# Tomcat 中Executor?

1.Tomcat希望將Executor也納入Lifecycle生命周期管理,所以讓它實(shí)現(xiàn)了Lifecycle接口 2.引入超時(shí)機(jī)制:也就是說當(dāng)work queue滿時(shí),會(huì)等待指定的時(shí)間,如果超時(shí)將拋出RejectedExecutionException,所以這里增加了一個(gè)void execute(Runnable command, long timeout, TimeUnit unit)方法; 其實(shí)本質(zhì)上,它構(gòu)造了JUC中ThreadPoolExecutor,通過它調(diào)用ThreadPoolExecutor的void execute(Runnable command, long timeout, TimeUnit unit)方法。

# Tomcat 中的設(shè)計(jì)模式?

責(zé)任鏈模式:管道機(jī)制

在軟件開發(fā)的常接觸的責(zé)任鏈模式是FilterChain,它體現(xiàn)在很多軟件設(shè)計(jì)中:

比如HttpServletRequest處理的過濾器中

當(dāng)一個(gè)request過來的時(shí)候,需要對(duì)這個(gè)request做一系列的加工,使用責(zé)任鏈模式可以使每個(gè)加工組件化,減少耦合。也可以使用在當(dāng)一個(gè)request過來的時(shí)候,需要找到合適的加工方式。當(dāng)一個(gè)加工方式不適合這個(gè)request的時(shí)候,傳遞到下一個(gè)加工方法,該加工方式再嘗試對(duì)request加工。

網(wǎng)上找了圖,這里我們后文將通過Tomcat請(qǐng)求處理向你闡述。

外觀模式:request請(qǐng)求 觀察者模式:事件監(jiān)聽

java中的事件機(jī)制的參與者有3種角色

Event Eource:事件源,發(fā)起事件的主體。Event Object:事件狀態(tài)對(duì)象,傳遞的信息載體,就好比Watcher的update方法的參數(shù),可以是事件源本身,一般作為參數(shù)存在于listerner 的方法之中。Event Listener:事件監(jiān)聽器,當(dāng)它監(jiān)聽到event object產(chǎn)生的時(shí)候,它就調(diào)用相應(yīng)的方法,進(jìn)行處理。

其實(shí)還有個(gè)東西比較重要:事件環(huán)境,在這個(gè)環(huán)境中,可以添加事件監(jiān)聽器,可以產(chǎn)生事件,可以觸發(fā)事件監(jiān)聽器。

模板方式: Lifecycle

LifecycleBase是使用了狀態(tài)機(jī)+模板模式來實(shí)現(xiàn)的。模板方法有下面這幾個(gè):

// 初始化方法

protected abstract void initInternal() throws LifecycleException;

// 啟動(dòng)方法

protected abstract void startInternal() throws LifecycleException;

// 停止方法

protected abstract void stopInternal() throws LifecycleException;

// 銷毀方法

protected abstract void destroyInternal() throws LifecycleException;

柚子快報(bào)邀請(qǐng)碼778899分享:【開發(fā)框架和中間件】

http://yzkb.51969.com/

好文鏈接

評(píng)論可見,查看隱藏內(nèi)容

本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場。

轉(zhuǎn)載請(qǐng)注明,如有侵權(quán),聯(lián)系刪除。

本文鏈接:http://gantiao.com.cn/post/18738770.html

發(fā)布評(píng)論

您暫未設(shè)置收款碼

請(qǐng)?jiān)谥黝}配置——文章設(shè)置里上傳

掃描二維碼手機(jī)訪問

文章目錄