Spring - 注解开发

news2024/11/27 21:44:15

文章目录

  • Spring的注解开发
  • 一、Bean 基本注解开发
    • 1.1 @Component Bean的配置
    • 1.2 其他注解配置Bean
    • 1.3 @Component 衍生注解
  • 二、Bean依赖注入注解开发
    • 2.1 @Value
    • 2.2 @Autowired
    • 2.3 @Qualifier
    • 2.4 @Resource
  • 三、非自定义注解开发
    • 3.1 无参非自定义注解开发
    • 3.2 有参非自定义注解开发
  • 四、Spring配置类的开发
    • 4.1 @Configuration注解
    • 4.2 @ComponentScan 组件扫描配置
    • 4.3 @PropertySource
    • 4.4 @Import注解
    • 4.5 加载配置类
  • 五、Spring配置其他注解
    • 5.1 @Primary
    • 5.2 @Profile
  • 六、Spring注解的解析原理
    • 6.1 xml方式组件扫描
    • 6.2 注解方式组件扫描
    • 6.3 总结
  • 七、注解方式整合Mybatis
    • 7.1 注解方式整合
    • 7.2 原理
  • 八、@Import 整合三方框架原理
    • 8.1 实现ImportSelector接口的类
    • 8.2 实现ImportBeanDefinitionRegistrar接口的类
    • 8.3 @Import注解参数是注解时

Spring的注解开发

可以对照Spring Bean、XML方式Bean配置、Bean实例化配置、Bean注入进行学习

一、Bean 基本注解开发

注解方式慢慢成为xml配置的替代方案

基本Bean注解,主要是使用注解的方式替代原有xml的<bean> 标签及其标签属性的配置

<bean id="" name="" class="" scope="" lazy-init="" init-method="" destroy-method="" 
      
abstract="" autowire="" factory-bean="" factory-method=""></bean>

使用@Component 注解替代<bean>标签

XML 配置注解描述
<bean id=“” class=“”>@Component被该注解标识的类,会在指定扫描范围内被Spring加载并实例化

1.1 @Component Bean的配置

value属性指定当前Bean实例的beanName,也可以省略不写,不写的情况下为当前类名首字母小写

//获取方式:applicationContext.getBean("userDao");
@Component("userDao")
public class UserDaoImpl implements UserDao {
}

//获取方式:applicationContext.getBean("userDaoImpl");
@Component
public class UserDaoImpl implements UserDao {
}

这样配置还不算完,Spring还不知道我们要扫描这个注解,所以还需要配置扫描的范围

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 告知Spring框架去com.zhangjngqi包及其子包下去扫描使用了注解的类 -->
    <context:component-scan base-package="com.zhangjingqi"/>
</beans>    

此时就能获取到注解创建的UserServiceImpl对象

ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
Object userService = applicationContext.getBean("userDaoImpl");
System.out.println(userService);

1.2 其他注解配置Bean

@Component就单纯一个value属性,那么xml配置<bean> 时那些属性怎么进行配置呢

Spring 是通过注解方式去配置的之前 <bean> 标签中的那些属性,例如:@Scope

<bean id="" name="" class="" scope="" lazy-init="" init-method="" destroy-method="" 
abstract="" autowire="" factory-bean="" factory-method=""></bean>

记不得了可以看一下这篇文章Spring Bean、XML方式Bean配置、Bean实例化配置、Bean注入

xml配置注解描述
<bean scope=“”>@Scope在类上或使用了@Bean标注的方法上,标注Bean的作用范围,取值为singleton或prototype
<bean lazy-init=“”>@Lazy在类上或使用了@Bean标注的方法上,标注Bean是否延迟加载,取值为true和false
<bean init-method=“”>@PostConstruct在方法上使用,标注Bean的实例化后执行的方法
<bean destroy-method=“”>@PreDestroy在方法上使用,标注Bean的销毁前执行方法
@Component("userDao")
@Scope("singleton")
@Lazy(true)
public class UserDaoImpl implements UserDao{
   @PostConstruct
   public void init(){}
    
   @PreDestroy
   public void destroy(){}
}

1.3 @Component 衍生注解

在这篇文章中也有对衍生注解的介绍,但是就是一点点而已SpringBoot——IOC与AOP

注解说明
@Controller标注在控制器
@Service标注在业务类上
@Repository标注在数据访问类上(由于与Mybatis整合,用的少)

不属于上面三层的,大多数使用@Component就可以

@Repository("userDao")
public class UserDaoImpl implements UserDao{}

@Service("userService")
public class UserServiceImpl implements UserService{}

@Controller("userService")
public class UserController {}

