Spring

[TOC]

Spring模塊

untitledq.png
  1. Core Container: 核心容器 spring-context:包含DI, IOC, springEl表達式
  2. Test: 加載Spring 上下文的Spring測試
  3. 數(shù)據(jù)訪問與集成
  4. web 與遠(yuǎn)程調(diào)用
  5. AOP 提供對AOP的支持

核心技術(shù)

DI 依賴注入 : IOC容器(控制反轉(zhuǎn)),利用依賴關(guān)系注入的方式,實現(xiàn)對象之間的解耦。

AOP 面向切面編程:把應(yīng)用各處可重用的功能分離出來形成重用組件。

在運行時,動態(tài)地將代碼切入到類的指定方法、指定位置上

bean容器

  • bean 工廠

  • 應(yīng)用上下文(基于bean工廠,提供應(yīng)用框架級別的應(yīng)用)

    ApplicationContext 是BeanFactory 子類,加強了很多功能。如果使用簡單的BeanFactory,大量的功能將失效,比如:transactions 和AOP . 避免直接用BeanFactory

untitled.png

應(yīng)用上下文

  • AnnotationConfigApplicationContext

  • AnnotationConfigWebApplicationContext

  • ClassPathXmlApplicationContext

  • FileSystemXmlApplicatonContext

  • XmlWebApplicationContext

    應(yīng)用上下文通過getBean()獲取bean

bean生命周期

untitled.jpg

回調(diào)機制

三種方式實現(xiàn):

  • 實現(xiàn)InitializingBean 和實現(xiàn)DisposableBean 回調(diào)接口
  • 自定義init()和destroy()方法 并XML 面配置 Init-method=”” ; Destroy-method=””
  • @PostConstruct and @PreDestroy 注解。

容器擴展點

實現(xiàn)BeanPostProcessor接口

在Spring容器完成實例化、配置、初始化bean之前之后執(zhí)行自定義邏輯。
可以配置多個BeanPostProcessor實例,可以設(shè)置BeanPostProcessors的order屬性來控制其執(zhí)行次序。
points:

  1. 接口中的兩個方法都要將傳入的bean返回,而不能返回null
  2. 不要將BeanPostProcessor標(biāo)記為延遲初始化,因為如果這樣做,Spring容器將不會注冊它們.
  3. BeanPostProcessors容器級別的。當(dāng)使用容器繼承時BeanPostProcessors不會繼承。

注冊方式:

  • xml: <bean class="com.spring.containerExtension.MyBeanPostProcessor" />
  • 注解:@Component 修飾自定義的BeanPostProcessor類。并加到context里面。(聲明時或者@ComponentScan)

使用注解的方式因為@Configuration類本身也會注冊為一個bean定義,還有一些系統(tǒng)的bean 所以不好控制有效范圍, 通過xml 方式容易控制。
e.g. @Required 就是用RequiredAnnotationBeanPostProcessor 實現(xiàn)的。

BeanFactoryPostProcessor

此接口的語法和BeanPostProcessor類似,用于讀取配置元數(shù)據(jù)并且在容器實例化bean之前修改bean配置。這個也是在自己容器內(nèi)有效,也不會繼承。

延遲初始化

ApplicationContext的各種實現(xiàn)默認(rèn)的初始化處理過程,都是盡早的創(chuàng)建、配置所有的單例bean。lazy-initialized bean告訴Ioc容器,只有在第一次請求的時候采取初始化,而不是在啟動容器時初始化。
通過設(shè)置<beans/>元素的default-lazy-init屬性,可以設(shè)置容器級別的延遲加載。
普通bean 需要的時候初始化。

裝配bean

定義bean

  • xml 顯式定義

  • 注解顯式定義

    @Configuration @Bean @Component @Repository @Service @Controller +組件掃描ComponentScan

@Configuration
@ComponentScan("com.spring.baseIOC")
public class ConfigWithAnnotationTest {

    @Bean   //修飾方法
    @Qualifier("bean1") //如果全局只有一個String 需要被注入,那這里可以省略Qualifier
    String method(@Qualifier("bean2") String a){
        return new String(a);
    }

    @Scope("prototype")
    @Bean(name = "bean2")
    @Qualifier("bean2_qual")
    String method2(){
        return new String("cvszz22");
    }

    @Autowired          //聲明這個field 會被自動注入
    @Qualifier("SomeBeanAnnotation")
    private SomeBeanAnnotation someBean;
}

