1. 程式人生 > >spring實戰筆記

spring實戰筆記

AnnotationConfigApplicationContext:從一個或多個基於java配置類中載入Spring應用上下文
AnnotationConfigWebAppApplicationContext:從一個或多個基於java配置類中載入Spring Web應用上下文

ClassPathXmlApplicationContext:從類路徑下的一個或多個XML檔案中載入上下文定義,把上下文定義檔案作為類資源
FileSystemXmlApplicationContext:從檔案系統下的一個或多個XML檔案中載入上下文定義
XmlWebApplicationContext:從WEB應用下的一個或多個XML檔案中載入上下文定義

ApplicationContext context= new  FileSystemXmlApplicationContext("c:/knight.xml");
ApplicationContext context= new  ClassPathXmlApplicationContext("knight.xml");
ApplicationContext context= new  	
				AnnotationConfigApplicationContext("com.springinaction
.knight.config.Knight.class");
getBean();  返回一個例項

//通過DI AOP和消除樣板式程式碼來簡化開發

Spring核心容器

Component    named

Autowired    Inject


javaConfig


@Configuration
@ComponentScan
public class CDPlayerConfig{
	@Bean
	public CDPlayer cdPlayer(){
		return new CDPlayer(sgtPeppers());  //cdPlayer   //sgtPeppers implements compactDisc
	}
		@Bean
	public CDPlayer anthorCdPlayer(){
		return new CDPlayer(sgtPeppers());  //cdPlayer
	}
}
//這裡呼叫了兩次sgtPeppers,spring預設是單例模式,因此第二次呼叫的時候會攔截,並返回已經建立的bean,
//即同一個sgtPeppers


xml配置
方法一:
<bean id="cdPlayer" class="com.qmylzx.view.CDPlayer">
	<constructor-arg ref="compactDisc"/>
	<property name="compactDisc"  ref="compactDisc"></property>
</bean>
方法二: c名稱空間和模式宣告         spring 3.0版本支援
<bean id="cdPlayer"  class="com.qmylzx.view.CDPlayer" c:cd-ref="compactDisc"></bean>
c:cd-ref="compactDisc"             若引數為字串 c:cd="hello world" 
c:   c名稱空間
cd 構造器引數名    
-ref  注入bean引用
compactDisc  bean的名字  
或者    c:_0-ref    c:_1-ref   或者  c:_cd-ref   當只有一個構造器引數的時候可以  c:_-ref 

當引數為list時    c名稱空間不支援list
<bean id="cdPlayer" class="com.qmylzx.view.CDPlayer">
	<constructor-arg>
		<list>  //<set></set>    
			<value></value>   //<ref bean="beanName"/>
			<value></value>   //<ref bean="beanName"/>
			<value></value>   //<ref bean="beanName"/>
		</list>
	</constructor-arg>
</bean>

p名稱空間  <bean id="cdPlayer" class="com.qmylzx.view.CDPlayer" p:compactDisc-ref="compactDisc"/>
p:compactDisc-ref="compactDisc"            若引數為字串 p:compactDisc="hello world" 
p: p名稱空間
compactDisc 屬性名
-ref   注入bean引用
compactDisc  bean的名字  


util-名稱空間         
<util:list id="trackList">   //給 List trackList;  賦值
	<value></value>
	<value></value>
	<value></value>
</util:list>

<bean id="compactDisc" class="com.qmylzx.view.BlankDisc" 
	p:title="hello world" p:artist="hello world" p:tracks-ref="trackList" />

Class BlankDisc{
	String title;  	String artist;  List tracks;
}

util: 
	constant 引用某個型別的public static域,並將其暴露為bean
	list 	 建立List型別的bean 其中包含值或引用
	map      建立Map型別的bean 其中包含值或引用
	properties  建立Properties型別的bean 
	property-path  引用一個bean 的屬性 並暴露為bean
	set      建立Set型別的bean 其中包含值或引用

@Configuration          //引入CompactDisc   或者
@Import(CDConfig.class) //用第三個類@Import({CDConfig.class,CDPlayerConfig.class})
public class CDPlayerConfig{
	@Bean
	public CDPlayer cdPlayer(CompactDisc compactDisc){ return new CDPlayer(compactDisc)}
}
//@Import 引入java配置檔案   引入xml用@ImportResource("classpath:config.xml")

<bean class="com.qmylzx.config.CDConfig"></bean>   //xml中引入javaconfig配置
//<import resource="config.xml"/>                         引入另外的xml配置

<!-- 開始元件掃描 -->
<context:component-scan base-package="com.qmylzx.ssm"></context:component-scan>


@Profile("dev")   //多個類似的Bean,當啟用名字為dev的Profile時Bean才會起作用
<bean profile="dev"></bean>   //  xml配置
@ActiveProfiles("dev")   //在使用類下標記
在web.xml中 servlet下的
<init-param>
<param-name>spring.profiles.default</param-name>
<param-value>dev,dev2</param-value>   //可以啟用多個,啟用彼此不相關的
								//dev2<param-value>dev</param-value>
</init-param>
//首先看spring.profiles.active  再看spring.profiles.default  都沒有則找沒有profile標記的bean初始化


@Conditional(MyCondition.class)     //spring4.0  給定的條件判定為真就啟用這個bean

public interface Condition{boolean matches(ConditionContext c,AnnotatedTypeMetadata a);}

public MyCondition implements Condition{
	boolean matches(ConditionContext c,AnnotatedTypeMetadata a){
		Environment e = c.getEnvironment(); return e.containsProperty("dev");
	}
}
//主要根據ConditionContext的條件進行判定  AnnotatedTypeMetadata可得到@Bean的還使用了什麼註解

@Primary   //一個介面存在多個例項化類bean的時候Spring無法判定使用哪一個
			//使用這個標記,可以設定首選bean
<bean primary="true"></bean>

//若Primary後還存在歧義 則可以用qulifier  需要配合autowired inject一起使用

@Autowired
@Qulifier("指定類名")

@Component
@Qulifier("cold") //這裡可以 指定類的 限定名 為cold   其他位置使用cold即可,不用擔心該bean被重構
class{
	
}

@bean
@Qulifier("cold")     //產生歧義的時候,可以自定義註解標籤來代替Qulifier


@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) //標記bean建立多少個
//Singleton     預設                             @Scope("prototype")也可以
//Prototype 每次注入或Spring上下文獲取建立一個新的
//Session WEB應用中每個會話建立一個 
//Rquest  WEB應用中每個req建立一個
<bean scope=""></bean>

