對於上面的知識圖解,需要一點一點的研究。
首先核心容器:
控制反轉 和 依賴注入
創建工程:
maven倉庫搜索 spring context :
引入后
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.1.RELEASE</version>
</dependency>
以前是通過 application.xml 進行配置設置
配置類 等同於以前的配置文件:
package com.toov5.bean; public class Person { int age; String name; public Person() { } public Person(int age, String name) { super(); this.age = age; this.name = name; } @Override public String toString() { return "Person [age=" + age + ", name=" + name + "]"; } }
config:
package com.toov5.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.toov5.bean.Person; @Configuration public class config { @Bean //給容器注冊一個Bean,類型為返回值的類型。xml中的id是用方法作為id public Person person() { return new Person(1, "toov5"); } }
測試類:
package com.toov5.test; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import com.toov5.bean.Person; import com.toov5.config.config; public class test { public static void main(String[] args) { @SuppressWarnings("resource") ApplicationContext applicationContex = new AnnotationConfigApplicationContext(config.class); // 之前是傳遞配置文件的位置 // 現在是我們設計的配置類的位置 Person bean = applicationContex.getBean(Person.class); // 通過類型去獲取 System.out.println(bean); String[] beanNamesForType = applicationContex.getBeanNamesForType(Person.class); // 根據類型找到bean的名字 for (String name : beanNamesForType) { System.out.println(name); // 返回bean的名字 我們可以在cofig中配置@Bean的名字 } } }
在xml配置的時候,我們使用的是包掃描的方式
<context: component-scan base-package= "com.toov5"> </context: conponent-scan>
包掃描: 只要標注了 @Controller @Service @Repository @Component
用注解搞定: @ComponentScan(value="com.toov5.bean")
可以指定要掃描的包
排除的包,包括可以按照 名字 按照類型等去排除 excludeFilters
指定的包,指定掃描的時候只需要包含哪些組件 includeFilters
還可以通過自定義規則 CUSTOM 通過 implements TypeFilter 重寫match方法
metadataReader : 讀取到的當前正在掃描的類的信息
metadataReaderFactory: 可以獲取到其他任何類
config:
package com.toov5.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.FilterType; import org.springframework.stereotype.Controller; import org.springframework.stereotype.Service; @Configuration //標記@Service 和 @Controller的 @ComponentScan(value="com.toov5",excludeFilters = { @Filter(type=FilterType.ANNOTATION,classes= { Controller.class,Service.class })}) //@ComponentScan(value="com.toov5") public class config { }
controller
package com.toov5.controller; import org.springframework.stereotype.Controller; @Controller public class BookController { }
Service
package com.toov5.service; import org.springframework.stereotype.Service; @Service public class BookService { }
Dao
package com.toov5.dao; import org.springframework.stereotype.Repository; @Repository public class BookDao { }
測試:
package com.toov5.test; import org.junit.jupiter.api.Test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import com.toov5.config.config; public class test { @SuppressWarnings("resource") @Test public void test01(){ AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(config.class); String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames(); for(String name : beanDefinitionNames) { System.out.println(name); } } }
config類:
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.FilterType; import org.springframework.stereotype.Controller; import org.springframework.stereotype.Service; @Configuration //標記@Service 和 @Controller的 @ComponentScan(value="com.toov5",excludeFilters = { // @Filter(type=FilterType.ANNOTATION,classes= { Controller.class,Service.class }), @Filter(type=FilterType.CUSTOM, classes= {MyTypeFilter.class}) }) //@ComponentScan(value="com.toov5") public class config { }
自定義的:
package com.toov5.config; import java.io.IOException; import org.springframework.core.io.Resource; import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.ClassMetadata; import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.classreading.MetadataReaderFactory; public class MyTypeFilter implements org.springframework.core.type.filter.TypeFilter{ public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { //獲取當前類注解的信息 AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata(); //獲取當前正在掃描的類的類信息 ClassMetadata classMetadata = metadataReader.getClassMetadata(); //獲取當前資源(類路徑) Resource resource = metadataReader.getResource(); // 獲取到類名 String className = classMetadata.getClassName(); System.out.println("---->"+className); return false; } }
運行結果:
可以繼續往下玩兒:
package com.toov5.config; import java.io.IOException; import org.springframework.core.io.Resource; import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.ClassMetadata; import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.classreading.MetadataReaderFactory; public class MyTypeFilter implements org.springframework.core.type.filter.TypeFilter{ public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { //獲取當前類注解的信息 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; } }
@Scope 設置作用域
config:
package com.toov5.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import com.toov5.Bean.Person; @Configuration //標記@Service 和 @Controller的 @ComponentScan(value="com.toov5") public class config { @Bean public Person person() { return new Person(12,"toov5"); } }
Test:
package com.toov5.test; import org.junit.Test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import com.toov5.config.config; public class test { @SuppressWarnings("resource") @Test public void test01(){ AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(config.class); String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames(); for(String name : beanDefinitionNames) { System.out.println(name); } //comfig 的@Bean 默認是單例的 Object p1 = applicationContext.getBean("person"); Object p2 = applicationContext.getBean("person"); System.out.println( p1==p2); } }
prototype: 多實例的
singleton: 單實例的(默認的)
request: 同一次請求創建一個實例
session: 同一個session創建一個實例
@Configuration //標記@Service 和 @Controller的 @ComponentScan(value="com.toov5") public class config { @Bean @Scope("prototype") public Person person() { return new Person(12,"toov5"); } }
單例情況下:
public class config { @Bean(name="people") @Scope() public Person person() { System.out.println("容器創建...."); return new Person(12,"toov5"); } }
測試類啟動ioc容器:
public class test { @SuppressWarnings("resource") @Test public void test01(){ AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(config.class); } }
單例模式,ioc容器啟動后會調用方法創建對象放到IOC容器總 以后每次獲取就是直接從容器中獲取. 類似於從map獲取 map.get()
如果是多例模式:
ioc容器啟動是不會創建對象的!
只有調用時候,並且調用一次,獲取一次 調用方法創建對象
config:
@Configuration //標記@Service 和 @Controller的 @ComponentScan(value="com.toov5") public class config { @Bean(name="people") @Scope("prototype") public Person person() { System.out.println("容器創建...."); return new Person(12,"toov5"); } }
public class test { @SuppressWarnings("resource") @Test public void test01(){ AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(config.class); System.out.println("IOC容器創建完成"); Object p1 = applicationContext.getBean("people"); System.out.println(p1); Object p2 = applicationContext.getBean("people"); System.out.println(p2); } }
@Lazy
懶加載: 單實例bean,默認在容器啟動的時候創建對象,懶加載容器啟動時候先不創建。第一次在使用獲取Bean時候才創建對象,並且進行初始化。
config:
@Configuration //標記@Service 和 @Controller的 @ComponentScan(value="com.toov5") public class config { @Bean(name="people") @Lazy public Person person() { System.out.println("容器創建...."); return new Person(12,"toov5"); } }
測試:
public class test { @SuppressWarnings("resource") @Test public void test01(){ AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(config.class); System.out.println("IOC容器創建完成"); Object p1 = applicationContext.getBean("people"); System.out.println(p1); Object p2 = applicationContext.getBean("people"); System.out.println(p2); } }
第一次獲取時候加載。只創建一次。
@Condition 按照條件注冊bean
也是springboot 底層大量使用的。 按照一定條件進行判斷,滿足條件給容器注冊Bean
原來情況:
@Configuration //標記@Service 和 @Controller的 @ComponentScan(value="com.toov5") public class config { @Bean(name="Jack") public Person person1() { System.out.println("容器創建...."); return new Person(50,"馬雲"); } @Bean(name="Linux") public Person person2() { System.out.println("容器創建...."); return new Person(51,"Linux"); } }
測試:
import com.toov5.Bean.Person; import com.toov5.config.config; public class test { @SuppressWarnings("resource") @Test public void test01(){ AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(config.class); String[] beanNamesForType = applicationContext.getBeanNamesForType(Person.class); for(String name : beanNamesForType) { System.out.println(name); } } }
打印:
要求:
如果操作系統是win10 被容器注冊 linux
如果是linux 給容器注冊 馬雲
代碼:
public class test { @SuppressWarnings("resource") @Test public void test01(){ AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(config.class); ConfigurableEnvironment environment = applicationContext.getEnvironment(); String osName = environment.getProperty("os.name"); System.out.println(osName); } }
打印:
動態獲取環境變量的值
context的巧用:
// 能獲取到ioc使用的beanfactory ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); //獲取到類加載器 ClassLoader classLoader = context.getClassLoader(); //獲取到bean定義的注冊類 能創建 獲取 查詢 bean 的定義 BeanDefinitionRegistry registry = context.getRegistry();
config:
@Configuration public class config { @Conditional({WindowsCondition.class}) @Bean(name="Windows") public Person person1() { System.out.println("容器創建...."); return new Person(50,"Windows"); } @Conditional({LinuxCondition.class}) @Bean(name="Linux") public Person person2() { System.out.println("容器創建...."); return new Person(51,"Linux"); } }
條件:
public class LinuxCondition implements Condition{ // ConditionContext 判斷條件能使用的上下文 // AnnotatedTypeMetadata 注釋信息 public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { //判斷是否linux 系統 //運行時環境信息 Environment environment = context.getEnvironment(); String property = environment.getProperty("os.name"); if (property.contains("Linux")) { return true; } return false; } }
條件:
public class WindowsCondition implements Condition { public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { Environment environment = context.getEnvironment(); String property = environment.getProperty("os.name"); if (property.contains("Windows")) { return true; } return false; } }
測試:
public class test { @SuppressWarnings("resource") @Test public void test01(){ AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(config.class); String[] beanNamesForType = applicationContext.getBeanNamesForType(Person.class); for(String name : beanNamesForType) { System.out.println(name); } } }
可以做更多的判斷,更多的條件
public class WindowsCondition implements Condition { public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { Environment environment = context.getEnvironment(); String property = environment.getProperty("os.name"); if (property.contains("Windows")) { return true; } BeanDefinitionRegistry registry = context.getRegistry(); boolean result = registry.containsBeanDefinition("Windows"); //容器中是否包含Windows // registry 判斷沒有 可以自己注冊一個 非常多的判斷條件 也可以給容器中注冊bean return false; } }
@Condition還可以標注在類上面, 滿足當前條件 這個類 中配置的所有Bean注冊才能生效
@Configuration @Conditional({WindowsCondition.class}) public class config { @Bean(name="Windows") public Person person1() { System.out.println("容器創建...."); return new Person(50,"Windows"); } @Bean(name="Linux") public Person person2() { System.out.println("容器創建...."); return new Person(51,"Linux"); } }
打印:
給容器中注冊組件:
1、 包+組件標注-@Controller @Service @Repository @Component
如果導入第三方包呢?
2、 別人寫類: @Bean 導入的第三方包里面的組件
3、 @Import 快速給容器中導入一個組件 id默認全類名
@ImportSelector 返回需要導入的組件的全類名數組
4、Spring提供的FactoryBean
測試:
public class test { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(config.class); private void printBeans(AnnotationConfigApplicationContext applicationContext) { String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames(); for(String name : beanDefinitionNames) { System.out.println(name); } } @Test public void test01(){ printBeans(applicationContext); } }
查看Ioc中的bean組件:
除了ioc自己的,還有那幾個正常的
這么玩兒:
package com.toov5.Bean; public class Animal { String color ; String name; public Animal() { // TODO Auto-generated constructor stub } public Animal(String color, String name) { super(); this.color = color; this.name = name; } }
config:
public class test { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(config.class); private void printBeans(AnnotationConfigApplicationContext applicationContext) { String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames(); for(String name : beanDefinitionNames) { System.out.println(name); } } @Test public void test01(){ printBeans(applicationContext); } }
結果:
如果導入多個 ,可以用數組形式@Import({A.class, B.class})
id 默認是全類名
config:
@Configuration @Conditional({WindowsCondition.class}) @Import( {Animal.class , MyImportSelector.class}) public class config { @Bean(name="Windows") public Person person1() { System.out.println("容器創建...."); return new Person(50,"Windows"); } @Bean(name="Linux") public Person person2() { System.out.println("容器創建...."); return new Person(51,"Linux"); } }
類:
//自定義邏輯 返回需要導入的組件 public class MyImportSelector implements ImportSelector{ //返回值,就是要導入到容器中的組件全類名 // AnnotationMetadata: 當前標注@Import注解的類的所有注解信息 public String[] selectImports(AnnotationMetadata importingClassMetadata) { return new String[] {"com.toov5.Bean.Fruit"} ; } }
結果:
從容器中獲取之:
public class test { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(config.class); private void printBeans(AnnotationConfigApplicationContext applicationContext) { String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames(); for(String name : beanDefinitionNames) { System.out.println(name); } } @Test public void test01(){ printBeans(applicationContext); Object bean = applicationContext.getBean(Fruit.class); System.out.println(bean); } }
結果:
業務判斷:
public class MyImportBeanDefinitionRegister implements ImportBeanDefinitionRegistrar{ //AnnotationMetadata 當前類的注解信息 和 其他信息 // BeanDefinitionRegistry: BeanDefinition注冊類 //可以調用BeanDefinitionRegistry方法,自定義來注冊Bean組件到容器中 public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { //寫邏輯 Boolean result =registry.containsBeanDefinition("com.toov5.Bean.Fruit"); //容器中是否有電腦 if (result) { //判斷邏輯 //指定bean的定義信息 包括scope等等 RootBeanDefinition beanDefinition = new RootBeanDefinition(Pen.class); //向容器中注冊bean的名字 registry.registerBeanDefinition("pen", beanDefinition); } } }
配置:
@Configuration @Conditional({WindowsCondition.class}) @Import( {Animal.class , MyImportSelector.class, MyImportBeanDefinitionRegister.class}) public class config { @Bean(name="Windows") public Person person1() { System.out.println("容器創建...."); return new Person(50,"Windows"); } @Bean(name="Linux") public Person person2() { System.out.println("容器創建...."); return new Person(51,"Linux"); } }
測試:
public class test { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(config.class); private void printBeans(AnnotationConfigApplicationContext applicationContext) { String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames(); for(String name : beanDefinitionNames) { System.out.println(name); } } @Test public void test01(){ printBeans(applicationContext); Object bean = applicationContext.getBean(Fruit.class); System.out.println(bean); } }
FactoryBean 工廠Bean 區別普通的Bean,導入到容器中,容器會調用無參構造器,然后創建一個對象注冊到的容器中
工廠Bean 是個工廠, 是個接口
容器調用:
返回對象給容器
還有兩個方法:
創建一個 Pen 的工廠Beran
import org.springframework.beans.factory.FactoryBean; import com.toov5.Bean.Pen; //創建一個spring 定義的工廠bean public class PenFactoryBean implements FactoryBean<Pen>{ //返回的對象會添加到容器中 public Pen getObject() throws Exception { // TODO Auto-generated method stub System.out.println("PenFactoryBean---getBean---"); return new Pen(); } public Class<?> getObjectType() { // TODO Auto-generated method stub return Pen.class; } //如果是單例(true) 則在容器中只會保存一份 public boolean isSingleton() { // TODO Auto-generated method stub return false; } //如果是false 則每次獲取都會創建新的 即調用 getObject 方法 }
然后將工廠bean 加入到容器
@Configuration @Import( {Animal.class , MyImportSelector.class, MyImportBeanDefinitionRegister.class}) public class config { @Bean public PenFactoryBean penFactoryBean() { return new PenFactoryBean(); } }
工廠Bean 獲取的是調用getObject創建的對象
public class test { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(config.class); private void printBeans(AnnotationConfigApplicationContext applicationContext) { String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames(); for(String name : beanDefinitionNames) { System.out.println(name); } } @Test public void test01(){ printBeans(applicationContext); Object bean1 = applicationContext.getBean("penFactoryBean"); Object bean2 = applicationContext.getBean("penFactoryBean"); System.out.println(bean1==bean2); Object bean3 = applicationContext.getBean("&penFactoryBean"); System.out.println(bean3); //加&獲取到工廠bean調用getObject創建的對象 //要獲取工廠Bean本身,需要id前面加& 否則就是工廠bean的本身了 } }
打印:
在於其他第三方框架整合時候 FactoryBean用的很多
方法一: Bean生命周期,Bean創建到初始化,到銷毀的過程。由容器管理。
初始化 和 銷毀的方法可以自定義
xml配置中 通過 init-method destory-method 配置方法指定
方法二: 注解中:
構造(對象創建)
單例: 在容器啟動的時候創建
多例:在每次獲取的時候創建
對於生命周期的方法聲明,用注解:
Bean: @Bean(initMethod="init", destroyMethod="destory")
public class Car { public Car() { System.out.println("無參構造初始化"); } public void init() { System.out.println("CarBean---init"); } public void destory() { System.out.println("CarBean---destory"); } }
config:
@Configuration public class BeanLife { @Bean(initMethod="init", destroyMethod="destory") public Car car() { return new Car(); } }
測試方法:
@Test public void test01(){ AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanLife.class); System.out.println("容器創建完成"); }
結果:
關閉容器:
@Test public void test01(){ AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanLife.class); System.out.println("容器創建完成"); //關閉容器 applicationContext.close(); }
在數據源的配置過程中 有很多屬性的配置需要用到 銷毀時候 關閉連接等等
總結:
初始化, 對象創建完成,並賦值,調用初始化方法
銷毀, 容器關閉時候。 但是如果是多例情況,創建對象是獲取時候才會執行。銷毀時候,是不進行的。容器不管的。
方法三: Spring 還提供了兩個接口: Bean通過實現InitializingBean (定義初始化邏輯)
DisposableBean (定義銷毀邏輯)
Bean:
@Component public class Cat implements InitializingBean, DisposableBean{ public Cat() { System.out.println("cat 構造函數..."); } //銷毀方法 public void destroy() throws Exception { System.out.println("cat destory"); } //初始化方法 public void afterPropertiesSet() throws Exception { System.out.println("cat afterPropertiesSet"); } }
配置:
@ComponentScan("com.toov5.Bean") @Configuration public class BeanLife { @Bean(initMethod="init", destroyMethod="destory") public Car car() { return new Car(); } }
測試:
@Test public void test01(){ AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanLife.class); System.out.println("容器創建完成"); //關閉容器 applicationContext.close(); }
還可以使用: JSP250規范的 @PostConstruct 在bean創建完成 並且屬性賦值完成執行初始化
@Predestory 當bean從容器中銷毀Bean之前 通知清理工作
Bean:
@Component public class Cat implements InitializingBean, DisposableBean{ public Cat() { System.out.println("cat 構造函數..."); } //銷毀方法 public void destroy() throws Exception { System.out.println("cat destory"); } //初始化方法 public void afterPropertiesSet() throws Exception { System.out.println("cat afterPropertiesSet"); } }
config:
@ComponentScan("com.toov5.Bean") @Configuration public class BeanLife { @Bean(initMethod="init", destroyMethod="destory") public Car car() { return new Car(); } }
test:
@Test public void test01(){ AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanLife.class); System.out.println("容器創建完成"); //關閉容器 applicationContext.close(); }
結果:
還可以使用interface BeanPostProcessor: 在bean初始化前后進行一些處理工作
postProcessBeforeInitialization( ) 初始化之前工作
postProcessAfterInitialization( ) 初始化之后工作
后置處理器: 初始化前后進行處理工作
每個bean在初始化init之前調用 postProcessBeforeInitialization
初始化init 之后調用 postProcessAfterInitialization
對於Bean 的生命周期:
構造器初始化, 初始化(可以自定義指定),初始化前后可以使用(BeanPostProcessor)進行攔截。 銷毀(自定義執行)
關於BeanPostProcessor 在Spring 底層的使用
@Component public class Dog implements ApplicationContextAware{ private ApplicationContext applicationContext; public Dog() { System.out.println("dog 構造函數初始化"); } @PostConstruct public void init() { System.out.println("對象創建 並且賦值之后 調用了 @PostConstruct"); } @PreDestroy public void destory() { System.out.println("容器關閉 移除bean,調用了 @PreDestroy"); } public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { //dog對象被創建后 ioc容器會被傳遞進來 this.applicationContext=applicationContext; } }
BeanValidationPostProcessor
可以做數據校驗, 當對象創建完 給Bean賦值以后。做數據校驗
還有
InitDestoryAnnottionBeanPostProcessor
AutowiredAnnotationBeanPostProcessor 對象創建完了之后 處理所有 @Autowried標注的屬性
小結: BeanPostProcessor 接口 : bean賦值,注入其他組件,@Autowried,生命周期注解,@Async 等等