Pre
为了更好地学习源码,我们有必要对基础知识进行一次简单的复习,只有在知道如何使用的基础上,再去阅读源码才能明白spring这些源码是对哪些功能的支持。
这里简单的梳理一下
xml配置文件
【配置文件 】
<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 https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="artisan" class="com.artisan.base.Artisan"/> </beans>
【读取Bean】
ClassPathXmlApplicationContext cx = new ClassPathXmlApplicationContext("classpath:spring.xml"); System.out.println(cx.getBean("artisan").getClass().getSimpleName()); }
【输出】
JavaConfig
【POJO】
public class Bean1 { }
【配置文件 】
@Configuration public class MainConfig { @Bean public Bean1 bean1(){ return new Bean1(); } }
【读取Bean— 传入配置类】
public static void main(String[] args) { AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(MainConfig.class); System.out.println(ac.getBean("bean1")); }
@Bean的形式, bean的默认名称是方法名,若@Bean(value=“bean的名称”) ,那么bean的名称是指定的名称。
【测试结果】
@CompentScan
在配置类上写@CompentScan注解来进行包扫描
【配置类】
package com.artisan.base.componentscan.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan("com.artisan.base.componentscan") public class CSMainConfig { }
【c - s -d 】
package com.artisan.base.componentscan.controller; import org.springframework.stereotype.Controller; @Controller public class ArtisanInfoController { }
package com.artisan.base.componentscan.service; import org.springframework.stereotype.Service; @Service public class ArtisanService { }
package com.artisan.base.componentscan.dao; import org.springframework.stereotype.Repository; @Repository public class ArtisanDao { }
【测试类】
package com.artisan.base.componentscan; import com.artisan.base.componentscan.config.CSMainConfig; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class CMTest { public static void main(String[] args) { AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(CSMainConfig.class); // 输出 单例池中的单例bean的名字 for (String beanDefinitionName : ac.getBeanDefinitionNames()) { System.out.println("bdName:" + beanDefinitionName); } } }
【输出】
excludeFilters
【配置类】
import com.artisan.base.componentscan.service.ArtisanService; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.FilterType; import org.springframework.stereotype.Controller; @Configuration @ComponentScan( basePackages = {"com.artisan.base.componentscan"}, excludeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Controller.class}), @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,value = {ArtisanService.class}) }) public class CSMainConfig { }
重点配置
@ComponentScan( basePackages = {"com.artisan.base.componentscan"}, excludeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Controller.class}), @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,value = {ArtisanService.class}) })
excludeFilters(排除@Controller注解的,和ArtisanService的)
【测试结果】
includeFilters
@Configuration @ComponentScan( basePackages = {"com.artisan.base.componentscan"}, includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, value = {Controller.class})}, useDefaultFilters = false)、 public class CSMainConfig { }
若使用包含的用法, 需要把useDefaultFilters属性设置为false(true表示扫描全部的)
【测试结果】
@ComponentScan.Filter type的类型
看下源码
public enum FilterType { /** * Filter candidates marked with a given annotation. * @see org.springframework.core.type.filter.AnnotationTypeFilter */ ANNOTATION, /** * Filter candidates assignable to a given type. * @see org.springframework.core.type.filter.AssignableTypeFilter */ ASSIGNABLE_TYPE, /** * Filter candidates matching a given AspectJ type pattern expression. * @see org.springframework.core.type.filter.AspectJTypeFilter */ ASPECTJ, /** * Filter candidates matching a given regex pattern. * @see org.springframework.core.type.filter.RegexPatternTypeFilter */ REGEX, /** Filter candidates using a given custom * {@link org.springframework.core.type.filter.TypeFilter} implementation. */ CUSTOM }
- ANNOTATION 注解形式 举个例子比如
@Compent @Controller @Service @Repository .......
- ASSIGNABLE_TYPE 指定类型的
FilterType.ASSIGNABLE_TYPE
举个例子
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,value = {ArtisanService.class})
- ASPECTJ 类型的 FilterType.ASPECTJ 不常用
- REGEX 正则表达式的 FilterType.REGEX 不常用
- CUSTOM 自定义类型
使用自定义过滤器CUSTOM
package com.artisan.base.componentscan.customFilterType; 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; import org.springframework.core.type.filter.TypeFilter; import java.io.IOException; public class ArtisanFilterType implements TypeFilter { @Overridez public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { //获取当前类的注解源信息 AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata(); //获取当前类的class的源信息 ClassMetadata classMetadata = metadataReader.getClassMetadata(); //获取当前类的资源信息 Resource resource = metadataReader.getResource(); System.out.println("类的路径:"+classMetadata.getClassName()); // 排除包含Artisan的Bean if(classMetadata.getClassName().contains("Artisan")) { return true; } return false; } }
【配置类】
@Configuration @ComponentScan(basePackages = {"com.artisan.base.componentscan"}, excludeFilters = {@ComponentScan.Filter(type = FilterType.CUSTOM, value = {ArtisanFilterType.class})}) public class CSMainConfig { }
【测试结果】
@Scope
public class Bean2 { public Bean2() { System.out.println("Bean2 Created"); } }
public class Bean3 { public Bean3() { System.out.println("Bean3 Created"); } }
【config】
package com.artisan.base.scope; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Scope; @Configuration public class Config { @Bean() public Bean2 bean2(){ return new Bean2(); } @Bean() @Scope(value = "prototype") public Bean3 bean3(){ return new Bean3(); } }
【测试bean的加载】
package com.artisan.base.scope; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class TestScope { public static void main(String[] args) { AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Config.class); System.out.println("========================"); // 每次调用都会实例化一个新的Bean3+ System.out.println(ac.getBean("bean3")); System.out.println(ac.getBean("bean3")); } }
【输出】
【结论】
- 在不指定@Scope的情况下,所有的bean都是单实例的bean,而且是饿汉加载 即 容器启动实例就创建好了
- 指定@Scope为 prototype 表示为原型bean,而且还是懒汉模式加载 , 即IOC容器启动的时候,并不会创建对象,而是 在第一次使用的时候才会创建 ,并且每次调用,都会实例化一个新的对象
@Lazy
Bean的懒加载@Lazy 主要针对单实例的bean 容器启动的时候,不创建对象,在第一次使用的时候才会创建该对象 ,后续调用不会新建对象,而是从单例池中获取缓存的bean。
继续使用上面的例子 ,给Bean2 加上Lazy注解
@Bean() @Lazy public Bean2 bean2(){ return new Bean2(); }
【测试】
import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class TestScope { public static void main(String[] args) { AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Config.class); System.out.println(ac.getBean("bean2")); System.out.println(ac.getBean("bean2")); } }
【结果】
@Conditional 条件判断
需求: 只有容器中有bean5 才装载bean6
value 是一个class数组, 需要实现Condition 接口 , 那我们也弄个呗
【自定义Condition 】
package com.artisan.base.condition; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.type.AnnotatedTypeMetadata; public class ArtisanCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { //判断容器中是否有Bean5 ,有的话 返回true if(context.getBeanFactory().containsBean("bean5")) { return true; } return false; } }
【config 】
package com.artisan.base.condition; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; @Configuration public class CDConfig { @Bean public Bean5 bean5(){ return new Bean5(); } // Conditional的条件返回true,才装载Bean6 @Bean @Conditional(value = ArtisanCondition.class) public Bean6 bean6(){ return new Bean6(); } }
【beans】
public class Bean5 { public Bean5() { System.out.println("Bean5 Created"); } }
public class Bean6 { public Bean6() { System.out.println("Bean6 Created"); } }
【正常情况】
package com.artisan.base.condition; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class CDTest { public static void main(String[] args) { AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(CDConfig.class); for (String beanDefinitionName : ac.getBeanDefinitionNames()) { System.out.println(beanDefinitionName); } } }
我们看到了 CDConfig中 bean5 上标注了@Bean
现在我们把Bean5的@Bean去掉