IOC:控制反轉
依賴注入DI:
一層層依賴,底盤根據輪子的大小來設計,如果輪子更改,變大了,都要改,災難
舉例說明:
1.一開始呢,構造方法是無參的
2.后來需求中想要,更改輪子的size,用上可變的構造參數,那么所有的函數都需要改
什么是依賴注入?
3.把輪子作為構造函數的參數注入到底盤中,一步步注入,則如果改輪子大小只需要更改輪子就行了,其他的類不需要更改。
以上是構造函數的方式實現依賴注入
除此之外還有如下注入方式:
setter注入
public class Bottom{
private Tire tire;
public void setTire(Tire tire) {
this.tire = tire;
} }
接口注入
注解注入(@Autowire)
構造器注入(上面的例子)
IOC容器的優勢:
避免在各處使用new來創建類并且可以統一維護(IOC Container)
創建實例的時候不需要知曉其中細節(下圖中藍色部分)
1.讀取bean的配置信息
2.生成一份bean定義的注冊表
3.根據注冊表去實例化bean
4.裝配好bean的依賴關系放入緩存池
5.依據java的反射和依賴關系使用bean
最核心的就是依賴注入和自動裝配
先了解一下幾個核心接口和類:
會把xml中定義的類或者注解里的類轉化為BeanDefinition
如圖中源碼
兩個核心接口BeanFactory和ApplicationContext(升級了的BeanFactory)
BeanFactory
比較核心的方法getbean
ApplicationContext
ApplicationContext的功能(繼承了多個接口)
BeanFactory:能夠管理、裝配bean
ResourcePatternResolver:能夠加載資源文件
MessageSource:能夠實現國際化等功能
ApplicationEventPublisher:能夠注冊監聽器,實現監聽機制
超級全的:
實戰
在config里面這樣定義一個bean:
package com.imooc.framework.ioc.config;
import com.imooc.framework.ioc.entity.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
// Configuration標簽會把類注入到容器里面 Bean標簽是把initPerson這個方法返回的實體類裝配到IOc容器當中
@Configuration
public class ApplicationConfig{
@Bean(name="person")
public Person initPerson(){
Person user = new Person();
user.setId(1L);
user.setName("Jack");
return user;
}
}
上面的方法不推薦,springboot是以下方式裝配bean的,就能被掃描到,不過掃描的是當前package和子package
@Component("person")
public class Person{
@Value("1")
private Long id;
@Value("Jack")
private String name;
//get set 方法省略
如果person類實現pet接口類的方法,將pet注入到person類(馴獸師)中
person類增加這幾行,就可以調用pet的方法類
@Autowired
private Pet pet;
public void call(){
pet.move();
}
//pet接口省略,只有一個方法move
@Component
@Primary(當有多個實現類的時候需要加入這個標簽)
public class Dog implements Pet{
@Override
public void move(){
System.out.println("running");
}
}
Spring Bean的作用域
singleton
Spring的默認作用域,容器里擁有唯一的Bean實例(適合無狀態的bean)
在Spring的IoC容器中只存在一個實例,所有對該對象的引用將共享這個實例。該實例從容器啟動,并因為第一次被請求而初始化之后,將一直存活到容器退出,
也就是說,它與IoC容器“幾乎”擁有相同的“壽命”。prototype
針對每個getBean請求,容器都會創建一個Bean實例(適合有狀態的bean)
對聲明為擁有prototype scope的bean定義,容器在接到該類型對象的請求的時候,會每次都重新生成一個新的對象實例給請求方。雖然這種類型的對象的實例化以及屬性設置等工作都是由容器負責的,但是只要準備完畢,并且對象實例返回給請求方之后,容器就不再擁有當前返回對象的引用,請求方需要自己負責當前返回對象的后繼生命周期的管理工作,包括該對象的銷毀。也就是說,容器每
次返回給請求方一個新的對象實例之后,就任由這個對象實例“自生自滅”了。
web容器支持一下三種作用域:
request
會為每個http請求創建一個bean實例,該bean僅在當前HTTP request內有效,當請求結束后,該對象的生命周期即告結束session
會為每個session創建一個bean實例,該bean僅在當前HTTP session內有效globalSession
會為每個全局的http session創建一個bean實例,該作用域僅對Portlet有效
Spring Bean 的生命周期
實例化bean并設置bean屬性「如前面例子中Person類的@Component("Person")+ 屬性name上面的@Value("Jack") 」
-
檢查Aware 接口(對SpringIOC容器的感知)并設置相關依賴
image.png BeanPostProcessor前置處理,在bean完成實例化之和完成一些自定義的處理邏輯(比如替換當前對象實例或者字節碼增強當前對象實例等。Spring的AOP則更多地使用BeanPostProcessor來為對象生成相應的代理對象)。
檢查是否實現接口InitiallizingBean以決定是否調用afterPropertiesSet,做一些屬性被設置之后一些自定義的事情(比如,在有些情況下,某個業務對象實例化完成后,還不能處于可以使用狀態。這個時候就可以讓該業務對象實現該接口,并在方法afterPropertiesSet()中完成對該業務對象的后續處理。)
檢查是否有自定義的init-method,做一些初始化的工作
-
BeanPostProcessor后置處理,bean初始化之后的自定義的工作
銷毀的過程
image.png
Spring AOP
關注點分離:不同問題交給不同的部分去解決,
面向切面編程AOP是這種技術的體現
通用化功能代碼的實現,對應的就是所謂的切面(Aspect)
業務功能代碼和切面代碼分開后,架構將變得高內聚低耦合
確保功能的完整性:切面最終需要被合并到業務中(織入)
實現AOP的技術,主要分為兩大類:一是采用動態代理技術,利用截取消息的方式,對該消息進行裝飾,以取代原有對象行為的執行;二是采用靜態織入的方式,引入特定的語法創建“方面”,從而使得編譯器可以在編譯期間織入有關“方面”的代碼。
控制層:
@RestController
public class HelloController{
@RequestMapping(value="/hello", method = RequestMethod.GET)
@ResponseBody
public String hello(){
String sentence ="Hello World";
System.out.println(sentence);
return sentence;
}
@RequestMapping(value="/hi", method = RequestMethod.GET)
@ResponseBody
public String hi(){
String sentence ="Hi World";
System.out.println(sentence);
return sentence;
}
}
可能會有許多的controller和不同的方法,每個都寫記錄日志的邏輯,成本很高
日志提醒切面
@Aspect
@Component
public class RequestLogAspect{
private static final Logger logger = LoggerFactory.getLogger(RequestLogAspect.class);
@Pointcut("execution(public * com.imooc.framework.web..*.*(..))")//這個下面的任何參數任意方法都是切入點,都打日志。
public void webLog(){}
@Before("webLog()")
public void doBefore(JoinPoint joinPoint){
// 接收到請求,記錄請求內容
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// 記錄下請求內容
logger.info("URL : "+ request.getRequestURL().toString());
logger.info("IP : "+ request.getRemoteAddr());
}
@AfterReturning(returning ="ret", pointcut ="webLog()")
public void doAfterReturning(Object ret){
// 處理完請求,返回內容
logger.info("RESPONSE : "+ ret);
}
}
Aspect:RequestLogAspect,
Target:HelloController
Join Point :hi方法和hello方法
Pointcut @Pointcut("execution(public * com.imooc.framework.web...(..))")
Advice :Before、AfterReturning 異常通知等
AOP的實現
JDKProxy:通過java的內部反射機制實現的,利用攔截器(攔截器必須實現InvocationHanlder)加上反射機制
Cglib:借助ASM(能夠操作java字節碼)實現的,對代理對象類的class文件加載進來,通過修改其字節碼生成子類來處理。實現CGLIB動態代理必須實現MethodInterceptor(方法攔截器)接口
總的來說反射機制生成類的過程中比較高效,ASM在生成類之后的執行過程比較高效
何時使用JDK還是CGLIB?
1)如果目標對象實現了接口,默認情況下會采用JDK的動態代理實現AOP。
2)如果目標對象實現了接口,可以強制使用CGLIB實現AOP。
3)如果目標對象沒有實現了接口,必須采用CGLIB庫,Spring會自動在JDK動態代理和CGLIB之間轉換。
代理模式:接口+真實實現類+代理類(里面有真實實現類)
例子:Payment 接口類,方法付錢,AliPay代理類,RealPayment 真實實現類
Payment接口
public interface Payment{
void pay();
}
public class RealPayment implements Payment{
@Override
public void pay(){
System.out.println("作為用戶,我只關心支付功能");
}
}
代理類
public class AliPay implements Payment{//代理類
private Payment payment;
public AliPay(Payment payment){
this.payment = payment;
}
public void beforePay(){
System.out.println("從招行取款");
}
@Override
public void pay(){
beforePay();
payment.pay();
afterPay();
}
public void afterPay(){
System.out.println("支付給慕課網");
}
}
public class ProxyDemo{
public static void main(String[] args){
Payment proxy =newAliPay(newRealPayment());
proxy.pay();
}
}
Spring里的代理模式的實現
- 真實實現類的邏輯包含在getBean方法里
- getBean方法返回的實際上是Proxy的實例
- Proxy實例是Spring采用JDK Proxy或CGLIB動態生成的
doGetBean方法中有個initializeBean方法,它調用了BeanPostProcessor,它調用了AbstractProxyCreator下面的wrapIfNecessary方法,里面有createProxy-> createAopProxy->DefaultAopProxyFactory.createAopProxy()
Spring事務管理
Spring并不直接管理事務,而是提供了多種事務管理器,他們將事務管理的職責委托給hibernate或者JTA等持久化機制所提供的相關平臺框架的事務來實現。
- JDBC事務
如果應用程序中直接使用JDBC來進行持久化,DataSourceTransactionManager會為你處理事務邊界。為了使用DataSourceTransactionManager,你需要使用如下的XML將其裝配到應用程序的上下文定義中:
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
實際上,DataSourceTransactionManager是通過調用Java.sql.Connection來管理事務,而后者是通過DataSource獲取到的。通過調用連接的commit()方法來提交事務,同樣,事務失敗則通過調用rollback()方法進行回滾。
- Hibernate事務
如果應用程序的持久化是通過Hibernate實現的,那么你需要使用HibernateTransactionManager。對于Hibernate3,需要在Spring上下文定義中添加如下的<bean>聲明:
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
sessionFactory屬性需要裝配一個Hibernate的session工廠,HibernateTransactionManager的實現細節是它將事務管理的職責委托給org.hibernate.Transaction對象,而后者是從Hibernate Session中獲取到的。當事務成功完成時,HibernateTransactionManager將會調用Transaction對象的commit()方法,反之,將會調用rollback()方法。
- Java持久化API事務(JPA)
Hibernate多年來一直是事實上的Java持久化標準,但是現在Java持久化API作為真正的Java持久化標準進入大家的視野。如果你計劃使用JPA的話,那你需要使用Spring的JpaTransactionManager來處理事務。你需要在Spring中這樣配置JpaTransactionManager:
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
JpaTransactionManager只需要裝配一個JPA實體管理工廠(javax.persistence.EntityManagerFactory接口的任意實現)。JpaTransactionManager將與由工廠所產生的JPA EntityManager合作來構建事務。
- Java原生API事務
如果你沒有使用以上所述的事務管理,或者是跨越了多個事務管理源(比如兩個或者是多個不同的數據源),你就需要使用JtaTransactionManager:
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManagerName" value="java:/TransactionManager" />
</bean>
JtaTransactionManager將事務管理的責任委托給javax.transaction.UserTransaction和javax.transaction.TransactionManager對象,其中事務成功完成通過UserTransaction.commit()方法提交,事務失敗通過UserTransaction.rollback()方法回滾。
Spring 事務中的隔離級別有哪幾種?
TransactionDefinition 接口中定義了五個表示隔離級別的常量:
- TransactionDefinition.ISOLATION_DEFAULT: 使用后端數據庫默認的隔離級別,Mysql 默認采用的 REPEATABLE_READ隔離級別 Oracle 默認采用的 READ_COMMITTED隔離級別.
- TransactionDefinition.ISOLATION_READ_UNCOMMITTED: 最低的隔離級別,允許讀取尚未提交的數據變更,可能會導致臟讀、幻讀或不可重復讀
- TransactionDefinition.ISOLATION_READ_COMMITTED: 允許讀取并發事務已經提交的數據,可以阻止臟讀,但是幻讀或不可重復讀仍有可能發生
- TransactionDefinition.ISOLATION_REPEATABLE_READ: 對同一字段的多次讀取結果都是一致的,除非數據是被本身事務自己所修改,可以阻止臟讀和不可重復讀,但幻讀仍有可能發生。
- TransactionDefinition.ISOLATION_SERIALIZABLE: 最高的隔離級別,完全服從ACID的隔離級別。所有的事務依次逐個執行,這樣事務之間就完全不可能產生干擾,也就是說,該級別可以防止臟讀、不可重復讀以及幻讀。但是這將嚴重影響程序的性能。通常情況下也不會用到該級別。
Spring 事務中哪幾種事務傳播行為?
支持當前事務的情況:
- TransactionDefinition.PROPAGATION_REQUIRED: 如果當前存在事務,則加入該事務;如果當前沒有事務,則創建一個新的事務。
- TransactionDefinition.PROPAGATION_SUPPORTS: 如果當前存在事務,則加入該事務;如果當前沒有事務,則以非事務的方式繼續運行。
- TransactionDefinition.PROPAGATION_MANDATORY: 如果當前存在事務,則加入該事務;如果當前沒有事務,則拋出異常。(mandatory:強制性)
不支持當前事務的情況:
- TransactionDefinition.PROPAGATION_REQUIRES_NEW: 創建一個新的事務,如果當前存在事務,則把當前事務掛起。
- TransactionDefinition.PROPAGATION_NOT_SUPPORTED: 以非事務方式運行,如果當前存在事務,則把當前事務掛起。
- TransactionDefinition.PROPAGATION_NEVER: 以非事務方式運行,如果當前存在事務,則拋出異常。
其他情況:
- TransactionDefinition.PROPAGATION_NESTED: 如果當前存在事務,則創建一個事務作為當前事務的嵌套事務來運行;如果當前沒有事務,則該取值等價于TransactionDefinition.PROPAGATION_REQUIRED。
各類注解
https://blog.csdn.net/lipinganq/article/details/79155072
SpingMVC 流程
(1)客戶端(瀏覽器)發送請求,直接請求到 DispatcherServlet。
(2)DispatcherServlet 根據請求信息調用 HandlerMapping,解析請求對應的 Handler。
(3)解析到對應的 Handler(也就是我們平常說的 Controller 控制器)后,開始由 HandlerAdapter 適配器處理。
(4)HandlerAdapter 會根據 Handler 來調用真正的處理器開處理請求,并處理相應的業務邏輯。
(5)處理器處理完業務后,會返回一個 ModelAndView 對象,Model 是返回的數據對象,View 是個邏輯上的 View。
(6)ViewResolver 會根據邏輯 View 查找實際的 View。
(7)DispaterServlet 把返回的 Model 傳給 View(視圖渲染)。
(8)把 View 返回給請求者(瀏覽器)
SpringMVC 重要組件說明
1、前端控制器DispatcherServlet(不需要工程師開發),由框架提供(重要)
作用:Spring MVC 的入口函數。接收請求,響應結果,相當于轉發器,中央處理器。有了 DispatcherServlet 減少了其它組件之間的耦合度。用戶請求到達前端控制器,它就相當于mvc模式中的c,DispatcherServlet是整個流程控制的中心,由它調用其它組件處理用戶的請求,DispatcherServlet的存在降低了組件之間的耦合性。
2、處理器映射器HandlerMapping(不需要工程師開發),由框架提供
作用:根據請求的url查找Handler。HandlerMapping負責根據用戶請求找到Handler即處理器(Controller),SpringMVC提供了不同的映射器實現不同的映射方式,例如:配置文件方式,實現接口方式,注解方式等。
3、處理器適配器HandlerAdapter
作用:按照特定規則(HandlerAdapter要求的規則)去執行Handler 通過HandlerAdapter對處理器進行執行,這是適配器模式的應用,通過擴展適配器可以對更多類型的處理器進行執行。
4、處理器Handler(需要工程師開發)
注意:編寫Handler時按照HandlerAdapter的要求去做,這樣適配器才可以去正確執行Handler,Handler 是繼DispatcherServlet前端控制器的后端控制器,在DispatcherServlet的控制下Handler對具體的用戶請求進行處理。 由于Handler涉及到具體的用戶業務請求,所以一般情況需要工程師根據業務需求開發Handler。
5、視圖解析器View resolver(不需要工程師開發),由框架提供
作用:進行視圖解析,根據邏輯視圖名解析成真正的視圖(view) View Resolver負責將處理結果生成View視圖,View Resolver首先根據邏輯視圖名解析成物理視圖名即具體的頁面地址,再生成View視圖對象,最后對View進行渲染將處理結果通過頁面展示給用戶。 springmvc框架提供了很多的View視圖類型,包括:jstlView、freemarkerView、pdfView等。 一般情況下需要通過頁面標簽或頁面模版技術將模型數據通過頁面展示給用戶,需要由工程師根據業務需求開發具體的頁面。
6、視圖View(需要工程師開發)
View是一個接口,實現類支持不同的View類型(jsp、freemarker、pdf...)
注意:處理器Handler(也就是我們平常說的Controller控制器)以及視圖層view都是需要我們自己手動開發的。其他的一些組件比如:前端控制器DispatcherServlet、處理器映射器HandlerMapping、處理器適配器HandlerAdapter等等都是框架提供給我們的,不需要自己手動開發。