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源码解析(干货多多)