@Scope(WebApplicationContext.SCOPE_SESSION,
		proxyMode=ScopedProxyMode.INTERFACES) //基於介面
//proxyMode=ScopedProxyMode.INTERFACES標記只有當客戶端使用時才被注入
//ScopedProxyMode.TARGET_CLASS   基於類
//                  //==  proxyMode=ScopedProxyMode.INTERFACES
<bean scope=""><aop:scoped-proxy proxy-target-class="true"/></bean>  


注入外部的值
@Configuration
@PropertySource("classpath:/com/qmylzx/app.properties")
public class ExpressConfig{
	@Autowired
	Environment env;
	@Bean
	public BlankDisc disc(){
		return new BlankDisc(env.getProperty("disc.title"),env.getProperty("disc.artist"));
	}//這裡getProperty配置檔案為空獲取null
}	//getRequiredProperty("disc.title")  配置檔案為空則丟擲異常
app.properties
	disc.title=hello world
	disc.artist=xx


SpringEL
#{語句}
#{例項類名}						//獲取類
#{double.屬性名}                //獲取類屬性
#{double.getInt()?.toString()}  //這裡? 表示會檢測getInt()方法返回值是否為null,
						//null的話不會呼叫toString()方法
@Value("#{systemProperties=['disc.title']}")   //獲取配置檔案屬性

#{2*T(java.lang.Math).PI*R^2}   //T()返回一個Class物件  算術運算
#{disc.title+'by'+disc.artist}  //連線字串      比較 ==  eq   
#{disc.email matches '[a-zA-z0-9]'}   //   matches '[a-zA-z0-9]'正則表示式
#{muisicbox.songs[T(java.java.lang.Math).random()*10]}   //  []取值
#{muisicbox.songs.?[artist eq 'Alan worker']}    //   .?[]  對集合進行過濾
//.^[]    .$[]    .![]



面向切面的Spring
	即:貫穿多個Service的某些方法的橫切關注點    如:日誌、事務、安全等
橫切關注點可以模組化為特殊的類,這些類被稱為切面;

通知advice
	切面的工作
		1.Before
		2.After
		3.After-returning
		4.After-throwing
		5.Around 通知包裹了被通知的方法,在被通知方法呼叫之前和之後執行自定義的行為