二、Bean依赖注入注解开发

Bean依赖注入的注解,主要是使用注解的方式替代xml的 标签完成属性的注入操作

之前的注入方式

<bean id="" class="">
   <property name="" value=""/>
   <property name="" ref=""/>
</bean>

如今注解注入方式

属性注入注解描述
@Value使用在字段或方法上,用于注入普通数据
@Autowired使用在字段或方法上,用于根据类型(byType)注入引用数据
@Qualifier使用在字段或方法上,结合@Autowired,根据名称注入
@Resource使用在字段或方法上,根据类型或名称进行注入

说明

@Resource注解是Java EE规范提供的,它在JDK中的javax.annotation.Resource包中定义。

在Spring中,可以使用@Resource注解来注入Bean,但它也支持其他注解,如@Autowired和@Inject。

这些注解提供了相同的功能,但有些微小的差异。

@Autowired注解是Spring提供的,而@Inject注解是JSR-330规范中定义的。

2.1 @Value

  • 直接注入普通属性
@Value("haohao")
private String username;

@Value("haohao")
public void setUsername(String username){
    System.out.println(username);
}

基本上没这么做的,主要看第二点,注入注入properties文件中的属性

  • 注入properties文件中的属性

SpringBoot读取方式 在SpringBoot中读取yaml配置文件中的数据、全部数据、部分数据_yaml 数组对象 读取_

下面来看一下Spring读取properties文件

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

@Value("${jdbc.username}")
public void setUsername(String username){
    System.out.println(username);
}

加载properties文件

<context:property-placeholder location="classpath:jdbc.properties"/>

2.2 @Autowired

//使用在属性上直接注入
@Autowired
private UserDao userDao;

//使用在方法上直接注入
@Autowired
public void setUserDao(UserDao userDao){
      System.out.println(userDao);
}

当容器中同一类型的Bean实例有多个时,会尝试自动根据名字进行匹配(如果名字匹配不成狗就会报错)

//匹配当前Bean
@Repository("userDao")
public class UserDaoImpl implements UserDao{}

@Repository("userDao2")
public class UserDaoImpl2 implements UserDao{}

下面的这段话也能注入UserDao,但是只不过仅仅在此方法中使用

@Autowired
public void xxx(UserDao userDao){
    System.out.println(userDao);
}

下面这段自动注入的含义是,在Spring容器中找UserDao类型的对象,然后放入到userDaoList集合当中

@Autowired
public void yyy(List<UserDao> userDaoList){
    System.out.println(userDaoList);
}

2.3 @Qualifier

@Qualifier配合@Autowired可以完成根据名称注入Bean实例,使用@Qualifier指定名称

@Autowired
@Qualifier("userDao2")
  private UserDao userDao;

@Autowired
@Qualifier("userDao2")
public void setUserDao(UserDao userDao){
  System.out.println(userDao);
}

别的地方@Qualifier可以单独使用

2.4 @Resource

@Resource注解存在与 javax.annotation 包中,Spring对其进行了解析

说明

@Resource注解是Java EE规范提供的,它在JDK中的javax.annotation.Resource包中定义。

在Spring中,可以使用@Resource注解来注入Bean,但它也支持其他注解,如@Autowired和@Inject。

这些注解提供了相同的功能,但有些微小的差异。

@Autowired注解是Spring提供的,而@Inject注解是JSR-330规范中定义的。

@Resource注解既可以根据类型注入,也可以根据名称注入,无参就是根据类型注入,有参数就是根据名称注入

@Resource
private UserDao userDao;

@Resource(name = "userDao2")
public void setUserDao(UserDao userDao){
    System.out.println(userDao);
}

@Resource注解会首先根据name属性指定的名称去查找对应的Bean进行注入,如果找到了,则使用名称匹配的方式注入;

如果没有指定name属性或者找不到匹配的Bean,则根据类型进行注入。

如果有多个同类型的Bean,也会使用名称匹配的方式来确定要注入哪个Bean。需要注意的是,如果有多个同类型且同名称的Bean,那么@Resource注解会抛出NoUniqueBeanDefinitionException异常。

@Resource与@Autowired区别

  • 来源不同:@Resource是Java EE规范定义的注解,而@Autowired是Spring框架定义的注解。

  • 注入方式不同:@Resource默认按照Bean的名称进行注入,如果找不到与名称相匹配的Bean,则按照类型进行注入。而@Autowired默认按照类型进行注入,如果出现多个相同类型的Bean,则再按照名称进行匹配。

  • 属性名称不同:@Autowired没有name属性,而@Resource有name属性,可以指定要注入Bean的名称。

  • 是否支持JSR-330:@Autowired支持JSR-330的@Inject注解,而@Resource不支持。

