写在前面

写在前面:最近在编译最新版spring源码的时候,踩了写小坑。先来看一下最新的官方文档:https://github.com/spring-projects/spring-framework 在build from source里面声明了最新的编译过程:https://github.com/spring-projects/spring-framework/wiki/Build-from-Source 由于最近spring做了一些升级,想要在本地构建spring源码,需要使用jdk17。 此外,最近gradle的语法也因为版本的原因,改动很大。想要在spring源码里面对gradle进行一些定制化配置的时候,可以参照gradle官网。 https://gradle.org/releases/

1.注解

1.1总览

  • @Bean 容器中注册组件
  • @Primary 同类组件如果有多个,标注主组件
  • @DependsOn 组件之间声明依赖关系
  • @Lazy 组件懒加载(最后使用的时候才会去创建)
  • @Scope 声明组件的作用范围
  • @Configuration 声明这是一个配置类,替换xml
  • @Component @Controller @Service @Repository
  • @Indexed 加速注解,所有标注了@Indexed的组件,直接会启动快速加载
  • @Order 数字越小,优先级越高,越先工作
  • @ComponentScan 包扫描
  • @Conditional 条件注入
  • @Import 导入第三方jar包中的组件,或者定制批量导入组件逻辑
  • @ImportResource 导入以前的xml配置文件,让其生效
  • @Profile 基于多环境激活
  • @PropertySource 外部properties配置文件和javaBean进行绑定,结合ConfigurationProperties
  • @PropertySources @PropertySource的组合注解
  • @Autowired 自动装配
  • @Qualifier 精准指定
  • @Resource jsr250规范的jdk自带注解
  • @Value 取值,计算机环境变量,jvm系统 xxx
  • @Lookup 单例组件依赖非单例组件,非单例组件获取需要使用方法

    1.2.案例

    com.yhd.annotation.AnnoMainTest

2.组件与SPI扩展点机制

2.1总览

  1. 基础接口
    Resource+ResourceLoader 将来自各种不同渠道的配置文件等进行一层抽象封装,让开发人员不必关注于底层的细节实现,通过资源加载器加载资源。
    BeanFactory ioc容器顶层接口 ✅
    BeanDefinition 从resource解析出bean的定义信息 ✅
    BeanDefinitionReader bean定义信息读取器 ,从resource 读取解析 BeanDefinition
    BeanDefinitionRegistry bean定义信息的注册中心 解析出来的BeanDefinition信息会被注册到这里
    ApplicationContext ioc核心接口 ✅
    Aware 实现xxxAware接口是为了能够获取到xxx相关的一些属性 ✅
    BeanNameAware
    BeanFactoryAware
    ApplicationEventPublisherAware
    ApplicationContextAware
    ApplicationStartupAware
    BeanClassLoaderAware
    ImportAware
    EnvironmentAware
  2. 生命周期-后置处理器
    BeanFactoryPostProcessor ✅
    BeanDefinitionRegistryPostProcessor ✅
    InitializingBean ✅
    DisposableBean ✅
    BeanPostProcessor ✅
    SmartInitializingSingleton ✅
  3. 监听器
    ApplicationListener ✅

    2.2案例

com.yhd.annotation.AnnoMainTest

补充:spring源码地址:https://gitee.com/yin_huidong/spring.git

�此源码main分支为手动翻译好的中文注释版spring源码。

一,组件注册

1.Configuration&Bean

@Configuration注解标识的类标识这是一个Spring的配置类。

@Bean注解:给容器中注册一个bean,id默认是方法名作为id。

  • value:指定id名
  • initMethod:指定初始化方法
  • destoryMethod:指定销毁方法

1.1 SpringConfig

  1. @Configuration
  2. public class SpringConfig {
  3. @Bean(value = "person",initMethod = "init",destroyMethod = "destroy")
  4. public Person person(){
  5. return new Person(1,"二十");
  6. }
  7. }

1.2 Person

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {

    private Integer id;

    private String name ;

    public void init(){
        System.out.println("init()...");
    }

    public void destroy(){
        System.out.println("destroy()...");
    }
}

