依賴注入會將所依賴的關係自動交給目標對象,而不是讓對象自己去獲取鎖。
依賴注入的方式之一:
構造器注入。
傳入的探險類型是Quest,也就是所有探險類型必須實現的一個接口。這裏的BraveKnight 沒有與任何特定的Quest實現發生耦合。這就是鬆耦合。
如果一個對象只通過接口(而不是具體實現或初始化過程)來表明依賴關係,那麼這種依賴就能夠在對象本身毫不知情的情況下,用不同的具體實現進行替換。
創建應用組件之間協作的行爲通常稱爲裝配,採用xml的裝配方式。
用ClassPathXmlApplicationContext 加載xml,並獲取Knight 的引用
//加載Spring上下文
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(“.xml”);
Knight knight =context.getBean(knight);
knight.embarkOnQuest();
context.close();
在Spring應用中,應用對象生存於Spring容器中,Spring容器負責創建對象,裝配他們,配置他們,並管理他們的整個生命週期。
容器是Spring框架的核心,
Spring使用上下文
AnnotationConfigAppliactionContext:從一個或多個基於Java的配置類中加載Spring應用上下文。
AnnotationConfigWebAppliactionContext:從一個或多個基於Java的配置類中加載SpringWeb應用上下文。
ClassPathXmlAppliactionContext:從類路徑下的一個或多個XML配置文件中加載上下文定義,把應用上下文的定義文件作爲類資源。
FIleSystemXmlapplicationcontext:從文件系統下的一個或多個XML配置文件中加載上下文定義。
XmlWebApplicationContext:從Web應用下的一個或多個XML配置文件中加載上下文定義。
FIleSystemXmlapplicationcontext加載應用上下文:
ClassPathXmlAppliactionContext從應用的類路徑下加載應用上下文:
FIleSystemXmlapplicationcontext在指定的文件系統路徑下查找knight.xml文件。
ClassPathXmlAppliactionContext是在所有的類路徑下查找knight.xml文件。
從Java配置中加載用於上下文:
Spring框架關注於通過DI,AOP和消除樣板式代碼來簡化企業級Java開發。
Spring的Instrumentation 模塊提供了爲JVM添加代理的功能,能夠爲Tomcat傳遞類文件,就像這些文件是被類加載器加載的一樣。
裝配Bean
創建應用對象之間協作關係的行爲通常被稱爲裝配。
Spring配置的可選方案
Spring容器負責創建應用程序中的bean並通過依賴注入來協調這些對象之間的關係。
Spring的三種裝配機制:
在xml中進行顯示配置
在java中進行顯示配置
隱式的bean發現機制和自動裝配
自動化裝配Bean
Spring從兩個角度來實現自動化配置:
組件掃描(component scannning):Spring會自動發現上下文中所創建的bean.
自動裝配(autowiring):Spring自動滿足bean之間的依賴。
@Component 表明該類會作爲組件類,並告知Spring要爲這個類創建bean.
@ComponentScan能夠在Spring中啓用組件掃描。 如果沒有其他配置的話,@ComponentScan 默認會掃描與配置類相同的包。
<context:component-scan base-package=”soundsystem”/>
爲組件掃描的bean命名
Spring應用上下文中所有的bean都會給定一個ID,Spring會根據類名爲其制定一個ID,SgtPeppersbean所給的ID爲sgtPeppers,將類名的第一個字母變爲小寫。
@Component(“lonelyHearts”)將制定的ID傳遞給@Component
@ComponentScan(basePackage={“soundsystem”,”video”}
通過Java代碼裝配bean
當你想要將第三方庫中的組件裝配到你的應用中,這種情況下是沒有辦法在它的類上添加@Component 和@Autowired註解的。
使用JavaConfig
public class CustomerBo {
public void printMsg(String msg) {
System.out.println("CustomerBo : " + msg);
}
}
public class SchedulerBo {
public void printMsg(String msg) {
System.out.println("SchedulerBo : " + msg);
}
}
@Configuration
public class CustomerConfig {
@Bean(name="customer")
public CustomerBo customerBo(){
return new CustomerBo();
}
}
@Configuration
public class SchedulerConfig {
@Bean(name="scheduler")
public SchedulerBo suchedulerBo(){
return new SchedulerBo();
}
}
使用@Import加載多個配置文件。
@Configuration
@Import({ CustomerConfig.class, SchedulerConfig.class })
public class AppConfig {
}
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(
AppConfig.class);
CustomerBo customer = (CustomerBo) context.getBean("customer");
customer.printMsg("Hello 11");
SchedulerBo scheduler = (SchedulerBo) context.getBean("scheduler");
scheduler.printMsg("Hello 22");
}
}
JavaConfig中引用XML配置
@import引入java類 @ImportResource(“classpath:cd-config.xml”)
classpath是指 WEB-INF文件夾下的classes目錄
解釋classes含義:
存放各種資源配置文件 eg.init.properties log4j.properties struts.xml
存放模板文件 eg.actionerror.ftl
存放class文件 對應的是項目開發時的src目錄編譯文件
將兩個或更多的配置組合起來。
@Profile註解應用在了類級別上,它會告訴Spring這個配置類中的bean只有在dev profile激活時才能創建,如果dev profile沒有激活的話,那麼帶有@Bean 註解的方法都會被忽略掉
方法級別上的profile註解
在XML中配置profile
通過<beans>元素的profile屬性,在XML中配置profile bean。
可以在根<beans>元素中嵌套定義<beans>元素,而不是爲每個環境都創建一個profile,這樣能將所有的profile bean 定義放在同一個XML文件中。
選擇該怎樣激活其中的某個profile呢?
Spring在確定哪個profile處於激活狀態時,需要依賴於兩個獨立的屬性:spring.profiles.active和spring.profiles.default。如果設置了spring.profiles.active,那麼它的值就可以用來確定哪個profile是激活的,如果未設置spring.profiles.active的值,spring 將會查找spring.profiles.default的值。
在web.xml文件中設置默認的profile
爲上下文設置默認的profile
<context-param>
<param-name>spring.profiles.default</param-name>
<param-value>dev</param-value>
</context-param>
爲Servlet設置默認的profile
<servlet>
<servlet-name></servlet-name>
<servlet-class></servlet-class>
<init-param>
param-name>spring.profiles.default</param-name>
<param-value>dev</param-value>
</init-param>
</servlet>
Spring提供了@ActiveProfiles註解,我們可以使用它來激活哪個profile
@ActiveProfiles(“dev”)
條件化地配置bean
@Conditional註解,可以根據給定的條件來創建bean
@Conditional 會通過Condition接口進行條件對比,
如果matches返回true將會創建bean
@Conditional(MagicExistsCondition.classs)
實現Condition接口,返回true,則sCondiit@Conditional註解上引用MagicExistsCondtion的bean都會被創建。
Bean的作用域
在默認情況下,Spring應用上下文中所有bean都是以單例的形式創建的。
單例Singleton 原型Prototype 會話Session 請求Request
使用ConfigurableBeanFactory類的SCOPE_PROTOTYPE也可以使用@Scope(“prototype”)
可以使用XML的bean配置作用域
WebApplicationContext 中的SCOPE_SESSION 會告訴Spring爲Web應用中每個會話創建一個ShoopingCart。ScopedProxyMode.INTERFACES解決了將會話作用域的bean注入到單例的問題
如果ShoppingCart是類的話,proxyMode將設置爲ScopedProxyMode.TARGET.CLASS
在XML中聲明作用域代理
<aop:scoped-proxy>是與@Scope註解的proxyMode屬性功能相同的元素。默認情況,使用CGLib創建目標類的代理,也可以將proxy-target-class屬性設置爲false.變成基於接口的代理
Spring注入外部的值:
在Spring中,處理外部值的最簡單的方式就是聲明屬性源並通過Spring的Enviromrnt來檢索屬性。
@PropertySource(“classpath:/com/soundsystem/app.properties”)
@Autowired
Enviroment env;
env.getProperty(“disc.title”);
Spring屬性佔位符
@Value
使用佔位符,需要配置一個PropertyPlaceholderConfigurer
SPEL表達式要放到#{}中
Spring面向切面
DI依賴注入有助於對象之間的解耦,而AOP可以實現橫切關注點與他們所影響的對象之間的解耦。
AOP,描述切面的常用術語有通知,切點,和連接點。
切面(切面的工作是通知) 連接點(通知的時機) 切點 有助於縮小切面所通知的連接點的範圍。
我們通常使用明確的類和方法名稱或是利用正則表達式定義所匹配的類和方法名稱來知道這些切點。
Aspect切面:切面是通知和切點的結合,在何時和何處完成其功能。
編寫切點
定義一個Performance接口
切點表達式:
execution(* concert.Performance.perform(..))
假設我們需要配置的切點僅匹配concert包。可以使用within()指示器來限制匹配。
execution(* concert.Performance.perform(..)&& within(concert.*))
限制切面的通知被編織到不爲woodstock的bean中
execution(* concert.Performance.perform()) and !bean(‘woodstock’)
定義切面
@pointcut 註解能夠在一個@Aspect切面內定義可重用的切點。
@Pointcout(“execution (** concert.Performance.perform(..))”)
public void performance(){}
@Before(“performance()”)
如果你就此止步的話,Audience只會是Spring容器中的一個Bean,使用JavaConfig,在配置類的類級別上通過使用EnableAspectJAutoProxy註解啓用自動代理功能。EnableAspectJAutoProxy
Spring中使用XML來裝配bean的話,需要使用Spring aop命名空間中的
<aop:aspectj-autoproxy>元素
在XML中聲明切面
xml中將無註解的Audience聲明爲切面:
<aop:config>
<aop:aspect ref=”audience”> 引用audience bean
<aop:before pointcut=”execution=(** concert.Performance.perform(..))” method=”silenceCellPhones” />
<aop:before pointcut=”execution=(** concert.Performance.perform(..))” method=”takeSeats” />
<aop:before pointcut=”execution=(** concert.Performance.perform(..))” method=”sapplause” />
<aop:before pointcut=”execution=(** concert.Performance.perform(..))” method=”demandRefund” />
</aop:aspect>
</aop:config>
使用<aop:pointcut>消除pointcut 內容重複的使用。
<aop:pointcut id=”performance” expression=”execution(** concert.Performance.perform(..))”/>
<aop:before pointcut-ref=”performance” method=”takeSeats”/>
<aop:around pointcut-ref=”” method=””/>
Spring AOP
它會在方法執行之前執行。創建一個實現 MethodBeforeAdvice 接口的類。
package com.yiibai.aop;
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
public class HijackBeforeMethod implements MethodBeforeAdvice
{
@Override
public void before(Method method, Object[] args, Object target)
throws Throwable {
System.out.println("HijackBeforeMethod : Before method hijacked!");
}
}
在 bean 配置文件(applicationContext.xml),創建一個 bean 的 HijackBeforeMethod 類,並命名爲“customerServiceProxy” 作爲一個新的代理對象。
‘target’ – 定義你想攔截的bean。
‘interceptorNames’ – 定義要應用這個代理/目標對象的類(通知)。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="customerService" class="com.yiibai.customer.services.CustomerService">
<property name="name" value="Yiibai Mook Kim" />
<property name="url" value="http://www.yiibai.com" />
</bean>
<bean id="hijackBeforeMethodBean" class="com.yiibai.aop.HijackBeforeMethod" />
<bean id="customerServiceProxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="customerService" />
<property name="interceptorNames">
<list>
<value>hijackBeforeMethodBean</value>
</list>
</property>
</bean>
</beans>
https://www.yiibai.com/spring/spring-aop-examples-advice.html
AspectJ
https://www.yiibai.com/spring/spring-aop-aspectj-annotation-example.html
啓用AspectJ
在 Spring 配置文件,把“<aop:aspectj-autoproxy />”,並定義Aspect(攔截)和普通的bean。
File : applicationContext.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">
<aop:aspectj-autoproxy />
<bean id="customerBo" class="com.yiibai.customer.bo.impl.CustomerBoImpl" />
<!-- Aspect -->
<bean id="logAspect" class="com.yiibai.aspect.LoggingAspect" />
</beans>
AspectJ @Before
在下面例子中,logBefore()方法將在 customerBo接口的 addCustomer()方法的執行之前被執行。
AspectJ的“切入點”是用來聲明哪種方法將被攔截,應該參考Spring AOP切入點指南,支持切入點表達式的完整列表。
File : LoggingAspect.java
package com.yiibai.aspect;
import org.aspectj.lang.JoinYiibai;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class LoggingAspect {
@Before("execution(* com.yiibai.customer.bo.CustomerBo.addCustomer(..))")
public void logBefore(JoinYiibai joinYiibai) {
System.out.println("logBefore() is running!");
System.out.println("hijacked : " + joinYiibai.getSignature().getName());
System.out.println("******");
}
}
SpringMVC的過程
- 請求傳給Spring 的DispatcherServlet前端控制器
- 會查詢一個或多個處理器映射來確定請求的下一站
- DispatcherServlet再將請求發送給指定的SpringMVC控制器
- 控制器將模型數據打包,並且標識出用於渲染輸出的視圖名,會將請求連同數據模型和視圖名一起送回DispatcherServlet.
- DispatcherServlet將會使用視圖解析器來將邏輯視圖名稱匹配爲一個特定的視圖實現。
- DispatcherServlet處理視圖的實現(可能是jSP),交付數據模型
- 視圖再使用數據模型渲染輸出(通過響應對象傳給客戶端)
DispatcherServlet和Servlet監聽器(ContextLoaderListener)的關係