四、Spring 注解


一、Spring 组件注解


1. @CompentScan 包扫描

/**
 * @author wy
 * describe `@ComponentScan`注解,包扫描。
 */
@Configuration
// 1. 扫描当前类所在包,及其子包下。
//@ComponentScan
// 2. 扫描指定包下。
//@ComponentScan(basePackages = {"com.qs.springannotation.component_scan.controller"})
// 3. 排除、包含。
@ComponentScan(basePackages = {"com.qs.springannotation.component_scan"},
        // 排除(注意:排除优先级高于包含)。
        excludeFilters = {
                // 根据`注解`排除。
                @ComponentScan.Filter(type = FilterType.ANNOTATION, value = {Controller.class}),
                // 根据`类型`排除。
                @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = {UserService.class}),
                // 根据`自定义`排除。
                @ComponentScan.Filter(type = FilterType.CUSTOM, value = CustomTypeFilter.class),
        },
        // 包含(注意:`useDefaultFilters=false`表示只扫描包含的)。
        useDefaultFilters = false,
        includeFilters = {
                @ComponentScan.Filter(type = FilterType.ANNOTATION, value = Repository.class)
        })
public class ComponentScanConfig {

    public ComponentScanConfig() {
        System.out.printf("`%s`无参构造", this.getClass().getName()).println();
    }
}

  • TypeFilter 接口
/**
 * @author wy
 * describe `TypeFilter`接口,类型过滤器。
 */
public class CustomTypeFilter implements TypeFilter {

    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        // 获取当前类的`资源信息`。
        Resource resource = metadataReader.getResource();

        // 获取当前类的`Class源信息`。
        ClassMetadata classMetadata = metadataReader.getClassMetadata();

        // 获取当前类的`注解源信息`。
        AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();

        String className = classMetadata.getClassName();
        System.out.printf("类的路径:%s", className).println();
        return className.contains("dao");
    }
}

public enum FilterType {

	/**
	 * 注解
	 */
	ANNOTATION,

	/**
	 * 类型
	 */
	ASSIGNABLE_TYPE,

	/**
	 * AspectJ
	 */
	ASPECTJ,

	/**
	 * 正则
	 */
	REGEX,

	/**
	 * 自定义
	 */
	CUSTOM

}

2. @Scope 范围

/**
 * `@Scope` 范围
 * 1. `singleton`:单例(默认,饿汉式加载,容器启动实例就创建好了)。
 * 2. `prototype`:多例(懒汉式加载,(IOC容器启动的时候,并不会创建对象,而是在第一次使用的时候才会创建)。
 * 3. `request`:同一次请求。
 * 4. `session`:同一个会话级别。
 */
@Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Bean("personPrototype")
public Person personPrototype() {
    return new Person("personPrototype");
}

/**
 * `@Scope` 范围
 */
@Test
public void testScope() {
    System.out.println("------------------------------");
    // 1. 创建容器(传入`配置类`)。
    AnnotationConfigApplicationContext applicationContext =
            new AnnotationConfigApplicationContext(BeanConfig.class);
    System.out.println("------------------------------");
    // 2. 容器中获取`Bean`。
    Object bean = applicationContext.getBean("personPrototype");
    // 3. 打印`Bean`。
    System.out.println(bean);

    // 2. 容器中获取`Bean`。
    Object bean2 = applicationContext.getBean("personPrototype");
    // 3. 打印`Bean`。
    System.out.println(bean2);
    /*
    ------------------------------
    `com.qs.springannotation.beans.Person`有参构造,`name = personPrototype`
    Person{name='personPrototype'}
    `com.qs.springannotation.beans.Person`有参构造,`name = personPrototype`
    Person{name='personPrototype'}
     */
    applicationContext.close();
}

3. @Lazy 懒加载

/**
 * `@Lazy` 懒加载(默认:非懒加载,创建即加载)。
 * 主要针对`单例Bean`,容器启动的时候,不创建对象,在第一次使用的时候才会创建该对象。
 */
@Lazy
@Bean(name = "personLazy")
public Person personLazy() {
    return new Person("personLazy");
}

/**
 * `@Lazy` 懒加载
 */
