Spring 注解开发

目录
  • 注解开发简介
  • 常用注解
    • 启用注解功能
    • bean 定义:@Component、@Controller、@Service、@Repository
    • bean 的引用类型属性注入:@Autowired、@Qualifier
    • bean 的引用类型属性注入:@Inject、@Named、@Resource
    • bean 的引用类型属性注入:@Primary
    • bean 的非引用类型属性注入:@Value
    • bean 的作用域:@Scope
    • bean 的生命周期:@PostConstruct、@PreDestroy
    • 加载第三方资源:@Bean
    • 加载 properties 文件:@PropertySource
    • 纯注解开发:@Configuration、@ComponentScan
    • 导入第三方配置:@Import
    • 综合案例
      • spring 配置类
      • dao 层
      • service 层
      • controller 层
  • 整合第三方技术
    • 注解整合 Mybatis
    • 注解整合 Junit
  • IoC 底层核心原理
    • IoC 核心接口
    • 组件扫描器:@ComponentScan
      • 配置扫描器
      • 自定义扫描器
    • 自定义导入器:ImportSelector
    • 自定义注册器:ImportBeanDefinitionRegistrar
    • bean 初始化过程解析
      • bean 统一初始化
      • 单个 bean 初始化


注解开发简介

注解开发的好处:使用注解的形式替代 xml 配置,将繁杂的 Spring 配置文件从工程中彻底消除掉,简化书写。

Spring 注解开发_第1张图片

注解驱动的弊端:

  • 为了达成注解驱动的目的,可能会将原先很简单的书写,变得更加复杂。

  • XML 中配置第三方开发的资源是很方便的,但使用注解驱动无法在第三方开发的资源中进行编辑,因此会增大开发工作量。

Spring 注解开发_第2张图片


常用注解

Spring 原始注解:主要是替代 的配置。

注解 说明
@Component 使用在类上用于实例化 Bean
@Controller 使用在 web 层类上用于实例化 Bean
@Service 使用在 service 层类上用于实例化 Bean
@Repository 使用在 dao 层类上用于实例化 Bean
@Autowired 使用在字段上用于根据类型依赖注入
@Qualifier 结合 @Autowired 一起使用,用于根据名称进行依赖注入引用类型
@Resource 相当于 @Autowired + @Qualifier,按照名称进行注入引用类型
@Value 注入普通类型的属性
@Scope 标注 Bean 的作用范围
@PostConstruct 使用在方法上标注该方法是 Bean 的初始化方法
@PreDestroy 使用在方法上标注该方法是 Bean 的销毁方法

Spring 新注解:

使用上面的注解还不能全部替代 xml 配置文件,还需要使用注解替代的配置如下:

  • 非自定义的Bean的配置:
  • 加载properties文件的配置:
  • 组件扫描的配置:
  • 引入其他文件:
注解 说明
@Configuration 用于指定当前类是一个 Spring 配置类,当创建容器时会从该类上加载注解。用于指定 Spring 在初始化容器时要扫描的包。
@ComponentScan 作用和在 Spring 的 xml 配置文件中的 一样。
@Bean 使用在方法上,标注将该方法的返回值存储到 Spring 容器中。
@PropertySource 用于加载 .properties 文件中的配置。
@Import 用于导入其他配置类。

启用注解功能

  • 启动注解扫描,加载类中配置的注解项:

  • 说明:

    • 在进行包所扫描时,会对配置的包及其子包中所有文件进行扫描。

    • 扫描过程是以文件夹递归迭代的形式进行的。

    • 扫描过程仅读取合法的 java 文件。

    • 扫描时仅读取 spring 可识别的注解。

    • 扫描结束后会将可识别的有效注解转化为 spring 对应的资源加入 IoC 容器。

  • 注意:

    • 无论是注解格式还是 XML 配置格式,最终都是将资源加载到 IoC 容器中,差别仅仅是数据读取方式不同。

    • 从加载效率上来说,注解优于 XML 配置文件。


bean 定义:@Component、@Controller、@Service、@Repository

  • 类型:类注解

  • 位置:类定义上方。

  • 作用:设置该类为 spring 管理的 bean 。

  • 示例:

@Component
public class ClassName{}
  • 说明:@Controller、@Service 、@Repository 是 @Component 的衍生注解,功能同 @Component 。

  • 相关属性:

    • value(默认) :定义 bean 的访问 id 。

