Spring|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|Spring 注解开发
文章图片

注解驱动的弊端:
  • 为了达成注解驱动的目的,可能会将原先很简单的书写,变得更加复杂。
  • XML 中配置第三方开发的资源是很方便的,但使用注解驱动无法在第三方开发的资源中进行编辑,因此会增大开发工作量。
Spring|Spring 注解开发
文章图片


常用注解 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
  • 【Spring|Spring 注解开发】类型:方法注解
  • 位置:方法定义上方。
  • 作用:设置该类作为 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="https://www.it610.com/article/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|Spring 注解开发
文章图片

注解整合 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|Spring 注解开发
文章图片


组件扫描器:@ComponentScan 组件扫描器:开发过程中,需要根据需求加载必要的 bean 或排除指定 bean。
Spring|Spring 注解开发
文章图片

应用场景:
  • 数据层接口测试
  • 业务层接口测试
  • 各种运行环境设置
配置扫描器
  • 名称:@ComponentScan
  • 类型:类注解
  • 位置:类定义上方
  • 作用:设置 spring 配置加载类扫描规则
  • 范例:
@Configuration @ComponentScan( value="https://www.it610.com/article/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 = "https://www.it610.com/article/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|Spring 注解开发
文章图片

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|Spring 注解开发
文章图片

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

    推荐阅读