文章目录
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去掉