Spring 注解回顾

在Spring框架里面常用的注解其实就那么几个,但是深入源码分析如果不懂很多的注解,源码是很难看懂的。

  • 一、@Configuration
  • 二、@Bean
  • 三、@ComponentScan
  • 四、@Scope
  • 五、@Condition
  • 六、@Import
  • 七、使用FactoryBean注册组件
  • 八.在这里也算上@Compont组件,为了后面好区分@Configuration

 

给容器中注册组件常用

包扫描+组件标注注解(@Component、@Service、@Controller、@Repository),主要是自己写的类,这三个用于不同的场景。
@Bean [导入的第三方包里面的组件]
@Import [快速给容器中导入一个组件]
Import(类名.class),容器中就会自动注册这个组件,id默认是组件的全名
ImportSelector:返回需要导入的组件的全类名的数组
ImportBeanDefinitionRegistrar:手动注册bean
使用Spring提供的FactoryBean(工厂bean)
默认获取到的是工厂bean调用getObject创建的对象
要获取到bean本身,需要给id前面加个&标识
@Conditional({Condition}) :按照一定的条件判断,满足条件给容器中注册bean
@Scope
prototype:多例的 ioc容器启动并不会去调用方法创建对象在容器中,而是每次获取时才会调用方法创建对象
singleton:单例的(默认值) ioc容器启动会调用方法创建对象放到ioc容器中,以后每次获取就是从容器中拿


一、@Configuration

@Configuration : 把一个类标记为spring的配置类,相当于之前的applicationContext.xml文件
1、看看之前通过applicationContext.xml配置文件来创建类的实例

public class SomeBean {}
View Code
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 创建SomeBean的实例,交给IoC来管理 -->
    <bean id="somebean" class="com.zy._01_hello.SomeBean"/>
</beans>
View Code

测试方法

public class SomeBeanTest {
    /*
        Spring XML Config
     */
    @Test
    public void test(){
        // 加载配置文件
        ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        // 从Spring容器中获取SomeBean的实例
        SomeBean someBean = ctx.getBean("somebean", SomeBean.class);
        System.out.println(someBean);
    }
}
View Code

这种方式是通过Spring XML Config的方式来将类交给Spring容器处理; 但是后来发现有很多这样的xml不容易管理,形成了配置类; 于是就有了后来的SpringBoot 在SpringBoot中几乎看不到配置文件了,取而代之的是Spring Java Config的配置类的形式!

2、创建配置类
// 把一个类标记为spring的配置类; (类名Config可以简单理解为XML中的 beans)

@Configuration
public class Config {

    @Bean
    public SomeBean somebean(){
        return new SomeBean();
    }
}
View Code

测试方法

public class SomeBeanTest {
    /*
        Spring Java Config
     */
    @Test
    public void test1(){
        ApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
        SomeBean someBean = ctx.getBean(SomeBean.class);
        System.out.println(someBean);
    }
}
View Code

这里要通过AnnotationConfigApplicationContext(config.class)来加载配置类,然后拿到配置类中SomeBean的实例即可(将组件的创建交给Spring容器处理, 也就是将组件注册到容器中)!


二、@Bean

  • @Bean相当于在配置文件中写的<bean id="" class="" />, 将一个类的创建实例交给Spring IoC来处理;

配置文件中写的bean

<bean id="" class="" name="" init-method="" destory-method="" scope="">
    <property name="" value=""/>
    <property name="" ref=""/>
</bean>
View Code

配置类中写的bean

@Configuration
public class Config {
    @Bean
    public SomeBean someBean1() {
        return new SomeBean();
    }

    @Bean
    public SomeBean someBean2() {
        return new SomeBean();
    }

    @Bean(name = {"sb", "sbb"})
    public SomeBean someBean3() {
        return new SomeBean();
    }
}
View Code

 

1、在配置类中@Bean的含义

  • 被@Bean标注的方法的名字 —> bean的id
  • 方法的返回值类型 —> bean的class类型
  • 除了默认使用方法的名字作为id外, 还可以通过@Bean(name={"xxx1", "xxx2"})来指定多个id名
@Test
public void test2() {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
    SomeBean someBean1 = ctx.getBean("someBean1", SomeBean.class); // bean的id去找
    System.out.println(someBean1);

    SomeBean someBean2 = ctx.getBean("someBean2", SomeBean.class);
    System.out.println(someBean2);

    SomeBean someBean3 = ctx.getBean("sb", SomeBean.class);
    System.out.println(someBean3);

    SomeBean someBean4 = ctx.getBean("sbb", SomeBean.class);
    System.out.println(someBean4);
}
View Code

 

