Spring 注解驱动开发学习笔记(一) -- Spring 注解

1. Spring 通用注解

1.1 @Configuration

标识在配置类上, 告诉 Spring 这是一个配置类, 配置类 == 配置文件

@Configuration
public class MainConfig {
}

1.2 @Bean

给容器注册一个 Bean, 类型(class)是返回值类型, id 默认是用方法名作为类型,

  • name, value
    指定 id, 两个属性用 @AliasFor 注解标识了, 互为别名, 若同时出现其值必须一样

  • initMethod, destroyMethod
    指定 Bean 的初始化和销毁方法

@Bean(name = "person", initMethod = "init", destroyMethod = "destroy")
public Person person() {
    return new Person("lisi", 20);
}

1.3 AnnotationConfigApplicationContext

  • 使用 xml 文件时用 ClassPathXmlApplicationContext
  • 使用配置类时用 AnnotationConfigApplicationContext
new AnnotationConfigApplicationContext(MainConfig.class);

1.4 @ComponentScan

标识在配置类上

  • basePackages
    指定要扫描的包

  • excludeFilters
    excludeFilters = Filter[]: 指定扫描的时候按照什么规则排除哪些组件

  • includeFilter
    includeFilters = Filter[]: 指定扫描的时候只需要包含哪些组件

  • useDefaultFilters
    默认为 true,会扫描到 @Service @Reposity @Controller 等,所以当使用 includeFilter 时, 需要配置成 false,只扫描指定的注解

1.4.1 @ComponentScan.Filter

表示 Filter[] 数组中的一个 Filter

  • type 属性
    FilterType.ANNOTATION: 按照注解类型
    FilterType.ASSIGNABLE_TYPE: 按照给定的类型
    FilterType.ASPECTJ: 使用 ASPECTJ 表达式, 不常用
    FilterType.REGEX: 使用正则指定
    FilterType.CUSTOM: 使用自定义规则(实现 org.springframework.core.type.filter.TypeFilter 接口)
  • classes 属性
    FilterType.ANNOTATION: 指定包含或不包含的注解的 class
    FilterType.ASSIGNABLE_TYPE: 指定包含或不包含的类的 class, 包含子类和实现类
    FilterType.CUSTOM: 指定自定义规则的实现类

1.5 @ComponentScans

可以多指定几个 @ComponentScan, 若只有一个 @ComponentScan, 则可以不写

  • value
    将多个 @ComponentScan 写在其中
@ComponentScans(value = {
        @ComponentScan(basePackages = "com.tc",
//                excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class})}
                includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class})},
                useDefaultFilters = false
        )}
)
public class MainConfig {
}

1.6 @Scope

可以在实体类上标识, 也可以在标识了 @Bean 注解的方法上标识

  • scopeName
    prototype: 多实例的: IOC 容器启动并不会去调用方法创建对象放在容器中, 每次获取的时候才会调用方法创建对象
    singleton: 单实例的(默认值): IOC 容器启动会调用方法创建对象放到 IOC 容器中, 以后每次获取就是直接从容器(map.get())中拿
    request: 同一次请求创建一个实例
    session: 同一个 session 创建一个实例
@Scope(scopeName = "prototype")
public Person person() {
}

1.7 @Lazy

懒加载
单实例 bean: 默认在容器启动的时候创建对象
懒加载: 容器启动不创建对象, 第一次使用(获取) Bean 创建对象, 并初始化

1.8 @Conditional

按照一定的条件进行判断, 满足条件才给容器中注册 bean
向注解中传入 实现了 Condition 的类的 Class 数组作为条件

/**
 * 若系统为 Windows 系统, 则满足条件
 */
public class WindowsCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
	    // 获取当前环境信息(包括环境变量等系统信息)
	    Environment environment = context.getEnvironment();
        String property = environment.getProperty("os.name");
        return property != null && property.contains("Windows");
    }
}

可以在方法上标识

@Bean(name = "bill")
@Conditional({WindowsCondition.class})
public Person person1() {
    return new Person("Bill Gates", 62);
}

也可以在类上标识