連線點joinpoint
	在應用程式執行過程中能插入切面的一個點,這個點可以是        呼叫方法、丟擲異常、修改欄位
	切面程式碼可以利用這些點插入到應用到正常流程中並 新增新的行為

切點pointcut
	切點有助於縮小 切面所通知的 連線點 的範圍
	通知定義切面的“什麼”和“何時” 切點 定義“何處”

切面
	通知與切點的結合

引入(Introduction)
	允許我們向現有的類新增新的屬性和方法。例如:建立Auditable類,記錄物件最後一次修改時的狀態
	只需要一個setLastModified(Date);方法和例項變數儲存該狀態就可以了,從而可以在不修改原有類的
	基礎上使其有新的行為和狀態。

織入(Weaving)
	把切面應用到目標物件並建立新的代理物件的過程。切面在指定的連線點織入到目標物件中,有多個點可以織入
	1.編譯期:需要特殊的編譯器  AspectJ織入編譯器
	2.類載入期:載入到jvm時被織入  需要特殊的類載入器    AspectJ 5支援load-time waving LTW
	3.執行期:應用在執行的某個時刻被織入。   Spring AOP

Spring AOP    因為 Spring 基於 動態代理 只支援 方法級別 的AOP,需要其他級別可用 Aspect 補充
	1.基於代理的經典Spring AOP
	2.純POJO切面
	
[email protected]
註解驅動的切面 //前三種是Spring AOP的變體 4.注入式AspectJ切面(適用於Spring各版本)
package com.qmylzx.concert;

public interface Performence {
    void perfrom();
}


							//springxml下可用//and   or   not
execution(* concert.Performence.perfrom(..) && within(concert.*))   // &&  ||    !
execution   在方法執行時觸發
* 返回任意型別
concert.Performence  方法所屬的型別
perfrom 方法
(..)   使用任意引數
within(concert.*)  在concert包下任意類的方法被呼叫時  執行
bean('beanName')   在指定beanName的方法被呼叫時   執行



package com.qmylzx.concert;

import org.aspectj.lang.annotation.*;

@Aspect
public class Audience {
    @Pointcut("execution(** com.qmylzx.concert.Performence.perform(..))")
    public void performence(){}
    @Before("performence()")// @Before("execution(** com.qmylzx.concert.Performence.perform(..))")
    public void slienceCellPhone() {//ready
        System.out.println("slienceCellPhone");
    }
    @AfterReturning("performence()")
    public void appluse(){//success
        System.out.println("appluse appluse appluse!");
    }
    @AfterThrowing("performence()")
    public void fail(){
        System.out.println("fail!!!");
    }
    @Around("performence()")
    public void safe(ProceedingJoinPoint proceedingJoinPoint){
        System.out.println("注意安全");
        proceedingJoinPoint.proceed();//呼叫Perform的方法
        System.out.println("注意安全");
    }
}

package com.qmylzx.concert;

@Configuration
@EnableAspectJAutoProxy   //啟用AspectJ註解的自動代理
@ComponentScan
public class ConcertConfig {
    @Bean
    public Audience audience(){
        return new Audience();
    }
}
<aop:aspectj-autoproxy/>   //springxml啟用AspectJ註解的自動代理


//記錄磁軌被使用的次數
@Pointcut("execution(* sound.cd.playTrack(int))"+"args(trackNumber)");
public void trackPlayed(int trackNumber){}

args(trackNumber) 指定引數  表明傳到playTrack()的int引數也會傳到通知中去  
trackNumber與方法前面匹配

Before("trackPlayed(trackNumber)")

------------------------------------------------------------------------------
@Aspect
class EncoreableIntroducer{
@DeclareParents(value="com.qmylzx.concert.Performence+",
                defaultImpl=DefaultEncoreable.class)
public static Encoreable en;
} 
//沒有使用通知,而是使用DeclareParents將Encoreable 介面引入Performence bean中
//value指定哪種型別的bean要引入該介面   Performence+ 表示 Performence 的子型別
//defaultImpl指定為了引入功能提供實現的類
//靜態屬性 指定了要引入的介面
同時需要 <bean class="com.qmylzx.concert.EncoreableIntroducer">

<aop:aspect>
    <aop:declare-parents types-matching="concert.Performance+"
    implements-interface="concert.Encoreable"
    default-impl="concert.DefaultEncoreable">
    </aop:declare-parents>  // default-impl可替換為delegate-ref="beanname"