1.3 Test

public class TestA {

    ApplicationContext ioc =
            new AnnotationConfigApplicationContext(SpringConfig.class);
    @Test
    public void testBean(){
        Person person = ioc.getBean("person", Person.class);
        System.out.println("person = " + person);
    }
}

2.RunWith&ContextConfiguration

@RunWith:Spring整合Junit4。

@ContextConfiguration:指定配置类。

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {SpringConfig.class})
public class TestA {

    @Autowired
    private Person person;
    @Test
    public void testBean(){
        System.out.println("person = " + person);
    }
}

3.ComponentScan

@ComponentScan(“com.yhd”) 组件扫描

  • value:指定扫描的包
  • includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Controller.class)}

只包含哪些包(需要指明:useDefaultFilters = false)

  • type

    type:FilterType.ANNOTATION:按照注解过滤 type=FilterType.ASSIGNABLE_TYPE:按照类型过滤 type=ASPECTJ,切面 type=REGEX,正则 type=CUSTOM,定制(需要实现TypeFilter接口)

  • excludeFilters:指明排除哪些包不被扫描

3.1 SpringConfig

@Configuration
@ComponentScan(value = "com.yhd",
        includeFilters = {
                @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Controller.class),
                @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE)
        }, excludeFilters = {}, useDefaultFilters = true)
public class SpringConfig {


    @Bean(value = "person", initMethod = "init", destroyMethod = "destroy")
    public Person person() {
        return new Person(1, "二十");
    }
}

3.2 Test

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {SpringConfig.class})
public class TestA {

    @Autowired
    private ApplicationContext ioc;

    @Test
    public void test(){
        //查看容器中bean的名字
        String[] names = ioc.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println("name = " + name);
        }
    }
}

4.Scope

作用:设置组件作用域。

@Scope 默认是单例的
点击进入该注解,在此点击进入文档注释中的ConfigurableBeanFactory
可以看到该注解的几个取值:

  • singleton 单例
  • prototype 多例
  • request 同一次请求
  • session 同一个session作用域

在scope=singleton时,对象在容器已创建立即加入容器。
在scope=prototype时,对象每次调用的时候都会添加到容器。

4.1 SpringConfig

@Configuration
public class SpringConfig {

    @Bean(value = "person", initMethod = "init", destroyMethod = "destroy")
    @Scope("prototype")
    public Person person() {
        return new Person(1, "二十");
    }
}

4.2 Test

@Test
public void test(){
    Person p1 = ioc.getBean("person", Person.class);
    Person p2 = ioc.getBean("person", Person.class);
    System.out.println(p1==p2?"true":"false");//false
}

5.Lazy

@Lazy 懒加载
单实例bean,默认在容器创建时候添加对象。
懒加载:容器启动不创建对象,第一次获取/使用时在创建对象并初始化。

5.1 SpringConfig

@Configuration
public class SpringConfig {

    @Lazy
    @Bean(value = "person", initMethod = "init", destroyMethod = "destroy")
    public Person person() {
        System.out.println("person已经加载到容器中!");
        return new Person(1, "二十");
    }
}

5.2 Test

@Test
public void test(){
    Person person = ioc.getBean("person", Person.class);
}

对比加上这个注解前后,容器中单实例bean的变化:

image.png
image.png

6.Conditional

@Conditional
点击进入
Class<? extends Condition>[] value();
发现这个注解里面的value属性需要传入一个Condition数组,
点击进入Condition
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
因此,可以实现Condition接口,来使用该注解

当该注解加在方法上,标识满足条件时,该方法会执行
当该注解加在类上时,表示满足条件时,该类里面的所有方法才会执行,否则一个都不执行。

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {

    /**
     * All {@link Condition}s that must {@linkplain Condition#matches match}
     * in order for the component to be registered.
     */
    Class<? extends Condition>[] value();

}
@FunctionalInterface
public interface Condition {