三、非自定义注解开发

@Bean注解必须被扫描到才可以完成下面的操作

@Bean所在的类必须加入到容器,可以加一个@Component注解,也可以添加@Configuration注解,一般是@Configuration注解

3.1 无参非自定义注解开发

非自定义Bean要通过工厂的方式进行实例化,使用@Bean标注方法即可

@Bean的属性为beanName,如不指定为当前工厂方法名称

@Bean(“dataSource”) 的含义

此注解的dataSource参数是方法dataSource()返回值的Bean的名字

测试一下

@Component
public class DataSourceTest {
    //将方法返回值Bean实例以@Bean注解指定的名称存储到Spring容器中
    @Bean("dataSource222")
    public DataSource dataSource(){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/mybatis");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        return dataSource;
    }
}

很完美

ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
Object userService = applicationContext.getBean("dataSource222");
System.out.println(userService);

如果不给@Bean添加参数呢,默认是什么名字?

​ 默认是方法名,如果是dataSource(),那Bean就叫dataSource,如果是DataSource(),那Bean就叫DataSource

@Bean
public DataSource dataSource(){
    DruidDataSource dataSource = new DruidDataSource();
    dataSource.setDriverClassName("com.mysql.jdbc.Driver");
    dataSource.setUrl("jdbc:mysql://localhost:3306/mybatis");
    dataSource.setUsername("root");
    dataSource.setPassword("root");
    return dataSource;
}

3.2 有参非自定义注解开发

注意! 下面方法所在的类是@Component注解修饰的,如果是@Configuration注解修饰的,部分方法会报错

如果@Bean工厂方法需要参数的话,则有如下几种注入方式

  • **使用@Autowired 根据类型自动进行Bean的匹配,@Autowired可以省略 **

    不省略的形式

@Bean
public DataSource beanTest01(@Autowired UserDao userDao,@Autowired UserService userService){
    System.out.println(userDao);
    System.out.println(userService);
    DruidDataSource dataSource = new DruidDataSource();
    return dataSource;
}

省略的形式

@Bean
public DataSource beanTest01(UserDao userDao,UserService userService){
    System.out.println(userDao);
    System.out.println(userService);
    DruidDataSource dataSource = new DruidDataSource();
    return dataSource;
}
  • 使用@Qualifier 根据名称进行Bean的匹配

以前是@Autowired+@Qualifier注解,在这个地方仅仅使用@Qualifier也可以

@Bean
public DataSource beanTest01(@Qualifier("userDaoImpl") UserDao userDao, UserService userService){
    System.out.println(userDao);
    System.out.println(userService);
    DruidDataSource dataSource = new DruidDataSource();
    return dataSource;
}
  • 使用@Value 根据名称进行普通数据类型匹配

    从properties文件中读取jdbc.driver开头的Key对应的Value

@Bean("dataSource")
public DataSource dataSource(@Value("${jdbc.driver}") String driverClassName){
    
    DruidDataSource dataSource = new DruidDataSource();
    dataSource.setDriverClassName(driverClassName);
    
}    

四、Spring配置类的开发

@Component等注解替代了<bean>标签,但是像**<import>、<context:componentScan> 等非<bean> 标签怎样去使用注解替代呢?**

<!-- 加载properties文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>

<!-- 组件扫描 -->
<context:component-scan base-package="com.itheima"/>

<!-- 引入其他xml文件 -->
<import resource="classpath:beans.xml"/>

4.1 @Configuration注解

@Configuration注解标识的类为配置类,替代原有xml配置文件,该注解第一个作用是标识该类是一个配置类,第二个作用是具备@Component作用

//标注当前类是一个配置类(替代配置文件的)
//底层也封装了@Component注解
@Configuration
public class SpringConfig {
}

4.2 @ComponentScan 组件扫描配置

@ComponentScan 组件扫描配置,替代原有xml文件中的<context:component-scan base-package=“”/>

@Configuration
@ComponentScan({"com.zhangjingqi.service","com.zhangjingqi.dao"})
public class SpringConfig {}
  • 指定一个或多个包名:扫描指定包及其子包下使用注解的类
  • 不配置包名:扫描当前@componentScan注解配置类所在包及其子包下的类

4.3 @PropertySource

注解用于加载外部properties资源配置,替代原有xml中的 <context:property placeholder location=“”/> 配置

@Configuration
@ComponentScan
@PropertySource({"classpath:jdbc.properties","classpath:xxx.properties"})
public class SpringConfig {}