</aop:aspect>        //其中<bean id="beanname" class="concert.DefaultEncoreable" />
------------------------------------------------------------------------------------
    <aop:config>
        <aop:aspect ref="Audience">
            <aop:pointcut id="per" expression="* concert.Performence.perfrom(..)"/>
            <aop:before method="slienceCellPhone" pointcut="execution(**               
                com.qmylzx.concert.Performence.perform(..))"></aop:before>
            //  pointcut-ref = "per"
        </aop:aspect>
    </aop:config>


    <aop:config>
        <aop:aspect ref="trackCounter">
            <aop:pointcut id="trackPlayed"
                          expression="execution(* com.qmylzx.concert.TrackCounter.playTrack(int)) and args(trackNumber)"/>
            <aop:before method="countTrack" pointcut-ref="trackPlayed"></aop:before>
        </aop:aspect>
    </aop:config>
//注入AspectJ切面
<bean class="com.qmylzx.CriticAspect" factory-method="aspectof" >
    <property name="criticEngine" ref="criticEngine"></property>
</bean>
//這個切面是在方法執行完成以後才注入的criticEngine 而CriticAspect在開始階段就已經建立
//現在要注入只能使用AspectJ自帶的靜態aspectof方法  需要指定factory-method="aspectof"


標註以後方法直接獲取而不用requestget
@RequertParam("")   @RequertParam(value="",defaultValue="")


								//也可以 /ssm/admin?spittleId=123456
@RequestMapping(/{spittleId})        //解析123456為引數  /ssm/admin/123456
public String spittr(@PathVariable("spittleId") long spittleId){
	
}

檢視解析器
InteranlResourceViewResolver      --JSP
VelocityViewResolver    --Velocity  
FreeMarkerViewResolver  --    FreeMarker
ThymeleafViewResolver   --Thymeleaf
<!-- 配置檢視解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/view/"></property>
    <property name="suffix" value=".jsp"></property>
</bean>
配置 支援Thymeleaf 
<bean id="viewResolver" class="org.thymeleaf.spring3.view.ThymeleafViewResolver"
p:templateEngine-ref="templateEngine"/>
<bean id="templateEngine" class="org.thymeleaf.spring3.SpringTemplateEngine"
p:templateResolver-ref="templateResolver"/>
<bean id="templateResolver" class="org.thymeleaf.templatesesolver.ServletContextTemplateResolver"
P:prefix="/WEB-INF/view/" p:suffix=".html" p:templateMode="HTML5"/>

<a th:href="@{/spittles}">Spittles</a>

//丟擲異常的時候會返回相應的HTTP狀態碼
@ResponseStatus(value=HttpStatus.NOT_FOUND,reason="not found")//404
public class MyException extends Exception{
	
}

@ExceptionHandler(MyException.class)  //標記方法,丟擲MyException異常會呼叫被標記的方法
@ControllerAdvice     標記的類為所有Controller處理異常


return "redirect:/admin/test";  //重定向到test頁面,當前req的資料會丟失

引數列表中RedirectAttributes model
model.addAttribute(key,obj);
model.addFlashAttribute(key,obj);  //會存資料到重定向的請求中


Spring Web Flow  //流程化應用程式,例如購買商品一步接一步
 
<flow:flow-executor id="flowExecutor"/>   //1..裝配流程執行器

<flow:flow-registry id="flowregistry" base-path="/WEB-INF/flows">  //2..配置流程登錄檔
	<flow:flow-location-pattern value="*-flow.xml"/> 
</flow:flow-registry>   //以-flow.xml結尾的XML視為流程定義
                        流程登錄檔基本路徑           流程定義
flow-location-pattern ==  /WEB-INF/flows + order + order-flow.xml
							               流程ID
  //或者
<flow:flow-location path="/WEB-INF/flows/order.xml"/>    //流程ID order

//3..處理流程請求
<bean class="org.springframework.webflow.mvc.servlet.FlowHandlerMapping">
	<property name="flowregistry" ref="flowregistry"
</bean>
//FlowHandlerMapping定義了一個id為order的流程,若請求的URL模式是(相對應用上下文路徑)'/order'
//就會匹配到這個流程上

//4..響應請求
<bean class="org.springframework.webflow.mvc.servlet.FlowHandlerAdapter">
	<property name="flowExecutor" ref="flowExecutor"></property>
</bean>

