spring實戰筆記
阿新 • • 發佈:2018-11-03
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