个人随笔
Spring5注解整理
2020-09-11 22:18:51

我们知道,Spring自从用了注解后就很方便了,我们可能都用过Spring的一些注解,比如@Controller@Service@Repository@Component等,下面我们就来总结下Spring各大注解的作用。

环境搭建

用maven搭建Spring项目超级简单,只需要加上如下以来即可:

  1. <dependency>
  2. <groupId>org.springframework</groupId>
  3. <artifactId>spring-context</artifactId>
  4. <version>5.0.11.RELEASE</version>
  5. </dependency>

是的,你没看错,就加这个就可以了,不用加什么aop,beans,corez之类的,因为maven的继承关系已经包括了,点进去可以发现依赖如下:

  1. ...
  2. <dependency>
  3. <groupId>org.springframework</groupId>
  4. <artifactId>spring-aop</artifactId>
  5. <version>5.0.11.RELEASE</version>
  6. <scope>compile</scope>
  7. </dependency>
  8. <dependency>
  9. <groupId>org.springframework</groupId>
  10. <artifactId>spring-beans</artifactId>
  11. <version>5.0.11.RELEASE</version>
  12. <scope>compile</scope>
  13. </dependency>
  14. <dependency>
  15. <groupId>org.springframework</groupId>
  16. <artifactId>spring-core</artifactId>
  17. <version>5.0.11.RELEASE</version>
  18. <scope>compile</scope>
  19. </dependency>
  20. <dependency>
  21. <groupId>org.springframework</groupId>
  22. <artifactId>spring-expression</artifactId>
  23. <version>5.0.11.RELEASE</version>
  24. <scope>compile</scope>
  25. </dependency>
  26. <dependency>
  27. <groupId>org.springframework</groupId>
  28. <artifactId>spring-instrument</artifactId>
  29. <version>5.0.11.RELEASE</version>
  30. <scope>compile</scope>
  31. <optional>true</optional>
  32. </dependency>
  33. ...

然后我们启动Spring项目用如下两个类即可,第一个是用配置文件的方式,第二个是用注解的方式。

  1. ClassPathXmlApplicationContext application = new ClassPathXmlApplicationContext("spring.xml");
  2. AnnotationConfigApplicationContext application=new AnnotationConfigApplicationContext(MyConfig.class);

在之前,我们新建Spring项目,都是直接按配置文件的方式,后面Spring支持注解后,就非常方便了,我推荐用注解,因为在注解里配置dataSource可以读取外部的配置文件,不一定是classpath下面的。

1、@Configurable

现在我们开始看第一个注解,这个注解的意思就是加上这个注解的类是一个配置类,也就相当于之前的application.xml,然后类里面用@Bean 来实例化对象就相当于配置文件中的。如下例子。

  1. @Configurable
  2. public class MyConfig {
  3. @Bean
  4. public User user() {
  5. User user = new User();
  6. user.setUsername("林蛋黄");
  7. return user;
  8. }
  9. }

然后启动的时候就用AnnotationConfigApplicationContext来启动。
也许,此时就会有人想问,这样子每个对象都要写一个方法,然后加上@Bean注解,岂不是超级麻烦。此时我们可以看第二个注解。

2、@ComponentScan

这个注解其实类似于配置文件的扫包范围,跟@Configurable一起使用,也就相当于配置文件中指定扫包范围。如下:

  1. @Configurable
  2. @ComponentScan(value="cn.myforever.start.object")
  3. public class MyConfig {
  4. }

此时,只要在cn.myforever.start.object包以及子包下面的类都会被扫描,我们只需要在需要实例化的类加上类似@Controller@Service@Repository@Component等注解就可以将对应的类加入到IOC容器中啦。是不是很方便。

这个注解还有过滤的作用,比如我们只想实例化指定包下面的某些类,可以用如下方式:

  1. @ComponentScan(value="cn.myforever.start.object",includeFilters= {@Filter(type=FilterType.ANNOTATION,classes=Controller.class)},useDefaultFilters = false)