Spring Web Flow可選狀態
	行為(Action) 流程邏輯發生的地方
	決策(Decision) 將流程分為兩個方向,基於資料的評估結果確定流程方向
	End        流程終止
	Subflow   子流程
	View    暫停流程並邀請使用者參與流程

	<view-state id="welcome"/> //在流程內標識這個狀態,沒指定其它檢視,則預設welcome檢視
	<view-state id="welcome" view="greeting"/>  //自定義檢視
	<view-state id="takePayment" model="flowScope.paymentDetails"/> //指明表單捆綁的物件
 行為狀態觸發bean的方法並且根據方法執行結果轉移到另外的狀態
	<action-state>
		<evaluate expression="pizzaFlowActions.saveOrder(order)"/>  //行為狀態要做的事情
		<transition to="thankYou"/>
	</action-state>

	<dicision-state id="checkDeliveryArea">
		<if test="pizzaFlowActions.checkDeliveryArea(customer.zipCode)"
		then="addCustomer" else="deliveryWarning"/>
	</dicision-state>

	<subflow-state id="order" subflow="pizza/order">
		<input name="order" value="order"/>
		<transition on="orderCreated" to="payment"/>  //子流程end狀態為orderCreated則進入payment
	</subflow-state>

	<end-state id="orderCreated" />  //view="se"  flowRedirect:

 轉移transition  
	  on 觸發轉移事件    to轉移
	  on-exception="com.MyException"

 <global-transitions>  //全域性狀態轉移,所有狀態都會擁有這個屬性
	<transition></transition>
 </global-transitions>

 <var name="customer" class="com.customer">

 <evaluate result="viewScope.toppingsList"  //計算結果放到toppingsList中
	expression="T(com.Topping).asList()"/>    

 <set name="flowScope.pizza" value="new com.domain.Pizza()">

Spring Web Flow作用域
	Conversation 最高層的流程開始建立,結束時銷燬,被所有流程共享
	Flow 流程開始建立,結束銷燬 ,只有建立它的流程可以看到它
	Request 請求進入流程時建立,流程返回時銷燬
	Flash  流程開始建立,結束銷燬
	View 進入檢視建立 ,結束銷燬 ,只在檢視內可見

start--> identify Customter -(customterReady)->builderOrder-(orderCreated)->takePayment
-(paymentTaken)->saveOrder-->thankCustomter
RPC
    RMI、Hessian和Burlap、HTTP invoker、JAX-RPC和JAX-WS
         2進位制訊息 xml訊息
RMI配置
    RmiServiceExporter 來配置註冊rpc服務
//服務端
@Bean
public RmiServiceExporter rmiServiceExporter(){
    RmiServiceExporter r = new  RmiServiceExporter();
    r.setService(SpitterService);
    r.setServiceName("SpitterService");
    r.setServiceInterface(SpitterService.class);
    r.setRegistryHost("rmi.spitter.com");//繫結服務到rmi.xx.com主機的1199埠
    r.setRegistryPort(1199);
    return r;
}
//客戶端
@Bean
public RmiProxyFactoryBean spitterService(){
    RmiProxyFactoryBean  rmiProxy= new RmiProxyFactoryBean();
    rmiProxy.setServiceUrl("rmi://localhost/SpitterService");
    rmiProxy.setServiceInterface(SpitterService.class);
return rmiProxy;
}
//客戶端使用
    @Autowired
    SpitterService spitterService;  //呼叫方法即可

------------------------------------------------------------
使用Hessian和Burlap    具有移植性  
service extends HessianServlet   且方法為public
////服務端
@Bean
public HessianServiceExporter hessianServiceExporter(){
    HessianServiceExporter h = new  HessianServiceExporter();
    h.setService(SpitterService);

    h.setServiceInterface(SpitterService.class);

    return h;
}
配置控制器
<servlet-mapping>
    <servlet-name>spitter</servlet-name>
    <servlet-pattern>*.service</servlet-pattern>
</servlet-mapping>

@Bean
public HandlerMapping hessianMapping(){
   SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
    Properties mappings = new Properties();
    mappings.setProperty("/spitter","hessianExportedSpitterService");
    mapping.setMappings(mappings);
    return mapping;
}

//客戶端
@Bean
public HessianProxyFactoryBean spitterService(){
    HessianProxyFactoryBean  hessianProxy= new HessianProxyFactoryBean();
    hessianProxy.setServiceUrl("http://localhost:8080/Spitter/spitter.service");
    hessianProxy.setServiceInterface(SpitterService.class);
    return hessianProxy;
}


Restful URL
    Create Post
    Read  Get
    Update PUt\Patch
    Delete Delete