尚硅谷Spring注解开发学习笔记

本文深入探讨了Spring框架的注解驱动开发,包括容器、扩展原理和Web相关概念。讲解了@Profile、@Bean、@ComponentScan、@Import、@Scope、@Conditional等注解的使用,以及如何自定义Bean后置处理器、注册组件、处理生命周期方法。同时,介绍了AOP、事务管理的实现,以及如何利用Spring容器底层组件如ServletContainerInitializer和WebApplicationInitializer。此外,文章还涵盖了SpringMVC的配置和异步请求处理。

文章目录

前言

此笔记为尚硅谷Spring注解驱动教程(雷丰阳源码级讲解)学习笔记

1、课程安排

在这里插入图片描述

根据上面这张脑图,我把整个专栏分成了三个大的部分,分别是:容器、扩展原理以及Web。

1.1、容器

容器作为整个专栏的第一大部分,内容包括:

  • AnnotationConfigApplicationContext
  • 组件添加
  • 组件赋值
  • 组件注入
  • AOP
  • 声明式事务

1.2、扩展原理

扩展原理作为整个专栏的第二大部分,内容包括:

  • BeanFactoryPostProcessor
  • BeanDefinitionRegistryPostProcessor
  • ApplicationListener
  • Spring容器创建过程

1.3、Web

Web作为整个专栏的第三大部分,内容包括:

  • servlet3.0
  • 异步请求

这部分,其实就是SpringMVC,在这个部分中,我们会重点来说异步请求。

2、配置文件开发

2.1、导入Spring-context依赖包

<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.3.13</version>
</dependency>

2.2、编写Spring配置文件

在 resources 目录下创建 application.xml 文件

<?xml version="1.0" encoding="UTF-8"?>
<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 http://www.springframework.org/schema/beans/spring-beans.xsd">

</beans>

2.3、编写Person类

public class Person {
   
   
    
    private String name;
    private Integer age;
	
    // 省略构造方法,get/set方法,toString方法
}

2.4、在Spring配置文件中创建Bean

<bean id="person" class="com.xjhqre.pojo.Person">
    <property name="name" value="xjhqre"/>
    <property name="age" value="18"/>
</bean>

2.5、编写测试类

public class SpringTest {
   
   
    public static void main(String[] args) {
   
   
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
        Person person = (Person) applicationContext.getBean("person");
        System.out.println(person);
    }
}

测试结果:

Person{
   
   name='xjhqre', age=18}

3、注解开发简单案例

3.1、创建配置类

在方法上面加上@Bean注解后,Spring会以方法返回类型作为组件的类型,方法名作为组件的 id

当向@Bean中添加参数时,默认添加的第一个参数是value,强制给组件赋值id

// 配置类等同于xml配置文件
@Configuration // 告诉Spring这是一个配置类
public class MainConfig {
   
   

    // 给容器中注册一个Bean;类型为返回值的类型,id默认是用方法名作为id
    @Bean("person")
    public Person person01(){
   
   
        return new Person("lisi", 20);
    }
}

3.2、测试代码

public static void main(String[] args) {
   
   
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
    Person bean = applicationContext.getBean(Person.class);
    System.out.println(bean);
}

测试结果:

Person{
   
   name='lisi', age=20}

4、包扫描

只要标注了@Controller、@Service、@Repository、@Component的,都会被扫描加入到容器里

**注意:**配置类自身也会被扫描到容器中,如果存在多个配置类,则多个配置类里的所有bean对象都会被扫描进容器中

4.1、配置文件包扫描

<context:component-scan base-package="com.xjhqre"/>

4.2、注解扫描

在配置类上添加注解@ComponentScan

@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.xjhqre")
public class MainConfig {
   
   
    // ...
}

4.3、排除扫描

Filter[] excludeFilters() default {
   
   };

注解排除excludeFilters的返回类型为Filter[]

Filter 的排除类型 FilterType 有一下几种:

  • ANNOTATION:按注解排除,常用
  • ASSIGNABLE_TYPE:按类型排除,常用
  • ASPECTJ:按AspectJ 类型模式表达式排除,不常用
  • REGEX:按正则表达式排除
  • CUSTOM:自定义排除

下面以ANNOTATION按注解排除来演示:

@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.xjhqre", excludeFilters = {
   
   
        @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {
   
   Controller.class, Service.class})
})
public class MainConfig {
   
   
    // ...
}

4.4、指定扫描

扫描的时候只需要包含哪些组件,编写方式和excludeFilters一样

注意要关闭默认的扫描过滤器

@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.xjhqre", includeFilters = {
   
   
        @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {
   
   Controller.class, Service.class})
}, useDefaultFilters = false)
public class MainConfig {
   
   
    // ...
}