加载外部yaml文件在SpringBoot中读取yaml配置文件中的数据、全部数据、部分数据_yaml 数组对象 读取_

4.4 @Import注解

相当于<import resource=“”>配置

首先说明DataSourceTest类上没有任何注解

@Configuration
@ComponentScan("com.zhangjingqi")//一定要扫描到DataSourceTest所在的包
@Import(DataSourceTest.class)
public class SpringConfig {
}

此时就将DataSourceTest注入容器了,并且DataSourceTest的@Bean所修饰 的Bean也注入容器了

DataSourceTest类相当于一个小的配置类(类似分部)

SpringConfig类在这里相当于一个大的配置类(类似总部)

public class DataSourceTest {
    //将方法返回值Bean实例以@Bean注解指定的名称存储到Spring容器中
    @Bean("dataSource")
    public DataSource dataSource(@Value("${jdbc.driver}") String driverClassName, UserDaoImpl userDao){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(driverClassName);
        dataSource.setUrl("jdbc:mysql://localhost:3306/mybatis");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        return dataSource;
    }


    @Bean
    public DataSource beanTest01(@Qualifier("userDaoImpl") UserDao userDao, UserService userService){
        System.out.println(userDao);
        System.out.println(userService);
        DruidDataSource dataSource = new DruidDataSource();
        return dataSource;
    }
}

4.5 加载配置类

//        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
//        注解方式加载Spring核心配置
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
        Object springConfig = applicationContext.getBean("springConfig");
        Object userDao = applicationContext.getBean("userDaoImpl");
        System.out.println(springConfig);
        System.out.println(userDao);

五、Spring配置其他注解

5.1 @Primary

@Primary注解用于标注相同类型的Bean优先被使用权

@Primary 是Spring3.0引入的,与@Component和@Bean一起使用,标注该Bean的优先级更高,则在通过类型获取Bean或通过@Autowired根据类型进行注入时,会选用优先级更高的

@Repository("userDao")
public class UserDaoImpl implements UserDao{}

@Repository("userDao2")
@Primary
public class UserDaoImpl2 implements UserDao{}
// 如果通过类型UserDao.class获取对象时,第二个Bean new UserDaoImpl2 会优先获取
@Bean
public UserDao userDao01(){
    return new UserDaoImpl();  
}

@Bean
@Primary
public UserDao userDao02(){
    return new UserDaoImpl2();
}

5.2 @Profile

@Profile 注解的作用同于xml配置时学习profile属性,是进行环境切换使用

<beans profile="test">

注解 @Profile 标注在类或方法上,标注当前产生的Bean从属于哪个环境,只有激活了当前环境,被标注的Bean才能被注册到Spring容器里

不指定环境的Bean,任何环境下都能注册到Spring容器里

@Repository("userDao")
@Profile("test")
public class UserDaoImpl implements UserDao{}

@Repository("userDao2")
public class UserDaoImpl2 implements UserDao{}

怎么激活环境

  • 使用命令行动态参数,虚拟机参数位置加载
 -Dspring.profiles.active=test
  • 使用代码的方式设置环境变量
System.setProperty("spring.profiles.active","test");

六、Spring注解的解析原理

​ @Component在类上标注完,Spring扫描到后,会被实例化成对象,再存储到Spring容器之中,现在研究一下为什么能创建对象

​ 依然是下面这张图

image-20230607114752525

上面这张图详细过程会在Spring Bean、XML方式Bean配置、Bean实例化配置、Bean注入有详细讲解

使用@Component等注解配置完毕后,要配置组件扫描才能使注解生效

  • xml配置组件扫描
<context:component-scan base-package="com.zhangjingqi"/>
  • 配置类配置组件扫描
@Configuration
@ComponentScan("com.zhangjingqi")
public class AppConfig {
}

6.1 xml方式组件扫描

下面 xmlns:context 这种方式叫做自定义命名空间的方式

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">

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

http://www.springframework.org/schema/context 命名空间最终对应了一个命名空间处理器

在下面这个地方就是对应的命名空间与对应的命名空间处理器的映射关系

image-20230607144757588

我们点进去之后,发现有“component-scan”image-20230607145048990

我们点进“component-scan”后面new的对象的类,此类中有一个parse方法

image-20230607145626687

然后再进入doScan方法,在此的方法后面会注册BeanDefinition

image-20230607145831678

注册的BeanDefinition也会加入到BeanDefinitionMap之中

此时下图中并没有UserDao,但是我们把断点放过去之后就有了(下面两张图连起来看)

image-20230607145957467

image-20230607150154950

我们把BeanDefinition信息存储到BeanDefinitionMap之后,最后就会经过Bean的生命周期,创建出实例Bean并且进入到单例池之中