注入bean

  • xml 顯式配置

    構(gòu)造器注入 屬性注入

  • 注解顯式配置

    @Autowired

  • 隱式的bean發(fā)現(xiàn)機制和自動裝配

注解注入在XML注入之前執(zhí)行,因此同時使用這兩種方式注入時,XML配置會覆蓋注解配置。

xml自動裝配

autowire="byName"

  • no 默認(rèn)非自動裝配
  • byName
  • byType
  • constructor

設(shè)置<bean/>元素的autowire-candidate屬性為false;可以排除他們自動注入到其他bean,但是它本身是可以被自動注入的。

通過設(shè)置<bean/>袁術(shù)的primary屬性為true來指定單個bean定義作為主候選bean。

常用注解

@Autowired注解可以應(yīng)用在setter, 構(gòu)造函數(shù)上或者屬性上,默認(rèn)required 屬性是 true 的。

@Required注解應(yīng)用于bean的setter方法, 聲明在配置時必須賦值
@Qualifier注解??梢詫ualifier關(guān)聯(lián)指定參數(shù).
public void prepare(@Qualifier("main")MovieCatalog movieCatalog){};

qualifier值,只是為了縮小類型匹配的范圍;他們并不能作為引用的bean的唯一標(biāo)示符。

@PostConstruct @PreDestroy bean的回調(diào)
@Repository, @Service, @Controller 各層代碼注解,分別注解dao , service, layers表現(xiàn)層. 他們默認(rèn)id是類名首字母小寫
@Repository能自動轉(zhuǎn)換異常。 @Component是他們的元注解。
@Scope("prototype") 指定bean作用域scope

@Import注解也允許從其他配置類(@Configuration)中加載@Bean定義。 @Import(ConfigA.class) 這樣相當(dāng)與使用AnnotationConfigApplicationContext 或者@ConponentScan @ImportResource("classpath:/com/acme/properties-config.xml") 配置類(@Configuration)中引用XML配置。
@Configuration注解的類表明該類的主要目的是作為bean定義的源。 @Configuration類本身也會注冊為一個bean定義,通過CGLIB創(chuàng)建一個子類。類內(nèi)所有的@Bean注解的方法也會注冊為bean定義。類不能是final.
@Bean注解用于表明一個方法將會實例化、配置、初始化一個新對象,該對象由Spring IoC容器管理。

@Primary該bean首選

@ComponentScan 自動探測類和自動注冊bean定義

注解方式:在@Configuration注解的類上增加@ComponentScan,其basePackages屬性就是上面兩個類的所在的父級包路徑。

xml: <context:component-scan base-package="org.example"/> 使用<context:component-scan>將會隱式的啟用<context:annotation-config>

@Configuration 只是聲明該類為bean源,還需要@ConponentScan 去加載到context 里面。或者在聲明AnnotationConfigApplicationContext 的時候都加到里面。

bean 作用域

singleton 默認(rèn)的。一個bean定義,在一個IoC容器內(nèi)只會產(chǎn)生一個對象。
prototype原型 每次都創(chuàng)建一個新的實例
request 每個HTTP請求都產(chǎn)生新的實例。Spring web上下文環(huán)境中有效。
session 生命周期在HTTP 會話期間。Spring web上下文環(huán)境中才有效。

一般來說 ,原型bean用于有狀態(tài)bean,單例bean用于無狀態(tài)bean。一個數(shù)據(jù)訪問對象(DAO)通常不會配置成原型作用域,因為通常DAO不會持有任何會話狀態(tài)。

如果想將HTTP request作用域(request,session,globalSession)bean注入給其他singleton bean,就得給作用域bean(request或者session)注入一個AOP代理用來替換作用域bean。容器會創(chuàng)建一個代理對象,該對象擁有和原對象完全相同的public方法。

因為容器初始化的時候singleton 需要加載,但此時session bean還未存在

<bean id="userPreferences" class="com.foo.UserPreferences" scope="session">
    <aop:scoped-proxy/>
</bean>
<bean id="userManager" class="com.foo.UserManager">
    <property name="userPreferences" ref="userPreferences"/>   //代理只會攔截public方法調(diào)用。非public方法不會“呼叫轉(zhuǎn)移”給實際的作用域bean。
</bean> 

若是在Java代碼中配置bean,使用@Scope注解并配置其proxyMode屬性.默認(rèn)配置是沒有代理ScopedProxyMode.NO, 但是你可以設(shè)置ScopedProxyMode.TARGET_CLASS或者ScopedProxyMode.INTERFACES。 @Scope(value ="session", proxyMode =ScopedProxyMode.TARGET_CLASS)