上面的意思是说,我们的扫包范围是cn.myforever.start.object,但是我们只实例化加了@Controller注解的类到IOC容器中去,其他的比如@Service都不理,useDefaultFilters需要为false。

我们也可以排除指定包下面的某些类不实例化,如下方式:

  1. @ComponentScan(value="cn.myforever.start.object",excludeFilters= {@Filter(type=FilterType.ANNOTATION,classes=Controller.class)},useDefaultFilters = true)

上面的意思是说,我们的扫包范围是cn.myforever.start.object,但是加了@Controller注解的类到将排除掉,不实例化到IOC容器中去,useDefaultFilters需要为true,否则将啥都不会注入。

FilterType 有四种类型

  1. ANNOTATION:注解类型
  2. ASSIGNABLE_TYPEANNOTATION:指定的类型
  3. ASPECTJ:按照Aspectj的表达式,基本上不会用到
  4. REGEX:按照正则表达式
  5. CUSTOM:自定义规则

3、@Bean

这个注解就不需要多说,相当于标签,用来实例化一个类到IOC容器中去。用在有或者继承有@Configurable的类中,使用方法如下:

  1. @Bean
  2. public User user() {
  3. User user = new User();
  4. user.setUsername("林蛋黄");
  5. return user;
  6. }

实例化的id为方法的名称,正常来说,我们用这个注解都是为了实例化第三方jar包中的类到IOC容器中,毕竟第三方jar包中的类我们不方便加上@Component等注解。

4、@Scope

该注解是指定实例化的类的作用域,spring默认实例化的类都是单例模式,可以有四种选择作用域:

  1. singleton 单例模式:全局有且仅有一个实例。
  2. prototype 原型模式:每次获取Bean的时候都会有一个新的实例。
  3. request 表示针对每次请求都会产生一个新的Bean对象,并且该Bean对象仅在当前Http请求内有效。
  4. session 表示每次请求都会产生一个新的Bean对象,并且该Bean仅在当前Http session内有效。

一般都与@Compnent或者@Bean这些注入对象的注解配合使用。那么既然spring默认是单例模式,是懒汉式还是饿汉式呢。其实默认是饿汉式的,也就是容器一启动就初始化到IOC容器中。我们可以在要实例化的类的默认构造方法中打印日志,然后启动容器会发现,在没有执行getBean之前就打印出来了。有@Lazy注解可以修改这个模式。

5、@Lazy

Lazy表示为懒加载,当真正需要引用获取的时候才会被加载true 表示为懒加载 false表示为在IOC容器加载的时候被创建。加上这个注解,默认就是true,可以指定为false.

  1. @Bean
  2. @Lazy
  3. public User user() {
  4. User user = new User();
  5. user.setUsername("林蛋黄");
  6. return user;
  7. }

饿汉式因为在容器启动的时候就初始化,所以比较占内存,但是效率高,不需要使用的时候才去加载。

6、@Controller@Service@Repository@Component

@Component的意思是加上这个注解的类会被实例化到IOC内存中,然后扫包的时候就会被加进去,其实@Controller@Service@Repository本质上用的就是@Component,我们可以点进去看下:

  1. @Target({ElementType.TYPE})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. @Component
  5. public @interface Controller {
  6. ...
  7. }

只不过用不同的名称表示不同的意义,在不同的场景可能会有不同的作用,比如在Web项目中,@Controller表示是一个控制器,接收web请求,@Service表示是一个业务处理逻辑的处理器,@Repository表示的是一个与数据库交互的Dao,当然我们也可以定义我们自己的注解、如下:

  1. @Target({ElementType.TYPE})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. @Component
  5. public @interface suibibk {
  6. ...
  7. }

那么只要是加了@suibibk注解的类,就会实例化到IOC容器中去。

7、@Conditional

这个注解是用于在满足一定条件后才会实例化某一个类的情况,比如有个场景,只有当前环境是window 7,才会实例化Window7这个类,如何操作呢。我们可以先建立一个MyCondition来实现Condition接口,然后实现一个匹配方法matches。然后当环境是Windows的时候返回true,否则返回false,代码如下(当然还有各种其他的判定):

  1. public class MyCondition implements Condition{
  2. public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
  3. Environment environment =context.getEnvironment();
  4. String osName = environment.getProperty("os.name");
  5. if("Windows 7".equals(osName)) {
  6. return true;
  7. }
  8. return false;
  9. }
  10. }