在 jdk8 之后可以写多个@ComponentScan,写法如下:

@Configuration // 告诉Spring这是一个配置类
@ComponentScans(
        value = {
   
   
                @ComponentScan(value = "com.xjhqre", includeFilters = {
   
   
                        @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {
   
   Controller.class, Service.class})
                }, useDefaultFilters = false)
        }
)
public class MainConfig {
   
   
    // ...
}

4.5、按类型扫描

@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.xjhqre", includeFilters = {
   
   
        @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {
   
   BookController.class})
}, useDefaultFilters = false)
public class MainConfig {
   
   
    // ...
}

4.6、自定义扫描

4.6.1、编写TypeFilter的实现类

public class MyTypeFilter implements TypeFilter {
   
   

    /**
     * @param metadataReader 读取到当前正在扫描类的信息
     * @param metadataReaderFactory 可以获取其他任何类的信息
     */
    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
   
   
        // 获取当前类注解的信息
        AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();

        // 获取当前正在扫描类的信息
        ClassMetadata classMetadata = metadataReader.getClassMetadata();

        // 获取当前类的资源信息(类的路径)
        Resource resource = metadataReader.getResource();

        String className = classMetadata.getClassName();
        System.out.println(className);
        return className.contains("er");
    }
}

4.6.2、修改配置类

@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.xjhqre", includeFilters = {
   
   
        @ComponentScan.Filter(type = FilterType.CUSTOM, classes = {
   
   MyTypeFilter.class})
}, useDefaultFilters = false)
public class MainConfig {
   
   
    // ...
}

5、@scope注解

5.1、默认单实例

5.1.1、编写配置类

@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.xjhqre")
public class MainConfig2 {
   
   

    @Bean("person")
    public Person person() {
   
   
        return new Person("张三", 25);
    }
}

5.1.2、编写测试

@Test
public void test2() {
   
   
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
    Person bean = applicationContext.getBean(Person.class);
    Person bean2 = applicationContext.getBean(Person.class);
    System.out.println(bean.hashCode());
    System.out.println(bean2.hashCode());
}

输出结果:

com.xjhqre.config.MainConfig2
com.xjhqre.config.MyTypeFilter
com.xjhqre.controller.BookController
com.xjhqre.pojo.Person
404214852
404214852

可以看到 bean 和 bean2 是同一个实例

5.2、@scope参数

ConfigurableBeanFactory.SCOPE_PROTOTYPE,

ConfigurableBeanFactory.SCOPE_SINGLETON,

org.springframework.web.context.WebApplicationContext.SCOPE_REQUEST,

org.springframework.web.context.WebApplicationContext.SCOPE_SESSION, value

后两个需要导入 Spring web 的jar包,且基本不用,只讨论前两个

ConfigurableBeanFactory中有两个常量:

// 单实例,默认值,在IOC容器启动时就会调用方法创建对象,以后每次获取直接从容器中拿(map.get())
String SCOPE_SINGLETON = "singleton";
// 多实例,在使用getBean()获取bean对象时才会创建
String SCOPE_PROTOTYPE = "prototype";

WebApplicationContext中的两个常量

// 同一次请求创建一个实例
String SCOPE_REQUEST = "request";
// 同一个session创建一个实例
String SCOPE_SESSION = "session";

5.3、测试多实例

5.3.1、修改配置类

@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.xjhqre")
public class MainConfig2 {
   
   
    
    @Bean("person")
    @Scope("prototype")
    public Person person() {
   
   
        return new Person("张三", 25);
    }

}

5.3.2、测试代码

@Test
public void test2() {
   
   
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
    Person bean = applicationContext.getBean(Person.class);
    Person bean2 = applicationContext.getBean(Person.class);
    System.out.println(bean.hashCode());
    System.out.println(bean2.hashCode());
}

测试结果:

com.xjhqre.config.MainConfig2
com.xjhqre.config.MyTypeFilter
com.xjhqre.controller.BookController
com.xjhqre.pojo.Person
961712517
1928931046

可以看到两个 bean 的哈希值不同

5.4、懒加载

针对单实例设置,因为单实例是在容器启动时创建对象

懒加载就是不让单实例在容器启动时创建,在第一次获取Bean时创建对象并初始化。

只需要在方法上添加注解@Lazy

@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.xjhqre")
public class MainConfig2 {
   
   

    @Bean("person")
    @Lazy
    public Person person() {
   
   
        return new Person("张三", 25);
    }

}

6、@Conditional注解(重点)

按照一定的条件进行判断,满足条件给容器中注册Bean

@Conditional源码

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

    // 传入一个Condition数组
    Class<? extends Condition>[] value();

}

Condition接口源码:

@FunctionalInterface
public interface Condition {
   
   

    /**
     * ConditionContext:判断条件能使用的上下文环境
     * AnnotatedTypeMetadata:注释信息
     */
    boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);

}

6.1、业务场景

如果是linux系统则注册linus,如果是windows系统则注册bill Gates

6.2、编写WindowsCondition

编写windows条件,需要实现接口Condition,重写matches方法

public class WindowsCondition implements Condition {
   
   
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
   
   
        // 判断是否是Windows系统
        // 1. 获取到IOC使用的beanFactory
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        // 2. 获取类加载器
        ClassLoader classLoader = context.getClassLoader();
        // 3. 获取运行环境
        Environment environment = context.getEnvironment();
        // 4. 获取到bean定义的注册类
        BeanDefinitionRegistry registry = context.getRegistry();

        // 容器中是否包含person,判断容器中的bean注册情况,也可以给容器中注册bean
        boolean person = registry.containsBeanDefinition("person");

        // 运行环境是否是Windows
        String property = environment.getProperty("os.name");
        assert property != null;
        return property.contains("Windows");
    }
}

6.3、编写LinuxCondition

public class LinuxCondition implements Condition {
   
   
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
   
   
        Environment environment = context.getEnvironment();
        String property = environment.getProperty("os.name");
        assert property != null;
        return property.contains("linux");
    }
}

6.4、编写配置类

@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.xjhqre")
public class MainConfig3 {
   
   

    @Bean("bill")
    @Conditional({
   
   WindowsCondition.class})
    public Person person() {
   
   
        return new Person("bill gates", 62);
    }

    @Bean("linus")
    @Conditional({
   
   LinuxCondition.class})
    public Person person2() {
   
   
        return new Person("linus", 48);
    }

}

6.5、编写测试

@Test
public void test3() {
   
   
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig3.class);
    Map<String, Person> beansOfType = applicationContext.getBeansOfType(Person.class);
    System.out.println(beansOfType);
}

测试结果:

{
   
   bill=Person{
   
   name='bill gates', age=62}}

当@Conditional配置在类上时,表示只有满足条件时,这个配置类配置的所有bean才会生效

7、组件注册方法

  1. 包扫描 + 组件标注注解(@Controller、@Service、@Repository、@Component),这种方式只能注册自己写的类
  2. @Bean,可以注册第三方包的类
  3. @Import:快速给容器中导入一个组件,id默认是全类名
    1. ImportSelector:一个接口,返回需要导入的组件的全类名数组
    2. ImportBeanDefinitionRegistrar:一个接口,手动注册bean到容器中
  4. 使用Spring提供的FactoryBean(工厂Bean)

7.1、@Import(重点)

7.1.1、编写配置类

@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.xjhqre")
@Import({
   
   Color.class, Red.class}) // 导入组件,id默认是组件的全类名
public class MainConfig3 {
   
   }

7.1.2、编写测试

@Test
public void test4() {
   
   
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig3.class);
    String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
    for (String beanDefinitionName : beanDefinitionNames) {
   
   
        System.out.println(beanDefinitionName);
    }
}

测试结果:

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfig3
com.xjhqre.pojo.Color
com.xjhqre.pojo.Red

7.2、ImportSelector

7.2.1、编写MyImportSelector

// 自定义逻辑,返回需要导入的组件
public class MyImportSelector implements ImportSelector {
   
   
    // 返回值就是要导入到容器中的组件的全类名
    // AnnotationMetadata:当前标注@Import注解类的所有信息
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
   
   
        return new String[]{
   
   "com.xjhqre.pojo.Blue", "com.xjhqre.pojo.Yellow"};
    }
}

7.2.2、修改配置类

@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.xjhqre")
@Import({
   
   Color.class, Red.class, MyImportSelector.class}) // 导入组件,id默认是组件的全类名
public class MainConfig3 {
   
   }

7.2.3、测试

@Test
public void test4() {
   
   
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig3.class);
    String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
    for (String beanDefinitionName : beanDefinitionNames) {
   
   
        System.out.println(beanDefinitionName);
    }
}

测试结果:

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfig3
com.xjhqre.pojo.Color
com.xjhqre.pojo.Red
com.xjhqre.pojo.Blue
com.xjhqre.pojo.Yellow

7.3、ImportBeanDefinitionRegistrar

7.3.1、编写MyImportBeanDefinitionRegistrar

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
   
   

    /**
     * AnnotationMetadata:当前类的注解信息
     * BeanDefinitionRegistry:注册类
     * 把所有需要添加到容器中的bean,调用BeanDefinitionRegistry.registerBeanDefinition手动注册
     */
    @Override
    public void registerBeanDefinitions
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值