IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Spring注解使用 -> 正文阅读

[Java知识库]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
  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-03-31 23:49:43  更:2022-03-31 23:51:16 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 7:35:57-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码