Spring注解简介
前言:当前SpringBoot以及SpringCloud是比较热门的,几乎省去了一切xml文件,绝大多数都是通过注解的方式进行开发。所以,本文主要讨论的是:Spring注解开发,如有不当之处,欢迎各位不吝指正。
一、配置一个Bean
首先,创建一个空的maven项目,并导入spring-context
依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.14.RELEASE</version>
</dependency>
1.1 基于xml配置一个bean
这里为什么要先说xml配置呢?主要是为了与注解配置形成一个对比。
这里以配置一个Student为例子:
- 首先,创建一个Student类
public class Student{
private String name;
private int age;
//get set等略去...
}
- 然后,进行xml文件配置,如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--配置bean Student-->
<bean id="student" class="com.study.test.bean.Student">
<!--给属性赋值-->
<property name="name" value="czj"></property>
<property name="age" value="25"></property>
</bean>
</beans>
- OK,到这里,Student就配置好了,注册到Spring容器中去了,接下来,我们进行测试:
public class Test {
public static void main(String[] args) {
//xml方式配置bean
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("test.xml"); //从xml文件获取容器上下文
Student student = (Student) applicationContext.getBean("student"); //通过xml配置的Student bean id 获取bean实例(student)
System.out.println(student);
}
}
- 最后打印结果不言而喻 ->
Student{name='czj', age=25}
OK,上面就是基于XML文件的简单配置。
接下来,我们将通过注解的方式实现Student Bena的配置,这时候,就不需要创建xml文件了,之前的xml文件可以删掉,也可以放在哪里不用管。
1.2 基于注解配置一个bean
- Student类和上面一样,我就不重复贴代码了;
- 然后就是配置类,其实相当于xml配置文件,如下:
@Configuration //告诉Spring 这(MyConfig)是一个配置类 其实就是相当于刚才的xml文件
public class MyConfig {
/**
* Bean 在IOC容器中 名字默认为方法名 也可以通过Bean注解修改名字
*/
@Bean("stu")
public Student student() {
return new Student("czj", 25);
}
}
可以看到:@Configuration
注解的作用的告诉Spring这是一个配置类,@Bean
的作用是表明这是一个Bean,Bean默认的名字是方法名。当然也可以通过注解直接设置Bean的名字,如上:Student Bean的名字将不再是student, 而是stu。
- 好了,让我们测试一下吧:
public class Test {
public static void main(String[] args) {
//注解方式配置bean 不需要xml文件
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class); //从配置类获取容器上下文
Student student = applicationContext.getBean(Student.class);
System.out.println(student);
//获取Spring IOC容器中的所有bean名字
String[] beanNamesForType = applicationContext.getBeanNamesForType(Student.class);
for (String name : beanNamesForType) {
System.out.println(name);
}
}
}
打印结果 -> Student{name='czj', age=25} stu
可见,和刚才xml配置文件一样,成功配置了Student Bean,并且通过@Bean("stu")
,设置了Bena的名字为stu。
简单总结一下:上面我们只是用到了 @Configuration
和@Bean
两个注解,接下来,将在基础上,进行其他注解的介绍。
二、Srping其他常用注解介绍
2.1 @Scope注解
众所周知,在Spring容器中,每个Bean实例默认
都是以单例
的形式存在的。那么,Bean的作用域具体有哪些以及如何设置呢?接下来,就让我们简单了解一下Bean的作用域
:
singleton
,在Spring容器仅存在一个Bean实例,也就是单例,Bean作用域默认是单例;prototype
,存在多个实例,每次调用时都会生成一个新的Bena实例,其实就是执行了 new Bean()的操作;request
,每次http请求都会创建一个Bean实例,仅适用于WebApplicationContext环境;session
,同一个http session共用一个Bean实例,仅适用于WebApplicationContext环境;global session
, 全局HttpSession共用一个Bean实例,一般用于Portlet应用环境,同样仅适用于WebApplicationContext环境。
用的比较多的是singleton
和prototype
,其他了解就好。
咳咳,言归正传,如何通过注解设置Bean的组用域呢?其实很简答,通过@Scope
注解即可完成,
举个栗子:将Student设置为多实例,只需要添加@Scope("prototype")
即可。
2.2 @Lazy注解
既然Spring容器Bean实例默认是单例,单例又有饿汉式以及懒汉式之分,那么,Spring容器的Bean实例是哪种形式呢?
其实,Spring容器启动时就会创建对象实例,也就是饿汉式加载。当然也可以实现懒汉式加载,也就是懒加载,通过添加@Lazy
注解即可实现,当用到的时候,才会去创建该实例。
2.3 @ComponentScan注解
@ComponentScan
注解相信大家都不陌生,它的主要作用是告诉Spring从哪里找到Bean,也就是包扫描
,并将它们注册到容器中。
在SpringBoot项目中,其启动类有一个名为@SpringBootApplication
的注解,点进去这个注解,你会发现,其实这是一个组合注解,它里面主要包含了@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan这三个注解,其中@ComponentScan
注解会默认
扫描启动类所在包
,及其下级包
。
那么问题来了,假如我需要排除一些包或者额外扫描其他包,该怎么做呢?
其实也不难,可以通过excludeFilters
或者includeFilters
实现。顾名思义,就是排除异己包括过滤。所以,现在的问题是,如何使用过这两个滤功能进而到达我们的目的。
这里以excludeFilters
为例,includeFilters也是类似的,换汤不换药,只不过作用刚好相反。
首先,直接看具体使用方式(用于类上):
@ComponentScan(
value = {"com.study.test"},
excludeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class}),
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {Student.class}),
@ComponentScan.Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class})
}
)
public class MyConfig {
//代码略...
}
说明:
- value 表示扫描那些路径下的包,是一个字符串数组;
- excludeFilters 表示排除的路径(也是一个数组),也就是不扫描的路径。器过滤类型有5种,如下:
public enum FilterType {
ANNOTATION,
ASSIGNABLE_TYPE,
ASPECTJ,
REGEX,
CUSTOM;
private FilterType() {
}
}
过滤类型说明:
ANNOTATION
,按照指定注解排除 ;ASSIGNABLE_TYPE
,按照指定类型排除;ASPECTJ
,按照切面进行排除,实际用的很少,这里不做讨论;REGEX
,按照正则表达式进行排除,实际用的很少,这里不做讨论;CUSTOM
,自定义过滤规则,需要实现TypeFilter
接口,这种是最灵活的,重点介绍这种。
自定义过滤规则,需要实现TypeFilter
接口,如下:
/**
* 实现TypeFilter 接口 进行相应的过滤操作
*/
public class MyTypeFilter implements TypeFilter {
/**
* @param metadataReader 当前正在扫描的类的信息
* @param metadataReaderFactory 可获取到其他类的信息
*/
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
//获取当前正在扫描的类的信息,包括注解 类信息 类路径之类的信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
ClassMetadata classMetadata = metadataReader.getClassMetadata();
Resource resource = metadataReader.getResource();
//然后可以通过这些类信息,根据业务需要,进行相应的排除
//需要排除的则 返回false 否则返回true 有种过滤器或者拦截器的感觉
return false;
}
}
OK,关于@ComponentScan
注解的介绍就到这里了。
顺带提一下@ComponentScans
注解,该注解的value
值是一个@ComponentScan数组。
2.4 @Conditional注解
该注解的作用
:可以根据条件进行匹配,满足条件的才像Spring容器中注册相应的Bean。
该注解可以用在方法上,也可以用在类上。
@Conditional(MyConditional.class)
使用该注解,需要实现org.springframework.context.annotation.Condition
接口,如上所示,MyConditional就是一个实现了Condition接口的类,如下:
/**
* 实现Condition接口的自定义类
*/
public class MyConditional implements Condition {
/**
* @param conditionContext IOC容器上下文(环境)
*/
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
/**
* 通过conditionContext可以获取如下信息 这些点进去ConditionContext就可以看到了
*
* BeanDefinitionRegistry getRegistry() -> bean定义的注册类
* ConfigurableListableBeanFactory getBeanFactory() -> IOC容器使用的BeanFactory
* Environment getEnvironment() -> 当前环境
* ResourceLoader getResourceLoader() -> 资源相关
* ClassLoader getClassLoader() -> 类加载器
*
* 符合条件的返回true 否则返回false 类似于过滤器拦截器的感觉
*/
//这里获取当前系统名字
Environment environment = conditionContext.getEnvironment();
String property = environment.getProperty("os.name");
System.out.println(property);
return property.contains("Windows");
}
}
2.4 @Import注解
前面说到过可以通过@Configuration
注解和@Bean
注解实现将组件注册到Spring容器中。但是好像有点麻烦,所以这里有一种更简单的办法,就是@Import
注解,该注解的作用是快速导入资源到Spring容器中,用法如下所示:
@Import({ClassA.class, ClassB.class, ClassC.class})
其中ClassA B C都是需要注册到Spring容器中的Bean,id默认是全类名。
当然,除了这种方法进行Bean注册之外,也可以通过选择器完成同样的功能,就不一一赘述了。