bean 的引用类型属性注入:@Autowired、@Qualifier

  • 类型:属性注解、方法注解

  • 位置:属性定义上方,方法定义上方。

  • 作用:设置对应属性的对象或对方法进行引用类型传参。

  • 说明:@Autowired 默认按类型装配,指定 @Qualifier 后则可以指定装配的 bean 的 id 。

  • 相关属性:

    • required:定义该属性是否允许为 null 。

bean 的引用类型属性注入:@Inject、@Named、@Resource

  • 说明:

    • @Inject 与 @Named 是 JSR330 规范中的注解,功能与 @Autowired 和 @Qualifier 完全相同,适用于不同架构场景。
    • @Resource 是 JSR250 规范中的注解,可以简化书写格式。
  • @Resource 相关属性:

    • name:设置注入的 bean 的 id 。

    • type:设置注入的 bean 的类型,接收的参数为 Class 类型。


bean 的引用类型属性注入:@Primary

  • 类型:类注解

  • 位置:类定义上方。

  • 作用:设置类对应的bean按类型装配时优先装配。

  • 说明:@Autowired 默认按类型装配,当出现相同类型的 bean,使用 @Primary 会提高按类型自动装配的优先级,但多个 @Primary 会导致优先级设置无效。


bean 的非引用类型属性注入:@Value

  • 类型:属性注解、方法注解

  • 位置:属性定义上方,方法定义上方。

  • 作用:设置对应属性的值或对方法进行传参。

  • 说明:

    • value 值仅支持非引用类型数据,赋值时对方法的所有参数全部赋值。

    • value 值支持读取 properties 文件中的属性值,通过类属性将 properties 中数据传入类中。

    • value 值支持 SpEL 。

    • @value 注解如果添加在属性上方,可以省略 set 方法(set 方法的目的是为属性赋值)。


bean 的作用域:@Scope

  • 类型:类注解

  • 位置:类定义上方。

  • 作用:设置该类作为 bean 对应的 scope 属性。

  • 相关属性

    • value(默认):定义 bean 的作用域,默认为 singleton 。

bean 的生命周期:@PostConstruct、@PreDestroy

  • 类型:方法注解

  • 位置:方法定义上方。

  • 作用:设置该类作为 bean 对应的生命周期方法。


加载第三方资源:@Bean

  • 类型:方法注解

  • 位置:方法定义上方。

  • 作用:设置该方法的返回值作为 spring 管理的 bean 。

  • 范例:

@Bean("dataSource")
public DruidDataSource createDataSource() {    return ……;    }
  • 说明:

    • 因为第三方 bean 无法在其源码上进行修改,因此可以使用 @Bean 解决第三方 bean 的引入问题。

    • 该注解用于替代 XML 配置中的静态工厂与实例工厂创建 bean,不区分方法是否为静态或非静态。

    • @Bean 所在的类必须被 spring 扫描加载,否则该注解无法生效。

  • 相关属性

    • value(默认):定义 bean 的访问 id 。

加载 properties 文件:@PropertySource

  • 类型:类注解

  • 位置:类定义上方。

  • 作用:加载 properties 文件中的属性值。

  • 范例:

@PropertySource(value="classpath:jdbc.properties")
public class ClassName {
    @Value("${propertiesAttributeName}")
    private String attributeName;
}
  • 说明:不支持*通配格式,一旦加载,所有 spring 控制的 bean 中均可使用对应属性值

  • 相关属性

    • value(默认):设置加载的 properties 文件名。

    • ignoreResourceNotFound:如果资源未找到,是否忽略,默认为 false 。


纯注解开发:@Configuration、@ComponentScan

  • 类型:类注解

  • 位置:类定义上方。

  • 作用:设置当前类为 spring 核心配置加载类(不再需要 spring 配置文件)。

  • 范例:

@Configuration
@ComponentScan("scanPackageName")
public class SpringConfigClassName{
}


- 说明:

  - 核心配合类用于替换 spring 核心配置文件,此类可以设置空的,不设置变量与属性。

  - bean 扫描工作使用注解 @ComponentScan 替代。

- 加载纯注解格式上下文对象,需要使用 AnnotationConfigApplicationContext:

```java
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);

导入第三方配置:@Import

  • 类型:类注解

  • 位置:类定义上方。

  • 作用:导入第三方 bean 作为 spring 控制的资源。

  • 范例:

@Configuration
@Import(OtherClassName.class)
public class ClassName {
}
  • 说明:

    • @Import 注解在同一个类上,仅允许添加一次,如果需要导入多个,使用数组的形式进行设定。

    • 在被导入的类中可以继续使用 @Import 导入其他资源(了解)。

    • @Bean 所在的类可以使用导入的形式进入 spring 容器,无需声明为 bean 。


综合案例

maven 依赖:

    
        
            org.springframework
            spring-context
            5.1.9.RELEASE
        
        
            com.alibaba
            druid
            1.1.16
        
    

spring 配置类

DataSourceConfig.java

package com.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;

// 数据源配置类
// 相当于 ,且不能用通配符*
@PropertySource("classpath:jdbc.properties")
public class DataSourceConfig {

    @Value("${jdbc.driver}")
    private static String driver;
    @Value("${jdbc.url}")
    private static String url;
    @Value("${jdbc.username}")
    private static String username;
    @Value("${jdbc.password}")
    private static String password;

    @Bean("dataSource")  // 将方法的返回值放置Spring容器中
    public static DruidDataSource getDataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(driver);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        return dataSource;
    }
}

SpringConfig.java

package com.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

// Spring核心配置类
@Configuration
@ComponentScan("com")  // 相当于 
@Import({DataSourceConfig.class})  // 相当于 
public class SpringConfig {

}

dao 层

UserDaoImpl.java

package com.dao.impl;

import com.dao.UserDao;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;

// 相当于 
// @Component("userDao")
@Repository("userDao")
public class UserDaoImpl implements UserDao {
    @Override
    public void save() {
        System.out.println("UserDao save...");
    }
}

service 层

UserServiceImpl.java

package com.service.impl;

import com.dao.UserDao;
import com.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import javax.sql.DataSource;

// 相当于 
// @Component("userService")
@Service("userService")
public class UserServiceImpl implements UserService {

    // 
    // @Autowired  // 可单独使用,按照数据类型从spring容器中进行匹配的(有多个相同数据类型的bean时则会有匹配问题)
    // @Qualifier("userDao")  // 指定bean的id从spring容器中匹配,且要结合@Autowired一起用
    @Resource(name="userDao")  // 相当于 @Autowired+@Autowired
    UserDao userDao;

    @Resource(name="dataSource")
    DataSource dataSource;

    @Value("${jdbc.driver}")  // 读取配置文件中的值
    private String driver;

    /* 使用注解开发可以省略set方法,使用配置文件则不能省略
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
    */

    @Override
    public void save() {
        System.out.println("driver: "+driver);
        System.out.println("dataSource: "+dataSource);
        userDao.save();
    }

    @PostConstruct
    public void init() {
        System.out.println("service对象的初始化方法");
    }

    @PreDestroy
    public void destroy() {
        System.out.println("service对象的销毁方法");
    }
}

controller 层

App.java

package com.controller;

import com.config.SpringConfig;
import com.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class App {

    public static void main(String[] args) {
        // ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        UserService userService = (UserService)context.getBean("userService");
        userService.save();
        context.close();
    }
}

运行结果

service对象的初始化方法
dataSource: {
    CreateTime:"2021-12-03 01:05:00",
    ActiveCount:0,
    PoolingCount:0,
    CreateCount:0,
    DestroyCount:0,
    CloseCount:0,
    ConnectCount:0,
    Connections:[
    ]
}
UserDao save...
service对象的销毁方法

整合第三方技术

注解整合 Mybatis

Spring 注解开发_第3张图片

注解整合 MyBatis 的开发步骤

  1. 修改 mybatis 外部配置文件格式为注解格式;
  2. 业务类使用 @Component 声明 bean,使用 @Autowired 注入对象;
  3. 建立配置文件 JDBCConfig 与 MyBatisConfig 类,并将其导入到核心配置类 SpringConfig;
  4. 开启注解扫描;
  5. 使用 AnnotationConfigApplicationContext 对象加载配置项。

项目工程地址

核心内容如下:

  • Maven 依赖:
        
            org.springframework
            spring-context
            5.1.9.RELEASE
        
        
            mysql
            mysql-connector-java
            8.0.11
        
        
            org.springframework
            spring-jdbc
            4.3.8.RELEASE
        
        
            com.alibaba
            druid
            1.1.16
        
        
            org.mybatis
            mybatis-spring
            1.3.0
        
        
            org.mybatis
            mybatis
            3.5.3
        
  • MybatisConfig.java(Mybatis 配置类):
package com.config;

import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;

import javax.sql.DataSource;

public class MybatisConfig {

    /*
    
    
        
        
    

    
    
        
    
     */

    // 以下注解代替以上配置文件内容:返回值会作为Spring容器的bean
    @Bean
    public SqlSessionFactoryBean getSqlSessionFactoryBean(@Autowired DataSource dataSource) {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        sqlSessionFactoryBean.setTypeAliasesPackage("com.domain");
        return sqlSessionFactoryBean;
    }

    @Bean
    public MapperScannerConfigurer getMapperScannerConfigurer() {
        MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
        mapperScannerConfigurer.setBasePackage("com.dao");
        return mapperScannerConfigurer;
    }

}
  • SpringConfig.java(Spring 核心配置类):
package com.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
@ComponentScan("com")  // 相当于 
@Import({DataSourceConfig.class, MybatisConfig.class})  // 相当于 
public class SpringConfig {

}

注解整合 Junit

注解整合 Junit 的开发步骤

  1. Spring 接管 Junit 的运行权,使用 Spring 专用的 Junit 类加载器;

2.为 Junit 测试用例设定对应的 Spring 容器:

  • 从 Spring 5.0 以后,要求 Junit 的版本必须是 4.12 或以上。

  • Junit 仅用于单元测试,不能将 Junit 的测试类配置成 Spring 的 bean,否则该配置将会被打包进入工程中。

示例:整合 Junit5

  • Maven 依赖:
        
            org.junit.jupiter
            junit-jupiter
            5.8.2
            test
        
        
            org.springframework
            spring-test
            5.1.9.RELEASE
        
  • 测试类:
package com.service;

import com.config.SpringConfig;
import com.domain.User;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;

import java.util.List;

// 设定spring专用的类加载器
@ExtendWith(SpringExtension.class)
// 设定加载的spring上下文对应的配置
@ContextConfiguration(classes=SpringConfig.class)
public class UserServiceTest {

    @Autowired
    UserService userService;

    @Test
    public void testFindById() {
        User user = userService.findById(1);
        // System.out.println(user);
        Assertions.assertEquals("Mick", user.getName());
    }

    @Test
    public void testFindAll() {
        List users = userService.findAll();
        Assertions.assertEquals(3, users.size());
    }

}

IoC 底层核心原理

IoC 核心接口

Spring 注解开发_第4张图片


组件扫描器:@ComponentScan

组件扫描器:开发过程中,需要根据需求加载必要的 bean 或排除指定 bean。

Spring 注解开发_第5张图片

应用场景:

  • 数据层接口测试
  • 业务层接口测试
  • 各种运行环境设置

配置扫描器

  • 名称:@ComponentScan

  • 类型:类注解

  • 位置:类定义上方

  • 作用:设置 spring 配置加载类扫描规则

  • 范例:

@Configuration
@ComponentScan(
    value="com",  // 设置基础扫描路径
    excludeFilters =      // 设置过滤规则,当前为排除过滤
    @ComponentScan.Filter(            // 设置过滤器
        type= FilterType.ANNOTATION,  // 设置过滤方式为按照注解进行过滤
        classes=Repository.class)     // 设置具体的过滤项。如不加载所有@Repository修饰的bean
)
public class SpringConfig {

}
  • includeFilters:设置包含性过滤器

  • excludeFilters:设置排除性过滤器

  • type:设置过滤器类型(过滤策略)

    • ANNOTATION
    • ASSIGNABLE_TYPE
    • ASPECTJ
    • REGEX
    • CUSTOM

自定义扫描器

  • 名称:TypeFilter

  • 类型:接口

  • 作用:自定义类型过滤器

示例:

  • 自定义扫描器
public class MyTypeFilter implements TypeFilter {
    public boolean match(MetadataReader mr, MetadataReaderFactory mrf) throws IOException {
        ClassMetadata cm = metadataReader.getClassMetadata();
        tring className = cm.getClassName();
        if(className.equals("com.itheima.dao.impl.BookDaoImpl")){
            return true;  // 进行过滤(拦截)
        }
        return false;  // 不过滤(放行)
    }
}
  • 配置类:
@Configuration
@ComponentScan(
        value = "com",
        excludeFilters = @ComponentScan.Filter(
                type= FilterType.CUSTOM,
                classes = MyTypeFilter.class
        )
)
public class SpringConfig {

}

自定义导入器:ImportSelector

bean 只有通过配置才可以进入 spring 容器,被 spring 加载并控制。配置 bean 的方式如下:

  • XML 文件中使用 标签配置

  • 使用 @Component 及衍生注解配置

企业开发过程中,通常需要配置大量的 bean,因此需要一种快速高效配置大量 bean 的方式。

ImportSelector 注解:

  • 类型:接口

  • 作用:自定义 bean 导入器(导入未加 @Component 注解的 bean)

示例:

  • 自定义导入器:
public class MyImportSelector implements ImportSelector {
    public String[] selectImports(AnnotationMetadata icm) {
        // 返回需要导入的bean数组。该bean即使没加@Component注解也能被扫描识别
        return new String[]{"com.dao.impl.AccountDaoImpl"};
    }
}
  • 配置类:
@Configuration
@ComponentScan("com")
@Import(MyImportSelector.class)  // 导入自定义导入器
public class SpringConfig {
}

自定义导入器的封装工具类:

import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.filter.AspectJTypeFilter;
import org.springframework.core.type.filter.TypeFilter;

import java.io.IOException;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

public class CustomerImportSelector implements ImportSelector {

    private String expression;

    public CustomerImportSelector(){
        try {
            //初始化时指定加载的properties文件名
            Properties loadAllProperties = PropertiesLoaderUtils.loadAllProperties("import.properties");
            //设定加载的属性名
            expression = loadAllProperties.getProperty("path");
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        //1.定义扫描包的名称
        String[] basePackages = null;
        //2.判断有@Import注解的类上是否有@ComponentScan注解
        if (importingClassMetadata.hasAnnotation(ComponentScan.class.getName())) {
            //3.取出@ComponentScan注解的属性
            Map annotationAttributes = importingClassMetadata.getAnnotationAttributes(ComponentScan.class.getName());
            //4.取出属性名称为basePackages属性的值
            basePackages = (String[]) annotationAttributes.get("basePackages");
        }
        //5.判断是否有此属性(如果没有ComponentScan注解则属性值为null,如果有ComponentScan注解,则basePackages默认为空数组)
        if (basePackages == null || basePackages.length == 0) {
            String basePackage = null;
            try {
                //6.取出包含@Import注解类的包名
                basePackage = Class.forName(importingClassMetadata.getClassName()).getPackage().getName();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            //7.存入数组中
            basePackages = new String[] {basePackage};
        }
        //8.创建类路径扫描器
        ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
        //9.创建类型过滤器(此处使用切入点表达式类型过滤器)
        TypeFilter typeFilter = new AspectJTypeFilter(expression,this.getClass().getClassLoader());
        //10.给扫描器加入类型过滤器
        scanner.addIncludeFilter(typeFilter);
        //11.创建存放全限定类名的集合
        Set classes = new HashSet<>();
        //12.填充集合数据
        for (String basePackage : basePackages) {
            scanner.findCandidateComponents(basePackage).forEach(beanDefinition -> classes.add(beanDefinition.getBeanClassName()));
        }
        //13.按照规则返回
        return classes.toArray(new String[classes.size()]);
    }
}

自定义注册器:ImportBeanDefinitionRegistrar

  • 类型:接口

  • 作用:自定义 bean 定义注册器(识别标记了 @Component 的 bean)

示例:

  • 自定义注册器:
// 表示com目录下的bean全部注册
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    public void registerBeanDefinitions(AnnotationMetadata icm, BeanDefinitionRegistry r) {
        ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(r, false);
        TypeFilter tf = new TypeFilter() {
            public boolean match(MetadataReader mr, MetadataReaderFactory mrf) throws IOException {
                return true;
            }
        };
        scanner.addIncludeFilter(tf);  // 包含
        // scanner.addExcludeFilter(tf);  // 排除
        scanner.scan("com");
    }
}
  • 配置类:
@Configuration
@Import(MyImportBeanDefinitionRegistrar.class)  // 作用等同于 @ComponentScan("com")
public class SpringConfig {
}

封装工具类:

import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.filter.AspectJTypeFilter;
import org.springframework.core.type.filter.TypeFilter;

import java.io.IOException;
import java.util.Map;
import java.util.Properties;

public class CustomeImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    private String expression;

    public CustomeImportBeanDefinitionRegistrar(){
        try {
            //初始化时指定加载的properties文件名
            Properties loadAllProperties = PropertiesLoaderUtils.loadAllProperties("import.properties");
            //设定加载的属性名
            expression = loadAllProperties.getProperty("path");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        //1.定义扫描包的名称
        String[] basePackages = null;
        //2.判断有@Import注解的类上是否有@ComponentScan注解
        if (importingClassMetadata.hasAnnotation(ComponentScan.class.getName())) {
            //3.取出@ComponentScan注解的属性
            Map annotationAttributes = importingClassMetadata.getAnnotationAttributes(ComponentScan.class.getName());
            //4.取出属性名称为basePackages属性的值
            basePackages = (String[]) annotationAttributes.get("basePackages");
        }
        //5.判断是否有此属性(如果没有ComponentScan注解则属性值为null,如果有ComponentScan注解,则basePackages默认为空数组)
        if (basePackages == null || basePackages.length == 0) {
            String basePackage = null;
            try {
                //6.取出包含@Import注解类的包名
                basePackage = Class.forName(importingClassMetadata.getClassName()).getPackage().getName();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            //7.存入数组中
            basePackages = new String[] {basePackage};
        }
        //8.创建类路径扫描器
        ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry, false);
        //9.创建类型过滤器(此处使用切入点表达式类型过滤器)
        TypeFilter typeFilter = new AspectJTypeFilter(expression,this.getClass().getClassLoader());
        //10.给扫描器加入类型过滤器
        scanner.addIncludeFilter(typeFilter);
        //11.扫描指定包
        scanner.scan(basePackages);
    }
}

bean 初始化过程解析

Spring 注解开发_第6张图片

bean 统一初始化

  • BeanFactoryPostProcessor

    • 作用:定义了在 bean 工厂对象创建后,bean 对象创建前执行的动作,用于对工厂进行创建后业务处理。

    • 运行时机:当前操作用于对工厂进行处理,仅运行一次。

  • BeanPostProcessor

    • 作用:定义了所有 bean 初始化前后进行的统一动作,用于对 bean 进行创建前业务处理与创建后业务处理。

    • 运行时机:当前操作伴随着每个 bean 的创建过程,每次创建 bean 均运行该操作。

  • InitializingBean

    • 作用:定义了每个 bean 的初始化前进行的动作,属于非统一性动作,用于对 bean 进行创建前业务处理。

    • 运行时机:当前操作伴随着任意一个 bean 的创建过程,保障其个性化业务处理。

  • 注意:上述操作均需要被 spring 容器加载方可运行。

示例:

  • BeanFactoryPostProcessor:
package com.post;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;

public class MyBeanFactory implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        System.out.println("Bean工厂制作好了");
    }
}
  • BeanPostProcessor:
package com.post;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class MyBean implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("bean之前巴拉巴拉");
        System.out.println(beanName);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("bean之后巴拉巴拉");
        return bean;
    }
}
  • InitializingBean:
package com.service.impl;

import com.dao.UserDao;
import com.domain.User;
import com.service.UserService;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;

@Service("userService")
public class UserServiceImpl implements InitializingBean {

    // 定义当前bean初始化操作,功效等同于init-method属性配置
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("UserServiceImpl......bean ...init......");
    }
}
  • 运行结果:
Bean工厂制作好了
bean之前巴拉巴拉
springConfig
bean之后巴拉巴拉
bean之前巴拉巴拉
com.config.DataSourceConfig
bean之后巴拉巴拉
bean之前巴拉巴拉
dataSource
bean之后巴拉巴拉
bean之前巴拉巴拉
getSqlSessionFactoryBean
bean之后巴拉巴拉
bean之后巴拉巴拉
bean之前巴拉巴拉
userDao
bean之后巴拉巴拉
bean之后巴拉巴拉
bean之前巴拉巴拉
userService
UserServiceImpl......bean ...init......
bean之后巴拉巴拉
bean之前巴拉巴拉
com.service.UserServiceTest.ORIGINAL
bean之后巴拉巴拉

单个 bean 初始化

Spring 注解开发_第7张图片

  • FactoryBean:对单一的 bean 的初始化过程进行封装,达到简化配置的目的。

FactoryBean 与 BeanFactory 区别

  • FactoryBean:封装单个 bean 的创建过程。通常是为了创建另一个 bean 而做的准备工作。

  • BeanFactory:Spring 容器顶层接口,统一定义了 bean 相关的获取操作。

示例:

import org.springframework.beans.factory.FactoryBean;

public class UserServiceImplFactoryBean implements FactoryBean {

    // 重点:返回数据
    @Override
    public Object getObject() throws Exception {
        return new UserServiceImpl();
    }

    @Override
    public Class getObjectType() {
        return null;
    }

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

你可能感兴趣的:(Spring 注解开发)