@Scope(value = WebApplicationContext.SCOPE_SESSION,proxyMode = ScopedProxyMode.TARGET_CLASS)

高級裝配

Environment環(huán)境

Environment環(huán)境在容器中是一個抽象的集合,是指應(yīng)用環(huán)境的2個方面: profiles 和 properties.

profile

bean定義profile是核心容器內(nèi)的一種機制,該機制能在不同環(huán)境中注冊不同的bean。
比如:

  • 開發(fā)期,QA或者產(chǎn)品上使用來自JNDI的不同數(shù)據(jù)源
  • 開發(fā)期使用監(jiān)控組件,當(dāng)部署以后則關(guān)閉監(jiān)控組件
Profile定義
  1. @Profile可以用于元數(shù)據(jù)注解,注解方法(@Bean 注解的方法),注解類(@Configuration。 i.g. @Profile("production")
  2. XML中的beans 也有profile 屬性。 profile="dev"

profile定義的bean都將被忽略,除非該profile被激活

如果給定的profile,使用了NOT操作(!)前綴,若當(dāng)前profile未被激活則注解元素將會注冊。對于@Profile({"p1", "!p2"}),在profile 'p1'被激活或者'p2'未激活時,發(fā)生注冊。

激活profile

1.編程式開啟方式

AnnotationConfigApplicationContext ctx =new AnnotationConfigApplicationContext(); 
ctx.getEnvironment().setActiveProfiles("dev");

2.使用spring.profiles.active屬性激活配置 。
spring.profiles.active="profile1,profile2"
3.servlet初始化參數(shù),web上下文參數(shù),環(huán)境變量等等

PropertySource

Spring的環(huán)境抽象可以檢索一系列的property sources屬性配置文件。
Spring的StandardEnvironment配置了2個PropertySource對象-其一是JVM系統(tǒng)properties(System.getProperties()),另一個是一組系統(tǒng)環(huán)境變量(System.getenv())。

@Component
@PropertySource(value ="classpath:com/spring/Property/quartz.properties")
public class MyProperty{

//為了支持spring el 表達式
    @Bean
    public static PropertySourcesPlaceholderConfigurer propertyConfig() {
        return new PropertySourcesPlaceholderConfigurer();
    }
}
@ContextConfiguration(classes = {MyProperty.class})
@RunWith(SpringJUnit4ClassRunner.class)
public class PropertyTest {

    @Autowired
    private Environment env;

    @Test
    public void test(){
        System.out.print(env);
    }
}

條件化的bean

使用@Conditional可以用到帶有@Bean 注解的方法上,指向一個實現(xiàn)了Condition接口的類,根據(jù)matches方法返回的boolean值確定是否注冊這個bean

 @Conditional(MyConditional.class)
    @Bean(name = "b2")
    public ClientServiceImpl getService() {
       return ...;
    }

方法替換

設(shè)置replaced-method元素,就可用其他實現(xiàn)來替換已經(jīng)部署的bean中存在的方法實現(xiàn)。

<bean id="myValueCalculator" class="x.y.z.MyValueCalculator">
    <replaced-method name="computeValue" replacer="replacementComputeValue"> 
      <!--替換computerValue方法-->
        <arg-type>String</arg-type>
    </replaced-method>
</bean>

<bean id="replacementComputeValue" class="a.b.c.ReplacementComputeValue"/>  
<!--ReplacementComputeValue 需要實現(xiàn)org.springframework.beans.factory.support.MethodReplacer 接口-->

方法注入

在容器范圍內(nèi)查找一個bean作為方法返回結(jié)果

 <bean id="A" class="com.spring.lookup_method.UserManager" >
    <lookup-method name="getUser" bean="B" />  
 </bean>
   
<bean id="B" class="com.spring.lookup_method.User"/>
       
public abstract class UserManager {
    protected abstract User getUser();

    public void run(){
        User user = getUser();
        System.out.println(user);
    }
}

class User{
    String name;
    public String getName() {return name;}
    public void setName(String name) {this.name = name;}
}

事務(wù)

@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.SERIALIZABLE)

事務(wù)傳播行為

