java面試--spring

目錄

springMVC請求流程詳解

BeanFactory 和 FactoryBean

ApplicationContext和beanfactory的區別

Spring常用註解總結

Spring 中用到了那些設計模式

spring 事務的傳播機制

spring中bean的作用域有哪些;

spring框架的優點;

Spring AOP 的理解,各個術語,他們是怎麼相互工作的

Spring 如何保證 Controller 併發的安全

Spring IOC 的理解


springMVC請求流程詳解

springMVC請求流程圖:

SpringMVC 的工作流程
【1】用戶發送請求至前端控制器 DispatcherServlet;
【2】DispatcherServlet 收到請求調用 HandlerMapping 處理器映射器;
【3】處理器映射器找到具體的處理器(可以根據 xml配置、註解進行查找),生成處理器對象及處理器攔截器(如果有則生成)一併返回給 DispatcherServlet;
【5】DispatcherServlet 調用 HandlerAdapter 處理器適配器;
【6】HandlerAdapter 經過適配調用具體的處理器(Controller,也叫後端控制器);
【7】Controller 執行完成返回 ModelAndView;
【8】HandlerAdapter 將 controller 執行結果 ModelAndView 返回給 DispatcherServlet;
【9】DispatcherServlet 將 ModelAndView 傳給 ViewReslover 視圖解析器;
【10】ViewReslover 解析後返回具體 View;
【11】DispatcherServlet 根據 View進行渲染視圖(即將模型數據填充至視圖中);
【12】DispatcherServlet 響應用戶;

【前端控制器-DispatcherServlet】:接收請求,響應結果,相當於轉發器,中央處理器。有了 DispatcherServlet減少了其它組件之間的耦合度。用戶請求到達前端控制器,它就相當於 mvc模式中的c,DispatcherServlet 是整個流程控制的中心,由它調用其它組件處理用戶的請求,DispatcherServlet 的存在降低了組件之間的耦合性。可以將其看做是 SpringMVC 實現中最爲核心的部分;DispatcherServlet 的啓動過程就是 SpringMVC 的啓動過程。DispatcherServlet  的工作大致分爲兩個部分:①、初始化部分:由 initServletBean() 啓動通過 initApplicationContext() 方法最終調用 DispatcherServlet 的 initStrategies 方法。DispatcherServlet 對 MVC 的其他模塊進行初始化。比如:HandlerMapping、ViewResolver 等。②、對 HTTP 請求進行響應:doService() 方法,在這個方法調用中封裝了 doDispatch() 這個 doDispatch 是實現 MVC模式的主要部分。不但建立了自己持有的 IOC容器還肩負着請求分發處理的重任。
【處理器映射器-HandlerMapping】:對於不同 Web請求有對應的映射 Spring 提供了不同的 HandlerMapping 作爲映射策略。這個策略可以根據需求選擇。默認的策略:BeanNameUrlHandlerMapping。根據請求的 url查找 Handler,HandlerMapping 負責根據用戶請求找到 Handler即處理器,SpringMVC提供了不同的映射器實現不同的映射方式,例如:配置文件方式,實現接口方式,註解方式等;
【處理器適配器-HandlerAdapter】:按照特定規則(HandlerAdapter要求的規則)去執行Handler,通過 HandlerAdapter 對處理器進行執行,這是適配器模式的應用,通過擴展適配器可以對更多類型的處理器進行執行;
【處理器-Handler】(需要工程師開發):注意:編寫 Handler 時按照 HandlerAdapter 的要求去做,這樣適配器纔可以去正確執行Handler,Handler 是繼 DispatcherServlet 前端控制器的後端控制器,在 DispatcherServlet的控制下 Handler對具體的用戶請求進行處理。由於Handler涉及到具體的用戶業務請求,所以一般情況需要工程師根據業務需求開發 Handler;
【視圖解析器View resolver】:進行視圖解析,根據邏輯視圖名解析成真正的視圖(view)。View Resolver 負責將處理結果生成View視圖,View Resolver 首先根據邏輯視圖名解析成物理視圖名即具體的頁面地址,再生成 View視圖對象,最後對 View進行渲染將處理結果通過頁面展示給用戶。 springmvc 框架提供了很多的View視圖類型,包括:jstlView、freemarkerView、pdfView等;
【視圖View】(需要工程師開發):View 是一個接口,實現類支持不同的View類型(jsp、freemarker、pdf...);

