Spring常用的一些注解说明
@Configuration
从Spring3.0,@Configuration用于定义配置类,可替换xml配置文件,被注解的类内部包含有一个或多个被@Bean注解的方法。
这些方法将会被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext类进行扫描,并用于构建bean定义。
@Bean
@Bean注解用于告诉方法,产生一个Bean对象,然后这个Bean对象交给Spring管理。产生这个Bean对象的方法Spring只会调用一次,随后这个Spring将会将这个Bean对象放在自己的IOC容器中。
SpringIOC 容器管理一个或者多个bean,这些bean都需要在@Configuration注解下进行创建,在一个方法上使用@Bean注解就表明这个方法需要交给Spring进行管理。
@Autowired、@Resource
@Resource和@Autowired注解都是用来实现依赖注入的。只是@AutoWried按by type自动注入,而@Resource默认按byName自动注入。
♣ @Autowired
- @Autowired具有强契约特征,其所标注的属性或参数必须是可装配的。如果没有Bean可以装配到@Autowired所标注的属性或参数中,自动装配就会失败,抛出NoSuchBeanDefinitionException.
-
@Autowired可以对类成员变量、方法及构造函数进行标注,让 spring 完成 bean 自动装配的工作。
- @Autowired 默认是按照类去匹配,配合 @Qualifier 指定按照名称去装配 bean。
♣ @Resource
- @Resource是JDK提供的注解,有两个重要属性,分别是name和type。
- @Resource依赖注入时查找bean的规则
- 既不指定name属性,也不指定type属性,则自动按byName方式进行查找。如果没有找到符合的bean,则回退为一个原始类型进行查找,如果找到就注入。
- 只是指定了@Resource注解的name,则按name后的名字去bean元素里查找有与之相等的name属性的bean。
- 只指定@Resource注解的type属性,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常。
- 既指定了@Resource的name属性又指定了type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常。
@Primary、@Qualifier
问题:当一个接口有2个不同实现时,使用@Autowired注解时会报org.springframework.beans.factory.NoUniqueBeanDefinitionException异常信息。
@Primary为默认优先选择,同时不可以同时设置多个,内部实质是设置BeanDefinition的primary属性。
@Qualifier注解,选择一个对象的名称,通常比较常用。
@PostConstruct、@PreDestroy
@PostConstruct是Java的注解,该注解被用来修饰一个非静态的void()方法。被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器执行一次。PostConstruct在构造函数之后执行,init()方法之前执行。
该注解的方法在整个Bean初始化中的执行顺序:Constructor(构造方法) -> @Autowired(依赖注入) -> @PostConstruct(注释的方法)。
被@PreDestroy修饰的方法会在服务器卸载Servlet的时候运行,并且只会被服务器调用一次,类似于Servlet的destroy()方法。被@PreDestroy修饰的方法会在destroy()方法之后运行,在Servlet被彻底卸载之前。
注意:@PostConstruct和@PreDestroy 标注不属于 Spring,它是在J2EE库- common-annotations.jar。
@Service
@Service用于标注业务层组件。
@service("service") 指定Bean的id,相当于 <bean id="service">,如果不指定的话,默认是类的全限定名称。
@Scope
@Scope 就是用来指定bean的作用域。即用来声明IOC容器中的对象应该处的限定场景或者说该对象的存活空间,即IOC容器在对象进入相应的scope之前,生成并装配这些对象,在该对象不再处于这些scope的限定之后,容器通常会销毁这些对象。
默认是单例模式,@Scope("singleton")。
- singleton:全局有且仅有一个实例
- prototype:每次获取Bean的时候会有一个新的实例
- request:表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP request内有效。
- session:表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP session内有效。
- application:将单个bean定义限定为ServletContext的生命周期。仅在支持web的Spring应用程序上下文中有效。
- websocket:将单个bean定义限定为WebSocket的生命周期。仅在支持web的Spring应用程序上下文中有效。
@Repository
@Repository用于标注数据访问组件,即DAO组件。
@Component
@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。
@ComponentScan、@ComponentScans
@ComponentScan: 指定包路径扫描,把@Controller、@Service、@Repository,@Component标注的类,实例化到spring容器中。
@Filter: 是@ComponentScan注解类中的子注解(内部注解),可以指定一些过滤规则
- FilterType.ANNOTATION:按照注解注入
- FilterType.ASSIGNABLE_TYPE:按照给定的类型注入
- FilterType.ASPECTJ:使用ASPECTJ表达式
- FilterType.REGEX:使用正则指定
- FilterType.CUSTOM:使用自定义规则
@ComponentScans:是@ComponentScan注解的集合,里面可以指定多个@ComponentScan注解,扫描多个包路径。
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Repeatable(ComponentScans.class) public @interface ComponentScan { //字符串形式指定扫描的包路径 @AliasFor("basePackages") String[] value() default {}; //字符串形式指定扫描的包路径 @AliasFor("value") String[] basePackages() default {}; //这个英文翻译,大概就是指定类或者接口的类型,会按照指定的类所在的包,扫描包下的类 Class<?>[] basePackageClasses() default {}; //为bean生成名称的规则的类,需要实现BeanNameGenerator接口,后面指定了默认的生成规则 Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class; //用于解析bean定义范围的策略接口。(单例,多例等 Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class; //各种作用域代理选项 ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT; //看解释,以及默认值(static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";),是扫描的文件类型 String resourcePattern() default ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN; //是否启动自动扫描所有标注了 @Controller、@Service、@Repository,@Component的类 boolean useDefaultFilters() default true; //扫描只包含Filter规则的类 Filter[] includeFilters() default {}; //排除规则Filter的类 Filter[] excludeFilters() default {}; //懒加载 boolean lazyInit() default false; @Retention(RetentionPolicy.RUNTIME) @Target({}) @interface Filter { //Filter的定义策略 FilterType type() default FilterType.ANNOTATION; //跟type()配合使用 @AliasFor("classes") Class<?>[] value() default {}; //跟type()配合使用 @AliasFor("value") Class<?>[] classes() default {}; /** 跟type()配合使用,大概就是,如果指定的类型是ASPECTJ,那么就是ASPECTJ的表达式,是REGEX,就是REGEX的表达式,不常用 * The pattern (or patterns) to use for the filter, as an alternative * to specifying a Class {@link #value}. * <p>If {@link #type} is set to {@link FilterType#ASPECTJ ASPECTJ}, * this is an AspectJ type pattern expression. If {@link #type} is * set to {@link FilterType#REGEX REGEX}, this is a regex pattern * for the fully-qualified class names to match. * @see #type * @see #classes */ String[] pattern() default {}; } }
如何使用?
//配置类==配置文件 @Configuration // 告诉Spring这是一个配置类 @ComponentScan(value = "com.bader", includeFilters = { //只扫描有@Controller注解的类 @Filter(type = FilterType.ANNOTATION, classes = { Controller.class }), //只扫描类型是BookService的类 @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = { BookService.class }), //自定义过滤规则 @Filter(type = FilterType.CUSTOM, classes = { MyTypeFilter.class }) }, useDefaultFilters = false) // @ComponentScan value:指定要扫描的包 // excludeFilters = Filter[] :指定扫描的时候按照什么规则排除那些组件 // includeFilters = Filter[] :指定扫描的时候只需要包含哪些组件 // FilterType.ANNOTATION:按照注解 // FilterType.ASSIGNABLE_TYPE:按照给定的类型; // FilterType.ASPECTJ:使用ASPECTJ表达式 // FilterType.REGEX:使用正则指定 // FilterType.CUSTOM:使用自定义规则 public class MyConfig { }
自定义的Filter,最后的CUSTOM,也说了,需要实现org.springframework.core.type.filter.TypeFilter接口。
public class MyTypeFilter implements TypeFilter { /** * metadataReader:读取到的当前正在扫描的类的信息 * metadataReaderFactory:可以获取到其他任何类信息的 */ @Override public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { // 获取当前类注解的信息 AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata(); // 获取当前正在扫描的类的类信息 ClassMetadata classMetadata = metadataReader.getClassMetadata(); // 获取当前类资源(类的路径) Resource resource = metadataReader.getResource(); String className = classMetadata.getClassName(); System.out.println("--->" + className); //根据扫描的className,包含service的,扫描成功 if (className.contains("service")) { return true; } return false; } }
@Controller、@RestController
用于标注控制层组件。
@RestController注解,相当于@Controller+@ResponseBody两个注解的结合,返回json数据不需要在方法前面加@ResponseBody注解了,但使用@RestController这个注解,就不能返回jsp,html页面,视图解析器无法解析jsp,html页面。
@ControllerAdvice、@RestControllerAdvice
@ControllerAdvice是一个增强的 Controller。使用这个 Controller ,可以实现三个方面的功能:
- 全局异常处理:结合@ExceptionHandler
- 全局数据绑定:结合@ModelAttribute
- 全局数据预处理:结合@InitBinder
@RequestBody、@ResponseBody
@ResponseBody注解将HttpRequest的请求体映射为Java的POJO对象。一般Get方法是没有body的,在post数据的时候可以指定json数据。
注意:对于想要转换的格式,记得要添加对应的依赖包。比如json的,添加Gson。或者jackson,必要时还要配置一下converter的bean。
@RequestBody主要用来接收前端传递给后端的json字符串中的数据的(请求体中的数据的);GET方式无请求体,所以使用@RequestBody接收数据时,前端不能使用GET方式提交数据,而是用POST方式进行提交。在后端的同一个接收方法里,@RequestBody与@RequestParam()可以同时使用,@RequestBody最多只能有一个,而@RequestParam()可以有多个。
@Value、@Validated
@Valid是javax.validation里的。
@Validated是@Valid 的一次封装,是Spring提供的校验机制使用。@Valid不提供分组功能。
@ExceptionHandler
@ExceptionHandler只有一个参数,可以是一个数组,表示要捕获的异常。@ExceptionHandler遵循就近原则,当异常发生时,Spring会选择最接近抛出异常的处理方法。比如上面ArithmeticException,这个异常有父类RuntimeException,RuntimeException还有父类Exception,但是我们通过debug,可以知道,它只会被pointException方法处理。
@RequestMapping
RequestMapping是一个用来处理请求地址映射的注解,可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。
@RequestMapping 除了修饰方法, 还可来修饰类 :
- 类定义处: 提供初步的请求映射信息。相对于 WEB 应用的根目录;
- 方法处: 提供进一步的细分映射信息。 相对于类定义处的 URL。
- 若类定义处未标注 @RequestMapping,则方法处标记的 URL相对于 WEB 应用的根目录
- 返回ModelAndView时的url会根据你的 @RequestMapping实际情况组成。
- 对应项目jsp位置则是一级路径对应一级文件目录:如url为/default/index对应项目中webapp/default/index.jsp
- 如果类上没有映射,那么url直接就是方法的映射;否则url为类上+方法上映射路径组合。
@RequestMapping注解有六个属性,下面我们把它分成三类进行说明:
- value:指定请求的实际地址,指定的地址可以是URI Template 模式;
- method: 指定请求的method类型, GET、POST、PUT、DELETE等,在RequestMethod定义;
- consumes: 指定处理请求的提交内容类型(Content-Type),例如application/json, text/html;
- produces: 指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回;
- params: 指定request中必须包含某些参数值时,才让该方法处理;
- headers: 指定request中必须包含某些指定的header值,才能让该方法处理请求;
@RequestMapping 一共有五种映射方式:
(1) 标准URL 映射是最简单的一种映射
@RequestMapping("/hello") 或 @RequestMapping({"/hello","/world"})
(2) Ant风格的 URL 映射
Ant 通配符有三种:
?:匹配任何单字符
*:匹配任意数量的字符(含 0 个)
**:匹配任意数量的目录(含 0 个)
@RequestMapping("/?/hello/")
@RequestMapping("/*/hello")
@RequestMapping("/**/hello")
(3) 占位符URL 映射
URL 中可以通过一个或多个 {} 占位符映射。
如:@RequestMapping("/user/{userId}/show")
可以通过@PathVariable("") 注解将占位符中的值绑定到方法参数上。
(4) 限制请求方法的URL 映射
@RequestMapping(value="/hello", method=RequestMethod.POST)
如需限制多个请求方法,以大括号包围,逗号隔开即可,例如:
method={RequestMethod.GET,RequestMethod.POST}
(5) 限制请求参数的URL 映射
@RequestMapping(value="/user/show", params="userId") 即请求中必须带有userId 参数。
params="userId" 请求参数中必须包含 userId
params="!userId" 请求参数中不能包含 userId
params="userId!=1" 请求参数中必须包含 userId,但不能为 1
params={"userId","userName"} 必须包含 userId 和 userName 参数。
可以通过@RequestParam("") 注解将请求参数绑定到方法参数上。
@PathVariable
@PathVariable 映射 URL 绑定的占位符:
- 带占位符的 URL 是 Spring3.0 新增的功能,该功能在SpringMVC 向 REST 目标挺进发展过程中具有里程碑的意义
- 通过 @PathVariable 可以将 URL 中占位符参数绑定到控制器处理方法的入参中:URL 中的 {xxx} 占位符可以通过@PathVariable(“xxx“) 绑定到操作方法的入参中。
@RequestHeader、@RequestParam、@CookieValue
1、@CookieValue
获取指定cookie的值,有以下三个属性:
value:对应cookie的key
defaultValue:当key值没有传输时将使用这个参数进行赋值
requied:是否必须。默认为 true, 表示请求参数中必须包含对应的参数,若不存在,将抛出异常。
2、@RequestHeader
@RequestHeader 注解,可以把Request请求header部分的值绑定到方法的参数上。
3、@RequestParam
将请求参数绑定到你控制器的方法参数上(是springmvc中接收普通参数的注解)。
语法:@RequestParam(value="参数名",required="true/false",defaultValue="")
value:参数名
required:是否包含该参数,默认为true,表示该请求路径中必须包含该参数,如果不包含就报错。
defaultValue:默认参数值,如果设置了该值,required=true将失效,自动为false,如果没有传该参数,就使用默认值。
@DependsOn
控制bean的创建顺序。
在另外一个实例创建之后才创建当前实例,也就是,最终两个实例都会创建,只是顺序不一样。
@Order
@Order或者接口Ordered的作用是定义Spring IOC容器中Bean的执行顺序的优先级,而不是定义Bean的加载顺序,Bean的加载顺序不受@Order或Ordered接口的影响;
值越小优先级越高。越先执行,可为负数。
@CrossOrigin
@CrossOrigin是用来处理跨域请求的注解。SpringMVC的版本要在4.2或以上版本才支持@CrossOrigin。
跨域,指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对JavaScript施加的安全限制。
所谓同源是指,域名,协议,端口均相同。
请注意:localhost和127.0.0.1虽然都指向本机,但也属于跨域。
浏览器执行javascript脚本时,会检查这个脚本属于哪个页面,如果不是同源页面,就不会被执行。
@CrossOrigin有以下两个参数:
origins:允许可访问的域列表
maxAge:准备响应前的缓存持续的最大时间(以秒为单位)。
Spring注解@CrossOrigin不起作用的原因:
(1) SpringMVC的版本要在4.2或以上版本才支持@CrossOrigin
(2) 非@CrossOrigin没有解决跨域请求问题,而是不正确的请求导致无法得到预期的响应,导致浏览器端提示跨域问题
(3) 在Controller注解上方添加@CrossOrigin注解后,仍然出现跨域问题,解决方案之一就是:在@RequestMapping注解中没有指定Get、Post方式,具体指定后,问题解决
@Conditional
根据条件,决定类是否加载到Spring Ioc容器中,在SpringBoot中有大量的运用。
@Conditional 注解接收的参数是 extends Condition 接口的泛型类,也就是说,我们要使用 @Conditional 注解,只需要实现 Condition 接口并重写其方法即可。
public class MyCondition implements Condition { public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { Environment env = context.getEnvironment(); String system = env.getProperty("os.name"); System.out.println("系统环境为 ===" + system); // 系统环境在Windows才加载该bean到容器中 if(system.contains("Windows")){ return true; } return false; } }
这样使用:
@Conditional(MyCondition.class) @Bean public User createUser(){ User user = new User(); user.setUserId(1); user.setUserName("linhw"); return user; }
@Import
@Import只能用在类上 ,@Import通过快速导入的方式实现把实例加入spring的IOC容器中。
@Import的三种用法主要包括:
- 直接填class数组方式
- ImportSelector方式【重点】
- ImportBeanDefinitionRegistrar方式
第一种用法:直接填对应的class数组,class数组可以有0到多个。
@Import({ 类名.class , 类名.class... }) public class TestDemo { }
对应的import的bean都将加入到spring容器中,这些在容器中bean名称是该类的全类名。
第二种用法:ImportSelector方式
这种方式的前提就是一个类要实现ImportSelector接口,并重写selectImports方法。
public class Myclass implements ImportSelector { //既然是接口肯定要实现这个接口的方法 @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { return new String[]{"com.codedot.Test.TestDemo3"}; } }
分析实现接口的selectImports方法中的:
- 返回值: 就是我们实际上要导入到容器中的组件全类名【重点 】
- 参数: AnnotationMetadata表示当前被@Import注解给标注的所有注解信息
需要注意的是selectImports方法可以返回空数组但是不能返回null,否则会报空指针异常!
第三种用法:ImportBeanDefinitionRegistrar方式
实现ImportBeanDefinitionRegistrar接口,重写registerBeanDefinitions方法。
public class Myclass2 implements ImportBeanDefinitionRegistrar { //该实现方法默认为空 @Override public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) { //指定bean定义信息(包括bean的类型、作用域...) RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(OrderService.class); //注册一个bean指定bean名字(id) beanDefinitionRegistry.registerBeanDefinition("TestDemo4444",rootBeanDefinition); } }
参数分析:
- 第一个参数:annotationMetadata 和之前的ImportSelector参数一样都是表示当前被@Import注解给标注的所有注解信息
- 第二个参数表示用于注册定义一个bean
@ImportResource
通过@ImportResource实现xml配置的装载,让配置文件里面的内容生效。
@PropertySource、@PropertySources
@PropertySource加载指定的属性文件(*.properties)到 Spring 的 Environment 中。可以配合 @Value 和 @ConfigurationProperties 使用。
属性文件:demo.properties
demo.name=huang demo.sex=1 demo.type=demo
@PropertySource 和 @Value 组合使用,可以将自定义属性文件中的属性变量值注入到当前类的使用@Value注解的成员变量中。
import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.PropertySource; import org.springframework.stereotype.Component; @Component @PropertySource(value = {"demo/props/demo.properties"}) public class ReadByPropertySourceAndValue { @Value("${demo.name}") private String name; @Value("${demo.sex}") private int sex; @Value("${demo.type}") private String type; @Override public String toString() { return "ReadByPropertySourceAndValue{" + "name='" + name + '\'' + ", sex=" + sex + ", type='" + type + '\'' + '}'; } }
@PropertySource 和 @ConfigurationProperties 组合使用,可以将属性文件与一个Java类绑定,将属性文件中的变量值注入到该Java类的成员变量中。
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.PropertySource; import org.springframework.stereotype.Component; @Component @PropertySource(value = {"demo/props/demo.properties"}) @ConfigurationProperties(prefix = "demo") public class ReadByPropertySourceAndConfProperties { private String name; private int sex; private String type; public void setName(String name) { this.name = name; } public void setSex(int sex) { this.sex = sex; } public void setType(String type) { this.type = type; } public String getName() { return name; } public int getSex() { return sex; } public String getType() { return type; } @Override public String toString() { return "ReadByPropertySourceAndConfProperties{" + "name='" + name + '\'' + ", sex=" + sex + ", type='" + type + '\'' + '}'; } }
@EnableAspectJAutoProxy
在配置类上添加@EnableAspectJAutoProxy注解,开启注解版的AOP功能。
@EnableAspectJAutoProxy有两个配置项:
- proxyTargetClass:如果不填proxyTargetClass=true,默认为false。true则强制使用 cglib动态代理。
- exposeProxy:设置@EnableAspectJAutoProxy(exposeProxy=true)表示通过aop框架暴露该代理对象,aopContext能够访问.
@InitBinder
在使用SpingMVC框架的项目中,经常会遇到页面某些数据类型是Date、Integer、Double等的数据要绑定到控制器的实体,或者控制器需要接受这些数据,如果这类数据类型不做处理的话将无法绑定。
我们可以使用注解@InitBinder来解决这些问题,这样SpingMVC在绑定表单之前,都会先注册这些编辑器。一般会将这些方法些在BaseController中,需要进行这类转换的控制器只需继承BaseController即可。其实Spring提供了很多的实现类,如CustomDateEditor、CustomBooleanEditor、CustomNumberEditor等,基本上是够用的。
public class BaseController { @InitBinder protected void initBinder(WebDataBinder binder) { binder.registerCustomEditor(Date.class, new MyDateEditor()); binder.registerCustomEditor(Double.class, new DoubleEditor()); binder.registerCustomEditor(Integer.class, new IntegerEditor()); } private class MyDateEditor extends PropertyEditorSupport { @Override public void setAsText(String text) throws IllegalArgumentException { SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date date = null; try { date = format.parse(text); } catch (ParseException e) { format = new SimpleDateFormat("yyyy-MM-dd"); try { date = format.parse(text); } catch (ParseException e1) { } } setValue(date); } } public class DoubleEditor extends PropertiesEditor { @Override public void setAsText(String text) throws IllegalArgumentException { if (text == null || text.equals("")) { text = "0"; } setValue(Double.parseDouble(text)); } @Override public String getAsText() { return getValue().toString(); } } public class IntegerEditor extends PropertiesEditor { @Override public void setAsText(String text) throws IllegalArgumentException { if (text == null || text.equals("")) { text = "0"; } setValue(Integer.parseInt(text)); } @Override public String getAsText() { return getValue().toString(); } } }
@RequestPart
支持对multipart/form-data 类型的参数进行参数绑定,支持的参数类型有 :MultipartFile,MulitpartResolver , Part 等参数。
对于其他任意的参数类型,类似于 requestBody 对参数的解析。
注意:requestParam 类型对于 multipart/form-data 也支持参数解析,区别在于处理字符串时不同
如果@RequestPart 注解的参数类型是MultipartFile 就必须配置 multipartResolver 解析器, 如果@RequestPart 注解的参数类型是Servlet 3.0 规范的 Part 参数,就不必配置multipartResolver 解析器。
配置解析器如下:
//配置MultipartResolver 解析器 @Bean public MultipartResolver multipartResolver(){ CommonsMultipartResolver multipartRe = new CommonsMultipartResolver(); multipartRe.setMaxUploadSize(1024000000); multipartRe.setResolveLazily(true); multipartRe.setMaxInMemorySize(4096); multipartRe.setDefaultEncoding("UTF-8");//设置默认字符集 return multipartRe; }