并没有用到BeanFactoryProcessor,而是在Spring容器创建时就去扫描,扫描到对应的类后将各种信息定义成BeanDefinition,放到BeanDefinitionMap之中

6.2 注解方式组件扫描

还是想办法把BeanDefinition放入到BeanDefinitionMap之中,就能创建实例化Bean

加载核心配置类

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);

配置类配置组件扫描(开发中这种方式比较多),此处有@ComponentScan

@Configuration
@ComponentScan("com.zhangjingqi")
public class SpringConfig {
    
}

点进容器本身AnnotationConfigApplicationContext

image-20230607151607932

点击this() 无参构造,发现创建了一个AnnotatedBeanDefinitionReader,也就是读取器

xml形式时,Bean从配置文件到ApplicationContext也用到了读取器,是XMLBeanDefinitionReader,而这里是AnnotatedBeanDefinitionReader

不同的配置下有不同的读取器

image-20230607151657225

进入AnnotatedBeanDefinitionReader注解BeanDefinition读取器看看

image-20230607152109035

再点击去上面的方法,主要是看下面这个图的最后一段代码registerAnnotationConfigProcessors

image-20230607152141072

点进registerAnnotationConfigProcessors去

image-20230607152315387

再点进去registerAnnotationConfigProcessors,并且打几个断点,最终执行完此方法后,这些都会注入进去

image-20230607152949051

image-20230607153034869

然后点击看一眼

image-20230607153127824

特别好,发现上面有ConfigurationClassPostProcessor类上有BeanDefinitionRegistryPostProcessor接口

image-20230607153147111

BeanDefinitionRegistryPostProcessor这个接口在下面图中的红色标记处见过,此接口就是注册BeanDefinition的

详细说明在Spring - Bean的实例化流程及生命周期_

image-20230607153236263

在类ConfigurationClassPostProcessor中搜postProcessBeanDefinitionRegistry方法,此方法就是向BeanDefinitionMap中注册BeanDefinition

image-20230607154105078

在这个方法中调用了一个processConfigBeanDefinitions方法,点进去

在这个方法中创建了一个解析器,并且调用了parse方法

image-20230607155411365

点进parse方法,然后发现又调用了一个重载的parse方法,点击去

image-20230607155528563

点进processConfigurationClass方法

image-20230607155607723

再点进下面这个方法

image-20230607155701949

再点进下面parse方法

image-20230607155817950

点进去下面的ClassPathBeanDefinitionScanner类doScan方法

image-20230607160039814

scanner就是ClassPathBeanDefinitionScanner

与xml方法中的是同一个扫描器

image-20230607155957472

然后发现下面的这段代码与xml方式组件扫描时同一段代码

image-20230607160144926




然后我们再回到AnnotationConfigUtils类中看AutowiredAnnotationBeanPostProcessor.class是什么东西,点击去看一看

image-20230607154219141

AutowiredAnnotationBeanPostProcessor类实现了一个接口MergedBeanDefinitionPostProcessor,MergedBeanDefinitionPostProcessor接口又实现了BeanPostProcessor接口

image-20230607154505098

BeanPostProcessor接口在下图见过

AutowiredAnnotationBeanPostProcessor类是进行属性注入的,属性注入的过程就是在Bean生命周期的过程中

image-20230607154632850

6.3 总结

看得我头疼,这部分二十分钟的课,我看了两个多小时

不管是xml方式组件扫描还是注解方式组件扫描,最终的扫描方式是一样的,都是使用ClassPathBeanDefinitionScanner扫描器的doScan方法

XML方式组件扫描直接在自定义命名空间的parse当中执行组件扫描最终解析

注解组件扫描是把BeanDefinitionRegistryPostProcessor注入容器之中最终内部再去执行ClassPathBeanDefinitionScanner扫描器的doScan方法,注册BeanDefinition

image-20230607161132635

七、注解方式整合Mybatis

7.1 注解方式整合

之前的配置方式

<!--配置数据源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
   <property name="url" value="jdbc:mysql://localhost:3306/mybatis"></property>
   <property name="username" value="root"></property>
   <property name="password" value="root"></property>
</bean>

<!--配置SqlSessionFactoryBean-->
<bean class="org.mybatis.spring.SqlSessionFactoryBean">
   <property name="dataSource" ref="dataSource"></property>
</bean>

<!--配置Mapper包扫描-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.itheima.dao"></property>
</bean>

如今的配置方式

@Configuration
@ComponentScan("com.zhangjingqi")
@MapperScan("com.zhangjingqi.mapper")
public class SpringConfig {
    @Bean
    public DataSource dataSource(){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/mybatis");
        dataSource.setUsername("root");
        dataSource.setPassword("root");

        return dataSource;
    }
    