    /**
     * Determine if the condition matches.
     * @param context the condition context
     * @param metadata metadata of the {@link org.springframework.core.type.AnnotationMetadata class}
     * or {@link org.springframework.core.type.MethodMetadata method} being checked
     * @return {@code true} if the condition matches and the component can be registered,
     * or {@code false} to veto the annotated component's registration
     */
    boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);

}

6.1 案例:根据当前系统环境将对应的bean加入到容器

6.1.1 SpringConfig

@Configuration
public class SpringConfig {


    @Bean("person")
    @Conditional({Mac.class})
    public Person person() {
        System.out.println("person已经加载到容器中!");
        return new Person(1, "二十");
    }
    @Bean("person2")
    @Conditional({Linux.class})
    public Person person2() {
        System.out.println("person已经加载到容器中!");
        return new Person(1, "二十");
    }
}

6.1.2 条件类

public class Mac implements Condition {

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {

        return context.getEnvironment().getProperty("os.name").contains("Mac");
    }
}
public class Linux implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return context.getEnvironment().getProperty("os.name").contains("linux");
    }
}

6.1.3 Test

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {SpringConfig.class})
public class TestA {

    @Autowired
    private ApplicationContext ioc;

    @Test
    public void test(){

        Person person = ioc.getBean("person", Person.class);
        Person person2 = ioc.getBean("person2", Person.class);
        System.out.println(person);
        System.out.println(person2);
    }
}

7.Import

用来快速导入一个bean,默认类名是该类的全限定类名,单例。

7.1 SpringConfig

@Configuration
@Import({Dog.class})
public class SpringConfig {


    @Bean("person")
    @Conditional({Mac.class})
    public Person person() {
        System.out.println("person已经加载到容器中!");
        return new Person(1, "二十");
    }
    @Bean("person2")
    @Conditional({Linux.class})
    public Person person2() {
        System.out.println("person已经加载到容器中!");
        return new Person(1, "二十");
    }
}

7.2 Test

@Test
public void test(){

    Dog dog = ioc.getBean(Dog.class);

    assert dog!=null;
    dog.setId(1);
    dog.setName("asdfg");
    System.out.println("dog = " + dog);
}

8.ImportSelector

public @interface Import {

    /**
     * {@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar}
     * or regular component classes to import.
     */
    Class<?>[] value();

}

点击查看@Import注解可以发现:

里面还可以传入{@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar}

importSelector其实就是一个接口,我们需要通过实现它来传入impot注解。

8.1 MyImportSelector

public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"com.yhd.pojo.Person","com.yhd.pojo.Dog"};
    }
}

8.2 SpringConfig

@Configuration
@Import({MyImportSelector.class})
public class SpringConfig {

}

8.3 Test

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {SpringConfig.class})
public class TestA {

    @Autowired
    private ApplicationContext ioc;

    @Test
    public void test(){
        String[] names = ioc.getBeanDefinitionNames();
        for (String name : names) System.out.println(name);
    }
}

9.ImportBeanDefinitionRegistrar

public interface ImportBeanDefinitionRegistrar {


    public void registerBeanDefinitions(
            AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);

}

9.1 MyImportBeanDefinitionRegistrar

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        registry.registerBeanDefinition("dog", new RootBeanDefinition("com.yhd.pojo.Dog"));
    }
}

9.2 MyImportBeanDefinitionRegistrar

@Configuration
@Import({MyImportBeanDefinitionRegistrar.class})
public class SpringConfig {

}

9.3 Test

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {SpringConfig.class})
public class TestA {

    @Autowired
    private ApplicationContext ioc;

    @Test
    public void test(){
        String[] names = ioc.getBeanDefinitionNames();
        for (String name : names) System.out.println(name);
    }
}

10.FactoryBean

spring的工厂模式造Bean

  1. 如果传入id获取到的是工厂造的bean
  2. 如果传入的是&id获取到的是工厂本身

10.1 PersonFactory

public class PersonFactory implements FactoryBean<Person> {
    @Override
    public Person getObject() throws Exception {
        return new Person(1,"FactoryBean");
    }

