第一.spring框架快速入門
1.1什么是spring 框架
Spring 框架是 Java 應用最廣的框架,它的成功來源於理念,而不是技術本身,它的理念包括 IoC (Inversion of Control,控制反轉)
和 AOP(Aspect Oriented Programming,面向切面編程)。
理念:每個bean與bean之間的關系統一交給SpringIOC容器管理
New UserService(); 掃包、注解形式注入 使用容器幫助我們創建對象 底層大量反射機制。
1.2spring框架的體系結構
1、Spring Core:主要組件是BeanFactory,創建JavaBean的工廠,使用控制反轉(IOC) 模式 將應用程序的配置和依賴性規范與實際的應用程序代碼分開。
2、Spring AOP:集成了面向切面的編程功能(AOP把一個業務流程分成幾部分,例如權限檢查、業務處理、日志記錄,
每個部分單獨處理,然后把它們組裝成完整的業務流程。每個部分被稱為切面),可以將聲明性事物管理集成到應用程序中。
3、Spring Cntext:一個核心配置文件,為Spring框架提供上下文信息。
4、Spring Do:Spring操作數據庫的模塊。
5、Spring ORM:Spring集成了各種orm(object relationship mapping 對象關系映射)框架的模塊,集成mybatis
6、Spring Web集成各種優秀的web層框架的模塊(Struts、Springmvc)
7、Spring web MVC:Spring web層框架
1.3快速構建spring環境
1.maven依賴信息
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.2</version>
</dependency>
</dependencies>
2.實體類
public class UserEntity { private Integer userId; private String userName; public Integer getUserId() { return userId; } public UserEntity(String userName,Integer userId){ this.userId = userId; this.userName = userName; } public void setUserId(Integer userId) { this.userId = userId; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } @Override public String toString() { return "UserEntity{" + "userId=" + userId + ", userName='" + userName + '\'' + '}'; } }
3.xml環境搭配
application.xml
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="userEntity" class="com.yehui.demo01.entity.UserEntity">
<property name="userName" value="yehui"/>
<property name="userId" value="10"/>
</bean>
</beans>
4.spring啟動類
//1.讀取spring配置文件,創建IOC容器
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("application.xml"); //2.從SpringIoc容器獲取userEntity
UserEntity userEntity = context.getBean("userEntity", UserEntity.class); System.out.println("xml讀取:"+userEntity);
5.注解方式搭建
1.@Configuration
使用Configuration 配置容器
//配置類==配置文件
@Configuration //告訴Spring這是一個配置類
public class MainConfig { @Bean //給容器中注冊一個Bean;類型為返回值的類型,id默認是用方法名作為id 主要作用是導入外包jar包
public UserEntity userEntity(){ return new UserEntity("name",32432); } }
使用注解形式加載ioc
public class TestMain { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class); UserEntity bean = context.getBean(UserEntity.class); System.out.println(bean); } }
2.@ComponentScan用法
掃包下注入springIOC容器中
配置類
//配置類==配置文件
@Configuration //告訴Spring這是一個配置類
@ComponentScan("com.yehui.demo01") public class MainConfig { //@ComponentScan value:指定要掃描的包
@Bean //給容器中注冊一個Bean;類型為返回值的類型,id默認是用方法名作為id
public UserEntity userEntity(){ return new UserEntity("name",32432); } }
分別在service、controller、dao包下面建立類,並打上對於的注解,例如下面
test類
public class TestMain { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class); //得到所有包下的所有注解類
String[] beanDefinitionNames = context.getBeanDefinitionNames(); for (String beanDefinitionName : beanDefinitionNames) { System.out.println(beanDefinitionName); } } }
效果:
includeFilters
FilterType 有四種類型
ANNOTATION:注解類型
ASSIGNABLE_TYPE:ANNOTATION:指定的類型
ASPECTJ:按照Aspectj的表達式,基本上不會用到
REGEX:按照正則表達式
CUSTOM:自定義規則
config類
//配置類==配置文件
@Configuration //告訴Spring這是一個配置類
@ComponentScan(value = "com.yehui.demo01", includeFilters = @ComponentScan.Filter(type = FilterType.CUSTOM,classes = MyTypeFilter.class),useDefaultFilters = false
/ includeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Controller.class),useDefaultFilters = false/
/excludeFilters ={ @ComponentScan.Filter( type = FilterType.ANNOTATION, classes = Controller.class) }/) public class MainConfig { //@ComponentScan value:指定要掃描的包 //@ComponentScan value:指定要掃描的包 //excludeFilters = Filter[] :指定掃描的時候按照什么規則排除那些組件 //includeFilters = Filter[] :指定掃描的時候只需要包含哪些組件 //FilterType.ANNOTATION:按照注解 //FilterType.ASSIGNABLE_TYPE:按照給定的類型; //FilterType.ASPECTJ:使用ASPECTJ表達式 //FilterType.REGEX:使用正則指定 //FilterType.CUSTOM:使用自定義規則
@Bean //給容器中注冊一個Bean;類型為返回值的類型,id默認是用方法名作為id
public UserEntity userEntity(){ return new UserEntity("name",32432); } }
自定義過濾器
public class MyTypeFilter implements TypeFilter { / metadataReader:讀取到的當前正在掃描的類的信息 metadataReaderFactory:可以獲取到其他任何類信息的 / @Override public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { // TODO Auto-generated method stub //獲取當前類注解的信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata(); //獲取當前正在掃描的類的類信息
ClassMetadata classMetadata = metadataReader.getClassMetadata(); //獲取當前類資源(類的路徑)
Resource resource = metadataReader.getResource(); String className = classMetadata.getClassName(); System.out.println("--->"+className); if(className.contains("er")){ return true; } return false; } }
3.@Scope
默認情況Spring容器是單例的
singleton單例模式
全局有且僅有一個實例。
prototype原型模式
每次獲取Bean的時候都會有一個新的實例。
request
request表示針對每次請求都會產生一個新的Bean對象,並且該Bean對象僅在當前Http請求內有效。
session
session作用域表示煤氣請求都會產生一個新的Bean對象,並且該Bean僅在當前Http session內有效。
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class); UserService userService1 = context.getBean(UserService.class); UserService userService2 = context.getBean(UserService.class); System.out.println(userService1==userService2);結果為true
4.@Lazy
Lazy表示為懶加載,當真正需要引用獲取的時候才會被加載
True 表示為懶加載 false表示為在IOC容器加載的時候被創建。
@Service @Lazy(true) public class UserService { public UserService() { System.out.println("UserService無參數構造被加載..."); } }
5.@Condition
Condition 是在spring4.0 增加的條件注解,通過這個可以功能可以實現選擇性的注入Bean操作,接下來先學習下Condition是如何使用的,然后分析spring源碼了解其中的實現原理。
實現案例:
在Spring容器加載中,如果當前環境是WIN7操作系統就裝配win7實體類、其他系統就不裝配。
//判斷是否操作系統
public class MyCondition implements Condition { /@param context 獲取當前的上下文路徑 @param metadata 獲取當前的注解信息 @return
/ @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { //獲取環境
Environment environment = context.getEnvironment(); // win7 linux win8 win10 蘋果系統MAC
String property = environment.getProperty("os.name"); if(property.contains("Window")){ return true; } return false; } } @Configuration @Conditional(MyCondition.class) public class MyConfig { @Bean public WindowEntity windowEntity(){ return new WindowEntity(); } }
6.@import注解
@Import 是被用來整合所有在@Configuration注解中定義的bean配置,作用主要將外部的jar包注入到springioc容器中 @Import(Win7Entity.class) 等於與@Bean
public class MyEntity { } @Configuration @Import(MyInportEntity.class) public class MyImportConfig { }
默認注冊beanid為 全路徑地址
@Import注解與@Bean注解區別:
bean注解注冊的 bean的id是以方法名稱 @Import以當前類完整路徑地址注冊 相比來說@Import注入類更加簡單
7.@ImportSelector
ImportSelector接口是至spring中導入外部配置的核心接口,在SpringBoot的自動化配置和@EnableXXX(功能性注解)都有它的存在
public class MyInportSelectorEntity { } @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) //導入類
@Import(MyImportSelector.class) public @interface EnableImport { } @Configuration //使用自定義注解
@EnableImport public class MyImportSelectorConfig { }
8.@ImportBeanDefinitionRegistrar
手動注冊bean到容器
/ 手動注冊 /
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { / AnnotationMetadata:當前類的注解信息 BeanDefinitionRegistry:BeanDefinition注冊類; 把所有需要添加到容器中的bean;調用 BeanDefinitionRegistry.registerBeanDefinition手工注冊進來 / @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { ////指定Bean定義信息;(Bean的類型,Bean。。。)
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(PayEntity.class); //注冊一個Bean,指定bean名
registry.registerBeanDefinition("payEntity",rootBeanDefinition); } }
9.使用FactoryBean注冊對象
public class FactoryEntity { } //創建一個spring 定義的工廠bean
public class MyFactory implements FactoryBean<FactoryEntity> { //返回一個FactoryEntity 對象
@Override public FactoryEntity getObject() throws Exception { return new FactoryEntity(); } @Override public Class<?> getObjectType() { return FactoryEntity.class; } //是單例? //true:這個bean是單實例,在容器中保存一份 //false:多實例,每次獲取都會創建一個新的bean;
@Override public boolean isSingleton() { return false; } } @Configuration public class MyFactoryCconfig { / 使用Spring提供的 FactoryBean(工廠Bean); 1)、默認獲取到的是工廠bean調用getObject創建的對象 2)、要獲取工廠Bean本身,我們需要給id前面加一個& &colorFactoryBean @return
/ @Bean public MyFactory myFactory(){ return new MyFactory(); } } public class MainTest { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyFactoryCconfig.class); Object myFactory = context.getBean("&myFactory"); Object myFactory1 = context.getBean("myFactory"); System.out.println(myFactory.getClass()); System.out.println(myFactory1.getClass()); } }
區別:BeanFactory是個Factory,也就是IOC容器或對象工廠,FactoryBean是個Bean。在Spring中,所有的Bean都是由BeanFactory(也就是IOC容器)來進行管理的。
(從IOC工廠中獲取bean對象)
但對FactoryBean而言,這個Bean不是簡單的Bean,而是一個能生產或者修飾對象生成的工廠Bean,它的實現與設計模式中的工廠模式和修飾器模式類似
(往ioc容器中存儲對象 注入對象)
@Service、@Repository 注入對象更好區分應用場景底層使用@Component 注意加上@Component掃包范圍
@Qualifier與@Primary注解區別
1.@qualifier根據名稱查找 @Primary設置默認的
10.AnnotationConfigApplicationContext注解啟動原理
Spring有2個核心接口:BeanFactory和ApplicationContext,其中ApplicationContext是BeanFactory的子接口。
他們都可代表Spring容器,Spring容器生成Bean實例的工廠,並且管理容器中的Bean。
BeanFactory與ApplicationContext的作用和區別?
作用:
1.BeanFactory負責讀取bean配置文檔,管理bean的加載,實例化,維護bean之間的依賴關系,負責bean的聲明周期
2.ApplicationContext 除了提供的上訴BeanFactory所提供的功能之外,還提供了更完整的框架功能:
a. 國際化支持
b. 資源訪問:Resource rs = ctx. getResource(“classpath:config.properties”), “file:c:/config.properties”
c. 事件傳遞:通過實現ApplicationContextAware接口
3. 常用的獲取ApplicationContext的方法:
FileSystemXmlApplicationContext:從文件系統或者url指定的xml配置文件創建,參數為配置文件名或文件名數組
ClassPathXmlApplicationContext:從classpath的xml配置文件創建,可以從jar包中讀取配置文件
WebApplicationContextUtils:從web應用的根目錄讀取配置文件,需要先在web.xml中配置,可以配置監聽器或者servlet來實現
AnnotationConfigApplicationContext是基於注解來使用的,它不需要配置文件,采用java配置類和各種注解來配置,是比較簡單的
方式,也是大勢所趨
11.AnnotationConfig啟動流程源碼分析
public AnnotationConfigApplicationContext() { //基於注解方式讀取到ioc容器
this.reader = new AnnotatedBeanDefinitionReader(this); //基於掃包方式讀取到ioc容器
this.scanner = new ClassPathBeanDefinitionScanner(this); }
<T> void doRegisterBean(Class<T> annotatedClass, @Nullable Supplier<T> instanceSupplier, @Nullable String name, @Nullable Class<? extends Annotation>[] qualifiers, BeanDefinitionCustomizer... definitionCustomizers) { //AnnotatedGenericBeanDefinition 注解方式啟動的配置注入ioc bean信息 AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
//判斷是否有condition條件注入的bean if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) { return; } //設置回調方式 abd.setInstanceSupplier(instanceSupplier);
//判斷config類上面是否加上@Scope作用域 ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
//設置scope abd.setScope(scopeMetadata.getScopeName());
//判斷是否有傳遞beanname如果沒有傳遞name的話 生成的beanname 默認是類名的小寫 String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry)); AnnotationConfigUtils.processCommonDefinitionAnnotations(abd); if (qualifiers != null) { for (Class<? extends Annotation> qualifier : qualifiers) { if (Primary.class == qualifier) { abd.setPrimary(true); } else if (Lazy.class == qualifier) { abd.setLazyInit(true); } else { abd.addQualifier(new AutowireCandidateQualifier(qualifier)); } } } for (BeanDefinitionCustomizer customizer : definitionCustomizers) { customizer.customize(abd); } BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName); definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry); }
流程圖
第二、spring的生命周期
2.1Bean實例生命周期的執行過程如下:
-
Spring對bean進行實例化,默認bean是單例;
-
Spring對bean進行依賴注入;
-
如果bean實現了BeanNameAware接口,spring將bean的id傳給setBeanName()方法;
-
如果bean實現了BeanFactoryAware接口,spring將調用setBeanFactory方法,將BeanFactory實例傳進來;
-
如果bean實現了ApplicationContextAware接口,它的setApplicationContext()方法將被調用,將應用上下文的引用傳入到bean中;
-
如果bean實現了BeanPostProcessor接口,它的postProcessBeforeInitialization方法將被調用;
-
如果bean實現了InitializingBean接口,spring將調用它的afterPropertiesSet接口方法,類似的如果bean使用了init-method屬性聲明了初始化方法,該方法也會被調用;
-
如果bean實現了BeanPostProcessor接口,它的postProcessAfterInitialization接口方法將被調用;
-
此時bean已經准備就緒,可以被應用程序使用了,他們將一直駐留在應用上下文中,直到該應用上下文被銷毀;
-
若bean實現了DisposableBean接口,spring將調用它的distroy()接口方法。同樣的,如果bean使用了destroy-method屬性聲明了銷毀方法,則該方法被調用;
SpringBean的生命周期Bean的創建(執行構造方法)→初始化(自定義init方法)→銷毀
可以自定義方法初始化與銷毀的方法
2.2Bean實例創建時間
單實例:在容器啟動的時候創建對象
多實例:在每次獲取對象的時候創建對象,容器啟動之后
@Scope("prototype")//多例情況下
@Component public class MyEntity { public MyEntity(){ System.out.println(">>>>>>無參構造函數執行"); } } @Configuration @ComponentScan("com.yehui.entity") public class MyConfig { } public class Test001 { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class); // 單例和多例到底在什么時候初始化的呢 默認的情況下
/ 1.單例默認情況下是在容器被加載的時候就會初始化 2.多例子在每次獲取bean的對象的時候就會去初始化 如何判斷對象是否初始化呢? 通過構造函數 / MyEntity bean = context.getBean(MyEntity.class); } }
2.3對象的初始化和銷毀
1.對象的初始化:
對象創建完成,並賦值好,調用初始化方法。。。
2.對象的銷毀
單實例:容器關閉的時候
多實例:容器不會管理這個bean;容器不會調用銷毀方法;
初始化和銷毀的方法
方法一.通過@Bean指定init-method和destroy-mothod;
方法二.通過讓bean實現initializingBean(定義初始化邏輯),DisposableBean(定義銷毀邏輯);
方法三、可以使用JSR250
@PostConstruct:在bean創建完成並且屬性賦值完成;來執行初始化方法
@PreDestroy:在容器銷毀bean之前通知我們進行清理工作
方法一代碼實現:
public class MyEntity { public MyEntity(){ System.out.println(">>>>>>無參構造函數執行"); } public void init(){ System.out.println("初始化方法"); } public void destroy(){ System.out.println("銷毀方法"); } } @Configuration @ComponentScan("com.yehui.entity") public class MyConfig { / initMethod 指定初始化方法執行 destroyMethod 指定銷毀方法 @return
/ @Bean(initMethod = "init",destroyMethod = "destroy") public MyEntity myEntity(){ return new MyEntity(); } } public class Test001 { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class); / bean初始化 指的就是對象已經創建里面所有的set方法都已經執行完畢了;指定方法執行 new User().setName 調用close方法銷毀單例對象 Map集合存儲對象 clean清除 / MyEntity bean = context.getBean(MyEntity.class); //銷毀
context.close(); } }
方法二代碼實現
@Component public class MemberEntity implements InitializingBean,DisposableBean { public MemberEntity(){ System.out.println("構造函數創建"); } //1.對象創建 對象屬性賦值 set方法全部走完
@Override public void afterPropertiesSet() throws Exception { System.out.println("初始化"); } @Override public void destroy() throws Exception { System.out.println("銷毀"); } } @Configuration @ComponentScan("com.yehui.entity") public class MyConfig { } public class Test001 { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class); / bean初始化 指的就是對象已經創建里面所有的set方法都已經執行完畢了;指定方法執行 new User().setName 調用close方法銷毀單例對象 Map集合存儲對象 clean清除 / MemberEntity bean = context.getBean(MemberEntity.class); //銷毀
context.close(); } }
方法三.代碼實現
@Component public class PosConstrutsEntity { public PosConstrutsEntity(){ System.out.println("這個是構造函數"); } //對象創建賦值之后調用 @PostConstruct public void init(){ System.out.println("初始化方法"); } //移除對象 @PreDestroy public void destroy(){ System.out.println("銷毀方法"); } } @Configuration @ComponentScan("com.yehui.entity") public class MyConfig { } public class Test001 { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class); PosConstrutsEntity bean = context.getBean(PosConstrutsEntity.class); //銷毀
context.close(); } }
2.4ApplicationContextAware接口
代碼實現
@Component public class MyApplicationContext implements ApplicationContextAware { ApplicationContext applicationContext; / spring底層中為什么能夠實現ApplicationContextAware接口 就能夠拿到ApplicationContext BeanPostProcessor(后置處理器作用就是可以對初始實現增強) 過濾器中,是否可以使用注解方式獲取bean對象 不可以(自己單獨獲取上下文 ApplicationContext) @param applicationContext @throws BeansException / @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; Object memberEntity = applicationContext.getBean("memberEntity"); System.out.println(memberEntity); } }
2.5BeanNameAware與BeanFactoryAware區別
@Component public class PayEntity implements BeanNameAware, BeanFactoryAware { @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { System.out.println("設置beanName"+beanFactory); } @Override public void setBeanName(String name) { System.out.println("name"+name); } } @Configuration @ComponentScan("com.yehui.entity") public class MyConfig { } public class Test001 { public static void main(String[] args) {
//IOC容器初始化單例對象都是循環遍歷調用getBean方法
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class); PayEntity bean = context.getBean(PayEntity.class); //銷毀
context.close(); } }
2.6.BeanPostProcessor的作用
BeanPostProcessor.postProcessBeforeInitialization
初始化:
對象創建完成,並賦值好,調用初始化方法。。。
BeanPostProcessor.postProcessAfterInitialization
BeanPostProcessor【interface】:bean的后置處理器;
在bean初始化前后進行一些處理工作;
postProcessBeforeInitialization:在初始化之前工作
postProcessAfterInitialization:在初始化之后工作
遍歷得到容器中所有的BeanPostProcessor;挨個執行beforeInitialization,
一但返回null,跳出for循環,不會執行后面的BeanPostProcessor.postProcessorsBeforeInitialization
BeanPostProcessor原理
populateBean(beanName, mbd, instanceWrapper);給bean進行屬性賦值
initializeBean
{
applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
invokeInitMethods(beanName, wrappedBean, mbd);執行自定義初始化
applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
Spring底層對 BeanPostProcessor 的使用;
bean賦值,注入其他組件,@Autowired,生命周期注解功能,@Async,xxx BeanPostProcessor;
Object wrappedBean = bean; if (mbd == null || !mbd.isSynthetic()) {
//前置 wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } try {
//執行自定義的初始化方法 invokeInitMethods(beanName, wrappedBean, mbd); } catch (Throwable ex) { throw new BeanCreationException( (mbd != null ? mbd.getResourceDescription() : null), beanName, "Invocation of init method failed", ex); } if (mbd == null || !mbd.isSynthetic()) {
//后置 wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); }
@Configuration @ComponentScan("com.yehui.entity") public class MyConfig { } @Component public class MyBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("方法之前執行:"+beanName); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("方法之后執行:"+beanName); return bean; } } public class Test001 { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class); MyBeanPostProcessor bean = context.getBean(MyBeanPostProcessor.class); //銷毀
context.close(); } }
spring生命周期流程圖
SpringBean的聲明周期總結:
源碼分析:
- 執行refresh() ; 刷新方法
- finishBeanFactoryInitialization()
- preInstantiateSingletons()
- getBean()→doGetBean()→createBean()→doCreateBean()→createBeanInstance(初始化對象)默認情況下使用Java的反射機制初始化對象, 也可以通過CGLIB實現初始化
- initializeBean()
- invokeAwareMethods() 判斷是否有aware接口依賴信息
- applyBeanPostProcessorsBeforeInitialization()執行前置處理
- invokeInitMethods()執行init方法
- applyBeanPostProcessorsAfterInitialization 執行后置增強
核心的一個接口BeanPostProcessors 目的對我們bean對象自定義初始化方法實現曾強
如何知道Bean對象到底是如何創建的?
反射機制
第三.SpringAop的編程切面編程
3.1什么是AOP編程
Spring 的aop可以在方法之前和之后實現增強
Aop面向切面編程,在方法之前和之后實現處理 應用場景在於:日志打印、事務實現、安全等。
3.2AOP的底層實現原理
動態代理技術
基於Jdk實現InvocationHandler 底層使用反射技術(基於接口實現)
基於CGLIB實現 字節碼技術(繼承模式)
3.3基於注解方式啟動Aop
引入maven依賴
<dependencies>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
</dependencies>
創建一個目標類
public class MathCalculator { public int div(int i,int j){ System.out.println("MathCalculator...div..."); return i/j; } }
創建一個切面類
/ @Aspect: 告訴Spring當前類是一個切面類 / @Aspect public class LogAspects { //抽取公共的切入點表達式 //1、本類引用 //2、其他的切面引用
@Pointcut("execution( com.yehui.aop..(..))") public void pointCut(){ } //@Before在目標方法之前切入;切入點表達式(指定在哪個方法切入)
@Before("pointCut()") public void logStart(JoinPoint joinPoint){ //得到方法名
String methodName = joinPoint.getSignature().getName(); //得到參數
Object[] params = joinPoint.getArgs(); //得到類
Class<?> clazz = joinPoint.getTarget().getClass(); //得到方法的參數類型
Class[] parameterTypes = ((MethodSignature) (joinPoint.getSignature())).getParameterTypes(); System.out.println(""+joinPoint.getSignature() .getName()+"運行。。。@Before:參數列表是:" +
"{"+Arrays.asList(params)+"}"+"方法的參數類型"+parameterTypes); } //后置通知
@After("pointCut()") public void logEnd(JoinPoint joinPoint){ System.out.println(""+joinPoint.getSignature().getName()+"結束。。。@After"); } / 環繞通知 @param joinPoint @return 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+"}"); } }
創建一個配置類
/ 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 / @Configuration @EnableAspectJAutoProxy public class AopConfig { / 配置一個切面類 @return
/ @Bean public LogAspects logAspects(){ return new LogAspects(); } / 目標方法類 @return
/ @Bean public MathCalculator mathCalculator(){ return new MathCalculator(); } }
啟動測試
public class Test001 { public static void main(String[] args) { AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(AopConfig.class); MathCalculator mathCalculator = context.getBean(MathCalculator.class); mathCalculator.div(1,2); } }
效果:
3.4純手寫spring事物框架
實現思路:自定義注解加aop
引入maven依賴
<groupId>com.yehui</groupId>
<artifactId>spring-transactional</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.6.RELEASE</version>
</parent>
<dependencies>
<!-- SpringBoot-整合Web組件 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- springboot-aop 技術 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency>
<!-- mysql 依賴 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
自定義注解
/ 自定義注解 / @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface ExTransactional { String value() default ""; }
事物工具類
@Component public class TransactionalUtils { / 獲取當前事務管理器 / @Autowired private DataSourceTransactionManager dataSourceTransactionManager; public TransactionStatus begin() { TransactionStatus transaction = dataSourceTransactionManager.getTransaction(new DefaultTransactionAttribute()); return transaction; } public void rollback(TransactionStatus transaction) { dataSourceTransactionManager.rollback(transaction); } public void commit(TransactionStatus transaction) { dataSourceTransactionManager.commit(transaction); } }
aop類
/ 自定義注解+aop事物攔截 / @Aspect @Component public class TransactionalAop { @Autowired private TransactionalUtils transactionalUtils; @Around("@annotation(com.yehui.annontation.ExTransactional)") public Object aroundAnnotation(ProceedingJoinPoint joinPoint) throws Throwable { TransactionStatus transactionStatus = null; //獲取目標類
Class<?> clazz = joinPoint.getTarget().getClass(); //獲取方法名
String methodName = joinPoint.getSignature().getName(); //獲取參數類型
Class[] parameterTypes = ((MethodSignature) (joinPoint.getSignature())).getParameterTypes(); //獲取方法
try { Method method = clazz.getMethod(methodName, parameterTypes); //獲取是否有注解
if(method.isAnnotationPresent(ExTransactional.class)){ //開啟事物
transactionStatus = transactionalUtils.begin(); Object object = joinPoint.proceed(); //提交事物
transactionalUtils.commit(transactionStatus); return object; } } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (Throwable throwable) { // 目標方法拋出異常的情況下 回滾當前事務
transactionalUtils.rollback(transactionStatus); return 0; } return joinPoint.proceed(); } }
創建一個mapper接口
@Mapper public interface AccountMapper { @Update("UPDATE account SET money = money-1000 WHERE id = 4") public void update(); }
service類
@Service public class AccountService { @Autowired private AccountMapper accountMapper; @ExTransactional public void update(){ accountMapper.update(); //int i=1/0;
} }
controller類
@RestController public class AccountController { @Autowired private AccountService accountService; @RequestMapping("/update") public void update(){ accountService.update(); } }
yml配置文件
spring: ###數據庫相關連接 datasource: username: root password: root driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/study?useUnicode=true&characterEncoding=UTF-8
啟動類
@SpringBootApplication public class AopStartApp { public static void main(String[] args) { SpringApplication.run(AopStartApp.class); } }
3.5springAop原理分析
如何分析原理:【看給容器中注冊了什么組件,這個組件什么時候工作,這個組件的功能是什么?】
3.5.1@EnableAspectJAutoProxy是什么?
@Import(AspectJAutoProxyRegistrar.class):給容器中導入AspectJAutoProxyRegistrar
利用AspectJAutoProxyRegistrar自定義給容器中注冊bean;BeanDefinetion
注冊bean信息:
Beanid:org.springframework.aop.config.internalAutoProxyCreator
class:AnnotationAwareAspectJAutoProxyCreator
給容器中注冊一個AnnotationAwareAspectJAutoProxyCreator;需要了解SpringAOP底層是如何實現的 離不開
AnnotationAwareAspectJAutoProxyCreator
得出結論:AnnotationAwareAspectJAutoProxyCreator祖宗是BeanPostProcessors子類。
3.5.2 AnnotationAwareAspectJAutoProxyCreator:
1.繼承關系
AnnotationAwareAspectJAutoProxyCreator
→AspectJAwareAdvisorAutoProxyCreator
→AbstractAdvisorAutoProxyCreator
→AbstractAutoProxyCreator
→implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware
→關注后置處理器(在bean初始化完成前后做事情)、自動裝配BeanFactory
AbstractAutoProxyCreator.setBeanFactory()
AbstractAutoProxyCreator.postProcessAfterInstantiation()后置處理器
AbstractAdvisorAutoProxyCreator.setBeanFactory()-》initBeanFactory()
AnnotationAwareAspectJAutoProxyCreator.initBeanFactory()
2.執行流程
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);
}
doCreateBean(beanName, mbdToUse, args);真正的去創建一個bean實例;和3.6流程一樣
AnnotationAwareAspectJAutoProxyCreator【InstantiationAwareBeanPostProcessor】 的作用:
1)、每一個bean創建之前,調用postProcessBeforeInstantiation();
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)、效果:
正常執行:前置通知-》目標方法-》后置通知-》返回通知
出現異常:前置通知-》目標方法-》后置通知-》異常通知
3.6spring aop調用鏈實現
3.6.1Aop底層原理分析
1.首先啟動Spring Aop時,會使用@EnableAspectJAutoProxy注解
2.將@Import(AspectJAutoProxyRegistrar.class)注入到spring aop容器中
3.AspectJAutoProxyRegistrar會注冊對象
BeanId:internalAutoProxyCreator
BeanClass:AnnotationAwareAspectJAutoProxyCreator
4.AnnotationAwareAspectJAutoProxyCreator最為核心,使用后置通知在bean的對象初始化的時候,實現對代理對象的增強。
AnnotationAwareAspectJAutoProxyCreator祖宗:
AbstractAutoProxyCreator 祖宗有是BeanPostProcessor
5.被代理對象在初始化的時候,AbstractAutoProxyCreator 經過這樣的一個類攔截。
-
判斷該被代理對象是否有被有實現過接口,如果有實現過接口就使用jdk動態代理,如果沒有實現接口則使用cglib動態代理。
3.6.2模擬SpringAop五個通知調用鏈關系
創建一個UserService接口
public class UserService { public void login(String name,String age){ System.out.println("姓名:"+name); System.out.println("年齡:"+age); } }
創建方法攔截接口
/ 定義一個通知接口 /
public interface MethodInterceptor { / 定義一個共同骨架 /
public void invoke(MethodInvocation methodInvocation); }
創建一個前置通知
/ 前置通知 /
public class BeforMethodInterceptor implements MethodInterceptor { @Override public void invoke(MethodInvocation methodInvocation) { System.out.println("前置通知====="); //執行目標方法
methodInvocation.procee(); } }
創建一個后置通知
public class AfterMethodInterceptor implements MethodInterceptor { @Override public void invoke(MethodInvocation methodInvocation) { //執行目標方法
methodInvocation.procee(); System.out.println("后置通知"); } }
創建一個環繞通知
public class AroundMethodInterceptor implements MethodInterceptor { public Object invoke(MethodInvocation methodInvocation) throws InvocationTargetException, IllegalAccessException { System.out.println("環繞通知之前執行...."); Object process = methodInvocation.process(); System.out.println("環繞通知之后執行...."); return process; } }
方法執行接口
/ 調用方法 /
public interface MethodInvocation { / 調用鏈 /
public void procee(); }
方法執行接口實現類
public class DefaultMethodInvacation implements MethodInvocation { / 存放調用鏈集合 / List<MethodInterceptor> methodInterceptors = new ArrayList<>(); private Object target;//目標對象
private Method method;//方法
private Object[] args;//參數名稱
private int index = 0; public DefaultMethodInvacation(List<MethodInterceptor> methodInterceptors){ this.methodInterceptors = methodInterceptors; } public DefaultMethodInvacation(List<MethodInterceptor> methodInterceptors, Object target,Method method,Object[] args){ this.methodInterceptors = methodInterceptors; this.target = target; this.method = method; this.args =args; } @Override public void procee() { if (methodInterceptors.size()==index){ try { method.invoke(target,args);//執行目標方法
return; } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } MethodInterceptor methodInterceptor = methodInterceptors.get(index++); methodInterceptor.invoke(this); } }
測試
public class Test001 { public static void main(String[] args) throws NoSuchMethodException { List<MethodInterceptor> methodInterceptors = new ArrayList<>(); methodInterceptors.add(new BeforMethodInterceptor()); methodInterceptors.add(new AfterMethodInterceptor()); UserService userService = new UserService(); Method loginMethod = userService.getClass().getMethod("login", String.class, String.class); DefaultMethodInvacation defaultMethodInvacation =
new DefaultMethodInvacation(methodInterceptors,new UserService(), loginMethod, new Object[]{"yehui","12323"}); defaultMethodInvacation.procee();//spring Aop底層 所有的通知最終使用遞歸算法調用+責任鏈設計模式
} }
3.7 聲明式事務
事物本身就是基於aop實現的
SpringAop通知鏈采用遞歸+責任鏈設計模式實現
環境搭建:
1、導入相關依賴
數據源、數據庫驅動、Spring-jdbc模塊
2、配置數據源、JdbcTemplate(Spring提供的簡化數據庫操作的工具)操作數據
@Configuration @EnableTransactionManagement public class TxConfig { / 配置數據源 @return
/ @Bean public DataSource dataSource(){ ComboPooledDataSource dataSource = new ComboPooledDataSource(); dataSource.setUser("root"); dataSource.setPassword("123456"); try { dataSource.setDriverClass("com.mysql.jdbc.Driver"); } catch (PropertyVetoException e) { e.printStackTrace(); } dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test"); return dataSource; } @Bean public JdbcTemplate jdbcTemplate(){ //Spring對@Configuration類會特殊處理;給容器中加組件的方法,多次調用都只是從容器中找組件
return new JdbcTemplate(dataSource()); } //注冊事務管理器在容器中
@Bean public PlatformTransactionManager transactionManager() throws Exception{ return new DataSourceTransactionManager(dataSource()); } }
@Service public class UserService { @Autowired private UserDao userDao; @Transactional public void insertUser(){ userDao.insert(); //otherDao.other();xxx
System.out.println("插入完成..."); int i = 10/0; } }
@Repository public class UserDao { @Autowired private JdbcTemplate jdbcTemplate; public void insert(){ String sql = "INSERT INTO `tbl_user`(username,age) VALUES(?,?)"; String username = UUID.randomUUID().toString().substring(0, 5); jdbcTemplate.update(sql, username,19); } }
3、給方法上標注 @Transactional 表示當前方法是一個事務方法;
4、 @EnableTransactionManagement 開啟基於注解的事務管理功能;
@EnableXXX
5、配置事務管理器來控制事務;
@Bean
public PlatformTransactionManager transactionManager()
6、原理:
1)@EnableTransactionManagement
1、利用TransactionManagementConfigurationSelector給容器導入組件
導入2個組件
AutoProxyRegistrar
ProxyTransactionManagementConfiguration
2、AutoProxyRegistrar
給容器注冊了一個InfrastructureAdvisorAutoProxyCreator組件
InfrastructureAdvisorAutoProxyCreator:利用后置處理器機制在對象創建以后,包裝對象,返回一個代理對象(增強器),代理對象執行方法利用攔截器鏈進行調用;
3、ProxyTransactionManagementConfiguration 做了什么?
1)、事務增強器要用事務注解的信息,AnnotationTransactionAttributeSource解析事務注解
2)、事務攔截器:
TransactionInterceptor;保存了事務屬性信息,事務管理器;封裝了事務的開啟、提交、回滾。
他是一個 MethodInterceptor;
在目標方法執行的時候;
執行攔截器鏈;
事務攔截器:
1)、先獲取事務相關的屬性
2)、再獲取PlatformTransactionManager,如果事先沒有添加指定任何transactionmanger
最終會從容器中按照類型獲取一個PlatformTransactionManager;
3)、執行目標方法
如果異常,獲取到事務管理器,利用事務管理回滾操作;
如果正常,利用事務管理器,提交事務
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) { // 開啟事務
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification); Object retVal = null; try { // 執行目標方法
retVal = invocation.proceedWithInvocation(); } catch (Throwable ex) { // 回滾當前事務
completeTransactionAfterThrowing(txInfo, ex); throw ex; } finally { cleanupTransactionInfo(txInfo); } //提交當前事務
commitTransactionAfterReturning(txInfo); return retVal; }
Spring的事務為什么會失效呢?
如果service 沒有將異常拋出的時候,事務可能會失效
3.9擴展原理
/** * 擴展原理: * BeanPostProcessor:bean后置處理器,bean創建對象初始化前后進行攔截工作的 * BeanFactoryPostProcessor:beanFactory的后置處理器 * 1、BeanFactoryPostProcessor: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及其子類) * 2)、把監聽器加入到容器; * 3)、只要容器中有相關事件的發布,我們就能監聽到這個事件; * ContextRefreshedEvent:容器刷新完成(所有bean都完全創建)會發布這個事件; * ContextClosedEvent:關閉容器會發布這個事件; * 4)、發布一個事件: * applicationContext.publishEvent(); * 原理: * 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.yehui.ex") @Configuration public class ExConfig { @Bean public Blue blue(){ return new Blue(); } }
@Component public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor { @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { System.out.println("postProcessBeanDefinitionRegistry:\t"+registry.getBeanDefinitionCount()); RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Blue.class); registry.registerBeanDefinition("hello",rootBeanDefinition); } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { System.out.println("postProcessBeanFactory:\t\t--"+beanFactory.getBeanDefinitionCount()); String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames(); System.out.println("postProcessBeanFactory:\t"+Arrays.asList(beanDefinitionNames)); } }
public class Blue { public Blue(){ System.out.println("blue...constructor"); } public void init(){ System.out.println("blue...init..."); } public void detory(){ System.out.println("blue...detory..."); } }
3.10spring的創建
Spring容器的refresh()【創建刷新】;
1、prepareRefresh()刷新前的預處理;
1)、initPropertySources()初始化一些屬性設置;子類自定義個性化的屬性設置方法;
2)、getEnvironment().validateRequiredProperties();檢驗屬性的合法等
3)、earlyApplicationEvents= new LinkedHashSet<ApplicationEvent>();保存容器中的一些早期的事件;
2、obtainFreshBeanFactory();獲取BeanFactory;
1)、refreshBeanFactory();刷新【創建】BeanFactory;
創建了一個this.beanFactory = new DefaultListableBeanFactory();
設置id;
2)、getBeanFactory();返回剛才GenericApplicationContext創建的BeanFactory對象;
3)、將創建的BeanFactory【DefaultListableBeanFactory】返回;
3、prepareBeanFactory(beanFactory);BeanFactory的預准備工作(BeanFactory進行一些設置);
1)、設置BeanFactory的類加載器、支持表達式解析器...
2)、添加部分BeanPostProcessor【ApplicationContextAwareProcessor】
3)、設置忽略的自動裝配的接口EnvironmentAware、EmbeddedValueResolverAware、xxx;
4)、注冊可以解析的自動裝配;我們能直接在任何組件中自動注入:
BeanFactory、ResourceLoader、ApplicationEventPublisher、ApplicationContext
5)、添加BeanPostProcessor【ApplicationListenerDetector】
6)、添加編譯時的AspectJ;
7)、給BeanFactory中注冊一些能用的組件;
environment【ConfigurableEnvironment】、
systemProperties【Map<String, Object>】、
systemEnvironment【Map<String, Object>】
4、postProcessBeanFactory(beanFactory);BeanFactory准備工作完成后進行的后置處理工作;
1)、子類通過重寫這個方法來在BeanFactory創建並預准備完成以后做進一步的設置
======================以上是BeanFactory的創建及預准備工作==================================
5、invokeBeanFactoryPostProcessors(beanFactory);執行BeanFactoryPostProcessor的方法;
BeanFactoryPostProcessor:BeanFactory的后置處理器。在BeanFactory標准初始化之后執行的;
兩個接口:BeanFactoryPostProcessor、BeanDefinitionRegistryPostProcessor
1)、執行BeanFactoryPostProcessor的方法;
先執行BeanDefinitionRegistryPostProcessor
2)、獲取所有的BeanDefinitionRegistryPostProcessor;
2)、看先執行實現了PriorityOrdered優先級接口的BeanDefinitionRegistryPostProcessor、
postProcessor.postProcessBeanDefinitionRegistry(registry)
3)、在執行實現了Ordered順序接口的BeanDefinitionRegistryPostProcessor;
postProcessor.postProcessBeanDefinitionRegistry(registry)
4)、最后執行沒有實現任何優先級或者是順序接口的BeanDefinitionRegistryPostProcessors;
postProcessor.postProcessBeanDefinitionRegistry(registry)
再執行BeanFactoryPostProcessor的方法
1)、獲取所有的BeanFactoryPostProcessor
2)、看先執行實現了PriorityOrdered優先級接口的BeanFactoryPostProcessor、
postProcessor.postProcessBeanFactory()
3)、在執行實現了Ordered順序接口的BeanFactoryPostProcessor;
postProcessor.postProcessBeanFactory()
4)、最后執行沒有實現任何優先級或者是順序接口的BeanFactoryPostProcessor;
postProcessor.postProcessBeanFactory()
6、registerBeanPostProcessors(beanFactory);注冊BeanPostProcessor(Bean的后置處理器)【 intercept bean creation】
不同接口類型的BeanPostProcessor;在Bean創建前后的執行時機是不一樣的
BeanPostProcessor、
DestructionAwareBeanPostProcessor、
InstantiationAwareBeanPostProcessor、
SmartInstantiationAwareBeanPostProcessor、
MergedBeanDefinitionPostProcessor【internalPostProcessors】、
1)、獲取所有的 BeanPostProcessor;后置處理器都默認可以通過PriorityOrdered、Ordered接口來執行優先級
2)、先注冊PriorityOrdered優先級接口的BeanPostProcessor;
把每一個BeanPostProcessor;添加到BeanFactory中
beanFactory.addBeanPostProcessor(postProcessor);
3)、再注冊Ordered接口的
4)、最后注冊沒有實現任何優先級接口的
5)、最終注冊MergedBeanDefinitionPostProcessor;
6)、注冊一個ApplicationListenerDetector;來在Bean創建完成后檢查是否是ApplicationListener,如果是
applicationContext.addApplicationListener((ApplicationListener<?>) bean);
7、initMessageSource();初始化MessageSource組件(做國際化功能;消息綁定,消息解析);
1)、獲取BeanFactory
2)、看容器中是否有id為messageSource的,類型是MessageSource的組件
如果有賦值給messageSource,如果沒有自己創建一個DelegatingMessageSource;
MessageSource:取出國際化配置文件中的某個key的值;能按照區域信息獲取;
3)、把創建好的MessageSource注冊在容器中,以后獲取國際化配置文件的值的時候,可以自動注入MessageSource;
beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);
MessageSource.getMessage(String code, Object[] args, String defaultMessage, Locale locale);
8、initApplicationEventMulticaster();初始化事件派發器;
1)、獲取BeanFactory
2)、從BeanFactory中獲取applicationEventMulticaster的ApplicationEventMulticaster;
3)、如果上一步沒有配置;創建一個SimpleApplicationEventMulticaster
4)、將創建的ApplicationEventMulticaster添加到BeanFactory中,以后其他組件直接自動注入
9、onRefresh();留給子容器(子類)
1、子類重寫這個方法,在容器刷新的時候可以自定義邏輯;
10、registerListeners();給容器中將所有項目里面的ApplicationListener注冊進來;
1、從容器中拿到所有的ApplicationListener
2、將每個監聽器添加到事件派發器中;
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
3、派發之前步驟產生的事件;
11、finishBeanFactoryInitialization(beanFactory);初始化所有剩下的單實例bean;
1、beanFactory.preInstantiateSingletons();初始化后剩下的單實例bean
1)、獲取容器中的所有Bean,依次進行初始化和創建對象
2)、獲取Bean的定義信息;RootBeanDefinition
3)、Bean不是抽象的,是單實例的,是懶加載;
1)、判斷是否是FactoryBean;是否是實現FactoryBean接口的Bean;
2)、不是工廠Bean。利用getBean(beanName);創建對象
0、getBean(beanName); ioc.getBean();
1、doGetBean(name, null, null, false);
2、先獲取緩存中保存的單實例Bean。如果能獲取到說明這個Bean之前被創建過(所有創建過的單實例Bean都會被緩存起來)
從private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);獲取的
3、緩存中獲取不到,開始Bean的創建對象流程;
4、標記當前bean已經被創建
5、獲取Bean的定義信息;
6、【獲取當前Bean依賴的其他Bean;如果有按照getBean()把依賴的Bean先創建出來;】
7、啟動單實例Bean的創建流程;
1)、createBean(beanName, mbd, args);
2)、Object bean = resolveBeforeInstantiation(beanName, mbdToUse);讓BeanPostProcessor先攔截返回代理對象;
【InstantiationAwareBeanPostProcessor】:提前執行;
先觸發:postProcessBeforeInstantiation();
如果有返回值:觸發postProcessAfterInitialization();
3)、如果前面的InstantiationAwareBeanPostProcessor沒有返回代理對象;調用4)
4)、Object beanInstance = doCreateBean(beanName, mbdToUse, args);創建Bean
1)、【創建Bean實例】;createBeanInstance(beanName, mbd, args);
利用工廠方法或者對象的構造器創建出Bean實例;
2)、applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
調用MergedBeanDefinitionPostProcessor的postProcessMergedBeanDefinition(mbd, beanType, beanName);
3)、【Bean屬性賦值】populateBean(beanName, mbd, instanceWrapper);
賦值之前:
1)、拿到InstantiationAwareBeanPostProcessor后置處理器;
postProcessAfterInstantiation();
2)、拿到InstantiationAwareBeanPostProcessor后置處理器;
postProcessPropertyValues();
=====賦值之前:===
3)、應用Bean屬性的值;為屬性利用setter方法等進行賦值;
applyPropertyValues(beanName, mbd, bw, pvs);
4)、【Bean初始化】initializeBean(beanName, exposedObject, mbd);
1)、【執行Aware接口方法】invokeAwareMethods(beanName, bean);執行xxxAware接口的方法
BeanNameAware\BeanClassLoaderAware\BeanFactoryAware
2)、【執行后置處理器初始化之前】applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
BeanPostProcessor.postProcessBeforeInitialization();
3)、【執行初始化方法】invokeInitMethods(beanName, wrappedBean, mbd);
1)、是否是InitializingBean接口的實現;執行接口規定的初始化;
2)、是否自定義初始化方法;
4)、【執行后置處理器初始化之后】applyBeanPostProcessorsAfterInitialization
BeanPostProcessor.postProcessAfterInitialization();
5)、注冊Bean的銷毀方法;
5)、將創建的Bean添加到緩存中singletonObjects;
ioc容器就是這些Map;很多的Map里面保存了單實例Bean,環境信息。。。。;
所有Bean都利用getBean創建完成以后;
檢查所有的Bean是否是SmartInitializingSingleton接口的;如果是;就執行afterSingletonsInstantiated();
12、finishRefresh();完成BeanFactory的初始化創建工作;IOC容器就創建完成;
1)、initLifecycleProcessor();初始化和生命周期有關的后置處理器;LifecycleProcessor
默認從容器中找是否有lifecycleProcessor的組件【LifecycleProcessor】;如果沒有new DefaultLifecycleProcessor();
加入到容器;
寫一個LifecycleProcessor的實現類,可以在BeanFactory
void onRefresh();
void onClose();
2)、 getLifecycleProcessor().onRefresh();
拿到前面定義的生命周期處理器(BeanFactory);回調onRefresh();
3)、publishEvent(new ContextRefreshedEvent(this));發布容器刷新完成事件;
4)、liveBeansView.registerApplicationContext(this);
3.11Spring的源碼總結
1)、Spring容器在啟動的時候,先會保存所有注冊進來的Bean的定義信息;
1)、xml注冊bean;<bean>
2)、注解注冊Bean;@Service、@Component、@Bean、xxx
2)、Spring容器會合適的時機創建這些Bean
1)、用到這個bean的時候;利用getBean創建bean;創建好以后保存在容器中;
2)、統一創建剩下所有的bean的時候;finishBeanFactoryInitialization();
3)、后置處理器;BeanPostProcessor
1)、每一個bean創建完成,都會使用各種后置處理器進行處理;來增強bean的功能;
AutowiredAnnotationBeanPostProcessor:處理自動注入
AnnotationAwareAspectJAutoProxyCreator:來做AOP功能;
xxx....
增強的功能注解:
AsyncAnnotationBeanPostProcessor
....
4)、事件驅動模型;
ApplicationListener;事件監聽;
ApplicationEventMulticaster;事件派發: