Spring注解开发【转载】

原文链接:Spring 注解开发 - Juno3550 - 博客园 (cnblogs.com)

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

Spring注解开发,你还有什么惊喜是朕不知道的?
注解驱动的弊端:

为了达成注解驱动的目的,可能会将原先很简单的书写,变得更加复杂。
XML 中配置第三方开发的资源是很方便的,但使用注解驱动无法在第三方开发的资源中进行编辑,因此会增大开发工作量。
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文件的配置:context:property-placeholder
组件扫描的配置:context:component-scan
引入其他文件:
注解

说明

@Configuration

用于指定当前类是一个 Spring 配置类,当创建容器时会从该类上加载注解。用于指定 Spring 在初始化容器时要扫描的包。

@ComponentScan

作用和在 Spring 的 xml 配置文件中的 <context:component-scan base-package=“com.itheima”/> 一样。

@Bean

使用在方法上,标注将该方法的返回值存储到 Spring 容器中。

@PropertySource

用于加载 .properties 文件中的配置。

@Import

用于导入其他配置类。

启用注解功能
启动注解扫描,加载类中配置的注解项:
<context:component-scan base-package=“packageName”/>
说明:在进行包所扫描时,会对配置的包及其子包中所有文件进行扫描。扫描过程是以文件夹递归迭代的形式进行的。扫描过程仅读取合法的 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:

AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);



导入第三方配置:@Import
类型:类注解
位置:类定义上方。
作用:导入第三方 bean 作为 spring 控制的资源。
范例:
@Configuration
@Import(OtherClassName.class)
public class ClassName {
}
说明:@Import 注解在同一个类上,仅允许添加一次,如果需要导入多个,使用数组的形式进行设定。在被导入的类中可以继续使用 @Import 导入其他资源(了解)。@Bean 所在的类可以使用导入的形式进入 spring 容器,无需声明为 bean 。



综合案例
maven 依赖:

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.16</version>
        </dependency>
    </dependencies>
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;

// 数据源配置类
// 相当于 <context:property-placeholder location="classpath:jdbc.properties"/>,且不能用通配符*
@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")  // 相当于 <context:component-scan base-package="com"/>
@Import({DataSourceConfig.class})  // 相当于 <import resource=""/>
public class SpringConfig {

}
dao 层
UserDaoImpl.java:

package com.dao.impl;

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

// 相当于 <bean id="UserDao" ref="com.dao.impl.UserDaoImpl"/>
// @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;

// 相当于 <bean id="UserService" ref="com.service.impl.UserServiceImpl"/>
// @Component("userService")
@Service("userService")
public class UserServiceImpl implements UserService {

    // <property name="userDao" ref="userDao"></property>
    // @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注解开发,你还有什么惊喜是朕不知道的?
注解整合 MyBatis 的开发步骤:

修改 mybatis 外部配置文件格式为注解格式;
业务类使用 @Component 声明 bean,使用 @Autowired 注入对象;
建立配置文件 JDBCConfigMyBatisConfig 类,并将其导入到核心配置类 SpringConfig;
开启注解扫描;
使用 AnnotationConfigApplicationContext 对象加载配置项。
项目工程地址

核心内容如下:

Maven 依赖:
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.11</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>4.3.8.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.16</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.3.0</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.3</version>
        </dependency>
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整合mybatis后,创建连接用的对象 -->
    <bean class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="typeAliasesPackage" value="com.domain"/>
    </bean>

    <!-- 扫描mybatis映射配置,将其作为spring的bean进行管理 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.dao"/>
    </bean>
     */

    // 以下注解代替以上配置文件内容:返回值会作为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")  // 相当于 <context:component-scan base-package="com"/>
@Import({DataSourceConfig.class, MybatisConfig.class})  // 相当于 <import resource=""/>
public class SpringConfig {

}



注解整合 Junit
注解整合 Junit 的开发步骤:

Spring 接管 Junit 的运行权,使用 Spring 专用的 Junit 类加载器;
2.Junit 测试用例设定对应的 Spring 容器:

从 Spring 5.0 以后,要求 Junit 的版本必须是 4.12 或以上。
Junit 仅用于单元测试,不能将 Junit 的测试类配置成 Spring 的 bean,否则该配置将会被打包进入工程中。
示例:整合 Junit5

Maven 依赖:
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>5.8.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>
测试类:
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<User> users = userService.findAll();
        Assertions.assertEquals(3, users.size());
    }

}



IoC 底层核心原理
IoC 核心接口
Spring注解开发,你还有什么惊喜是朕不知道的?



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

Spring注解开发,你还有什么惊喜是朕不知道的?
应用场景:

数据层接口测试
业务层接口测试
各种运行环境设置
配置扫描器
名称:@ComponentScan
类型:类注解
位置:类定义上方
作用:设置 spring 配置加载类扫描规则
范例:
@Configuration
@ComponentScan(
    value="com",  // 设置基础扫描路径
    excludeFilters =      // 设置过滤规则,当前为排除过滤
    @ComponentScan.Filter(            // 设置过滤器
        type= FilterType.ANNOTATION,  // 设置过滤方式为按照注解进行过滤
        classes=Repository.class)     // 设置具体的过滤项。如不加载所有@Repository修饰的bean
)
public class SpringConfig {

}
includeFilters:设置包含性过滤器
excludeFilters:设置排除性过滤器
type:设置过滤器类型(过滤策略)ANNOTATIONASSIGNABLE_TYPEASPECTJREGEXCUSTOM
自定义扫描器
名称: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 文件中使用 <bean/> 标签配置
使用 @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<String, Object> 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<String> 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<String, Object> 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注解开发,你还有什么惊喜是朕不知道的?
bean 统一初始化
BeanFactoryPostProcessor作用:定义了在 bean 工厂对象创建后,bean 对象创建前执行的动作,用于对工厂进行创建后业务处理。运行时机:当前操作用于对工厂进行处理,仅运行一次。
BeanPostProcessor作用:定义了所有 bean 初始化前后进行的统一动作,用于对 bean 进行创建前业务处理与创建后业务处理。运行时机:当前操作伴随着每个 bean 的创建过程,每次创建 bean 均运行该操作。
InitializingBean作用:定义了每个 bean 的初始化前进行的动作,属于非统一性动作,用于对 bean 进行创建前业务处理。运行时机:当前操作伴随着任意一个 bean 的创建过程,保障其个性化业务处理。
注意:上述操作均需要被 spring 容器加载方可运行。
示例:

BeanFactoryPostProcessorpackage 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工厂制作好了");
    }
}
BeanPostProcessorpackage 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;
    }
}
InitializingBeanpackage 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注解开发,你还有什么惊喜是朕不知道的?
FactoryBean:对单一的 bean 的初始化过程进行封装,达到简化配置的目的。
FactoryBeanBeanFactory 区别:

FactoryBean:封装单个 bean 的创建过程。通常是为了创建另一个 bean 而做的准备工作。
BeanFactorySpring 容器顶层接口,统一定义了 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 注解开发 - Juno3550 - 博客园 (cnblogs.com)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值