Spring註解驅動開發(二)
阿新 • • 發佈:2018-12-14
- AOP-AOP功能測試
/** * 切面類 * @author lfy * * @Aspect: 告訴Spring當前類是一個切面類 * */ @Aspect public class LogAspects { //抽取公共的切入點表示式 //1、本類引用 //2、其他的切面引用 @Pointcut("execution(public int com.zhou.aop.MathCalculator.*(..))") public void pointCut(){}; //@Before在目標方法之前切入;切入點表示式(指定在哪個方法切入) @Before("pointCut()") public void logStart(JoinPoint joinPoint){ Object[] args = joinPoint.getArgs(); System.out.println(""+joinPoint.getSignature().getName()+"執行。。。@Before:引數列表是:{"+Arrays.asList(args)+"}"); } @After("com.zhou.aop.LogAspects.pointCut()") public void logEnd(JoinPoint joinPoint){ System.out.println(""+joinPoint.getSignature().getName()+"結束。。。@After"); } //JoinPoint一定要出現在引數表的第一位 @AfterReturning(value="pointCut()",returning="result") public void logReturn(JoinPoint joinPoint,Object result){ System.out.println(""+joinPoint.getSignature().getName()+"正常返回。。。@AfterReturning:執行結果:{"+result+"}"); } @AfterThrowing(value="pointCut()",throwing="exception") public void logException(JoinPoint joinPoint,Exception exception){ System.out.println(""+joinPoint.getSignature().getName()+"異常。。。異常資訊:{"+exception+"}"); } }
public class MathCalculator {
public int div(int i,int j){
System.out.println("MathCalculator...div...");
return i/j;
}
}
public class IOCTest_AOP { @Test public void test01(){ AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfAOP.class); //1、不要自己建立物件 // MathCalculator mathCalculator = new MathCalculator(); // mathCalculator.div(1, 1); MathCalculator mathCalculator = applicationContext.getBean(MathCalculator.class); mathCalculator.div(1, 0); applicationContext.close(); } }
- [原始碼]-AOP原理[email protected]
- [原始碼]-AOP原理-AnnotationAwareAspectJAutoProxyCreator分析
- [原始碼]-AOP原理-AnnotationAwareAspectJAutoProxyCreator執行時機
- [原始碼]-AOP原理-獲取攔截器鏈-MethodInterceptor
- [原始碼]-AOP原理-鏈式呼叫通知方法
/** * AOP:【動態代理】 * 指在程式執行期間動態的將某段程式碼切入到指定方法指定位置進行執行的程式設計方式; * * 1、匯入aop模組;Spring AOP:(spring-aspects) * 2、定義一個業務邏輯類(MathCalculator);在業務邏輯執行的時候將日誌進行列印(方法之前、方法執行結束、方法出現異常,xxx) * 3、定義一個日誌切面類(LogAspects):切面類裡面的方法需要動態感知MathCalculator.div執行到哪裡然後執行; * 通知方法: * 前置通知(@Before):logStart:在目標方法(div)執行之前執行 * 後置通知(@After):logEnd:在目標方法(div)執行結束之後執行(無論方法正常結束還是異常結束) * 返回通知(@AfterReturning):logReturn:在目標方法(div)正常返回之後執行 * 異常通知(@AfterThrowing):logException:在目標方法(div)出現異常以後執行 * 環繞通知(@Around):動態代理,手動推進目標方法執行(joinPoint.procced()) * 4、給切面類的目標方法標註何時何地執行(通知註解); * 5、將切面類和業務邏輯類(目標方法所在類)都加入到容器中; * 6、必須告訴Spring哪個類是切面類(給切面類上加一個註解:@Aspect) * [7]、給配置類中加 @EnableAspectJAutoProxy 【開啟基於註解的aop模式】 * 在Spring中很多的 @EnableXXX; * * 三步: * 1)、將業務邏輯元件和切面類都加入到容器中;告訴Spring哪個是切面類(@Aspect) * 2)、在切面類上的每一個通知方法上標註通知註解,告訴Spring何時何地執行(切入點表示式) * 3)、開啟基於註解的aop模式;@EnableAspectJAutoProxy * * AOP原理:【看給容器中註冊了什麼元件,這個元件什麼時候工作,這個元件的功能是什麼?】 * @EnableAspectJAutoProxy; * 1、@EnableAspectJAutoProxy是什麼? * @Import(AspectJAutoProxyRegistrar.class):給容器中匯入AspectJAutoProxyRegistrar * 利用AspectJAutoProxyRegistrar自定義給容器中註冊bean;BeanDefinetion * internalAutoProxyCreator=AnnotationAwareAspectJAutoProxyCreator * * 給容器中註冊一個AnnotationAwareAspectJAutoProxyCreator; * * 2、 AnnotationAwareAspectJAutoProxyCreator: * AnnotationAwareAspectJAutoProxyCreator * ->AspectJAwareAdvisorAutoProxyCreator * ->AbstractAdvisorAutoProxyCreator * ->AbstractAutoProxyCreator * implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware * 關注後置處理器(在bean初始化完成前後做事情)、自動裝配BeanFactory * * AbstractAutoProxyCreator.setBeanFactory() * AbstractAutoProxyCreator.有後置處理器的邏輯; * * AbstractAdvisorAutoProxyCreator.setBeanFactory()-》initBeanFactory() * * AnnotationAwareAspectJAutoProxyCreator.initBeanFactory() * * * 流程: * 1)、傳入配置類,建立ioc容器 * 2)、註冊配置類,呼叫refresh()重新整理容器; * 3)、registerBeanPostProcessors(beanFactory);註冊bean的後置處理器來方便攔截bean的建立; * 1)、先獲取ioc容器已經定義了的需要建立物件的所有BeanPostProcessor * 2)、給容器中加別的BeanPostProcessor * 3)、優先註冊實現了PriorityOrdered介面的BeanPostProcessor; * 4)、再給容器中註冊實現了Ordered介面的BeanPostProcessor; * 5)、註冊沒實現優先順序介面的BeanPostProcessor; * 6)、註冊BeanPostProcessor,實際上就是建立BeanPostProcessor物件,儲存在容器中; * 建立internalAutoProxyCreator的BeanPostProcessor【AnnotationAwareAspectJAutoProxyCreator】 * 1)、建立Bean的例項 * 2)、populateBean;給bean的各種屬性賦值 * 3)、initializeBean:初始化bean; * 1)、invokeAwareMethods():處理Aware介面的方法回撥 * 2)、applyBeanPostProcessorsBeforeInitialization():應用後置處理器的postProcessBeforeInitialization() * 3)、invokeInitMethods();執行自定義的初始化方法 * 4)、applyBeanPostProcessorsAfterInitialization();執行後置處理器的postProcessAfterInitialization(); * 4)、BeanPostProcessor(AnnotationAwareAspectJAutoProxyCreator)建立成功;--》aspectJAdvisorsBuilder * 7)、把BeanPostProcessor註冊到BeanFactory中; * beanFactory.addBeanPostProcessor(postProcessor); * =======以上是建立和註冊AnnotationAwareAspectJAutoProxyCreator的過程======== * * AnnotationAwareAspectJAutoProxyCreator => InstantiationAwareBeanPostProcessor * 4)、finishBeanFactoryInitialization(beanFactory);完成BeanFactory初始化工作;建立剩下的單例項bean * 1)、遍歷獲取容器中所有的Bean,依次建立物件getBean(beanName); * getBean->doGetBean()->getSingleton()-> * 2)、建立bean * 【AnnotationAwareAspectJAutoProxyCreator在所有bean建立之前會有一個攔截,InstantiationAwareBeanPostProcessor,會呼叫postProcessBeforeInstantiation()】 * 1)、先從快取中獲取當前bean,如果能獲取到,說明bean是之前被建立過的,直接使用,否則再建立; * 只要建立好的Bean都會被快取起來 * 2)、createBean();建立bean; * AnnotationAwareAspectJAutoProxyCreator 會在任何bean建立之前先嚐試返回bean的例項 * 【BeanPostProcessor是在Bean物件建立完成初始化前後呼叫的】 * 【InstantiationAwareBeanPostProcessor是在建立Bean例項之前先嚐試用後置處理器返回物件的】 * 1)、resolveBeforeInstantiation(beanName, mbdToUse);解析BeforeInstantiation * 希望後置處理器在此能返回一個代理物件;如果能返回代理物件就使用,如果不能就繼續 * 1)、後置處理器先嚐試返回物件; * bean = applyBeanPostProcessorsBeforeInstantiation(): * 拿到所有後置處理器,如果是InstantiationAwareBeanPostProcessor; * 就執行postProcessBeforeInstantiation * if (bean != null) { bean = applyBeanPostProcessorsAfterInitialization(bean, beanName); } * * 2)、doCreateBean(beanName, mbdToUse, args);真正的去建立一個bean例項;和3.6流程一樣; * 3)、 * * * AnnotationAwareAspectJAutoProxyCreator【InstantiationAwareBeanPostProcessor】 的作用: * 1)、每一個bean建立之前,呼叫postProcessBeforeInstantiation(); * 關心MathCalculator和LogAspect的建立 * 1)、判斷當前bean是否在advisedBeans中(儲存了所有需要增強bean) * 2)、判斷當前bean是否是基礎型別的Advice、Pointcut、Advisor、AopInfrastructureBean, * 或者是否是切面(@Aspect) * 3)、是否需要跳過 * 1)、獲取候選的增強器(切面裡面的通知方法)【List<Advisor> candidateAdvisors】 * 每一個封裝的通知方法的增強器是 InstantiationModelAwarePointcutAdvisor; * 判斷每一個增強器是否是 AspectJPointcutAdvisor 型別的;返回true * 2)、永遠返回false * * 2)、建立物件 * postProcessAfterInitialization; * return wrapIfNecessary(bean, beanName, cacheKey);//包裝如果需要的情況下 * 1)、獲取當前bean的所有增強器(通知方法) Object[] specificInterceptors * 1、找到候選的所有的增強器(找哪些通知方法是需要切入當前bean方法的) * 2、獲取到能在bean使用的增強器。 * 3、給增強器排序 * 2)、儲存當前bean在advisedBeans中; * 3)、如果當前bean需要增強,建立當前bean的代理物件; * 1)、獲取所有增強器(通知方法) * 2)、儲存到proxyFactory * 3)、建立代理物件:Spring自動決定 * JdkDynamicAopProxy(config);jdk動態代理; * ObjenesisCglibAopProxy(config);cglib的動態代理; * 4)、給容器中返回當前元件使用cglib增強了的代理物件; * 5)、以後容器中獲取到的就是這個元件的代理物件,執行目標方法的時候,代理物件就會執行通知方法的流程; * * * 3)、目標方法執行 ; * 容器中儲存了元件的代理物件(cglib增強後的物件),這個物件裡面儲存了詳細資訊(比如增強器,目標物件,xxx); * 1)、CglibAopProxy.intercept();攔截目標方法的執行 * 2)、根據ProxyFactory物件獲取將要執行的目標方法攔截器鏈; * List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); * 1)、List<Object> interceptorList儲存所有攔截器 5 * 一個預設的ExposeInvocationInterceptor 和 4個增強器; * 2)、遍歷所有的增強器,將其轉為Interceptor; * registry.getInterceptors(advisor); * 3)、將增強器轉為List<MethodInterceptor>; * 如果是MethodInterceptor,直接加入到集合中 * 如果不是,使用AdvisorAdapter將增強器轉為MethodInterceptor; * 轉換完成返回MethodInterceptor陣列; * * 3)、如果沒有攔截器鏈,直接執行目標方法; * 攔截器鏈(每一個通知方法又被包裝為方法攔截器,利用MethodInterceptor機制) * 4)、如果有攔截器鏈,把需要執行的目標物件,目標方法, * 攔截器鏈等資訊傳入建立一個 CglibMethodInvocation 物件, * 並呼叫 Object retVal = mi.proceed(); * 5)、攔截器鏈的觸發過程; * 1)、如果沒有攔截器執行執行目標方法,或者攔截器的索引和攔截器陣列-1大小一樣(指定到了最後一個攔截器)執行目標方法; * 2)、鏈式獲取每一個攔截器,攔截器執行invoke方法,每一個攔截器等待下一個攔截器執行完成返回以後再來執行; * 攔截器鏈的機制,保證通知方法與目標方法的執行順序; * * 總結: * 1)、 @EnableAspectJAutoProxy 開啟AOP功能 * 2)、 @EnableAspectJAutoProxy 會給容器中註冊一個元件 AnnotationAwareAspectJAutoProxyCreator * 3)、AnnotationAwareAspectJAutoProxyCreator是一個後置處理器; * 4)、容器的建立流程: * 1)、registerBeanPostProcessors()註冊後置處理器;建立AnnotationAwareAspectJAutoProxyCreator物件 * 2)、finishBeanFactoryInitialization()初始化剩下的單例項bean * 1)、建立業務邏輯元件和切面元件 * 2)、AnnotationAwareAspectJAutoProxyCreator攔截元件的建立過程 * 3)、元件建立完之後,判斷元件是否需要增強 * 是:切面的通知方法,包裝成增強器(Advisor);給業務邏輯元件建立一個代理物件(cglib); * 5)、執行目標方法: * 1)、代理物件執行目標方法 * 2)、CglibAopProxy.intercept(); * 1)、得到目標方法的攔截器鏈(增強器包裝成攔截器MethodInterceptor) * 2)、利用攔截器的鏈式機制,依次進入每一個攔截器進行執行; * 3)、效果: * 正常執行:前置通知-》目標方法-》後置通知-》返回通知 * 出現異常:前置通知-》目標方法-》後置通知-》異常通知 * * * */ @EnableAspectJAutoProxy @Configuration public class MainConfigOfAOP { //業務邏輯類加入容器中 @Bean public MathCalculator calculator(){ return new MathCalculator(); } //切面類加入到容器中 @Bean public LogAspects logAspects(){ return new LogAspects(); } }
- 宣告式事務-環境搭建
- [原始碼]-宣告式事務-原始碼分析
/**
* 宣告式事務:
*
* 環境搭建:
* 1、匯入相關依賴
* 資料來源、資料庫驅動、Spring-jdbc模組
* 2、配置資料來源、JdbcTemplate(Spring提供的簡化資料庫操作的工具)操作資料
* 3、給方法上標註 @Transactional 表示當前方法是一個事務方法;
* 4、 @EnableTransactionManagement 開啟基於註解的事務管理功能;
* @EnableXXX
* 5、配置事務管理器來控制事務;
* @Bean
* public PlatformTransactionManager transactionManager()
*
*
* 原理:
* 1)、@EnableTransactionManagement
* 利用TransactionManagementConfigurationSelector給容器中會匯入元件
* 匯入兩個元件
* AutoProxyRegistrar
* ProxyTransactionManagementConfiguration
* 2)、AutoProxyRegistrar:
* 給容器中註冊一個 InfrastructureAdvisorAutoProxyCreator 元件;
* InfrastructureAdvisorAutoProxyCreator:?
* 利用後置處理器機制在物件建立以後,包裝物件,返回一個代理物件(增強器),代理物件執行方法利用攔截器鏈進行呼叫;
*
* 3)、ProxyTransactionManagementConfiguration 做了什麼?
* 1、給容器中註冊事務增強器;
* 1)、事務增強器要用事務註解的資訊,AnnotationTransactionAttributeSource解析事務註解
* 2)、事務攔截器:
* TransactionInterceptor;儲存了事務屬性資訊,事務管理器;
* 他是一個 MethodInterceptor;
* 在目標方法執行的時候;
* 執行攔截器鏈;
* 事務攔截器:
* 1)、先獲取事務相關的屬性
* 2)、再獲取PlatformTransactionManager,如果事先沒有新增指定任何transactionmanger
* 最終會從容器中按照型別獲取一個PlatformTransactionManager;
* 3)、執行目標方法
* 如果異常,獲取到事務管理器,利用事務管理回滾操作;
* 如果正常,利用事務管理器,提交事務
*
*/
@EnableTransactionManagement
@ComponentScan("com.zhou.tx")
@Configuration
public class TxConfig {
//資料來源
@Bean
public DataSource dataSource() throws Exception{
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser("root");
dataSource.setPassword("123456");
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
return dataSource;
}
//
@Bean
public JdbcTemplate jdbcTemplate() throws Exception{
//Spring對@Configuration類會特殊處理;給容器中加元件的方法,多次呼叫都只是從容器中找元件
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource());
return jdbcTemplate;
}
//註冊事務管理器在容器中
@Bean
public PlatformTransactionManager transactionManager() throws Exception{
return new DataSourceTransactionManager(dataSource());
}
}
- 擴充套件原理-BeanFactoryPostProcessor
@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("MyBeanFactoryPostProcessor...postProcessBeanFactory...");
int count = beanFactory.getBeanDefinitionCount();
String[] names = beanFactory.getBeanDefinitionNames();
System.out.println("當前BeanFactory中有"+count+" 個Bean");
System.out.println(Arrays.asList(names));
}
}
- 擴充套件原理-BeanDefinitionRegistryPostProcessor
@Component
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor{
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// TODO Auto-generated method stub
System.out.println("MyBeanDefinitionRegistryPostProcessor...bean的數量:"+beanFactory.getBeanDefinitionCount());
}
//BeanDefinitionRegistry Bean定義資訊的儲存中心,以後BeanFactory就是按照BeanDefinitionRegistry裡面儲存的每一個bean定義資訊建立bean例項;
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
// TODO Auto-generated method stub
System.out.println("postProcessBeanDefinitionRegistry...bean的數量:"+registry.getBeanDefinitionCount());
//RootBeanDefinition beanDefinition = new RootBeanDefinition(Blue.class);
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Blue.class).getBeanDefinition();
registry.registerBeanDefinition("hello", beanDefinition);
}
}
- 擴充套件原理-ApplicationListener用法
@Component
public class MyApplicationListener implements ApplicationListener<ApplicationEvent> {
//當容器中釋出此事件以後,方法觸發
@Override
public void onApplicationEvent(ApplicationEvent event) {
// TODO Auto-generated method stub
System.out.println("收到事件:"+event);
}
}
public class IOCTest_Ext {
@Test
public void test01(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ExtConfig.class);
//釋出事件;
applicationContext.publishEvent(new ApplicationEvent(new String("我釋出的時間")) {
});
applicationContext.close();
}
}
- 擴充套件原理-ApplicationListener原理
- 擴充套件原理[email protected]與SmartInitializingSingleton
@Service
public class UserService {
@EventListener(classes={ApplicationEvent.class})
public void listen(ApplicationEvent event){
System.out.println("UserService。。監聽到的事件:"+event);
}
}
/**
* 擴充套件原理:
* BeanPostProcessor:bean後置處理器,bean建立物件初始化前後進行攔截工作的
*
* 1、BeanFactoryPostProcessor:beanFactory的後置處理器;
* 在BeanFactory標準初始化之後呼叫,來定製和修改BeanFactory的內容;
* 所有的bean定義已經儲存載入到beanFactory,但是bean的例項還未建立
*
*
* BeanFactoryPostProcessor原理:
* 1)、ioc容器建立物件
* 2)、invokeBeanFactoryPostProcessors(beanFactory);
* 如何找到所有的BeanFactoryPostProcessor並執行他們的方法;
* 1)、直接在BeanFactory中找到所有型別是BeanFactoryPostProcessor的元件,並執行他們的方法
* 2)、在初始化建立其他元件前面執行
*
* 2、BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor
* postProcessBeanDefinitionRegistry();
* 在所有bean定義資訊將要被載入,bean例項還未建立的;
*
* 優先於BeanFactoryPostProcessor執行;
* 利用BeanDefinitionRegistryPostProcessor給容器中再額外新增一些元件;
*
* 原理:
* 1)、ioc建立物件
* 2)、refresh()-》invokeBeanFactoryPostProcessors(beanFactory);
* 3)、從容器中獲取到所有的BeanDefinitionRegistryPostProcessor元件。
* 1、依次觸發所有的postProcessBeanDefinitionRegistry()方法
* 2、再來觸發postProcessBeanFactory()方法BeanFactoryPostProcessor;
*
* 4)、再來從容器中找到BeanFactoryPostProcessor元件;然後依次觸發postProcessBeanFactory()方法
*
* 3、ApplicationListener:監聽容器中釋出的事件。事件驅動模型開發;
* public interface ApplicationListener<E extends ApplicationEvent>
* 監聽 ApplicationEvent 及其下面的子事件;
*
* 步驟:
* 1)、寫一個監聽器(ApplicationListener實現類)來監聽某個事件(ApplicationEvent及其子類)
* @EventListener;
* 原理:使用EventListenerMethodProcessor處理器來解析方法上的@EventListener;
*
* 2)、把監聽器加入到容器;
* 3)、只要容器中有相關事件的釋出,我們就能監聽到這個事件;
* ContextRefreshedEvent:容器重新整理完成(所有bean都完全建立)會發布這個事件;
* ContextClosedEvent:關閉容器會發布這個事件;
* 4)、釋出一個事件:
* applicationContext.publishEvent();
*
* 原理:
* ContextRefreshedEvent、IOCTest_Ext$1[source=我釋出的時間]、ContextClosedEvent;
* 1)、ContextRefreshedEvent事件:
* 1)、容器建立物件:refresh();
* 2)、finishRefresh();容器重新整理完成會發布ContextRefreshedEvent事件
* 2)、自己釋出事件;
* 3)、容器關閉會發布ContextClosedEvent;
*
* 【事件釋出流程】:
* 3)、publishEvent(new ContextRefreshedEvent(this));
* 1)、獲取事件的多播器(派發器):getApplicationEventMulticaster()
* 2)、multicastEvent派發事件:
* 3)、獲取到所有的ApplicationListener;
* for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
* 1)、如果有Executor,可以支援使用Executor進行非同步派發;
* Executor executor = getTaskExecutor();
* 2)、否則,同步的方式直接執行listener方法;invokeListener(listener, event);
* 拿到listener回撥onApplicationEvent方法;
*
* 【事件多播器(派發器)】
* 1)、容器建立物件:refresh();
* 2)、initApplicationEventMulticaster();初始化ApplicationEventMulticaster;
* 1)、先去容器中找有沒有id=“applicationEventMulticaster”的元件;
* 2)、如果沒有this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
* 並且加入到容器中,我們就可以在其他元件要派發事件,自動注入這個applicationEventMulticaster;
*
* 【容器中有哪些監聽器】
* 1)、容器建立物件:refresh();
* 2)、registerListeners();
* 從容器中拿到所有的監聽器,把他們註冊到applicationEventMulticaster中;
* String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
* //將listener註冊到ApplicationEventMulticaster中
* getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
*
* SmartInitializingSingleton 原理:->afterSingletonsInstantiated();
* 1)、ioc容器建立物件並refresh();
* 2)、finishBeanFactoryInitialization(beanFactory);初始化剩下的單例項bean;
* 1)、先建立所有的單例項bean;getBean();
* 2)、獲取所有建立好的單例項bean,判斷是否是SmartInitializingSingleton型別的;
* 如果是就呼叫afterSingletonsInstantiated();
*
*
*
*/
@ComponentScan("com.zhou.ext")
@Configuration
public class ExtConfig {
@Bean
public Blue blue(){
return new Blue();
}
}