PROPAGATION_SUPPORTS 有事務(wù)就用,沒有就不用
PROPAGATION_NOT_SUPPORTED 不用事務(wù),如果當(dāng)前有就掛起事務(wù)
PROPAGATION_MANDATORY 有就用,沒有就拋異常
PROPAGATION_NEVER 有事務(wù)就跑出異常
PROPAGATION_REQUIRED 有就用,沒有就新建
PROPAGATION_REQUIRES_NEW 新建一個,如果當(dāng)前有就掛起當(dāng)前事務(wù)
Propagation.NESTED 沒有就新建,有就當(dāng)前事務(wù)中再嵌套事務(wù)運行 — 嵌套事務(wù)中可以定義儲存點,因此可以獨立于外部的Transaction而進行rollback

數(shù)據(jù)庫的隔離級別

數(shù)據(jù)庫事務(wù)隔離級別:一個事務(wù)對數(shù)據(jù)庫的修改與并行的另一個事務(wù)的隔離程度。

并發(fā)訪問數(shù)據(jù)庫存在的問題

  1. 臟讀:事務(wù)T1更新了一行記錄,還未提交所做的修改,這個T2讀取了更新后的數(shù)據(jù),然后T1執(zhí)行回滾操作,取消剛才的修改,所以T2所讀取的行就無效,也就是臟數(shù)據(jù)。
  2. 不可重復(fù)讀?。菏聞?wù)T1讀取一行記錄,緊接著事務(wù)T2修改了T1剛剛讀取的記錄,然后T1再次查詢,發(fā)現(xiàn)與第一次讀取的記錄不同,這稱為不可重復(fù)讀。
  3. 幻讀:事務(wù)T1讀取一條指定where條件的語句,返回結(jié)果集。此時事務(wù)T2插入一行新記錄,恰好滿足T1的where條件。然后T1使用相同的條件再次查詢,結(jié)果集中可以看到T2插入的記錄,這條新紀(jì)錄就是幻想。

不可重復(fù)讀和臟讀的區(qū)別是,臟讀是某一事務(wù)讀取了另一個事務(wù)未提交的臟數(shù)據(jù),而不可重復(fù)讀和幻讀是讀取了前一事務(wù)提交的數(shù)據(jù)。

不可重復(fù)讀的重點是修改,幻讀的重點在于新增或者刪除

四種隔離級別

  1. Read uncommitted (讀未提交):最低級別,任何情況都無法保證。
  2. Read committed (讀已提交):可避免臟讀的發(fā)生。
  3. Repeatable read (可重復(fù)讀):可避免臟讀、不可重復(fù)讀的發(fā)生。
  4. Serializable (串行化):可避免臟讀、不可重復(fù)讀、幻讀的發(fā)生。

MySQL數(shù)據(jù)庫中,支持上面四種隔離級別,默認(rèn)的為Repeatable read (可重復(fù)讀);而在Oracle數(shù)據(jù)庫中,只支持Read committed (讀已提交)和Serializable (串行化)級別這兩種級別,其中默認(rèn)的為Read committed級別。

設(shè)置數(shù)據(jù)庫的隔離級別一定要是在開啟事務(wù)之前,隔離級別的設(shè)置只對當(dāng)前鏈接有效。

例子:

左面是事務(wù)T1,右面是事務(wù)T2,因為T2級別為SERIALIZABLE,所以即使事務(wù)T1在提交了數(shù)據(jù)之后,事務(wù)T2還是看不到T1提交的數(shù)據(jù),幻想讀和不可重復(fù)讀都不允許了。

那如何能查看到T1新增的記錄呢? 上面T1和T2是并發(fā)執(zhí)行,在T1執(zhí)行insert的時候如果事務(wù)T2已經(jīng)開始了,因為T2級別是SERIALIZABLE,所以T2所查詢的數(shù)據(jù)集是T2事務(wù)開始前數(shù)據(jù)庫的數(shù)據(jù)。即事務(wù)T1在事務(wù)T2開始之后的insert和update操作的影響都不會影響事務(wù)T2?,F(xiàn)在重新開啟一個事務(wù)T3 就可以看到T1新增的記錄了。

AOP

五種類型的通知

  • @Before
  • @After
  • @AfterReturning 成功執(zhí)行后
  • @AfterThrowing 拋異常后
  • @Around

Spring 的AspectJ自動代理只是使用AspectJ作為創(chuàng)建切面的指導(dǎo),切面仍然是基于代理的。本質(zhì)上,它仍然是Spring 基于代理的切面。如果要使用全部AspectJ功能需要不依賴Spring 來創(chuàng)建切面并且在運行時使用AspectJ

@Aspect
@Component
public class MyInterceptor implements Ordered {