<web-app>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
 
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/app-context.xml</param-value>
    </context-param>
 
    <servlet>
        <servlet-name>app</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value></param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
 
    <servlet-mapping>
        <servlet-name>app</servlet-name>
        <url-pattern>/app/*</url-pattern>
    </servlet-mapping>
</web-app>

BeanFactory 和 FactoryBean

【1】BeanFactory:Spring IoC容器設計中,我們可以看到兩個主要的容器系列,其中一個就是實現 BeanFactory接口的簡單容器系列,這系列容器只實現了容器的基本功能;該接口是 IoC容器的頂級接口,是 IoC容器的最基礎實現,也是訪問 Spring容器的根接口,負責對 Bean的創建,訪問等工作。最典型的容器就是 DefaultListableBeanFactory 。
【2】FactoryBean:是一種工廠 bean,可以返回 bean的實例,可以通過實現該接口對 Bean進行額外的操作,例如根據不同的配置類型返回不同類型的 Bean,簡化 xml配置等;其在使用上也有些特殊,大家還記得 BeanFactory 中有一個字符常量String FACTORY_BEAN_PREFIX = "&"; 當我們去獲取 BeanFactory類型的 bean時,如果 beanName不加&則獲取到對應 bean的實例;如果 beanName 加上 &,則獲取到 BeanFactory本身的實例;FactoryBean 接口對應 Spring框架來說佔有重要的地位,Spring 本身就提供了70多個 FactoryBean的實現。他們隱藏了實例化一些複雜的細節,給上層應用帶來了便利。從Spring3.0 開始,FactoryBean 開始支持泛型。

ApplicationContext和beanfactory的區別

BeanFacotry是spring中比較原始的Factory。

ApplicationContext接口,它由BeanFactory接口派生而來,因而提供BeanFactory所有的功能。並且他還實現了Resource這些是一個更高級的容器。

BeanFactroy只有在使用到某個Bean時(調用getBean()),纔對該Bean進行加載實例化,這樣,我們就不能發現一些存在的Spring的配置問題。

ApplicationContext則相反,它是在容器啓動時,一次性創建了所有的Bean。這樣,在容器啓動時,我們就可以發現Spring中存在的配置錯誤。 

Spring常用註解總結

@Controller

標識一個該類是Spring MVC controller處理器,用來創建處理http請求的對象.

@RestController

@Controller+@ResponseBody

Spring4之後加入的註解,原來在@Controller中返回json需要@ResponseBody來配合,如果直接用@RestController替代@Controller就不需要再配置@ResponseBody,默認返回json格式。

@Service

用於標註業務層組件,說白了就是加入你有一個用註解的方式把這個類注入到spring配置中

@Autowired

用來裝配bean,都可以寫在字段上,或者方法上。

默認情況下必須要求依賴對象必須存在,如果要允許null值,可以設置它的required屬性爲false,例如:@Autowired(required=false)

@Resource

@Resource的作用相當於@Autowired

只不過@Autowired按byType自動注入,

而@Resource默認按 byName自動注入罷了。

@Resource有兩個屬性是比較重要的,分是name和type,Spring將@Resource註解的name屬性解析爲bean的名字,而type屬性則解析爲bean的類型。所以如果使用name屬性,則使用byName的自動注入策略,而使用type屬性時則使用byType自動注入策略。如果既不指定name也不指定type屬性,這時將通過反射機制使用byName自動注入策略。

@Resource裝配順序:

1、如果同時指定了name和type,則從Spring上下文中找到唯一匹配的bean進行裝配,找不到則拋出異常

2、如果指定了name,則從上下文中查找名稱(id)匹配的bean進行裝配,找不到則拋出異常

3、如果指定了type,則從上下文中找到類型匹配的唯一bean進行裝配,找不到或者找到多個,都會拋出異常

4、如果既沒有指定name,又沒有指定type,則自動按照byName方式進行裝配;如果沒有匹配,則回退爲一個原始類型進行匹配,如果匹配則自動裝配;
@RequestMapping

類定義處: 提供初步的請求映射信息,相對於 WEB 應用的根目錄。

方法處: 提供進一步的細分映射信息,相對於類定義處的 URL。
@RequestParam

用於將請求參數區數據映射到功能處理方法的參數

例如

這個id就是要接收從接口傳遞過來的參數id的值的,如果接口傳遞過來的參數名和你接收的不一致,也可以如下

其中course_id就是接口傳遞的參數,id就是映射course_id的參數名

@Repository

用於標註數據訪問組件,即DAO組件
@Component

泛指組件,當組件不好歸類的時候,我們可以使用這個註解進行標註
@Scope

用來配置 spring bean 的作用域,它標識 bean 的作用域。

默認值是單例

1、singleton:單例模式,全局有且僅有一個實例

2、prototype:原型模式,每次獲取Bean的時候會有一個新的實例

3、request:request表示該針對每一次HTTP請求都會產生一個新的bean,同時該bean僅在當前HTTP request內有效

4、session:session作用域表示該針對每一次HTTP請求都會產生一個新的bean,同時該bean僅在當前HTTP session內有效

5、global session:只在portal應用中有用,給每一個 global http session 新建一個Bean實例。

@Cacheable

用來標記緩存查詢。可用用於方法或者類中,當標記在一個方法上時表示該方法是支持緩存的,當標記在一個類上時則表示該類所有的方法都是支持緩存的。

參數列表

比如@Cacheable(value="UserCache") 標識的是當調用了標記了這個註解的方法時,邏輯默認加上從緩存中獲取結果的邏輯,如果緩存中沒有數據,則執行用戶編寫查詢邏輯,查詢成功之後,同時將結果放入緩存中。

但凡說到緩存,都是key-value的形式的,因此key就是方法中的參數(id),value就是查詢的結果,而命名空間UserCache是在spring*.xml中定義.

@CacheEvict

用來標記要清空緩存的方法,當這個方法被調用後,即會清空緩存。@CacheEvict(value=”UserCache”)

參數列表

@Required

適用於bean屬性setter方法,並表示受影響的bean屬性必須在XML配置文件在配置時進行填充。否則,容器會拋出一個BeanInitializationException異常。
@Qualifier

當你創建多個具有相同類型的 bean 時,並且想要用一個屬性只爲它們其中的一個進行裝配,在這種情況下,你可以使用 @Qualifier 註釋和 @Autowired 註釋通過指定哪一個真正的 bean 將會被裝配來消除混亂。

Spring 中用到了那些設計模式

【1】工廠設計模式:Spring 使用工廠模式通過 BeanFactory、ApplicationContext 創建 bean 對象。
【2】代理設計模式:Spring AOP 功能的實現。
【3】單例設計模式:Spring 中的 Bean 默認都是單例的。
【4】模板方法模式:Spring 中 jdbcTemplate、hibernateTemplate 等以 Template 結尾的對數據庫操作的類,它們就使用到了模板模式。
【5】包裝器設計模式:我們的項目需要連接多個數據庫,而且不同的客戶在每次訪問中根據需要會去訪問不同的數據庫。這種模式讓我們可以根據客戶的需求能夠動態切換不同的數據源。
【6】觀察者模式:Spring 事件驅動模型就是觀察者模式很經典的一個應用。
【7】適配器模式:Spring AOP 的增強或通知(Advice)使用到了適配器模式、spring MVC 中也是用到了適配器模式適配。
【8】職責鏈模式:Spring AOP 中通過攔截器鏈將 Advice(通知器) 進行封裝,與代理模式配合完成 AOP 功能。

spring 事務的傳播機制

spring 對事務的控制,是使用 aop 切面實現的,我們不用關心事務的開始,提交 ,回滾,只需要在方法上加 @Transactional 註解,這時候就有問題了。

  • 場景一: serviceA 方法調用了 serviceB 方法,但兩個方法都有事務,這個時候如果 serviceB 方法異常,是讓 serviceB 方法提交,還是兩個一起回滾。   
  • 場景二:serviceA 方法調用了 serviceB 方法,但是隻有 serviceA 方法加了事務,是否把 serviceB 也加入 serviceA 的事務,如果 serviceB 異常,是否回滾 serviceA 。
  • 場景三:serviceA 方法調用了 serviceB 方法,兩者都有事務,serviceB 已經正常執行完,但 serviceA 異常,是否需要回滾 serviceB 的數據。

因爲 spring 是使用 aop 來代理事務控制 ,是針對於接口或類的,所以在同一個 service 類中兩個方法的調用,傳播機制是不生效的

傳播機制類型(七種)

一般用得比較多的是 PROPAGATION_REQUIRED , REQUIRES_NEW

下面的類型都是針對於被調用方法來說的,理解起來要想象成兩個 service 方法的調用纔可以。

PROPAGATION_REQUIRED (默認)propagation required

  • 支持當前事務,如果當前沒有事務,則新建事務

  • 如果當前存在事務,則加入當前事務,合併成一個事務

REQUIRES_NEW requires new

  • 新建事務,如果當前存在事務,則把當前事務掛起

  • 這個方法會獨立提交事務,不受調用者的事務影響,父級異常,它也是正常提交

NESTED

  • 如果當前存在事務,它將會成爲父級事務的一個子事務,方法結束後並沒有提交,只有等父事務結束才提交

  • 如果當前沒有事務,則新建事務

  • 如果它異常,父級可以捕獲它的異常而不進行回滾,正常提交

  • 但如果父級異常,它必然回滾,這就是和 REQUIRES_NEW 的區別

SUPPORTS

  • 如果當前存在事務,則加入事務

  • 如果當前不存在事務,則以非事務方式運行,這個和不寫沒區別

NOT_SUPPORTED

  • 以非事務方式運行

  • 如果當前存在事務,則把當前事務掛起

MANDATORY

  • 如果當前存在事務,則運行在當前事務中

  • 如果當前無事務,則拋出異常,也即父級方法必須有事務

NEVER

  • 以非事務方式運行,如果當前存在事務,則拋出異常,即父級方法必須無事務

spring中bean的作用域有哪些;

scope配置項有5個屬性,用於描述不同的作用域。

① singleton

使用該屬性定義Bean時,IOC容器僅創建一個Bean實例,IOC容器每次返回的是同一個Bean實例。

② prototype

使用該屬性定義Bean時,IOC容器可以創建多個Bean實例,每次返回的都是一個新的實例。

③ request

該屬性僅對HTTP請求產生作用,使用該屬性定義Bean時,每次HTTP請求都會創建一個新的Bean,適用於WebApplicationContext環境。

④ session

該屬性僅用於HTTP Session,同一個Session共享一個Bean實例。不同Session使用不同的實例。

⑤ global-session

該屬性僅用於HTTP Session,同session作用域不同的是,所有的Session共享一個Bean實例。

 其中比較常用的是singleton和prototype兩種作用域。對於singleton作用域的Bean,每次請求該Bean都將獲得相同的實例。容器負責跟蹤Bean實例的狀態,負責維護Bean實例的生命週期行爲;如果一個Bean被設置成prototype作用域,程序每次請求該id的Bean,Spring都會新建一個Bean實例,然後返回給程序。在這種情況下,Spring容器僅僅使用new 關鍵字創建Bean實例,一旦創建成功,容器不在跟蹤實例,也不會維護Bean實例的狀態。

  如果不指定Bean的作用域,Spring默認使用singleton作用域。Java在創建Java實例時,需要進行內存申請;銷燬實例時,需要完成垃圾回收,這些工作都會導致系統開銷的增加。因此,prototype作用域Bean的創建、銷燬代價比較大。而singleton作用域的Bean實例一旦創建成功,可以重複使用。因此,除非必要,否則儘量避免將Bean被設置成prototype作用域。

spring框架的優點;

1、非侵入式設計

Spring是一種非侵入式(non-invasive)框架,它可以使應用程序代碼對框架的依賴最小化。

2、方便解耦、簡化開發

Spring就是一個大工廠,可以將所有對象的創建和依賴關係的維護工作都交給Spring容器的管理,大大的降低了組件之間的耦合性。

3、支持AOP

Spring提供了對AOP的支持,它允許將一些通用任務,如安全、事物、日誌等進行集中式處理,從而提高了程序的複用性。

4、支持聲明式事務處理

只需要通過配置就可以完成對事物的管理,而無須手動編程。

5、方便程序的測試

Spring提供了對Junit4的支持,可以通過註解方便的測試Spring程序。

6、方便集成各種優秀框架

Spring不排斥各種優秀的開源框架,其內部提供了對各種優秀框架(如Struts、Hibernate、MyBatis、Quartz等)的直接支持。

7、降低Jave EE API的使用難度。

Spring對Java EE開發中非常難用的一些API(如JDBC、JavaMail等),都提供了封裝,使這些API應用難度大大降低。

Spring AOP 的理解,各個術語,他們是怎麼相互工作的

Spring 用代理類+攔截器鏈包裹切面,把它們織入到 Spring管理的 Bean中。也就是說代理類僞裝成目標類,它會截取對目標類中方法的調用,讓調用者對目標類的調用都先變成調用僞裝類,僞裝類中就先執行了切面,再把調用轉發給真正的目標Bean。

【1】通知器(Advice):當我們完成對目標方法的切面增強設計(advice)和關注點的設計(pointcut)以後,需要一個對象把它們結合起來。主要包括:①、@Before:前置通知,方法執行之前執行;②、@After(finally):後置通知,方法執行之後執行,無論成功執行還是拋出異常。③、@AfterReturning:返回通知,方法成功執行之後執行,異常或者錯誤不執行。可以獲取方法返回值。④、@AfterThrowing:異常通知,方法拋出異常之後才執行,成功時不執行。⑤、@Around:環繞通知,包括以上四中註解!能決定目標方法是否執行和執行時間。

@Around("point()")
public void exec(ProceedingJoinPoint point) throws Throwable{
    System.out.println("...............Before(此處執行的代碼相當於-前置通知)...............");
    try{
        point.proceed();//有此代碼,被切入的方法體纔會執行,如果被切入的方法有返回值,則返回值爲null,見3
        System.out.println("..........AfterReturning(此處執行的代碼相當於-返回通知)..........");
    }catch(Exception e){
        System.out.println("...........AfterThrowing(此處執行的代碼相當於-異常通知)..........");
    }finally{
        System.out.println("...........After1(此處執行的代碼相當於-後置通知).............");
    }
    System.out.println("........After2(此處執行的代碼相當於-後置通知).........");
}


【2】連接點(JoinPoint):JoinPoint 對象封裝了 SpringAop中切面方法的信息,在切面方法中添加 JoinPoint 參數,就可以獲取到封裝了該方法信息的 JoinPoint對象。ProceedingJoinPoint 對象:ProceedingJoinPoint 對象是 JoinPoint 的子接口,該對象只用在 @Around 的切面方法中,添加了 Object proceed()執行目標方法,例如上面的例子。作爲方法的參數傳入:

/**
 * 前置方法,在目標方法執行前執行
 * @param joinPoint 封裝了代理方法信息的對象,若用不到則可以忽略不寫
 */