2、配置initMethod、destroyMethod的方法
构造(对象创建)

  • 单例: 在容器启动(加载配置类/加载配置文件)的时候创建对象;

                    容器启动先创建对象, 然后调用init(初始化方法);

  • 多例: 在每次获取bean(getBean())的时候创建对象;

                    容器创建完成之后, 才创建对象完成init(初始化);
方式一: 可以在@Bean中的属性initMethod, destroyMethod来指定初始化,销毁方法

@Bean(name="sb", initMethod = "init", destroyMethod = "destory")
View Code

方式二: @PostConstruct,@PreDestroy来指定初始化,销毁方法

public class SomeBean {

    // 方式二: 配置init,destory  @PostConstruct,@PreDestroy

    @PostConstruct
    public void init() {
        System.out.println("SomeBean.init");
    }

    @PreDestroy
    public void destory() {
        System.out.println("SomeBean.destory");
    }
}
View Code

测试方法

public class SomeBeanTest {
    /*
        Spring Java Config
     */
    @Test
    void test(){
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
        SomeBean someBean = ctx.getBean("sb", SomeBean.class);
        System.out.println(someBean);
        ctx.close(); // 非Spring Test,容器不会正常关闭,调用close方法才可以
    }
}
View Code

3、@Bean依赖注入的方式

otherBean实体

public class OtherBean {
}

@Setter
@Getter
public class SomeBean {
    private OtherBean otherBean;
}
View Code

 

方式一: 通过类似内部bean的方式

@Configuration
public class Config {
    /*
        方式一: 相当于内部Bean的形式
        <bean id="" class="">
            <property name="otherBean">
                <bean class="" />  内部bean的方式
            </property>
        </bean>
        这种方式用的很少!
     */
    @Bean
    public SomeBean someBean() {
        SomeBean sb = new SomeBean();
        sb.setOtherBean(new OtherBean());
        return sb;
    }
}
View Code

 

方式二: 通过调用需要注入的Bean的方式名()即可

@Configuration
public class Config {
    @Bean
    public SomeBean someBean() {
        SomeBean sb = new SomeBean();
        sb.setOtherBean(otherBean());
        return sb;
    }

    @Bean
    public SomeBean someBean2() {
        SomeBean sb = new SomeBean();
        sb.setOtherBean(otherBean());
        return sb;
    }

    @Bean
    public OtherBean otherBean() {
        return new OtherBean();
    }
}
View Code

方式三: 需要依赖的Bean, 放入到参数列表中,会自动注入;
            当有多个OtherBean的实例时,可以使用 @Qualifier("bean的id")来指定
    有多个OtherBean时,在某个bean上添加 @Primary, 会优先注入该bean
    有多个OtherBean时,在参数列表中通过形参名称来指定对应的bean

@Bean
// public SomeBean someBean(@Qualifier("otherBean") OtherBean ob) {
public SomeBean someBean(OtherBean otherBean) {
    SomeBean sb = new SomeBean();
    sb.setOtherBean(otherBean);
    return sb;
}

@Bean
//@Primary
public OtherBean otherBean() {
    return new OtherBean("ob1");
}

@Bean
public OtherBean otherBean2() {
    return new OtherBean("ob2");
}
View Code

三、@ComponentScan

可以完成Spring组件的自动扫描(默认情况下,会去扫描被标注的类的对应的包(及其子包中)的所有的类;

配置类Config

@Configuration
// 可以完成Spring组件的自动扫描(默认情况下,会去扫描被标注的类的对应的包(及其子包中)的所有的类
//@ComponentScan(basePackages = "com.zy._04_componentscan") // 也可以自己指定扫描的范围
@ComponentScan
public class Config {
}
View Code

 

OtherBean和SomeBean类

@Component //设置该类作为Spring管理的组件
public class OtherBean {
}

@Component
public class SomeBean {
    @Autowired // 将Spring通过@Component创建好的OtherBean的实例,注入到下面的属性中
    private OtherBean otherBean;
}
View Code

组件注册时的过滤条件

  • @ComponentScan value:指定要扫描的包
  • excludeFilters=Filter[]:指定扫描包的时候按照什么规则排除哪些组件
  • includeFilters=Filter[]:指定扫描包的时候要包含哪些组件,需将useDefaultFilters置false
  • FilterType.ANNOTATION:按照注解
  • FilterType.ASSIGNABLE_TYPE:按照指定的类型
  • FilterType.REGEX:使用正则指定
  • FilterType.CUSTOM:使用自定义规则
// 配置类 == 配置文件
@Configuration  // 告诉Spring这是一个配置类
//@ComponentScan(value = "com.zy")    // 不写默认扫描当前类所在包(及其子包)下的所有类(指定要扫描的包)

//@ComponentScan(value = "com.zy", excludeFilters = {
//        // excludeFilters: FilterType是过滤条件(这里是根据注解来过滤); 将Controller,Service的bean过滤掉(不扫描它们)
//        @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class})
//})
@ComponentScan(value = "com.zy", includeFilters = {
        // excludeFilters: FilterType是过滤条件(这里是根据注解来过滤); 将Controller,Service的bean过滤掉(不扫描它们)
        @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class}),
        @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {BookService.class})
}, useDefaultFilters = false)