// 类中组件统一设置, 满足当前条件, 这个类中配置的所有 bean 注册时才能生效
@Conditional(WindowsCondition.class)
public class MainConfig {
}

1.9 @Import

  • 直接传入目标类的 Class 对象
public class Color {
}
// @Import 导入组件, id 默认是组件的全类名
@Import({Color.class})
public class MainConfig2 {
}
  • 还可以传入实现了 org.springframework.context.annotation.ImportSelector 接口的实现类, 来实现导入多个组件
public class MyImportSelector implements ImportSelector {
    /**
     * @param importingClassMetadata 当前标注 @Import 注解的类的所有注解信息
     * @return 导入到容器中的组件全类名
     */
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"全类名1", "全类名2", ...};
    }
}
@Import({MyImportSelector.class})
public class MainConfig2 {
}
  • 还可以传入实现了 org.springframework.context.annotation.ImportBeanDefinitionRegistrar 接口的实现类
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    /**
     * @param importingClassMetadata 当前类的注解信息
     * @param registry               BeanDefinition 注册类, 把所有需要添加到容器中的 bean,
     *                               调用 BeanDefinitionRegistry.registerBeanDefinition 手动注册进来
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        boolean definition = registry.containsBeanDefinition("Red");
        boolean definition2 = registry.containsBeanDefinition("Blue");
        if (definition && definition2) { // 如果容器中有 id 为 "Red" 和 "Blue" 的 Bean
            // 向容器中添加一个 Color 类的对象, id 为 color
            RootBeanDefinition beanDefinition = new RootBeanDefinition(Color.class);
            registry.registerBeanDefinition("color", beanDefinition);
        }
    }
}
@Import({MyImportBeanDefinitionRegistrar.class})
public class MainConfig2 {
}

2. Spring 注解 – 生命周期

2.1 @Bean

@Bean 的 init-method 和 destroy-method 与生命周期相关, 上面讲过, 不多赘述

2.2 InitializingBean, DisposableBean

通过让 Bean 实现 InitializingBean(定义初始化逻辑), DisposableBean(定义销毁逻辑)

@Component
public class Cat implements InitializingBean, DisposableBean {
    @Override
    public void destroy() throws Exception {
        System.out.println("Cat Destroy");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("Cat AfterPropertiesSet");
    }
}

2.3 @PostConstruct, @PreDestroy

@Component
public class Dog {
    // 对象创建并赋值之后调用
    @PostConstruct
    public void init() {
        System.out.println("Dog @PostConstruct");
    }

    // 容器移除对象之前调用
    @PreDestroy
    public void destroy() {
        System.out.println("Dog @PreDestroy");
    }
}

2.4 BeanPostProcessor

实现 org.springframework.beans.factory.config.BeanPostProcessor 接口, 并注册到 IOC 容器中, 可以在所有 Bean 的初始化前后工作

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessBeforeInitialization -> " + beanName + " -> " + bean);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessAfterInitialization -> " + beanName + " -> " + bean);
        return bean;
    }
}

3. Spring 注解 – 属性赋值

3.1 @Value

使用 @Value 赋值

  1. 基本数值
  2. 可以写 SpEL #{}
  3. 可以写 ${}, 取出配置文件 [properties] 中的值(在运行环境变量里面的值), 与 @PropertySource 配合使用
// person.properties 内容
person.nickName=小张三
public class Person {
    @Value("张三")
    private String name;
    @Value("#{20 - 2}")
    private Integer age;
    @Value("${person.nickName}")
    private String nickName;
}

3.2 @PropertySource

读取外部配置文件中的 k/v 对, 保存到运行的环境变量中, 加载完外部的配置文件以后使用 ${} 取出配置文件的值

  • value
    为 String 数组, 可包含多个配置文件的路径
@Configuration
@PropertySource(value = {"classpath:/person.properties"})
public class MainConfigOfPropertyValues {
}

4. Spring 注解 – 自动注入

4.1 @Autowired

默认优先按照类型去容器中找对应的组件, 如果找到多个相同类型的组件, 再将属性的名称作为组件的 id 去容器中查找

  • required
    若在 IOC 容器中没有匹配的属性, 则可以不强制要求装配
@Autowired(required = false)

4.2 @Qualifier

指定需要装配的组件的 id, 而不是使用属性名

@Service
public class BookService {
    private BookDao bookDao;

    @Autowired
    @Qualifier("bookDao2")
    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }
}

4.3 @Primary

指定 Spring 自动装配时首选的 bean, 也可以继续使用 @Qualifier 指定需要装配的 bean 的名字

/*
 * 若无 @Qualifier 指定, 首先使用此 Bean
 */