@Test
public void testLazy() {
    System.out.println("------------------------------");
    // 1. 创建容器(传入`配置类`)。
    AnnotationConfigApplicationContext applicationContext =
            new AnnotationConfigApplicationContext(BeanConfig.class);
    System.out.println("------------------------------");
    // 2. 容器中获取`Bean`。
    Object bean = applicationContext.getBean("personLazy");
    // 3. 打印`Bean`。
    System.out.println(bean);
    /*
    ------------------------------
    ------------------------------
    `com.qs.springannotation.beans.Person`有参构造,`name = personLazy`
    Person{name='personLazy'}
     */
}

4. @Conditional 指定依赖的条件

/**
 * @author wy
 * describe 条件
 */
public class ConditionalConfig {

    @Bean
    public Qs qs() {
        return new Qs(1);
    }

    /**
     * `@Conditional` 指定依赖的条件。
     * 当前容器中必须存在`qs组件`,才会创建`qsHome组件`。
     */
    @Conditional(QsCondition.class)
    @Bean
    public QsHome qsHome(Qs qs) {
        return new QsHome(qs);
    }
}

/**
 * @author wy
 * describe `Condition`接口,条件。
 */
public class QsCondition implements Condition {

    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        // 判断容器中是否存在`qs组件`。
        return conditionContext.getBeanFactory().containsBean("qs");
    }
}

/**
 * `@Conditional` 指定依赖的条件
 */
@Test
public void testConditional() {
    System.out.println("------------------------------");
    // 1. 创建容器(传入`配置类`)。
    AnnotationConfigApplicationContext applicationContext =
            new AnnotationConfigApplicationContext(ConditionalConfig.class);
    System.out.println("------------------------------");
    // 2. 容器中获取`Bean`。
    Qs qs = applicationContext.getBean(Qs.class);
    // 3. 打印`Bean`。
    System.out.println(qs);

    // 2. 容器中获取`Bean`。
    QsHome qsHome = applicationContext.getBean(QsHome.class);
    // 3. 打印`Bean`。
    System.out.println(qsHome);
    /*
    ------------------------------
    `com.qs.springannotation.conditional.Qs`有参构造,`flag = 1`
    `com.qs.springannotation.conditional.QsHome`有参构造,`qs = Qs(flag=1)`
    ------------------------------
    Qs(flag=1)
    QsHome(qs=Qs(flag=1))
     */
}

5. @Import 导入组件

/**
 * @author wy
 * describe `@Import`注解,导入`Bean`。
 * 用于导入第三方组件。
 */
@Configuration
// 1. 导入一个`Bean`。
//@Import({com.qs.springannotation.import_bean.Qs.class})
// 2. 导入多个`Bean`。
//@Import({Qs.class, QsHome.class})
// 3. `ImportSelector`接口,自动装配。
//@Import({QsHome.class, MyImportSelector.class})
// 4. `ImportBeanDefinitionRegistrar`接口,注册`Bean定义`。
@Import({QsHome.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class})
public class ImportConfig {
}

  • ImportSelector
/**
 * @author wy
 * describe `ImportSelector`自动装配
 */
public class MyImportSelector implements ImportSelector {

    /**
     * 可以获取导入类的注解信息。
     */
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        // 导入组件的id,为全类名路径。
        return new String[]{"com.qs.springannotation.import_bean.Qs"};
    }
}

  • ImportBeanDefinitionRegistrar 注册 Bean 定义。
/**
 * @author wy
 * describe `ImportBeanDefinitionRegistrar`接口,注册`Bean定义`。
 */
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
        RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(QsHome.class);
        // 导入组件,可以指定`Bean名称`。
        beanDefinitionRegistry.registerBeanDefinition("qsHome2", rootBeanDefinition);
    }
}

6. @AutoWired 自动装配

/**
 * @author wy
 * describe `@Autowired`注解,自动装配。
 */
@Data
@Service
public class AutowiredService {

    private int flag = 0;

    /**
     * `@Autowired`注解,自动装配。
     * 自动装配首先时按照`类型装配`,若在`IOC容器`中发现了多个相同类型的组件,那么就按照`属性名称`来进行装配。
     * <p>
     * 比如:容器中有二个`AutowiredDao`类型的组件,`autowiredDao`和`autowiredDao1`。
     * 1. 通过`@AutoWired`来修饰的`autowiredDao`属性名称时,那么就加载容器的`autowiredDao`组件。
     * 2. 若属性名称为`autowiredDao1`时,那么就加载`autowiredDao1`组件。
     */
    @Autowired
    private AutowiredDao autowiredDao;

    @Autowired
    private AutowiredDao autowiredDao1;

    /**
     * 假设容器中即没有`autowiredDao9`,那么在装配的时候就会抛出异常。
     * No qualifying bean of type 'com.qs.springannotation.autowired.dao.AutowiredDao' available:
     * expected single matching bean but found 3: autowiredDao,autowiredDao1,autowiredDao2
     * 若不想抛异常,需要指定`required=false`就可以了。
     */
    @Autowired(required = false)
    @Qualifier(value = "autowiredDao9")
    private AutowiredDao autowiredDao9;
}

6.1 @Autowired 方法注入
/**
 * 标注在`配置类上的入参`中(可以不写)。
 */
@Bean
public AutowiredMethodService autowiredMethodService2(@Autowired AutowiredDao autowiredDao) {
    AutowiredMethodService service = new AutowiredMethodService();
    service.setAutowiredDao(autowiredDao);
    return service;
}

@Data
@Primary
@Service
public class AutowiredMethodService {

    @Autowired
    private AutowiredDao autowiredDao;

    public AutowiredMethodService() {
        System.out.printf("`%s`无参构造", this.getClass().getName()).println();
    }

    /**
     * 标注在`构造方法`上。
     */
    @Autowired
    public AutowiredMethodService(AutowiredDao autowiredDao1) {
        System.out.printf("`%s`有参构造,`autowiredDao = %s`", this.getClass().getName(), autowiredDao).println();
        this.autowiredDao = autowiredDao1;
    }

    public AutowiredDao getAutowiredDao() {
        return autowiredDao;
    }

    /**
     * 标注在`setXX`上。
     */
    @Autowired
    public void setAutowiredDao(AutowiredDao autowiredDao2) {
        System.out.printf("`%s`.setAutowiredDao,`autowiredDao = %s`", this.getClass().getName(), autowiredDao).println();
        this.autowiredDao = autowiredDao2;
    }
}

7. @Qualifier 指定名称装配

/**
 * @author wy
 * describe `@Qualifier`指定名称装配
 */
@Data
@Service
public class QualifierService {

    private int flag = 1;

    /**
     * `@Qualifier`注解,指定名称装配。
     * <p>
     * `@AutoWired`默认使用`BY_NAME`。
     * 当发现多个`同类型的Bean`,就通过`BY_NAME`。
     * 需要通过名称装配,使用`@Qualifier`指定名称装配。
     */
    @Qualifier(value = "autowiredDao1")
    @Autowired
    private AutowiredDao autowiredDao;

    @Qualifier(value = "autowiredDao")
    @Autowired
    private AutowiredDao autowiredDao1;

}

8. @Primary 存在多个按类型装配时标记默认的

/**
 *`@Primary`注解,按类型(`BY_TYPE`)装配时优先。
 */
@Primary
@Bean
public AutowiredDao autowiredDao2() {
    AutowiredDao dao = new AutowiredDao();
    dao.setFlag(2);
    return dao;
}

/**
 * 默认通过类型(`BY_TYPE`),当发现多个`同类型的Bean`,就通过名称(`BY_NAME`)。
 */
@Primary
@Bean(autowire = Autowire.BY_TYPE)
//    @Bean(autowire = Autowire.BY_NAME)
public AutowiredService autowiredService3() {
    AutowiredService service = new AutowiredService();
    service.setFlag(3);
    return service;
}

/**
 * @author wy
 * describe `@Primary`注解,按类型(`BY_TYPE`)装配时优先。
 */
@Data
@Service
public class PrimaryService {

    private int flag = 2;

    /**
     * `@Autowired`失败问题。
     * 1. `Service`使用类型(`BY_TYPE`)注入时,`Dao`会注入`@Primary`修饰的。
     * 2. `Service`使用名称(`BY_NAME`)注入时,`Dao`只能使用名称注入。
     */
    @Autowired
    private AutowiredDao autowiredDao;

    @Qualifier(value = "autowiredDao1")
    @Autowired
    private AutowiredDao autowiredDao1;

    @Autowired
    private AutowiredDao autowiredDao2;
}

10. @Resource JSR250 规范

/**
 * @author wy
 * describe `@Resource`注解,`JSR250`规范。
 * 和`@AutoWired`的功能差不多一样,但是不支持`@Primary`和`@Qualifier`。
 */
@Data
@Service
public class ResourceService {

    @Resource
    private AutowiredDao resourceDao;
}

11. @InJect JSR330 规范

/**
 * @author wy
 * describe `@InJect`注解,`JSR330`规范。
 * 需要导入`jar`包`javax.inject`。
 * 和`@AutoWired`的功能差不多一样,支持`@Primary`和`@Qualifier`,但是没有`require=false`的功能。
 */
@Service
@Data
public class InjectService {

    @Inject
    private AutowiredDao injectDao;
}

二、Spring 配置注解


1. @Profile 环境注入

/**
 * @author wy
 * describe `@Profile`注解,环境注入,根据环境来激活标识不同的Bean。
 * 1. `@Profile`标识在类上,那么只有当前环境匹配,整个配置类才会生效。
 * 2. `@Profile`标识在`Bean`上 ,那么只有当前环境的Bean才会被激活。
 * 3. 没有标志为`@Profile`的`Bean`,不管在什么环境都可以被激活。
 * <p>
 * `EmbeddedValueResolverAware`接口,内嵌式值解析器。
 */
@Configuration
@PropertySource(value = {"classpath:ds.properties"})
public class ProfileConfig implements EmbeddedValueResolverAware {

    private String classDriver;

    private String jdbcUrl;

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

    @Value("${ds.password}")
    private String password;

    @Override
    public void setEmbeddedValueResolver(StringValueResolver stringValueResolver) {
        this.jdbcUrl = stringValueResolver.resolveStringValue("${ds.jdbcUrl}");
        this.classDriver = stringValueResolver.resolveStringValue("${ds.classDriver}");
    }

    /**
     * 标识为测试环境,才会被装配。
     */
    @Bean
    @Profile(value = "test")
    public DataSource testDS() {
        return buildDataSource(new DruidDataSource());
    }

    /**
     * 标识开发环境,才会被激活。
     */
    @Bean
    @Profile(value = "dev")
    public DataSource devDS() {
        return buildDataSource(new DruidDataSource());
    }

    /**
     * 标识生产环境,才会被激活。
     */
    @Bean
    @Profile(value = "prod")
    public DataSource prodDS() {
        return buildDataSource(new DruidDataSource());
    }

    private DataSource buildDataSource(DruidDataSource dataSource) {
        dataSource.setDriverClassName(classDriver);
        dataSource.setUrl(jdbcUrl);
        dataSource.setUsername(userName);
        dataSource.setPassword(password);
        return dataSource;
    }
}

AnnotationConfigApplicationContext applicationContext =
                new AnnotationConfigApplicationContext();
// 设置环境属性(或者JVM参数`-Dspring.profiles.active=test,dev,prod`)
applicationContext
	.getEnvironment()
	.setActiveProfiles("test", "dev");

2. @PropertySource + @Value 组件赋值

  • @PropertySource 导入属性配置文件
/**
 * @author wy
 * describe `@PropertySource`导入属性配置文件。
 */
@Configuration
@PropertySource(value = {"classpath:person.properties"})
public class ValueConfig {

    @Bean
    public Person person() {
        return new Person();
    }
}

  • @Value 注入属性
/**
 * @author wy
 * describe `@Value`注解,注入属性
 */
@Data
public class Person {

    @Value("骑士")
    private String firstName;

    /**
     * `SPEL`表达式。
     */
    @Value("#{28-8}")
    private Integer age;

	/**
     * 读取外部配置文件的值
     */
    @Value("${person.lastName}")
    private String lastName;
}

3. @ConditionalOnExpression 控制是否初始化 Bean

cache.service=redis

@ConditionalOnExpression("'${cache.service:default}'.equals('redis')")
@Service
public class CacheServiceRedisImpl implements CacheService {

    @Override
    public String getName() {
        return this.getClass().getName();
    }

}

@ConditionalOnExpression("'${cache.service:default}'.equals('default')")
@Service
public class CacheServiceDefaultImpl implements CacheService {

    @Override
    public String getName() {
        return this.getClass().getName();
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

骑士梦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值