// @ComponentScan   value: 指定扫描的包
// excludeFilters = Filter[] : 指定扫描的时候按照什么规则排除哪些组件
// includeFilters = Filter[] : 指定扫描的时候只需要包含哪些组件
// FilterType.ANNOTATION: 按照注解作为过滤规则
// FilterType.ASSIGNABLE_TYPE: 按照给定的类型作为过滤规则
public class MainConfig {

    // 给容器中注册一个Bean, 类型为返回值类型, id默认是方法名
    @Bean("person")
    public Person person(){
        return new Person("lisi", 22);
    }
}
View Code

测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = MainConfig.class)
public class IoCTest {

    @Test
    public void test1(){
        ApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig.class);
        // 看容器中有哪些bean,返回这些bean的名称
        String[] beanDefinitionNames = ctx.getBeanDefinitionNames();
        for (String name : beanDefinitionNames) {
            System.out.println(name);
        }
    }
}
View Code

四、@Scope

  • 用来表示bean的范围(是单例还是多例)
  • prototype: 多例的 : IoC容器启动 并不会去调用方法创建对象 放在容器中, 每次获取bean的时候才会调用方法创建对象;
  • singleton: 单例的 : IoC容器启动就会 调用方法创建对象 放到IoC容器中;
  • request: 同一次请求创建一个实例
  • session: 同一个session创建一个实例
@Scope("prototype")
    //@Scope // 默认不写value就是singleton
    @Bean
    public Person person(){
        System.out.println("给容器中添加Person...");
        return new Person("张三", 22);
    }
View Code

 

@Test
public void test2(){
    ApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig2.class);
    // 看容器中有哪些bean,返回这些bean的名称
//        String[] beanDefinitionNames = ctx.getBeanDefinitionNames();
//        for (String name : beanDefinitionNames) {
//            System.out.println(name);
//        }
    // 默认是单例的
    System.out.println("IoC容器创建完成...");
    Object person1 = ctx.getBean("person");
    Object person2 = ctx.getBean("person");
    System.out.println(person1 == person2);
}
View Code

   @Lazy
    第一次使用Bean的时候创建,不使用则不创建(即使IoC容器启动了) ;

//@Scope("prototype")
    @Scope
    @Lazy //第一次使用Bean的时候创建,不使用则不创建(即使IoC容器启动了)
    @Bean
    public Person person(){
        System.out.println("给容器中添加Person...");
        return new Person("张三", 22);
    }
View Code

五、@Condition

@Conditional: 按照一定的条件进行判断,满足条件给容器中注册bean

MacOSXCondition

// 判断是否Mac系统
public class MacOSXConditaion implements Condition {
    /**
     *
     * @param conditionContext :判断条件能使用的上下文(环境)
     * @param annotatedTypeMetadata : 注释信息
     * @return
     */
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        // 是否Mac系统

        //1. 能获取到IoC使用的beanfactory
        ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
        //2. 获取类加载器
        ClassLoader classLoader = conditionContext.getClassLoader();
        //3. 获取当前环境信息(封住系统的的环境信息等,虚拟机等信息)
        Environment environment = conditionContext.getEnvironment();
        //4. 获取到bean定义的注册类
        BeanDefinitionRegistry registry = conditionContext.getRegistry();

        String property = environment.getProperty("os.name");
        if (property.contains("Mac OS X"))
            return true;

        return false;
    }
}
View Code

WindowsCondition

// 判断是否windows系统
public class WindowsCondition implements Condition {
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        Environment environment = conditionContext.getEnvironment();
        String property = environment.getProperty("os.name");
        if (property.contains("Windows"))
            return true;
        return false;
    }
}
View Code

 

测试:

@Test
public void test3(){
    // 根据Person类型来获取容器中bean的名称
    String[] beanNamesForType = ctx.getBeanNamesForType(Person.class);

    // 动态获取环境变量的值: Mac OS X
    Environment environment = ctx.getEnvironment();
    String property = environment.getProperty("os.name");
    System.out.println(property);

    for (String name : beanNamesForType) {
        System.out.println(name);
    }

    Map<String, Person> persons = ctx.getBeansOfType(Person.class);
    System.out.println(persons);
}
View Code

