- 一、组件注册
一、组件注册
1.1-spring注解驱动开发
1.3-组件注册 Configuration、Bean、ComponentScan(s)、TypeFilter
以前添加组件的方式
现在添加的方式:使用配置类
项目目录
2. @ComponentScan()
<!--包扫描、只要标注了@Controller、@Service、@Repository、@Component,都会被自动扫描加入容器中-->
<context:component-scan base-package="top.p3wj"></context:component-scan>
现在写法
includeFilters内容是[]Filters,而Filters的内容是type和 []clazz
总之就是设置排除路径的
FilterType.ASSIGNABLE_TYPE 按照给定的类型
FilterType.ASPECTJ 使用ASPECTJ表达式(不太常用)
FilterType.REGEX 使用正则表达式
FilterType.CUSTOM 自定义
实现TypeFilter
@Configuration
//这里设置的是按注解排除,然后class等于排除的哪个注解类。还可以按照Aspectj等各种排除
@ComponentScan(value = "annotation", includeFilters = {@ComponentScan.Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class})})
public class MainConfig {
@Bean("person")
@Lazy
@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
public Person addPerson() {
System.out.println("person添加到容器里...");
return new Person("李四", 25);
}
}
public class MyTypeFilter implements TypeFilter {
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
/**
* @param metadataReader 读取到当前正在扫描的类的信息
* @param metadataReaderFactory 获取到其他类的信息
*/
//获取当前类注解信息
AnnotationMetadata am = metadataReader.getAnnotationMetadata();
//获取当前正在扫描的类的类信息
ClassMetadata cm = metadataReader.getClassMetadata();
//获取当前类资源(类路径、文件名等)
Resource res = metadataReader.getResource();
String cn = cm.getClassName();
System.out.println("--->"+cn);
System.out.println("<-- fileName is :"+res.getFilename());
if(cn.contains("er")) {
return true;
}
return false;
}
}
比如上面:MyTypeFilter定义了扫描规则:
如果组件名字里包含 er,才返回true,即可以添加到组件。如果不包含,返回false,即不能添加进去。
看上面的运行结果就知道了:扫描了model包下的Person,annotation包下的MyTypeFilter和XmlBeanDemo,但是因为只有有myTypeFilter里包含er,所以只有他自己被添加到了容器。
而person,和mainConfig和扫描无关,是通过@Configuration添加进来的。(只要有这个注解,就会把对应的组件加进去)
@Scope, 设置单例还是多例模式。
以前:
* ConfigurableBeanFactory#SCOPE_PROTOTYPE prototype 多实例
* ConfigurableBeanFactory#SCOPE_SINGLETON singleton 单实例(默认值)
* org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST request 同一次请求创建一个实例
* org.springframework.web.context.WebApplicationContext#SCOPE_SESSION session 同一个session创建一个实例
现在:
单例:ioc容器启动会创建对象,放到ioc容器中,以后每次获取就是直接从容器(map.get())中拿
多例:ioc容器启动并不会去调用方法创建对象放在容器中。每次获取的时候才会调用方法创建对象。
@Lazy懒加载
单实例bean,默认在容器启动的时候创建对象
懒加载:容器启动不创建对象,第一次使用(获取)Bean创建对象,并初始化
如果没有@Lazy,上面输出就没有第一句。只要用到person这个组件的时候,才会有这句
@Conditional
要求:如果系统环境linux,加入名为linuxPerson的Person组件,如果是window,加入名为winPerson的Person组件
@Configuration
@ComponentScan(value = "annotation")
public class MainConfig {
@Bean("winPerson")
@Conditional(WinCondition.class)
public Person person01() {
return new Person("肖钢玉",39);
}
@Bean("LinuxPerson")
@Conditional(LinuxCondition.class)
public Person person02() {
return new Person("季昌明",60);
}
}
public class LinuxCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
//context:条件上下文。 可以获取产生ioc容器的bean工厂、类加载器、环境、可以定义bean的注册registry
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
ClassLoader classLoader = context.getClassLoader();
Environment environment = context.getEnvironment();
BeanDefinitionRegistry registry = context.getRegistry();
String osName = environment.getProperty("os.name");
if(osName.contains("Linux")) {
return true;
}
return false;
}
}
public class WinCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment();
String osName = environment.getProperty("os.name");
if(osName.contains("Win")) {
return true;
}
return false;
}
}
他的value是个Condition类型的类。所以必须传入一个Condition类型的类。
Condition:
条件上下文:
当MainConfig里的@Bean被执行到,即ioc加载person到容器里的时候,这个条件才会被运行,此时整个程序要做什么呢?此时要判断条件是否满足,但是这个条件可能很复杂,可能跟运行环境有关,可能跟ioc容器的创建工厂有关,所以他的权力一定是很大的。如同上面的那样,他有权利拿到环境,拿到bean工厂的类加载器等等。
还可以设置运行参数,改win系统为linux系统
这个注解也可放到类上,那么如果条件不满足,类里面所有的方法都不会被执行
@Import也是一个添加组件的方法,传入一个Class做value。 会自动把这个类添加到容器里。组件名是这个类的全类名
为什么已经有了那么多的添加组件的方法,还要有这个呢?
如果我们只想快速添加一个无参构造方法的类到容器里,用这个就是一个非常好的选择。
import还可以导入一个 ImportSelector,是啥不讲了,就是一个可以批量导入组建的东西。
还可以导入ImportBeanDefinitionRegistrar、还有beanFactory都不看了,遇到的时候再说。
1.7.2-组件注册 @ImportBeanDefinationRegister:手动注册Bean
注意,import方式注入的名称为全类名
1.8-组件注册 @FactoryBean
通过此方法把对象放到容器中
结果:工厂获取的是调用getObject创建的对象
@Override
public boolean isSingleton() {
return false;
}
isSingleton是false情况下是多实例,每一次获取都调用getObject
1.
2.
Reason:
使用Spring提供的FactoryBean(工厂Bean)
* 1) 默认获取到到是工厂bean调用getObject创建的对象
* 2) 要获取工厂Bean本身,我们需要给id前面加一个&
* &colorFactoryBean
生命周期
第一种:用@Bean的initMethod属性
顺序如下:先构造器->初始化 -> 容器创建完毕 -> 最后销毁
第二种:实现初始化接口InitializingBean和销毁接口DispaosableBean
第三种:@PostConstruct 、@PreDestroy
第四种:BeanPostProcessor
执行顺序
说明:初始化就是在构造器执行之后的 initMethod,afterPropertiesSet,postProcessBeforeInitialization这些。
@Value给属性赋值,两种方法
@Autowired自动注入
@Autowired、@Qualifier
使用Autowired可以自动注入。可以配合使用@Qualifier(),完成通过id注入
还有个Autowired(required = false):这时候不注入也不报错
@Primary
还可以使用@Primary,指定首选注入的bean,这样当使用@Autowired注入的时候,可以首先注入这个bean
@Resource、@Inject
标注位置:方法上、形参上等等
标注位置2:构造器上,以及默认可省的情况
容器初始化组件的时候,会默认调用组件类的无参构造方法。
比如这里,就会调用car的默认构造方法。
因为car的name已经默认赋值,所以每个car实例的name都=“benz”,这和构造器无关。
如果没有无参构造方法,且只有一个有参构造方法,就会调用其有参构造方法,并且默认Autowired。 下面两个的效果相同(前提是Boss上面必须@Component这种能够自己变成组件的注解)
使用@Bean注册,也可以省略@Autowired(只要是注册组件,就可以省略)
下面两个效果相等
自定义组件想使用spring容器的一些底层组件:实现 xxxAware
Aware接口本身什么也没有
这些xxxAware接口的作用(以方法回调的形式完成赋值):让实现类可以实现里面的方法,以完成组件注入。
举个例子: Red类想要使用spring的组件 ApplicationContext和BeanFactory。
那么就让他实现这两个xxAware接口,通过实现他们的方法拿到相应的组件,并且赋给自己的属性即可。
看一个好玩的组件:字符串解析器
原理:
每一个Aware后面都有一个AwareProcessor来进行处理。
@Profile环境搭建
这个可以设置不同的环境,比如:测试环境、开发环境、运行环境
常见的使用场景:配置不同的数据源。测试环境是哪个表,开始是哪个,运行是哪个
注意,上面需要加@Profile()注解来确定自己的所属环境,如果不标,默认适用于所有环境。
另外,上面给属性或者参数赋值共用了3种办法:
1.适用@{$()}从配置文件拿值。 2.在形参上使用。 3.实现EmbeddedValueResolverAware接口,用他的解析器完成赋值。
激活的两种方式:命令行和代码形式
命令行:
代码形式:
这里的MainConfigOfProfile.class相当于是一个配置类。类似上面的MainConfig.class。里面是上面的数据源的配置