@Bean(name = "bookDao2")
@Primary
public BookDao bookDao() {
    return new BookDao();
}

4.4 @Resource

可以和 @Autowired 一样实现自动装配功能. 默认是按照组件名称进行装配的
不支持 @Primary, 不支持类似 @Autowired(required = false) 的功能

  • name
    指定自动注入的 Bean 的 name, 可以不写, 默认按照组件名称进行装配的
@Service
public class BookService {
	private BookDao bookDao;
	
    @Resource(name = "bookDao2")
    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }
}

4.5 @Inject

需要导入 javax.inject 的包, 和 @Autowired 的功能类似. 但是没有 required = false 的功能

@Service
public class BookService {
    private BookDao bookDao;
    
    @Inject
    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }
}

4.6 ApplicationContextAware, BeanNameAware, EmbeddedValueResolverAware

把 Spring 底层一些组件注入到自定义的 Bean 中

  1. ApplicationContextAware: 得到 ApplicationContext
  2. BeanNameAware: 获得当前 Bean 的 name
  3. EmbeddedValueResolverAware: 获得一个解析字符串的 Resolver 解析器
@Component
public class Red implements ApplicationContextAware, BeanNameAware, EmbeddedValueResolverAware {
    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("传入的 ApplicationContext: " + applicationContext);
        this.applicationContext = applicationContext;
    }

    @Override
    public void setBeanName(String name) {
        System.out.println("当前 Bean 的名字: " + name); // 当前 Bean 的名字: red
    }

    @Override
    public void setEmbeddedValueResolver(StringValueResolver resolver) {
        String resolveStringValue = resolver.resolveStringValue("你好 ${os.name} 我是 #{20*18}");
        System.out.println("解析的字符串: " + resolveStringValue); // 解析的字符串: 你好 Windows 10 我是 360
    }
}

4.7 @Profile

指定组件在哪个环境的情况下才能被注册到容器中, 不指定, 则任何环境下都能注册这个组件

  1. 加了 @Profile 标识的 bean, 只有这个环境被激活的时候才能注册到容器中, 默认是 default 环境
  2. 写在配置类上, 只有是指定的环境的时候, 整个配置类里面的所有配置才能开始生效
  3. 没有 @Profile 标识, 没有标注环境标识的 bean, 在任何环境下都是加载的
@Configuration
@Profile("test")
public class MainConfigOfProfile {
	/**
	 * 注册一个 DataSource, 要求必须在 test 环境下才能被创建
	 */
	@Bean(name = "testDataSource")
	@Profile("test")
	public DataSource dataSourceTest() throws PropertyVetoException {
	    ComboPooledDataSource dataSource = new ComboPooledDataSource();
	    dataSource.setUser("user");
	    dataSource.setPassword("password");
	    dataSource.setDriverClass("driverClass");
	    dataSource.setJdbcUrl("jdbcUrl");
	    return dataSource;
	}
}

激活指定的环境:

  1. 使用命令行动态参数: 在虚拟机参数位置加 -Dspring.profiles.active=test
  2. 代码的方式激活某种环境(要用 ApplicationContext 的无参构造器)
// 1. 创建一个applicationContext
ApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 2. 设置需要激活的环境, String... 可传入多个要激活的环境
applicationContext.getEnvironment().setActiveProfiles("dev");
// 3. 注册主配置类
applicationContext.register(MainConfigOfProfile.class);
// 4. 启动刷新容器
applicationContext.refresh();
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值