那么我们就可以在我们的类上加上@Conditional注解,如下:

  1. @Component
  2. @Conditional(MyCondition.class)
  3. public class Window7 {
  4. }

这样子就做到了条件控制。

8、@Import注解

这个注解是很重要并且很好用的注解,在Springboot中使用很广,作用就是实例化一个类到IOC容器中去,下面是使用的三种情况:

实例化一个类
这种情况跟@Bean差不多,都是为了实例化一个第三方jar包中的类,如下:

  1. @Configurable
  2. @Import({MyUser.class})
  3. public class MyConfig {
  4. }

这样子MyUser就会被注入到IOC容器中去,但是跟@Bean有一个小区别,就是@Bean的ID是方法名,@Import的是类全名:cn.myforever.second.MyUser。

实例化一个实现了ImportSelector的类的selectImports方法返回的数组类全名
比如一个类实现了ImportSelector:

  1. public class MyImportSelector implements ImportSelector{
  2. public String[] selectImports(AnnotationMetadata importingClassMetadata) {
  3. return new String[] {"cn.myforever.second.User2"};
  4. }
  5. }

这个类的selectImports方法返回了一个类全名的数组。然后如果用@Import导入,那么会执行这个类的selectImports方法,并且会把返回数组中的所有类都实例化。

  1. @Configurable
  2. @Import({MyImportSelector.class})
  3. public class MyConfig {
  4. }

实例化一个实现了ImportBeanDefinitionRegistrar的类的registerBeanDefinitions方法里定义的类
比如一个类实现了ImportBeanDefinitionRegistrar:

  1. public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
  2. public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
  3. RootBeanDefinition beanDefinition = new RootBeanDefinition(User3.class);
  4. registry.registerBeanDefinition("user3", beanDefinition);
  5. }
  6. }

其实看到BeanDefinition就知到这个也是实例化一个对象。

  1. @Configurable
  2. @Import({MyImportBeanDefinitionRegistrar.class})
  3. public class MyConfig {
  4. }

此时User3会被实例化,并且ID为user3。

9、Autowired

这个是来给对象注入对象依赖,不用多说,如下:

  1. public class MyUser {
  2. @Autowired
  3. private UserService userService;
  4. }

会将实现了UserService接口的类实例化对象后注入到MyUser这个类的对象属性中。当然前提是MyUser也需要被实例化。默认会去找实现了UserService类的对象,但是我们如果改为如下:

  1. public class MyUser {
  2. @Autowired
  3. private UserService userServiceImpl01;
  4. }

那么久会去找实现了UserServie的UserServiceImpl01类,对象。
但是假如我们就是要叫做userService,而实现了UserService接口的有几个,怎么办呢,如果直接@Autowired,肯定会报错的。

nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type ‘cn.myforever.second.UserService’ available: expected single matching bean but found 2: userServiceImpl01,userServiceImpl02nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type ‘cn.myforever.second.UserService’ available: expected single matching bean but found 2: userServiceImpl01,userServiceImpl02

此时可以用如下两个注解解决。

10、@Qualifier

只要加上这个注解就指定了哪一个实现类。如下:

  1. public class MyUser {
  2. @Autowired
  3. @Qualifier("userServiceImpl01")
  4. private UserService userService;
  5. }

那么久会直接找到UserServiceImpl01,不会报错,也可以用下面的注解。

11、@Primary

这个注解是指定当前类对象优先。

  1. @Service
  2. @Primary
  3. public class UserServiceImpl01 implements UserService{
  4. ...
  5. }

这样子也不会报错。

未完待续。。。

 10

啊!这个可能是世界上最丑的留言输入框功能~


当然,也是最丑的留言列表

有疑问发邮件到 : suibibk@qq.com 侵权立删
Copyright : 个人随笔   备案号 : 粤ICP备18099399号-2