@Before("declareJoinPointerExpression()")
public void beforeMethod(JoinPoint joinPoint){
    System.out.println("目標方法名爲:" + joinPoint.getSignature().getName());
    System.out.println("目標方法所屬類的簡單類名:" + joinPoint.getSignature().getDeclaringType().getSimpleName());
    System.out.println("目標方法所屬類的類名:" + joinPoint.getSignature().getDeclaringTypeName());
    System.out.println("目標方法聲明類型:" + Modifier.toString(joinPoint.getSignature().getModifiers()));
    //獲取傳入目標方法的參數
    Object[] args = joinPoint.getArgs();
    for (int i = 0; i < args.length; i++) {
        System.out.println("第" + (i+1) + "個參數爲:" + args[i]);
    }
    System.out.println("被代理的對象:" + joinPoint.getTarget());
    System.out.println("代理對象自己:" + joinPoint.getThis());
}


【3】切入點(Pointcut):上面說的連接點的基礎上,來定義切入點,你的一個類裏,有15個方法,那就有幾十個連接點了對把,但是你並不想在所有方法附近都使用通知(使用叫織入,以後再說),你只想讓其中的幾個,在調用這幾個方法之前,之後或者拋出異常時乾點什麼,那麼就用切點來定義這幾個方法,讓切入點來篩選連接點,選中那幾個你想要的方法。

