一、组件注册

1.1-spring注解驱动开发

spring注解开发 - 图1

1.3-组件注册 Configuration、Bean、ComponentScan(s)、TypeFilter

以前添加组件的方式
image.pngimage.png

现在添加的方式:使用配置类
image.pngimage.png

项目目录

image.png

2. @ComponentScan()

  1. <!--包扫描、只要标注了@Controller、@Service、@Repository、@Component,都会被自动扫描加入容器中-->
  2. <context:component-scan base-package="top.p3wj"></context:component-scan>

现在写法
image.png
includeFilters内容是[]Filters,而Filters的内容是type和 []clazz
image.png
总之就是设置排除路径的

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;
    }
}

image.png
比如上面:MyTypeFilter定义了扫描规则:
如果组件名字里包含 er,才返回true,即可以添加到组件。如果不包含,返回false,即不能添加进去。

看上面的运行结果就知道了:扫描了model包下的Person,annotation包下的MyTypeFilter和XmlBeanDemo,但是因为只有有myTypeFilter里包含er,所以只有他自己被添加到了容器。
而person,和mainConfig和扫描无关,是通过@Configuration添加进来的。(只要有这个注解,就会把对应的组件加进去)

@Scope, 设置单例还是多例模式。

以前:
image.pngimage.png

* 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创建一个实例

现在:

image.png
单例:ioc容器启动会创建对象,放到ioc容器中,以后每次获取就是直接从容器(map.get())中拿
多例:ioc容器启动并不会去调用方法创建对象放在容器中。每次获取的时候才会调用方法创建对象。

@Lazy懒加载

单实例bean,默认在容器启动的时候创建对象
懒加载:容器启动不创建对象,第一次使用(获取)Bean创建对象,并初始化
image.png
image.png
image.png
如果没有@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;
    }
}

image.png
他的value是个Condition类型的类。所以必须传入一个Condition类型的类。

Condition:

image.png
条件上下文:
当MainConfig里的@Bean被执行到,即ioc加载person到容器里的时候,这个条件才会被运行,此时整个程序要做什么呢?此时要判断条件是否满足,但是这个条件可能很复杂,可能跟运行环境有关,可能跟ioc容器的创建工厂有关,所以他的权力一定是很大的。如同上面的那样,他有权利拿到环境,拿到bean工厂的类加载器等等。

还可以设置运行参数,改win系统为linux系统
image.png

这个注解也可放到类上,那么如果条件不满足,类里面所有的方法都不会被执行

@Import也是一个添加组件的方法,传入一个Class做value。 会自动把这个类添加到容器里。组件名是这个类的全类名

image.png
为什么已经有了那么多的添加组件的方法,还要有这个呢?
如果我们只想快速添加一个无参构造方法的类到容器里,用这个就是一个非常好的选择。

import还可以导入一个 ImportSelector,是啥不讲了,就是一个可以批量导入组建的东西。
image.png
还可以导入ImportBeanDefinitionRegistrar、还有beanFactory都不看了,遇到的时候再说。

1.7.2-组件注册 @ImportBeanDefinationRegister:手动注册Bean

注意,import方式注入的名称为全类名
spring注解开发 - 图21
spring注解开发 - 图22

1.8-组件注册 @FactoryBean

spring注解开发 - 图23

通过此方法把对象放到容器中
spring注解开发 - 图24

spring注解开发 - 图25

结果:
spring注解开发 - 图26
工厂获取的是调用getObject创建的对象

@Override
public boolean isSingleton() {
    return false;
}

isSingleton是false情况下是多实例,每一次获取都调用getObject
1.
spring注解开发 - 图27
2.
spring注解开发 - 图28

Reason:
spring注解开发 - 图29

使用Spring提供的FactoryBean(工厂Bean)
*              1)  默认获取到到是工厂bean调用getObject创建的对象
*              2)  要获取工厂Bean本身,我们需要给id前面加一个&
*                  &colorFactoryBean

生命周期

三种办法

第一种:用@Bean的initMethod属性

image.png
image.pngimage.png
顺序如下:先构造器->初始化 -> 容器创建完毕 -> 最后销毁
image.png

第二种:实现初始化接口InitializingBean和销毁接口DispaosableBean

image.pngimage.png

第三种:@PostConstruct 、@PreDestroy

image.png
image.png

第四种:BeanPostProcessor

image.png
image.pngimage.png

执行顺序

说明:初始化就是在构造器执行之后的 initMethod,afterPropertiesSet,postProcessBeforeInitialization这些。
image.png

@Value给属性赋值,两种方法

image.png
image.pngimage.png

@Autowired自动注入

@Autowired、@Qualifier

使用Autowired可以自动注入。可以配合使用@Qualifier(),完成通过id注入
还有个Autowired(required = false):这时候不注入也不报错

@Primary

还可以使用@Primary,指定首选注入的bean,这样当使用@Autowired注入的时候,可以首先注入这个bean
image.png

@Resource、@Inject

image.png

标注位置:方法上、形参上等等

image.pngimage.png

标注位置2:构造器上,以及默认可省的情况

容器初始化组件的时候,会默认调用组件类的无参构造方法。
image.png
比如这里,就会调用car的默认构造方法。
因为car的name已经默认赋值,所以每个car实例的name都=“benz”,这和构造器无关。

如果没有无参构造方法,且只有一个有参构造方法,就会调用其有参构造方法,并且默认Autowired。 下面两个的效果相同(前提是Boss上面必须@Component这种能够自己变成组件的注解)
image.pngimage.png

使用@Bean注册,也可以省略@Autowired(只要是注册组件,就可以省略)
下面两个效果相等
image.pngimage.png

自定义组件想使用spring容器的一些底层组件:实现 xxxAware

Aware接口本身什么也没有
image.png
image.png
这些xxxAware接口的作用(以方法回调的形式完成赋值):让实现类可以实现里面的方法,以完成组件注入。
举个例子: Red类想要使用spring的组件 ApplicationContext和BeanFactory。
那么就让他实现这两个xxAware接口,通过实现他们的方法拿到相应的组件,并且赋给自己的属性即可。
image.png

看一个好玩的组件:字符串解析器

image.pngimage.png

原理:

每一个Aware后面都有一个AwareProcessor来进行处理。

@Profile环境搭建

这个可以设置不同的环境,比如:测试环境、开发环境、运行环境
常见的使用场景:配置不同的数据源。测试环境是哪个表,开始是哪个,运行是哪个
image.pngimage.png
注意,上面需要加@Profile()注解来确定自己的所属环境,如果不标,默认适用于所有环境。
另外,上面给属性或者参数赋值共用了3种办法:
1.适用@{$()}从配置文件拿值。 2.在形参上使用。 3.实现EmbeddedValueResolverAware接口,用他的解析器完成赋值。

激活的两种方式:命令行和代码形式

命令行:
image.png
代码形式:
image.png
这里的MainConfigOfProfile.class相当于是一个配置类。类似上面的MainConfig.class。里面是上面的数据源的配置