IoC(Inversion of Control 控制反轉),詳細的概念見Spring系列(一):Spring核心概念html
① 新建一個Bean: java
package com.toby.ioc.component; /** * @desc: * @author: toby * @date: 2019/7/13 1:49 */ public class TobyBean{ public TobyBean(){ System.out.println("TobyBean Constructor"); } }
② 在resources下面新建一個spring.xmlgit
xml配置以下:spring
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="tobyBean" class="com.toby.ioc.component.TobyBean"/> </beans>
③ 寫一個測試類進行測試數組
package com.toby.ioc.xml; import com.toby.ioc.component.TobyBean; import org.junit.Before; import org.junit.Test; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * @desc: 基於xml * @author: toby * @date: 2019/8/6 17:36 */ public class XmlTest { private ClassPathXmlApplicationContext context; @Before public void before(){ context = new ClassPathXmlApplicationContext("spring.xml"); } @Test public void test(){ TobyBean tobyBean = context.getBean(TobyBean.class); System.out.println(tobyBean); } }
總結:因爲如今基本基於spring boot 約定大於配置,並且大量的xml配置也不易於維護,因此這裏就簡單介紹下基於xml的原理:首先讀取資源配置文件,而後解析成BeanDefinition,最後利用反射進行相應的實例化操做。咱們接下來重點講解基於註解的方式session
① 同上面基於xml同樣,須要一個Beanapp
② 新建一個配置類定義相應的Bean信息ide
package com.toby.ioc.config; import com.toby.ioc.component.TobyBean; import org.springframework.context.annotation.*; /** * @desc: ioc config 類 * @author: toby * @date: 2019/7/13 1:10 */ @Configuration public class IocConfig { @Bean public TobyBean tobyBean(){ return new TobyBean(); } }
③ 寫一個測試類進行測試post
package com.toby.ioc.configuration; import com.toby.ioc.config.IocConfig; import org.junit.Before; import org.junit.Test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @desc: 基於配置類 * @author: toby * @date: 2019/8/6 17:59 */ public class ConfigurationTest { private AnnotationConfigApplicationContext context; @Before public void before(){ context = new AnnotationConfigApplicationContext(IocConfig.class); } @Test public void test(){ System.out.println(context.getBean("tobyBean")); } }
package com.toby.ioc.config; import com.toby.ioc.component.TobyBean; import org.springframework.context.annotation.*; /** * @desc: ioc config 類 * @author: toby * @date: 2019/7/13 1:10 */ @Configuration public class IocConfig { @Bean public TobyBean tobyBean(){ return new TobyBean(); } }
① 在不指定@Scope的狀況下,全部的bean都是單實例的bean,並且是餓漢加載(容器啓動實例就建立好了)單元測試
② @Scope爲prototype表示爲多實例的,並且仍是懶漢模式加載(IOC容器啓動的時候,並不會建立對象,而是在每次使用的時候纔會建立)注意:當指定多例的時候是沒法解決循環依賴的後續源碼會分析
@Configuration public class IocConfig { @Bean @Scope("prototype") public TobyBean tobyBean(){ return new TobyBean(); } }
如何測試是否多實例:
public class IocMain { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(IocConfig.class); TobyBean tobyBean1 = context.getBean(TobyBean.class); TobyBean tobyBean2 = context.getBean(TobyBean.class); //單例返回true 多例返回false System.out.println(tobyBean1 == tobyBean2); } }
③ @Scope指定的做用域取值:singleton 單實例的(默認),prototype 多實例的,request 同一次請求,session 同一個會話級別
Bean的懶加載@Lazy(主要針對單實例的bean在容器啓動的時候,不建立對象,而在第一次使用的時候纔會建立該對象,多實例bean沒有懶加載一說)
@Configuration public class IocConfig { @Bean @Lazy public TobyBean tobyBean(){ return new TobyBean(); } }
在配置類上寫@CompentScan註解來進行包掃描
① 常規用法:這樣在basePackages包下面具備@Controller @Service @Repository @Component註解的組件都會被加載到spring容器中
@Configuration @ComponentScan(basePackages = {"com.toby.ioc"}) public class IocConfig { }
② 排除用法:excludeFilters(排除@Controller註解和TobyService)
@Configuration @ComponentScan(basePackages = {"com.toby.ioc"},excludeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Controller.class}), @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,value = {TobyService.class}) }) public class IocConfig { }
③ 包含用法:includeFilters,注意:若使用包含,須要把useDefaultFilters屬性設置爲false(true表示掃描所有的),後續源碼解析會說到這個緣由
@Configuration @ComponentScan(basePackages = {"com.toby.ioc"},includeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Controller.class, Service.class}) },useDefaultFilters = false) public class IocConfig { }
④ 自定義Filter用法:
自定義一個TobyTypeFilter實現TypeFilter
public class TobyTypeFilter implements TypeFilter { @Override public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { //獲取當前類的class的源信息 ClassMetadata classMetadata = metadataReader.getClassMetadata(); //類名稱中包含Dao就能夠被掃描到 if(classMetadata.getClassName().contains("Dao")) { return true; } return false; } }
配置類:
@Configuration @ComponentScan(basePackages = {"com.toby.ioc"},includeFilters = { @ComponentScan.Filter(type = FilterType.CUSTOM,value = TobyTypeFilter.class) },useDefaultFilters = false) public class IocConfig { }
① 新建2個Bean TobyA和TobyB 以下:
public class TobyA { public TobyA() { System.out.println("TobyA Constructor"); } }
public class TobyB { public TobyB() { System.out.println("TobyB Constructor"); } }
② 新建一個TobyCondition實現Condition接口
public class TobyCondition implements Condition { private static final String TOBY_A_BEAN_NAME = "tobyA"; @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { //判斷容器中是否有TobyA組件 if(context.getBeanFactory().containsBean(TOBY_A_BEAN_NAME)){ return true; } return false; } }
③ 配置類 只有當容器中有TobyA的時候才實例化TobyB
@Configuration public class IocConfig { @Bean public TobyA tobyA(){ return new TobyA(); } @Bean @Conditional(TobyCondition.class) public TobyB tobyB(){ return new TobyB(); } }
① 經過@ComponentScan包掃描 + @Controller、@Service、@Repository、@Component 針對咱們本身寫的組件能夠經過該方式來加載到容器中
② 經過@Bean的方式來導入組件(適用於導入第三方組件)
③ 經過@Import
Ⅰ 經過@Import直接導入組件(導入組件的id爲全限定類名)
配置類:
@Configuration @Import({TobyBean.class}) public class IocConfig { }
Ⅱ 經過@Import的ImportSelector類實現組件的導入(導入組件的id爲全限定類名),自定義的TobyImportSelector須要實現ImportSelector接口。
public class TobyImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { //返回全限定類名的數組 return new String[]{"com.toby.ioc.component.TobyBean"}; } }
配置類:
@Configuration @Import({TobyImportSelector.class}) public class IocConfig { }
Ⅲ 經過@Import的ImportBeanDefinitionRegistrar導入組件 (能夠指定bean的名稱),自定義TobyImportBeanDefinitionRegistrar實現ImportBeanDefinitionRegistrar。
public class TobyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { //建立一個bean定義對象 RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(TobyBean.class); //把bean定義對象導入到容器中 registry.registerBeanDefinition("tobyBean",rootBeanDefinition); } }
配置類:
@Configuration @Import({TobyImportBeanDefinitionRegistrar.class}) public class IocConfig { }
④ 經過實現FactoryBean接口來實現註冊組件
建立一個FactoryBean,注意要獲取FactoryBean自己須要在beanName前面加上&
@Component public class TobyBeanFactoryBean implements FactoryBean<TobyBean> { @Override public TobyBean getObject() throws Exception { return new TobyBean(); } @Override public Class<?> getObjectType() { return TobyBean.class; } @Override public boolean isSingleton() { return false; } }
單元測試:
public class FactoryBeanTest { private AnnotationConfigApplicationContext context; @Before public void before(){ context = new AnnotationConfigApplicationContext(IocConfig.class); } @Test public void test(){ //獲取TobyBean System.out.println(context.getBean("tobyBeanFactoryBean")); //如何獲取TobyBeanFactoryBean System.out.println(context.getBean("&tobyBeanFactoryBean")); } }
① 經過@Bean的initMethod和destroyMethod屬性
新建一個LifeCycleBean1 Bean:
package com.toby.ioc.beanlifecycle; /** * @desc: bean生命週期1 * @author: toby * @date: 2019/7/13 1:26 */ public class LifeCycleBean1 { public LifeCycleBean1(){ System.out.println("LifeCycleBean1 Constructor"); } public void init(){ System.out.println("LifeCycleBean1 Init"); } public void destroy(){ System.out.println("LifeCycleBean1 Destroy"); } }
配置類:
@Configuration public class IocConfig { @Bean(initMethod = "init",destroyMethod = "destroy") public LifeCycleBean1 lifeCycleBean1(){ return new LifeCycleBean1(); } }
②經過實現InitializingBean, DisposableBean2個接口
新建一個LifeCycleBean2
package com.toby.ioc.beanlifecycle; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.stereotype.Component; /** * @desc: bean生命週期2 經過實現2個接口 * @author: toby * @date: 2019/7/13 1:30 */ @Component public class LifeCycleBean2 implements InitializingBean, DisposableBean { public LifeCycleBean2(){ System.out.println("LifeCycleBean2 Constructor"); } @Override public void destroy() throws Exception { System.out.println("LifeCycleBean2 destroy"); } @Override public void afterPropertiesSet() throws Exception { System.out.println("LifeCycleBean2 afterPropertiesSet"); } }
③ 經過JSR250規範提供的註解@PostConstruct和@PreDestroy標註的方法
新建一個LifeCycleBean3
package com.toby.ioc.beanlifecycle; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; /** * @desc: bean生命週期3 經過2個註解 * @author: toby * @date: 2019/7/13 1:30 */ @Component public class LifeCycleBean3{ public LifeCycleBean3(){ System.out.println("LifeCycleBean3 Constructor"); } @PostConstruct public void init(){ System.out.println("LifeCycleBean3 init"); } @PreDestroy public void destroy(){ System.out.println("LifeCycleBean3 destroy"); } }
① BeanPostProcessor:也稱爲Bean後置處理器,它是Spring中定義的接口,在Spring容器的建立過程當中(具體爲Bean初始化先後)會回調BeanPostProcessor中定義的兩個方法。分別是postProcessBeforeInitialization(初始化以前)和postProcessAfterInitialization(初始化以後)
自定義TobyBeanPostProcessor後置處理器:
package com.toby.ioc.processor; import com.toby.ioc.component.TobyBean; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.stereotype.Component; /** * @desc: bean的後置處理器 * @author: toby * @date: 2019/7/13 2:08 */ @Component public class TobyBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if(bean instanceof TobyBean){ System.out.println("立刻開始初始化TobyBean了,注意下"); } return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if(bean instanceof TobyBean){ System.out.println("初始化完成TobyBean了,注意下"); } return bean; } }
② BeanFactoryPostProcessor:Bean工廠的後置處理器,觸發時機bean定義註冊以後bean實例化以前
自定義TobyBeanFactoryPostProcessor Bean工廠的後置處理器:
package com.toby.ioc.processor; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.stereotype.Component; /** * @desc: bean工廠的後置處理器 觸發時機 bean定義註冊以後 bean實例化以前 * @author: toby * @date: 2019/7/21 23:04 */ @Component public class TobyBeanFactoryPostProcessor implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { System.out.println("調用了TobyBeanFactoryPostProcessor的postProcessBeanFactory方法"); for(String beanName : beanFactory.getBeanDefinitionNames()){ if("tobyBean".equals(beanName)){ BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName); beanDefinition.setLazyInit(true); } } } }
③ BeanDefinitionRegistryPostProcessor:Bean定義的後置處理器,它繼承了BeanFactoryPostProcessor,觸發時機,在bean的定義註冊以前
自定義TobyBeanDefinitionRegistryPostProcessor Bean定義的後置處理器
package com.toby.ioc.processor; import com.toby.ioc.component.TobyBean; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.stereotype.Component; /** * @desc: bean定義的後置處理器 * @author: toby * @date: 2019/7/21 23:11 */ @Component public class TobyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor { @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { System.out.println("調用TobyBeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法"); System.out.println("bean定義的數據量:"+registry.getBeanDefinitionCount()); RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(TobyBean.class); registry.registerBeanDefinition("tobyBean",rootBeanDefinition); } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { System.out.println("調用TobyBeanDefinitionRegistryPostProcessor的postProcessBeanFactory方法"); System.out.println(beanFactory.getBeanDefinitionCount()); } }
Spring提供了大量的Aware接口,使得咱們可使用Spring的一些底層提供的容器,資源好比獲取ApplicationContext就能夠實現ApplicationContextAware接口,獲取BeanFactory就能夠實現BeanFactoryAware,這些Aware接口的回調是在Bean初始化 initializeBean() 方法中進行回調的
好比咱們要使用Spring底層的ApplicationContext,則須要實現ApplicationContextAware以下:
package com.toby.ioc.aware; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; /** * @desc: 應用中須要獲取spring的上下文 * @author: toby * @date: 2019/7/13 1:15 */ @Component public class TobyApplicationContextAware implements ApplicationContextAware { /** * spring上下文 */ private ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { System.out.println("應用程序獲取到了spring 容器"); this.applicationContext = applicationContext; } }
每一個對象都有本身生命週期的需求,主要方法:isAutoStartup()返回true時,Spring容器啓動時會去執行start()方法。isRunning()返回true的時候,容器銷燬時會調用stop()方法。好比eruaka啓動的入口就是經過實現SmartLifecycle接口來實現
自定義TobyLifecycle實現SmartLifecycle接口:
package com.toby.ioc.lifecycle; import org.springframework.context.SmartLifecycle; import org.springframework.stereotype.Component; /** * @desc: 每一個對象都有本身生命週期的需求,好比eruaka啓動的入口就是用這個實現的 * @author: toby * @date: 2019/7/13 2:00 */ @Component public class TobyLifecycle implements SmartLifecycle { @Override public boolean isAutoStartup() { return true; } @Override public void stop(Runnable callback) { } @Override public void start() { System.out.println("TobyLifecycle start"); } @Override public void stop() { } @Override public boolean isRunning() { return false; } @Override public int getPhase() { return 0; } }
① @Autowired 默認狀況下:首先是按照類型進行裝配,若在IOC容器中發現了多個相同類型的組件,那麼就按照屬性名稱來進行裝配。
② @Autowired 假設咱們須要指定特定的組件來進行裝配,咱們能夠經過使用@Qualifier("tobyDao")來指定裝配的組件或者在配置類上的@Bean加上@Primary註解
@Autowired + @Qualifier:
@Service public class TobyService { @Autowired @Qualifier("tobyDao") private TobyDao tobyDao; public TobyDao getTobyDao(){ return this.tobyDao; } }
@Bean + @Primary:
@Configuration public class IocConfig { @Bean @Primary public TobyDao tobyDao(){ return new TobyDao(); } @Bean public TobyDao tobyDao2(){ return new TobyDao(); } }
③ 假設咱們指定Autowire.BY_TYPE,這時候容器出現2個及以上,那麼在裝配的時候就會拋出異常
@Configuration public class PrincipleConfig { @Bean public PrincipleBean principleBean(){ return new PrincipleBean(); } @Bean(autowire = Autowire.BY_TYPE) public PrincipleAspect principleAspect(){ return new PrincipleAspect(); } @Bean public PrincipleLog principleLog(){ return new PrincipleLog(); } @Bean public PrincipleLog principleLog2(){ return new PrincipleLog(); } }
④ @Resource(JSR250規範)功能和@AutoWired的功能差很少同樣,可是不支持@Primary和@Qualifier的支持
⑤ @Inject(JSR330規範)須要導入jar包依賴功能和支持@Primary功能,可是沒有Require=false的功能
總結:經過上面的示例,對Spring IoC經常使用註解以及接口有必定了解,Spring系列完整代碼在碼雲:spring系列,接下來將進入:Spring系列(三):Spring IoC源碼解析(乾貨多多)