    @Override
    public Class<?> getObjectType() {
        return Person.class;
    }

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

10.2 SpringConfig

@Configuration
public class SpringConfig {

    @Bean
    public PersonFactory personFactory(){
        return new PersonFactory();
    }
}

10.3 Test

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {SpringConfig.class})
public class TestA {

    @Autowired
    private ApplicationContext ioc;

    @Test
    public void test(){
        PersonFactory personFactory = (PersonFactory) ioc.getBean("&personFactory");
        System.out.println("personFactory = " + personFactory);

        Person person = (Person) ioc.getBean("personFactory");
        System.out.println("person = " + person);

        /*personFactory = com.yhd.factory.PersonFactory@6e20b53a
        person = Person(id=1, name=FactoryBean)*/
    }
}

10.4 原理

public interface BeanFactory {

    //more能获取到工厂bean的方法就是在id前加上前缀&
    String FACTORY_BEAN_PREFIX = "&";

11.LookUp

如果有一个类C,需要用到类B,如果使用@Autowired注解注入B,那么B每次调用都是同一个对象,即使B不是单例的,现在我希望每次调用B都是不一样的,那么实现方案有2个:

11.1 每次从容器中获取B

@Component
@Scope(scopeName= ConfigurableBeanFactory.SCOPE_PROTOTYPE) //原型 也就是非单例
public class B {
    public  void sayHi(){
        System.out.println("hi");
    }
}
@Component
public  class C implements ApplicationContextAware {
    private ApplicationContext applicationContext;
    public void hello(){
        B b = (B)applicationContext.getBean("b");
        b.sayHi();
    }


    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext=applicationContext;
    }
}

11.2 使用@lookup注解

@Component
@Scope(scopeName= ConfigurableBeanFactory.SCOPE_PROTOTYPE) //原型 也就是非单例
public class B {
    public  void sayHi(){
        System.out.println("hi");
    }
}

@Component
public abstract class C {

    public void hello(){
        B b = getB();
        b.sayHi();
    }

    @Lookup
    public abstract B getB(); //一般都是抽象方法
}

二,生命周期

1.@Bean指定初始化和销毁方法

bean的生命周期:创建,初始化,使用,销毁。

容器管理bean的生命周期:我们可以自定义初始化和销毁方法,容器在bean进行到当前生命周期的时候,来调用我们自定义的初始化和销毁方法。

单例模式:先是执行对象的无参构造,赋值后,执行初始化方法,在创建容器的时候加入对象,在容器关闭时,执行销毁方法。

多例模式:先创建容器,每次调用对象时,调用无参构造方法,赋值后,执行初始化方法。对象的销毁由java垃圾回收机制回收。

1.1 单例

@Data
@AllArgsConstructor
public class Person {

    private Integer id;

    private String name ;

    public void init(){
        System.out.println("init()...");
    }

    public void destroy(){
        System.out.println("destroy()...");
    }

    public Person(){
        System.out.println("constructor()....");
    }
}
@Configuration
public class SpringConfig {

    @Bean(value = "person",initMethod = "init",destroyMethod = "destroy")
    public Person person(){
        return new Person();
    }
}
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {SpringConfig.class})
public class TestA {

    @Autowired
    private ApplicationContext ioc;
    //constructor
    @Test
    public void test(){
        Person person = ioc.getBean("person", Person.class);
        System.out.println("person = " + person);
    }
}

image.png

1.2 多例

@Configuration
public class SpringConfig {
    @Scope(value = "prototype")
    @Bean(value = "person",initMethod = "init",destroyMethod = "destroy")
    public Person person(){
        return new Person();
    }
}

image.png

2.实现接口完成对象的初始化和销毁

InitializingBean, DisposableBean

@Data
@AllArgsConstructor
public class Person implements InitializingBean, DisposableBean {

    private Integer id;

    private String name ;

    public Person(){
        System.out.println("constructor()....");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("destroy()....");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("afterPropertiesSet()....");
    }

}
@Configuration
public class SpringConfig {