/**
 * 定義一個切入點表達式,用來確定哪些類需要代理
 * execution(* aopdemo.*.*(..))代表aopdemo包下所有類的所有方法都會被代理
 */
@Pointcut("execution(* aopdemo.*.*(..))")
public void declareJoinPointerExpression() {}


【4】切面(Aspect):切面是通知和切入點的結合。現在發現了吧,沒連接點什麼事情,連接點就是爲了讓你好理解切入點,搞出來的,明白這個概念就行了。通知說明了幹什麼和什麼時候幹(什麼時候通過方法名中的before、after、around等就能知道),而切入點說明了在哪幹(指定到底是哪個方法),這就是一個完整的切面定義。上面的方法都在此類中定義:

@Aspect
@Component
public class aopAspect {

【5】引入(introduction):允許我們向現有的類添加新方法屬性。這不就是把切面(也就是新方法屬性:通知定義的)用到目標類中嗎。
【6】目標(target):引入中所提到的目標類,也就是要被通知的對象,也就是真正的業務邏輯,他可以在毫不知情的情況下,被咱們織入切面。而自己專注於業務本身的邏輯。
【7】織入(weaving):織入是將增強添加到目標類具體連接點上的過程,AOP有三種織入方式:①編譯時織入:需要特殊的Java編譯器(例如AspectJ的ajc);②裝載期織入:要求使用特殊的類加載器,在裝載類的時候對類進行增強;③運行時織入:在運行時爲目標類生成代理實現增強。Spring採用了動態代理的方式實現了運行時織入,而AspectJ採用了編譯期織入和裝載期織入的方式。
【8】在 SpringAopDemoApplication 中增加註解 @EnableAspectJAutoProxy

@SpringBootApplication
@EnableAspectJAutoProxy
public class SpringAopDemoApplication {
    public static void main(String[] args) {
        ApplicationContext applicationContext = SpringApplication.run(SpringAopDemoApplication.class, args);
    }
}

Spring 如何保證 Controller 併發的安全

Controller 默認是單例的,一般情況下,如果用Spring MVC 的 Controller時,儘量不在 Controller中使用實例變量。否則會出現線程不安全性的情況,導致數據邏輯混亂。正因爲單例所以不是線程安全的。舉個簡單例子:

@Controller
public class ScopeTestController {
 
