Spring注解使用

通过组件扫描和自动注入已经大大简化了我们的开发,然而,Spring仍然不满足于此,经过版本的迭代,现在我们已经可以完全抛弃配置文件使用Spring进行开发了,一起来看看吧。

@Configuration & @Bean

现在我们不创建Spring的配置文件,那么如何将一个组件注册到容器中呢?其实,我们仍然是需要一个配置文件的,不过这个配置文件能够以一个类的形式存在:

@Configuration
public class MyConfiguration {
}

@Configuration用于将一个普通的Java类变为一个Spring的配置类,现在这个类就相当于之前的配置文件了,此时如果想注册一个组件,则使用@Bean注解:

@Configuration
public class MyConfiguration {
    @Bean
    public User user() {
        return new User();
    }
}

这里需要注意几点,若有方法被@Bean注解标注,则该方法的返回值则为需要注册的组件,而方法名则为组件在容器中的名字,当然了,这些都需要建立在代码是写在配置类的前提下。

如果想要修改组件的名字,可以修改方法名:

@Bean
public User myUser() {
    return new User();
}

若是不想修改方法名,@Bean注解也提供了修改名字的方式:

@Bean(name = "myUser")
public User user() {
    return new User();
}

@Bean中还有initMethod和destroyMethod属性,它们分别用于指定组件的两个生命周期方法:

@Bean(name = "myUser",initMethod = "init",destroyMethod = "destroy")
public User user() {
    return new User();
}

@ComponentScan

@ComponentScan注解是用来完成组件扫描的,它需要标注在配置类上:

@Configuration
@ComponentScan("com.wwj.spring.demo")
public class MyConfiguration {
}

它的作用等价于如下配置:

<context:component-scan base-package="com.wwj.spring.demo"/>

我们来聊一聊关于@ComponentScan的一些高级用法,该注解是可以在扫描时指定扫描规则的,比如我们想扫描com.wwj.spring.demo这个包,但是包里有一些类、或者一些注解的内容是我们不想要注册的,此时我们就可以指定扫描规则,如下:

@Configuration
@ComponentScan(value = "com.wwj.spring.demo", excludeFilters = {
        @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {
                User.class
        })
})
public class MyConfiguration {
}

在如上的配置中,excludeFilters用来配置需要排除的组件,需要借助@Filter注解,@Filter注解中的type属性用于指定以哪种方式排除组件,Spring一共提供了5种匹配方式:

  1. _ANNOTATION:_以给定的注释进行匹配

  2. _ASSIGNABLE_TYPE:_以给定的类型进行匹配

  3. _ASPECTJ:_以给定的AspectJ表达式匹配

  4. _REGEX:_以给定的正则表达式匹配

  5. _CUSTOM:_以给定的自定义规则匹配

所以如果想要具体排除某个组件,则使用ASSIGNABLE_TYPE,如果想要排除某个注解标注的所有组件,则使用ANNOTATION:

@Configuration
@ComponentScan(value = "com.wwj.spring.demo", excludeFilters = {
        @Filter(type = FilterType.ANNOTATION, classes = {
                Service.class
        })
})

将excludeFilters切换为includeFilters,功能将变为只扫描匹配的组件,如下:

@Configuration
@ComponentScan(value = "com.wwj.spring.demo", includeFilters = {
        @Filter(type = FilterType.ANNOTATION, classes = {
                Service.class
        })
},useDefaultFilters = false)
public class MyConfiguration {
}

以上配置的作用是扫描com.wwj.spring.demo包下被@Service注解标注的组件,注意一点,由于Spring默认的扫描规则就是扫描所有带@Component注解的组件,所以若是想实现只扫描某个注解,则需要添加配置useDefaultFilters = false来禁用掉Spirng默认的扫描规则。

若是想实现自定义扫描规则,也非常简单,只需实现TypeFilter接口:

public class MyFilter implements TypeFilter {
    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        // 获取完整注解元数据
        AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
        // 获取类的元数据
        ClassMetadata classMetadata = metadataReader.getClassMetadata();
        // 获取类文件的资源引用
        Resource resource = metadataReader.getResource();
        // 获取类名
        String className = classMetadata.getClassName();
        return className.equals("user");
    }
}

然后进行配置即可:

@Configuration
@ComponentScan(value = "com.wwj.spring.demo", includeFilters = {
        @Filter(type = FilterType.CUSTOM, classes = {
                MyFilter.class
        })
}, useDefaultFilters = false)
public class MyConfiguration {
}

千万别忘了配置useDefaultFilters = false,此时将只能扫描到名字为user的组件。

这些内容在配置文件中也是可以进行配置的,简单举一个例子吧:

<context:component-scan base-package="com.wwj.spring.demo" use-default-filters="false">
  <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
</context:component-scan>

@Scope

@Scope用于指定组件的作用域,关于作用域在上一篇我们已经介绍过了, 所以用法其实非常简单:

    @Bean
    @Scope("prototype")
    public User user() {
        return new User();
    }