    @Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
       SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
       sqlSessionFactoryBean.setDataSource(dataSource);
       return sqlSessionFactoryBean;
     }
  
}    

扫描

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);

7.2 原理

,Spring整合MyBatis的原理,关键在于@MapperScan,@MapperScan不是Spring提供的注解,是MyBatis为了整合Spring,在整合包org.mybatis.spring.annotation中提供的注解

@MapperScan注解内部有一个注解是@Import,然后导入了一个类MapperScannerRegister

image-20230607201559606

点进MapperScannerRegister类,发现此类实现一个接口ImportBeanDefinitionRegister,一看就是一个BeanDefinition注册器

image-20230607201737519

ImportBeanDefinitionRegister接口中有一个方法registerBeanDefinitions

image-20230607202005304

再回到MapperScannerRegister类中,搜一下registerBeanDefinitions方法

然后发现类MapperScannerRegister中对registerBeanDefinitions方法重载了

image-20230607202141737

看一下重载的registerBeanDefinitions方法,然后发现此方法中在构建BeanDefinition时传入了MapperScannerConfig类

<!--配置Mapper包扫描-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.itheima.dao"></property>
</bean>

这个类就是在xml配置文件中配置的类org.mybatis.spring.mapper.MapperScannerConfigurer

image-20230607202422589

MapperScannerConfigurer实现了BeanDefinitionRegisterProcessor接口,这个接口我们见到了很多次了

image-20230607202727434

不管是注解方式还是xml方式,最终还会回到MapperScannerConfigurer

八、@Import 整合三方框架原理

自定义Springboot的Start会用到这个注解SpringBoot原理——起步依赖与自动装配以及自定义starter_

@Import可以导入如下三种类

  • 普通的配置类
  • 实现ImportSelector接口的类
  • 实现ImportBeanDefinitionRegistrar接口的类

第一种在上面演示过了,演示第二种和第三种

8.1 实现ImportSelector接口的类

配置类

@Configuration
@ComponentScan("com.zhangjingqi")
@Import(MyImportSelector.class)
public class SpringConfig {...}

实现ImportSelector接口的类

public class MyImportSelector implements ImportSelector {
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        // 返回的是一个数组,封装的是需要被注册到Spring容器中的Bean的全限定名(全包名)
//        return new String[0];
        return new String[]{OtherBean2.class.getName()};
    }
}

我们这个类要注入容器当中

public class OtherBean2 {
}

测试一下

这里是根据类型获取,如果是根据beanName的话非常的长,是类的全限定名

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
Object otherBean2 = applicationContext.getBean("otherBean2");
System.out.println(otherBean2);

参数annotationMetadata叫做注解媒体数组,该对象内部封装的是当前使用使用了@Import注解的类上的其他注解的元信息

比如MyImportSelector类是在SpringConfig类中导入的,则annotationMetadata参数就维护着SpringConfig类上其他注解的信息,比如说SpringConfig类上的@Configuration注解与@ComponentScan(“com.zhangjingqi”)注解。这些都是可以获取到的
尝一下

public class MyImportSelector implements ImportSelector {
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
//       参数是一个注解的全限定名
//        annotationMetadata.getAnnotationAttributes("org.springframework.context.annotation.ComponentScan");
        Map<String, Object> annotationAttributes = annotationMetadata.getAnnotationAttributes(ComponentScan.class.getName());
        annotationAttributes.forEach((attrName,attrValue)->{
            System.out.println(attrName+"="+attrValue);
        });

        // 返回的是一个数组,封装的是需要被注册到Spring容器中的Bean的全限定名(全包名)
//        return new String[0];
        return new String[]{OtherBean2.class.getName()};
    }
}

控制台输出的内容,有一些看不太懂

image-20230607212023479

8.2 实现ImportBeanDefinitionRegistrar接口的类

也是向容器当中注入BeanDefinition

ImportBeanDefinitionRegistrar接口类

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
//      向容器当中注入BeanDefinition
        BeanDefinition beanDefinition = new RootBeanDefinition();
        beanDefinition.setBeanClassName(OtherBean2.class.getName());
        registry.registerBeanDefinition("otherBean2",beanDefinition);
    }
}

配置类

@Configuration
@ComponentScan("com.zhangjingqi")
@Import(MyImportBeanDefinitionRegistrar.class)
public class SpringConfig {...}

测试类

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
Object otherBean2 = applicationContext.getBean("otherBean2");
System.out.println(otherBean2);