    private int num = 0;
 
    @RequestMapping("/testScope")
    public void testScope() {
        System.out.println(++num);
    }
 
    @RequestMapping("/testScope2")
    public void testScope2() {
        System.out.println(++num);
    }
 
}

【1】首先訪問 http://localhost:8080/testScope,得到的答案是1;
【2】然後我們再訪問 http://localhost:8080/testScope2,得到的答案是 2。 此時就不是我們想要的答案,num已經被修改。所有 request都訪問同一個 Controller時,這裏的私有變量就是共用的,也就是說某個 request中如果修改了這個變量,那麼在別的請求中也可讀到這個修改的內容。

【解決辦法】:

【1】不要在 Controller 中定義成員變量;
【2】萬一必須要定義一個非靜態成員變量時候,則通過註解 @Scope(“prototype”),將其設置爲多例模式。
【3】在 Controller 中使用 ThreadLocal 變量;

Spring IOC 的理解

  1. 誰控制誰:在傳統的開發模式下,我們都是採用直接 new 一個對象的方式來創建對象,也就是說你依賴的對象直接由你自己控制,但是有了 IOC 容器後,則直接由 IoC 容器來控制。所以“誰控制誰”,當然是 IoC 容器控制對象。
  2. 控制什麼:控制對象。
  3. 爲何是反轉:沒有 IoC 的時候我們都是在自己對象中主動去創建被依賴的對象,這是正轉。但是有了 IoC 後,所依賴的對象直接由 IoC 容器創建後注入到被注入的對象中,依賴的對象由原來的主動獲取變成被動接受,所以是反轉。
  4. 哪些方面反轉了:所依賴對象的獲取被反轉了。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章