    @Bean(value = "person")
    public Person person(){
        return new Person();
    }
}
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {SpringConfig.class})
public class TestA {

    @Autowired
    private ApplicationContext ioc;
    //constructor
    @Test
    public void test(){
        System.out.println("容器创建成功!");
        Person person = ioc.getBean("person", Person.class);
        System.out.println("person = " + person);
    }
}

image.png

3.BeanPostProcessor

bean的后置处理器,在bean的初始化前后做一些处理工作,需要加入到容器中。

public class MyBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("初始化前");
        return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("初始化后");
        return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
    }
}
@Configuration
@Import(MyBeanPostProcessor.class)
public class SpringConfig {

    @Bean(value = "person")
    public Person person(){
        return new Person();
    }
}

4.BeanFactoryPostProcessor

bean工厂的后置处理器,可以在spring解析完配置文件,创建对象之前,对bean的定义信息进行修改。

@Component
public class Computer {

   String name;

   public String getName() {
      return name;
   }

   public void setName(String name) {
      this.name = name;
   }
}
@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
   @Override
   public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
      System.out.println("=======BeanFactoryPostProcessor=========");
      BeanDefinition person = beanFactory.getBeanDefinition("computer");
      person.setScope(BeanDefinition.SCOPE_PROTOTYPE);
      System.out.println("=======BeanFactoryPostProcessor=========");
   }
}
/**
 * 默认Computer对象是单实例的,
 * 通过beanFactory的后置处理器处理之后,变成原型的
 * <p>
 * 执行时机:打断点
 */
private static void testBeanFactoryPostProcessor() {
   /*ioc容器创建的12个核心方法里面*/
   Computer computer = ioc.getBean(Computer.class);
   Computer computer2 = ioc.getBean(Computer.class);
   System.out.println(computer == computer2);
}

5.BeanDefinitionRegistryPostProcessor

他是bean工厂后置处理器的子类
插播一条广告:BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor之间的关系?
BeanDefinitionRegistryPostProcessor 是 BeanFactoryPostProcessor的子类和扩展
它里面 搞了一个新的方法 postProcessBeanDefinitionRegistry ,可以往容器中注册更多的bd信息。
扩展点:
①BeanFactoryPostProcessor 对bd信息进行修改
②postProcessBeanDefinitionRegistry 添加更多的bd信息

public class RegistryBean {
}
@Component
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {


   @Override
   public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
      /*这个是她的父类里面的方法 对 bd信息进行修改*/
   }

   @Override
   public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
      /*这个是给他本身的方法,可以添加额外的 bd 信息*/
      GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
      beanDefinition.setBeanClass(RegistryBean.class);
      //beanDefinition.setScope();
      registry.registerBeanDefinition("registryBean",beanDefinition);
   }
}
private static void testBeanDefinitionRegistryPostProcessor() {
   RegistryBean registryBean = ioc.getBean("registryBean", RegistryBean.class);
   System.out.println("registryBean = " + registryBean);
}

6.SmartInitializingSingleton

在所有的单实例bean 通过getBean方法完成初始化之后,就会去查找这个类型的SmartInitializingSingleton 的 组件 ,执行里面的 方法.

@Component
public class MySmartInitializingSingleton implements SmartInitializingSingleton {
   @Override
   public void afterSingletonsInstantiated() {
      System.out.println("断点调试。。。。");
   }
}

7.Lifecycle && SmartLifecycle

容器创建完成之后的回调,相当于
传递的参数autoStartUpOnly是干嘛的?
表示只启动SmartLifeCycle生命周期对象,并且启动的对象autoStartUpOnly必须是true,
不会启动普通的生命周期对象,
false的时候,会启动全部的生命周期对象。

public class DemoLifeCycle implements Lifecycle {

   private boolean running =false;
   @Override
   public void start() {
      this.running=true;
      System.out.println("demo one start!");
   }

   @Override
   public void stop() {
      this.running=false;
      System.out.println("demo one stop!");
   }

   @Override
   public boolean isRunning() {
      return running;
   }
}
public class DemoSmartLifeCycle implements SmartLifecycle {