不过多介绍,但由此可以引申出一个新的注解:@Lazy,该注解的作用是指定组件是否懒加载,默认情况下,所有组件会在容器启动的时候被创建,而如果标注@Lazy,则组件会在第一次使用时被创建。我们可以来试验一下,首先编写一个User类:

public class User {
    public User() {
        System.out.println("user对象被创建...");
    }
}

编写测试代码:

public static void main(String[] args) throws Exception {
    ApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class);
}

当没有添加@Lazy注解,控制台输出:

user对象被创建...

当添加了@Lazy注解,控制台没有任何输出,只有调用了context.getBean_(_"user"_)_;User对象才会被创建。

@Conditional

@Conditional注解的功能是以指定的条件来注册组件,现在我们有两个组件:

@Configuration
public class MyConfiguration {

    @Bean
    public Watermelon watermelon() {
        return new Watermelon();
    }

    @Bean
    public Kiwi kiwi() {
        return new Kiwi();
    }
}

一个是夏季水果西瓜,一个是冬季水果猕猴桃,现在有一个需求是当传入参数为夏天时,就注册西瓜,当传入参数是冬天时,就注册猕猴桃,该如何实现呢?

我们可以借助@Conditional注解来实现,首先创建类实现Condition接口:

public class SummerCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        String season = System.getProperty("season");
        return "summer".equals(season);
    }
}
public class WinterCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        String season = System.getProperty("season");
        return "winter".equals(season);
    }
}

接下来就可以使用它们进行配置了:

@Configuration
public class MyConfiguration {

    @Bean
    @Conditional({SummerCondition.class})
    public Watermelon watermelon() {
        return new Watermelon();
    }

    @Bean
    @Conditional({WinterCondition.class})
    public Kiwi kiwi() {
        return new Kiwi();
    }
}

此时在虚拟机参数位置填写-Dseason=summer,Watermelon将被注册,当参数被修改为-Dseason=winter时,Kiwi将被注册,SpringBoot框架的底层就大量地使用到了这个注解,不过这是题外话了,我将在后续SpringBoot系统的文章中对其再度进行介绍。

@Import

我们已经知道,目前将一个组件注册到容器中有多种方式,使用@Bean或者组件扫描都可以,然而在某些情况下,这些方式都不太方便,比如将一个第三方的组件注册到容器中,此时我们可以借助@Import注解:

@Configuration
@Import({Cat.class, Dog.class})
public class MyConfiguration {
}

另一种方式是使用ImportSelector,创建类实现ImportSelector接口:

public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{
                "com.wwj.spring.demo.entity.Cat",
                "com.wwj.spring.demo.entity.Dog"
        };
    }
}

将需要注册到容器中的组件全类名写到数组中,然后@Import注解只需要填写这个类的信息即可:

@Configuration
@Import({MyImportSelector.class})
public class MyConfiguration {
}

还有一种方式是实现ImportBeanDefinitionRegistrar接口,它与第二种方式类似,与之不同的是,这种方式可以自定义组件注册到容器中的名字:

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        registry.registerBeanDefinition("myDog", new RootBeanDefinition(Dog.class));
        registry.registerBeanDefinition("myCat", new RootBeanDefinition(Cat.class));
    }
}

配置如下:

@Configuration
@Import({MyImportBeanDefinitionRegistrar.class})
public class MyConfiguration {
}

需要注意的是第一种和第二种方式注册的组件,其在容器中的名字是组件的全类名。

FactoryBean

FactoryBean也是Spring提供的一种注册组件的方式,不过它比较特殊,看一个例子:

public class User implements FactoryBean {
    @Override
    public Object getObject() throws Exception {
        return new User();
    }

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

    @Override
    public boolean isSingleton() {
        return true;
    }
}

这三个方法非常好理解:

  1. getObject:需要注册的组件

  2. getObjectType:需要注册的组件类型

  3. isSingleton:需要注册的组件是否是单例

那有同学提出疑问了,这种方式岂不是更加麻烦了,有必要存在吗?当然有了,它牛就牛在你可以随意篡改需要注册的组件,比如:

public class User implements FactoryBean {
    @Override
    public Object getObject() throws Exception {
        return new Cat();
    }

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

    @Override
    public boolean isSingleton() {
        return true;
    }
}

现在看似注册的是User对象,其实注册的是Cat,不信我们试试:

public static void main(String[] args) throws Exception {
    ApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class);
    Object user = context.getBean("user");
    System.out.println(user.getClass());
}

运行结果:

class com.wwj.spring.demo.entity.Cat

若是想要获得实现了FactoryBean接口的User对象本身,则需要在名字面前添加&

public static void main(String[] args) throws Exception {
    ApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class);
    Object user = context.getBean("&user");
    System.out.println(user.getClass());
}

运行结果:

class com.wwj.spring.demo.entity.User
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值