8.3 @Import注解参数是注解时

自定义注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(MyImportBeanDefinitionRegistrar.class)
public @interface MyMapperScan {
    
}

在下面配置类添加MyMapper自定义注解,相当于把@Import(MyImportBeanDefinitionRegistrar.class)语句藏了一层

@Configuration
@ComponentScan("com.zhangjingqi")
//@Import(MyImportBeanDefinitionRegistrar.class)
@MyMapperScan
public class SpringConfig {.....}

MyImportBeanDefinitionRegistrar类中方法

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
//      向容器当中注入BeanDefinition
        BeanDefinition beanDefinition = new RootBeanDefinition();
        beanDefinition.setBeanClassName(OtherBean2.class.getName());
        registry.registerBeanDefinition("otherBean2",beanDefinition);
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/620665.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Domino 14新内核

大家好&#xff0c;才是真的好。 还记得去年&#xff0c;我们不断跟进而放出的Notes/Domino产品路线图吗&#xff1f;是的&#xff0c;HCL正在按照产品路线图稳步推进&#xff0c;而很多人提出的idea&#xff0c;也逐步加入到产品中&#xff0c;等会我们也会聊到。 我最喜欢这…

MySQL安装-Linux版

MySQL-Linux版安装 1、准备一台Linux服务器 云服务器或者虚拟机都可以&#xff1b; Linux的版本为 CentOS7&#xff1b; 2、 下载Linux版MySQL安装包 下载地址 3、上传MySQL安装包 使用FinalShell软件上传即可&#xff01; 4、 创建目录,并解压 mkdir mysqltar -xvf my…

【Web服务器】Nginx网站服务

文章目录 一、Nginx 概述1.什么是 Nginx2.Nginx 的特点3.Nginx 应用场景 二、Nginx 服务基础1.编译安装 Nginx 服务1.1 布置环境1.2 安装依赖包1.3 创建运行用户、组1.4 编译安装 2.Nginx 的运行控制2.1 检查配置文件2.2 启动、停止 Nginx2.3 日志分割以及升级 Nginx 服务2.4 添…

调用腾讯API实现图片滤镜

目录 1. 作者介绍2. 图像滤波介绍3. 实验过程及结果&#xff08;附完整代码&#xff09;3.1 准备工作3.2 实验代码3.3 实验结果 1. 作者介绍 班梦威&#xff0c;男&#xff0c;西安工程大学电子信息学院&#xff0c;2022级研究生 研究方向&#xff1a;模式识别与人工智能 电子…

企业如何有效制定企业信息化发展规划?(附信息化模板)

如何有效制定企业信息化发展规划&#xff1f;企业信息化发展规划是一个宏大而又复杂的命题&#xff0c;这篇来掰开揉碎讲一下企业应该如何有效制定信息化发展规划。 这里不给大家灌鸡汤&#xff0c;也不给大家画大饼&#xff0c;就说些实在的。 如果你想找经验方法&#xff0…

Lambda表达式与函数式编程

文章目录 函数式编程——Stream流概述为什么学?函数式编程思想 Lambda表达式概述Lambda表达式的前身省略规则 Stream流概述案例数据准备创建流中间操作终结操作reduce归并注意事项 Optional概述创建对象安全消费值获取值安全获取值过滤数据转换 函数式接口常用的默认方法 方法…

APPSCAN扫描https协议的网站证书安装过程(Googel浏览器)

【1】首先打开我们的appscan,点击外部设备。 【2】点击记录代理配置 【3】弹出选项后&#xff0c;在记录代理下我们可以看到AppScan SSL证书&#xff0c;这点我们点击导出 【4】这里你选择一个合适的位置&#xff0c;点击保存 【5】保存后的文件是一个zip压缩包&#xff0c;…

GPT4和Claude100k测试使用

总述 程序员们通常使用大量代码&#xff0c;找到一个能够使用Claude100k和GPT4的&#xff0c;长代码优化有希望啦&#xff01; Liaobots&#xff1a;支持GPT4和Claude100k 不定期供应GPT4 32k&#xff0c;支持最多24000字符请求 大家有时候会觉得GPT4 8k不够用&#xff0c;…

TensorFlow入门知识

个人理解 TensorFlow是集齐了很多深度学习相关的算法的框架&#xff0c;你可以利用他搭建自己的神经网络模型。对于开发者来说&#xff0c;告诉TensorFlow一批特征数据和最终的答案数据&#xff0c;让其通过一个神经网络模型进行训练&#xff0c;最终输出模型。模型将应用于应…

DSP-OMAPL-138 RTOS开发(1)——报错总结