   private boolean running = false;
   @Override
   public void start() {
      this.running=true;
      System.out.println("demo two start!");
   }

   @Override
   public void stop() {
      this.running=false;
      System.out.println("demo two stop!");
   }

   @Override
   public boolean isRunning() {
      return running;
   }
}

三,属性赋值

1.Value

@Value注解可以给属性赋值,支持SPEL表达式,${},,基本数值。

@Data
@AllArgsConstructor
public class Person  {
    @Value("1")
    private Integer id;
    @Value("二十")
    private String name ;

    public Person(){
        System.out.println("constructor()....");
    }

}

2.PropertySource

@PropertySource加载外部属性文件

支持一次写多个,标注在配置类上。

person.name=二十
person.age=20
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person  {
    @Value("${person.age}")
    private Integer id;
    @Value("${person.name}")
    private String name ;
}
@Configuration
@PropertySource("classpath:person.properties")
public class SpringConfig {

    @Bean(value = "person")
    public Person person(){
        return new Person();
    }
}

四,自动装配

Spring利用依赖注入DI,完成对IOC容器中各个组件的依赖关系赋值。

1.@Autowired && @Qualifier && @Primary

  1. 默认优先按照类型去容器中找对应的组件,ioc.getBean(BookDao.class);
  2. 如果找到多个相同类型的组件,再讲属性的名称作为组件的id去容器中查找ioc.getBean("bookDao");
  3. @Qualifier("bookDao"),使用@Qualifier指定要装配的组件的id,而不是使用属性名
  4. 自动装配默认一定要将属性赋值好,没有就会报错.可以使用@Autowired(required=false)
  5. @Primary 让Spring进行自动装配的时候,默认使用首选bean,也可以继续使用@Qualifier指定需要装配的bean的id

2.@Resource && @Inject

spring还支持使用@Resource(jsr250)和@inject(jsr330)【java规范的注解】

@Resource:可以和@Autowired一样实现自动装配,默认按照组件名称进行装配.不支持@Primary和required=false

@Inject:需要导入javax.inject包,没有required=false

3.方法,构造器位置的自动注入

@Autowired:构造器,参数,方法,属性,都是从容器中获取参数组件的值。

  1. 标注在方法上:@Bean+方法参数,参数从容器中获取,默认不写@Autowired效果是一样的,都能自动装配
  2. 标在构造器上:如果组件只有一个构造器,这个有参构造器的@Autowired可以省略
  3. 放在参数位置

3.1 构造器注入

@Component
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person  {
    private Integer id;
    private String name ;
}
@Component
@Data
@NoArgsConstructor
public class Dog {

    private Person person;

    @Autowired
    //此处注解可以省略
    private Dog(Person person){
        this.person=person;
    }
}
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {SpringConfig.class})
public class TestA {

    @Autowired
    private ApplicationContext ioc;
    //constructor
    @Test
    public void test(){
        Dog dog = ioc.getBean("dog", Dog.class);
        System.out.println("dog = " + dog);
    }
}

3.2 set()注入

@Component
@Data
@NoArgsConstructor
public class Dog {

    private Person person;

    @Autowired
    //此处的注解不能省略
    public void setPerson(Person person){
        this.person=person;
    }
}

3.3 @Bean的方式

@Configuration
public class SpringConfig {

    @Bean(value = "person")
    public Person person() {
        return new Person();
    }

    @Bean(value = "dog")
    @Autowired
    //此处的注解可以省略不写
    public Dog dog(Person person) {
        return new Dog(person);
    }
}

4.Aware接口,自定义组件使用Spring底层的组件

自定义接口想要使用Spring底层的一些组件(ApplicationContext,BeanFactory,xxx)

自定义接口实现xxxAware,在创建对象的时候,会调用接口规定的方法注入相关组件,Aware吧Spring底层的一些接口注入到自定义的Bean

xxxAware:功能:使用xxxProcessor

ApplicationContextAware-->ApplicationContextAwareProcessor