    //第一個* 代表任意返回值, .. 代表任意參數(shù)
    //符合條件的才被切入
    @Pointcut("execution(* com.spring.aop.MyService.*(..))")
    private void anyMethod(){}//定義一個切入點

    @Before("anyMethod() && args(name,param2)")
    public void doAccessCheck(String name,int param2){
        System.out.println(name);
        System.out.println(param2);
        System.out.println("前置通知");
    }

    @AfterReturning("anyMethod()")
    public void doAfter(){
        System.out.println("后置通知");
    }
}
 <context:component-scan base-package="com.spring.aop" />
    <!--打開aop -->
    <aop:aspectj-autoproxy proxy-target-class="true"/>

    <bean id="serviceBean" class="com.spring.aop.MyService">
    </bean>
 public static void main(String[] args) {
   ApplicationContext ctx = new ClassPathXmlApplicationContext("/aop.xml");
   MyService bean = (MyService) ctx.getBean("serviceBean");
   bean.update("param1",2);
}

切面的織入順序

order 的值越小,說明越先被執(zhí)行

@Component
@Aspect
public class MyAspectJ implements Ordered{
...
    @Override
    public int getOrder() {
        return 1;
    }
}
<!-- 注解方式配置事物 -->
<tx:annotation-driven transaction-manager="transactionManager" order="2"/>

這樣就實現(xiàn)了我們自己寫的aop在事務(wù)介入之前就執(zhí)行了!

JSR-330

Spring3.0開始,Spring提供了對JSR-330標(biāo)準(zhǔn)注解(依賴注入)的支持。這些注解以Spring注解相同的方式被掃描。

<dependency>
    <groupId>javax.inject</groupId>
    <artifactId>javax.inject</artifactId>
    <version>1</version>
</dependency>
Spring javax.inject.*
@Autowired @Inject @Inject 沒有required屬性
@Component @Named
@Scope("singleton") @Singleton JSR-330默認(rèn)的作用域類似于Spring的prototype原型作用域。為了保持Spring的一致性,在Spring容器中的JSR-330的bean聲明,默認(rèn)是singleton單例。除了singleton,若要設(shè)置作用域,得使用Spring的@Scope注解。javax.inject也提供了一個@Scope注解。然而,這個注解僅僅是為了讓你創(chuàng)建自定義注解用的譯注,也就是元注解的源碼注解。
@Qualifier @Named
@Value 無等價注解
@Required 無等價注解
@Lazy 無等價注解

others

spring-web 初始化配置 (spring-mvc 不需要這個)
為了支持request,sesssion,global session這種級別bean的作用域(web作用域bean),在定義bean之前需要一些初始化的配置。

若使用 Spring Web MVC訪問這些作用域bean,實際上是使用Srping DispatcherServlet類或者DispatcherPortlet類處理request,則無需特別配置:DispatcherServlet 和 DispatcherPortlet已經(jīng)暴露了所有的相關(guān)狀態(tài)。
非Spring的DispacherServlet處理請求(比如,JSF或者Struts2)。

  1. 小于2.5 的Servlet web容器: RequestContextFilter
  2. Servlet 2.5的web容器則需要注冊org.springframework.web.context.request.RequestContextListener ServletRequestListener。
  3. Servlet 3.0+,這些設(shè)置可以通過編程式方式使用WebApplicationInitializer接口完成。

DispatcherServlet,RequestContextListener,RequestContextFilter都是做相同的事,也就是綁定HTTP request對象到服務(wù)的Thread線程中,并開啟接下來 用到的session-scoped功能。

SSH 需要上面的spring + spring-web +struts2配置。如果是用spring-mvc 只需spring + spring-mvc 自己的servlet (DispatcherServlet)配置就可以了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 47,290評論 6 342
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,715評論 19 139
  • 什么是Spring Spring是一個開源的Java EE開發(fā)框架。Spring框架的核心功能可以應(yīng)用在任何Jav...
    jemmm閱讀 16,786評論 1 133
  • 1. 前幾天和朋友出去逛,看到很精致的冰箱貼,吸引了她,就快要入手。 想了想,上班獨居的生活,既沒有人要提醒她什么...
    涼風(fēng)有信YY閱讀 177評論 0 0
  • 01 有人曾這樣說過,小學(xué)是一個班的小學(xué),初中是一群人的初中,高中是幾個人的高中,而大學(xué)是一個人的大學(xué)。年少的我對...
    馬鈴薯煮土豆閱讀 460評論 20 9

友情鏈接更多精彩內(nèi)容