六、@Import

  • 作用:用于导入其他的配置类
  • 也可以导入一个需要注册的组件(类), id默认是全类名;

DataSource类

public class DataSource {
}
View Code

RedisTemplate类

public class RedisTemplate {
}
View Code

DataSourceConfig配置类

@Configuration
public class DataSourceConfig {
    @Bean
    public DataSource dataSource(){
        return new DataSource();
    }
}
View Code

 

RedisConfig配置类

@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate redisTemplate(){
        return new RedisTemplate();
    }
}
View Code

AppConfig配置类

@Configuration
@Import({DataSourceConfig.class, RedisConfig.class})
public class AppConfig {
}
View Code

ImportTest测试类

@RunWith(SpringJUnit4ClassRunner.class)
//@ContextConfiguration(classes = {DataSourceConfig.class, RedisConfig.class})
@ContextConfiguration(classes = AppConfig.class)
public class ImportTest {

    @Autowired
    private DataSource ds;

    @Autowired
    private RedisTemplate rt;

    @Test
    public void test() {
        System.out.println(ds);
        System.out.println(rt);
    }
}
View Code

 

1、ImportSelector 接口

     返回需要导入的组件的全类名

  •      需要导入的组件

自定义MyImportSelector

// 自定义逻辑返回需要导入的组件
public class MyImportSelector implements ImportSelector {
    /**
     *
     * @param annotationMetadata 当前标注@Import注解类的所有注解信息
     * @return 返回要导入到容器中的组件的全类名
     */
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        return new String[]{"com.zy.beans.Blue","com.zy.beans.Red"};
    }
}
View Code

配置类

@Configuration
@Import(MyImportSelector.class)
public class MainConfig2 {
}
View Code

测试

@Test
public void testImport(){
    ApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig2.class);
    printBeans((AnnotationConfigApplicationContext) ctx);
    Blue bean = ctx.getBean(Blue.class);
    System.out.println(bean); // 这样就可以成功在Spring 容器中获取到bean了
}

private void printBeans(AnnotationConfigApplicationContext atx){
    String[] definitionNames = atx.getBeanDefinitionNames();
    for (String name : definitionNames) {
        System.out.println(name);
    }
}
View Code

 

2、@ImportResource

  • @ImportResource来引入xml配置文件, 使xml和javaconfig共同使用;
public class OtherBean {
}

@Setter
@Getter
public class SomeBean {
    private OtherBean otherBean;
}
View Code

 

配置类

@Configuration
@ImportResource("classpath:applicationContext.xml")
public class AppConfig {
    @Bean
    public SomeBean someBean(OtherBean otherBean){
        SomeBean sb = new SomeBean();
        sb.setOtherBean(otherBean);
        return sb;
    }
}
View Code

测试类

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AppConfig.class)
public class AppTest {

    @Autowired
    private SomeBean someBean;

    @Test
    public void test() {
        System.out.println(someBean.getOtherBean());
    }
}
View Code

 

七、使用FactoryBean注册组件

/ 创建一个Spring定义的FactoryBean
public class ColorFactoryBean implements FactoryBean<Color> {

    // 返回一个Color对象,这个对象会添加到容器中
    @Override
    public Color getObject() throws Exception {
        System.out.println("ColorFactoryBean.getObject");
        return new Color();
    }

    @Override
    public Class<?> getObjectType() {
        return Color.class;
    }

    // 是否是单例: true, 在容器中只会保留一份
    @Override
    public boolean isSingleton() {
        return true;
    }
}
View Code

 

配置类

@Configuration
public class MainConfig{
    // 实际返回的是getObject()方法返回的对象
    @Bean
    public ColorFactoryBean colorFactoryBean(){
        return new ColorFactoryBean();
    }
}
View Code

 

测试

@Test
public void testImport() {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig2.class);
    printBeans((AnnotationConfigApplicationContext) ctx);
    Blue bean = ctx.getBean(Blue.class);
    System.out.println(bean);

    // 工厂Bean获取的是调用getObject创建的对象
    Object bean2 = ctx.getBean("colorFactoryBean");
    Object bean3 = ctx.getBean("colorFactoryBean");
    System.out.println("bean的类型:" + bean2.getClass());
    System.out.println(bean2 == bean3);

    // 获取ColorFactorybean的本身
    Object bean4 = ctx.getBean("&colorFactoryBean");
    System.out.println(bean4.getClass());
}

private void printBeans(AnnotationConfigApplicationContext atx) {
    String[] definitionNames = atx.getBeanDefinitionNames();
    for (String name : definitionNames) {
        System.out.println(name);
    }
}
View Code

 

posted @ 2020-05-08 10:01  IT路上的小白  阅读(185)  评论(0编辑  收藏  举报