@Component
public class Dog implements ApplicationContextAware, BeanFactoryAware {

    private ApplicationContext ioc;
    private BeanFactory beanFactory;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        //自此处打断点
        this.ioc=applicationContext;
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        //此处打断点
        this.beanFactory=beanFactory;
    }
}

5.@Profile根据环境装配

@Profile:指定组件在那个环境的情况下才能被注册到容器中,不指定,任何环境下都能注册这个组件

  1. 加了环境标识的bean,只有这个环境被激活的时候才能注册到容器中,默认是default环境
  2. 写在配置类上,只有是指定的环境的时候,整个配置类里面的所有配置才能开始生效
  3. 没有标识环境标识的bean在任何环境下都是加载的。
@Configuration
@PropertySource("classpath:jdbc.properties")
public class SpringConfig implements EmbeddedValueResolverAware {

    @Value("${jdbc.username}")
    private String name;

    private StringValueResolver resolver;

    private String driver;

    @Profile("dev")
    @Bean("dev")
    public DataSource dataSource1(@Value("${jdbc.password}") String pwd){
        DruidDataSource source = new DruidDataSource();
        source.setName(name);
        source.setPassword(pwd);
        source.setDriverClassName(driver);
        source.setUrl("jdbc:mysql:///test");
        return source;
    }
    @Profile("pro")
    @Bean("pro")
    public DataSource dataSource2(@Value("${jdbc.password}") String pwd){
        DruidDataSource source = new DruidDataSource();
        source.setName(name);
        source.setPassword(pwd);
        source.setDriverClassName(driver);
        source.setUrl("jdbc:mysql:///ssm");
        return source;
    }
    @Profile("test")
    @Bean("test")
    public DataSource dataSource3(@Value("${jdbc.password}") String pwd){
        DruidDataSource source = new DruidDataSource();
        source.setName(name);
        source.setPassword(pwd);
        source.setDriverClassName(driver);
        source.setUrl("jdbc:mysql:///zuoye");
        return source;
    }

    @Override
    public void setEmbeddedValueResolver(StringValueResolver resolver) {
        this.resolver=resolver;
        driver = resolver.resolveStringValue("${jdbc.driverClassName}");
    }
}

激活环境:

  1. 使用命令行动态参数:在虚拟机参数位置加载:

-Dspring.profiles.active=test

  1. 代码方式激活
@Test
public void test() {

    AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext();
    ioc.getEnvironment().setActiveProfiles("dev");
    ioc.register(SpringConfig4.class);
    ioc.refresh();
    String[] names = ioc.getBeanNamesForType(DataSource.class);
    for (String name : names) {
        System.out.println(name);
    }
}

五,事件驱动

1.ApplicationListener && ApplicationEvent

通过自定义不同类型的事件,使用不同的监听器监听不同类型的事件,做到jvm进程内的消息队列,事件驱动,解耦。

public class MyEvent extends ApplicationEvent {

   String message;

   public MyEvent(Object source) {
      super(source);
   }

   public MyEvent(Object source,String message) {
      super(source);
      this.message=message;
   }

   public void print(){
      System.out.println("发布了一个事件:"+message);
   }
}
public class MyListener implements ApplicationListener<MyEvent> {


   @Override
   public void onApplicationEvent(MyEvent event) {
      event.print();
   }
}
/**
 * 测试spring 的 ioc 容器的事件发布
 */
private static void testPublishEvent() {
   ApplicationContext ioc = new ClassPathXmlApplicationContext(CONFIG_LOCATION);
   ioc.publishEvent(new MyEvent("", "这是我自定义的一个事件"));
}

2.@EventListener

对上面写法的一个优化,更加简洁,开发量更少,懒人必备神器。

private static void testEventListener() {
   ioc.publishEvent(new ApplicationEvent("hello,spring") {
      @Override
      public Object getSource() {
         return super.getSource();
      }
   });
}
@Component
public class MyEventListener {

   @EventListener(classes = ApplicationEvent.class)
   public void listener(ApplicationEvent event){
      System.out.println("event = " + event);
   }
}