1 git的作用 第一个问题&#xff0c;也不算错误&#xff1a;工程文件会有一个名称会有一个横着的箭头并且文件名前面都有问好&#xff0c;不算错误&#xff08;算个发现&#xff09;&#xff0c;但是发现将git文件删去即可&#xff0c;如果没有&#xff0c;右击工程文件->选…

用逆向思维学习技术

tip: 作为程序员一定学习编程之道&#xff0c;一定要对代码的编写有追求&#xff0c;不能实现就完事了。我们应该让自己写的代码更加优雅&#xff0c;即使这会费时费力。 推荐&#xff1a;体系化学习Java&#xff08;Java面试专题&#xff09; 前言 学习任何知识&#xff0c…

新Ubuntu怎么装Nidia驱动,cuda和cudnn

怎么安装nvidia驱动 软件更新->附加驱动 选择一个喜欢的 或者找推荐的 Ubuntu 20.04安装CUDA & CUDNN 手把手带你撸_ubuntu20.04 无图形化安装cuda_哈希Map的博客-CSDN博客 sudo ubuntu-drivers autoinstall 怎么安装cuda gcc 不用降级 &#xff08;我没降级就安装好…

设计模式-02.经典设计原则-第二节[必读]

设计模式经典设计原则-第二节 依赖反转原则&#xff08;DIP&#xff09; 关于 SOLID 原则&#xff0c;我们已经学过单一职责、开闭、里式替换、接口隔离这四个原则。今天&#xff0c;我们再来学习最后一个原则&#xff1a;依赖反转原则。在前面&#xff0c;我们讲到&#xff…

C++ 深入理解多态及拓展

文章目录 1. 理解虚表1.1 虚表1.2 验证1.3 子类虚表1.4 相同类不同对象的虚表 2. 静态绑定和动态绑定2.1 静态绑定2.2 动态绑定 3. 多态的实现原理3.1 向上转型3.2 多继承3.3 原理 4. 拓展4.1 构造函数能不能是虚函数4.2 父类和子类的析构函数在底层的命名问题4.3 对象之间无法…

c++实现smtp发送邮件,支持ssl的465端口发送,支持附件、一次发送多人、抄送等

前言 c实现smtp发送邮件&#xff0c;支持ssl的465端口发送&#xff0c;支持附件、一次发送多人、抄送等。 这里只使用了openssl库&#xff08;用来支持ssl的465端口&#xff09;&#xff0c;其他部分是原生c,支持在win/linux运行。 网上很多都是原始的支持25端口&#xff0c;明…

Fiddler抓包工具之高级工具栏中的重定向AutoResponder的用法

重定向AutoResponder的用法 关于Fiddler的AutoResponder重定向功能&#xff0c;主要是时进行会话的拦截&#xff0c;然后替换原始资源的功能。 它与手动修该reponse是一样的&#xff0c;只是更加方便了&#xff0c;可以创建相应的rules&#xff0c;适合批处理的重定向功能。 …

[SQL Server]数据库入门之多表查询

&#x1f3ac; 博客主页&#xff1a;博主链接 &#x1f3a5; 本文由 M malloc 原创&#xff0c;首发于 CSDN&#x1f649; &#x1f384; 学习专栏推荐&#xff1a;LeetCode刷题集&#xff01; &#x1f3c5; 欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指…

使用vant,实现密码输入框右边提供可视按钮(最简单)

在实际项目开发中&#xff0c;要实现密码输入框带密码可见切换按钮&#xff08;右侧的眼睛&#xff09;&#xff0c;点眼睛可以显示或隐藏密码。 实现原理&#xff1a;动态绑定输入框类型 1.绑定密码框的type属性&#xff0c;在密码框使用插槽 ps&#xff1a;由于icon标签不…

美股怎么交易?有哪些美股交易基础知识?

美股市场相对成熟&#xff0c;投资回报率也更高一些&#xff0c;受到投资者喜爱。美股怎么交易&#xff1f;首先就需要了解美股交易基础知识。 美股交易基础知识一、美股交易市场 美股主要交易市场有NYSE纽约证券交易所、NASDAQ纳斯达克证券市场、AMEX美国证券交易所。 美股交…

GitOps 最佳实践(上)| 基于 Amazon EKS 构建 CI/CD 流水线

GitOps 是目前比较理想的方法来实现基于 Kuberentes 集群的持续部署。 了解了 GitOps 的概念以及 CI/CD 流水线的架构&#xff0c;接下来我们将通过以下四个模块逐步完成构建 CI/CD 流水线的最佳实践&#xff1a; 通过 IaC 部署云基础架构&#xff1b;在 Amazon EKS 集群上部…