spring和springboot项目中常用的注解
1、常用的注解
1、@Component注解
package soundsystem;
public interface CompactDisc {
void play();
}
package soundsystem;
import org.springframework.stereotype.Component;
@Component
public class SgtPeppers implements CompactDisc {
private String title = "Sgt. Peppers's Longly Hearts";
private String artist = "The Beatles";
@Override
public void play() {
System.out.println("Playing " + title + " by " + artist);
}
}
上面实现类上使用了@Component注解,该注解表明该实现类会作为组件类,告诉spring容器要为该类创建一个bean,spring会自动帮我们创建。不过组件扫描默认是不开启的,我们需要显式配置一下spring,使用@ComponentScan注解开启组件扫描,从而命令spring去寻找带有@Component注解的类,并为其创建bean。(补充:@Controller、@Service、@Repository注解与@Component注解的作用相同。)
为组件扫描的bean命名:
@Component("lonelyHearts")
public class SgtPeppers implements CompactDisc {
……
}
Spring应用上下文中所有的bean都会有一个ID,此处我们明确的为SgtPeppers bean(或者叫CompactDisc bean)设置ID,ID的值为lonelyHearts。如果没有明确设置ID,默认是将类名首字母小写作为ID的值,如上面的sgtPeppers。
Spring中也可以使用@Name替代@Component注解,不过@Name注解不常用。
Spring容器扫描到类上带有@Component注解时,会解析该类的信息保存到对应的BeanDefinition对象中,并将BeanDefinition注册到BeanDefinitionMap中,BeanDefinitionMap就是一个Map,以beanName为键,以BeanDefinition为值。Spring容器最终会通过BeanDefinition生成一个bean,也就是说通过@Component注解可以生成bean。如果没有在类上加上@Component注解,也想通过spring容器生成并管理bean,该怎么办呢?可以写一个配置类(带有@Configuration注解的类),在配置类中写一个带有@Bean注解的方法,通过方法生成想要的bean(这就是通过显示的java代码装配的方式创建bean)。另外还有一种方法,通过@Import注解将对象交给spring容器管理。
2、@ComponentScan注解
创建配置类并开启组件扫描:
package soundsystem;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan
public class CDPlayerConfig {
}
@ComponentScan注解能够在spring中开启组件扫描,该注解会使spring默认扫描配置类所在的包及其子包(可以明确设置组件扫描的基础包,见下面讲解),查找带有@Component注解的类,在spring上下文中自动创建一个bean。比如此处配置类CDPlayerConfig在soundsystem包中,spring会扫描soundsystem包及其所有子包,发现实现类SgtPeppers上面有@Component注解,所以spring会自动创建SgtPeppers类的一个bean。
另外,在XML配置文件中可以使用component-scan启用组件扫描:
<content:component-scan base-package=”soundsystem” />
设置组件扫描的基础包:
@Configuration
@ComponentScan("soundsystem")
public class CDPlayerConfig {
}
如果@ComponentScan没有设置任何属性的话,默认是以配置类所在的包作为基础包(base package)来扫描组件的。此处设置扫描的基础包为soundsystem。可以使用basePackages属性更加清晰的表明我们所设置的是基础包,该属性的值可以是字符串数组。
@Configuration
@ComponentScan(basePackages = "soundsystem")
public class CDPlayerConfig {
}
// 或者是字符串数组
@Configuration
@ComponentScan(basePackages = {"soundsystem","video"})
public class CDPlayerConfig {
}
另外,可以将basePackages属性换成basePackageClasses属性,其属性值就是包中包含的类或接口,这些类所在的包将会作为组件扫描的基础包。
@Configuration
@ComponentScan(basePackageClasses = {CDPlayer.class,DVDPlayer.class})
public class CDPlayerConfig {
}
3、@Configuration注解
在注解出现之前,XML配置方式是Spring IoC容器的唯一配置方式。之前,我们将配置信息集中写在XML中,如今使用注解,配置信息的载体由XML文件转移到了Java类中。我们通常将用于存放配置信息的类的类名以“Config”结尾,比如AppDaoConfig.java、AppServiceConfig.java 等等。我们需要在用于指定配置信息的类上加上@Configuration注解,以明确指出该类是Bean配置的信息源。加上@Configuration注解的类就是配置类,该注解的作用就是告诉spring,配置类起到了与XML配置文件相同的作用,只不过不用写那么多复杂的配置文件了。spring应用上下文AnnotationConfigApplicationContext 将配置类中标注了@Bean注解的方法的返回值识别为Spring Bean,并注册到容器中,受IoC 容器管理。用@Configuration注解类,等价于XML中配置beans,用@Bean标注方法等价于XML中配置bean。AnnotationConfigApplicationContext 在解析配置类时,会将配置类自身注册为一个 Bean,因为 @Configuration 注解本身定义时被 @Component 标注了。因此可以说,一个 @Configuration 同时也是一个 @Component。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
String value() default "";
}
@Bean注解
package soundsystem;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class CDPlayerConfig {
@Bean
public CompactDisc sgtPeppers(){
return new SgtPeppers();
}
@Bean(name = "lonelyHearts")
public CompactDisc peppers(){
return new SgtPeppers();
}
}
@Bean注解会告诉spring这个方法将会返回一个对象,该对象要注册为spring应用上下文中的bean,方法体中包含了最终产生bean实例的逻辑。
默认情况下,bean的ID与带有@Bean注解的方法名是一样的,在上例中,bean的名字就是sgtPeppers,也可以通过name属性为bean指定一个名字,例如上例中的"lonelyHearts"。
4、@ContextConfiguration注解
package soundsystem;
import static org.junit.Assert.assertNotNull;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=CDPlayerConfig.class)
public class CDPlayerTest {
@Autowired
private CompactDisc cd;
@Test
public void cdShouldNotBeNull() {
assertNotNull(cd);
}
}
在测试类上使用了@ContextConfiguration(classes=CDPlayerConfig.class)注解,该注解会告诉spring需要在CDPlayerConfig配置类中加载应用上下文ApplicationContext配置。由于在CDPlayerConfig类上使用了@ComponentScan注解,同时CompactDisc的实现类上使用了@Component注解,所以最终spring会在上下文中创建CompactDisc的一个bean。为了证明这一点,在测试类中使用了@ Autowired注解,将CompactDisc bean注入到测试代码中。最后用断言方法验证cd是否为空,如果cd不是null,说明spring能够自动创建CompactDisc的一个bean。
另外,在测试类CDPlayerTest上使用了@RunWith(SpringJUnit4ClassRunner.class)注解,以便在测试开始的时候自动创建spring的应用上下文。
@RunWith : 运行器
@RunWith(JUnit4.class)就是指用JUnit4来运行
@RunWith(SpringJUnit4ClassRunner.class),让测试运行于Spring测试环境
@RunWith(Suite.class)的话就是一套测试集合
5、@Autowired注解
@Autowired注解的作用是将一个bean注入到另一个bean中。在上面的测试类CDPlayerTest中,为了使用CompactDisc cd对象,通过@Autowired将CompactDisc cd对象注入到了测试类CDPlayerTest中。再看一个例子:
package soundsystem;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class CDPlayer implements MediaPlayer {
private CompactDisc cd;
@Autowired
public CDPlayer(CompactDisc cd){
this.cd = cd;
}
public void play(){
cd.play();
}
}
在这个例子中@Autowired注解用在了构造方法上,该注解不仅可以用在构造方法上,还可以用在属性的setter方法上,以及其他的普通方法上,发挥的作用都是相同的。@Autowired注解经常被用在属性上,用于自动注入依赖。
@Autowired注解默认根据byType完成自动装配,其次根据byName寻找所需的bean。
1、如果没有符合类型的bean,则抛出异常。
2、如果有且只有一个符合类型的bean,则完成自动装配。
3、如果有多个符合类型的bean,则根据byName寻找。
4、由于bean的名称在spring容器中是唯一的,如果根据byName找到了所需的bean,则完成自动装配。如果没有找到,则抛出异常。
@Autowired注入属性为null的原因及解决方法:
原因:
1、配置缺失,比如未开启注解扫描驱动、注入组件未注册;
2、使用 new 关键字创建的对象不受spring容器管理,无法注入;
3、注入静态变量, 静态变量/类变量不是对象的属性,而是一个类的属性,spring则是基于对象层面上的依赖注入。
对于静态变量的注入,可以通过以下两种方式实现。
方式一:set方法上添加@Autowired注解,类定义上添加@Component注解
@Component
public class MongoFileOperationUtil {
private static AdvancedDatastore dsForRW;
@Autowired
public void setDatastore(AdvancedDatastore dsForRW) {
MongoFileOperationUtil.dsForRW = dsForRW;
}
}
首先Spring要能扫描到AdvancedDatastore的bean,然后通过setter方法注入。
注意:静态变量上不需要再添加@Autowired注解。
方式二:@PostConstruct方式实现
@PostConstruct 注解的方法在加载类的构造函数之后执行,@PostConstruct注解的方法将会在依赖注入完成后被自动调用。
@Component
public class FileOperation {
@Autowired
private static FileReadWrite fileRW;
private static MyFileOperation myFileOperation;
@PostConstruct
public void init() {
myFileOperation= this;
myFileOperation.fileRW= this.fileRW;
}
}
6、@Resource注解
@Resource 的作用相当于 @Autowired,只不过 @Autowired 默认按byType自动注入,而@Resource 默认按byName自动注入罢了。@Resource 有两个属性是比较重要的,分别是 name和type,Spring将@Resource 注释的name属性解析为Bean的名字,而type属性则解析为Bean的类型。所以如果使用name属性,则使用byName的自动注入策略,而使用type 属性时则使用byType自动注入策略。如果既不指定name也不指定type属性,这时将通过反射机制使用byName自动注入策略。
public class Boss {
// 自动注入类型为 Car 的 Bean
@Resource
private Car car;
// 自动注入 bean 名称为 office 的 Bean
@Resource(name = "office")
private Office office;
}
@Autowired与@Resource的区别:
@Autowired 默认是先通过 byType 的方式去注入的,如果找不到,则抛异常。如果通过byType找到多个符合要求的bean,再根据byName的方式寻找,找到则完成自动装配,否则抛出异常。该注解由AutowiredAnnotationBeanPostProcessor解析。
@Resource 默认先按 byName的方式进行匹配,如果匹配不到,再按 byType的方式进行匹配,如果找不到或找到多个,则抛异常。如果@Resource注解指定了name属性,则按名称自动注入bean,若没有找到指定名称的bean,则不会再按类型注入bean,此时会报没有名称为xxx的bean被定义的异常,如No bean named 'office' is defined。@Resource注解由CommonAnnotationBeanPostProcessor解析。
7、@Primary、@Qualifier
如果一个接口有多个实现类,在自动注入接口时,会选择哪一个实现类进行注入?
@Primary和@Qualifier 注解的区别在于:
用@Primary告诉spring优先选择哪一个具体的实现。
用@Qualifer告诉spring真正使用哪一个具体的实现。
例如,有一个接口和两个实现类:
public interface Singer {
String sing(String lyrics);
}
@Component // 加注解,让spring识别
public class MetalSinger implements Singer{
@Override
public String sing(String lyrics) {
return "I am singing with DIO voice: "+lyrics;
}
}
@Primary
@Component
public class OperaSinger implements Singer{
@Override
public String sing(String lyrics) {
return "I am singing in Bocelli voice: "+lyrics;
}
}
测试类:
@Component
public class SingerService {
private static final Logger logger = LoggerFactory.getLogger(SingerService.class);
@Autowired
private Singer singer;
public String sing(){
return singer.sing("song lyrics");
}
}
输出结果:"I am singing in Bocelli voice: song lyrics"
用@Primary 告诉spring优先选择哪一个具体的实现。
如果在OperaSinger类上没有加上@Primary注解,则出现下面的异常:
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [main.service.Singer] is defined: expected single matching bean but found 2: metalSinger,operaSinger
对于上面可以使用@Qualifier代替@Primary,具体实现如下所示:
public interface Singer {
String sing(String lyrics);
}
@Service("singerOne") // 加注解,让spring识别
public class MetalSinger implements Singer{
@Override
public String sing(String lyrics) {
return "I am singing with DIO voice: "+lyrics;
}
}
@Service("singerTwo")
public class OperaSinger implements Singer{
@Override
public String sing(String lyrics) {
return "I am singing in Bocelli voice: "+lyrics;
}
}
测试类:
@Component
public class SingerService {
private static final Logger logger = LoggerFactory.getLogger(SingerService.class);
@Autowired
@Qualifier("singerTwo")
private Singer singer;
public static void main(String[] args){
return singer.sing("song lyrics");
}
}
@Qualifier注解必须与@Autowired注解一起使用。@Qualifier注解中的参数可以使用类名,如此处的@Qualifier("singerTwo")可以换成@Qualifier("OperaSinger")。也可以使用@Resource注解代替@Autowired和@Qualifier注解,即@Resource(name="singerTwo")。
8、@Controller和@RestController
@Controller : 注解在类上,声明这个类是springmvc里的Controller,将其声明为一个spring的Bean。
@ResponseBody : 支持将返回值放入response体内,而不是返回一个页面(返回的是一个数据)。
@RestController注解相当于@ResponseBody + @Controller合在一起的作用。
1)如果只是使用@RestController注解Controller,则Controller中的方法无法返回jsp页面,配置的视图解析器InternalResourceViewResolver不起作用,返回的内容就是Return 里的内容。例如Controller类中有个方法返回的结果是return "success",本来应该跳到success.jsp页面的,但是浏览器上却直接显示success。
2)如果需要返回到指定页面,则需要用 @Controller配合视图解析器InternalResourceViewResolver才行。
3)如果需要返回JSON,XML或自定义mediaType内容到页面,则需要在对应的方法上加上@ResponseBody注解。
9、@RequestMapping
@RequestMapping是一个用来处理请求地址映射的注解,可用于类或者方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。@RequestMapping注解有六个属性,下面进行详细的说明。
1.value, method.
value:指定请求的实际地址,指定的地址可以是URI Template模式。
method:指定请求的method类型,GET、POST、PUT、DELETE等。
2.consumes, produces.
consumes:指定处理请求的提交内容类型(Content-Type),例如application/json,text/html;
produces:指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回;
3.params, headers.
params:指定request中必须包含某些参数值才让该方法处理。
headers:指定request中必须包含某些指定的header值,才能让该方法处理请求。
handler method参数绑定常用的注解,根据他们处理的request的不同内容部分分为四类:
A:处理request uri部分的注解:@PathVariable;
B:处理request header部分的注解:@RequestHeader, @CookieValue;
C:处理request body部分的注解:@RequestParam, @RequestBody;
D:处理attribute类型的注解:@SessionAttributes, @ModelAttribute;
1.@PathVariable
当使用@RequestMapping URI template样式映射时,即someUrl/{paramId},这时的paramId可通过@PathVariable注解绑定它传过来的值到方法的参数上。
2.@RequestHeader、@CookieValue
@RequestHeader注解可以把Request请求header部分的值绑定到方法的参数上。
@CookieValue可以把Request header中关于cookie的值绑定到方法的参数上。
package com.example.myproject01;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloWorld {
@RequestMapping(value = "/hello",method = RequestMethod.GET)
public String hello() {
return "hello world!!";
}
@RequestMapping("/helloName/{name}")
public String helloName(@PathVariable("name") String name){
return "hello " + name;
}
}
@GetMapping(查询)是一个组合注解,是@RequestMapping(method = RequestMethod.GET)的缩写。该注解将HTTP Get映射到特定的处理方法上。@PostMapping(新增)、@PutMapping(更新)、@DeleteMapping(删除)也都是组合注解。
@Valid注解:
在对象参数前使用@Valid注解,@Valid注解表示对对象属性进行验证,同时添加BindingResult对象,该对象接收验证的结果,如果验证不通过,就打印出错误信息,并返回null终止向下进行。另外,还需要在实体类中设置需要验证的属性条件。在本例中,在实体类里给属性age加上@Min注解,加上需要验证的限制条件。
Controller类:
package com.example.myproject.controller;
import com.example.myproject.domain.Person;
import com.example.myproject.domain.PersonRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
@RestController
public class PersonController {
@Autowired
private PersonRepository personRepository;
//在对象参数前使用@Valid注解
@PostMapping(value = "/postPerson")
private Person postPerson(@Valid Person person, BindingResult bindingResult){
if(bindingResult.hasErrors()){
System.out.println(bindingResult.getFieldError().getDefaultMessage());
return null;
}
person.setId(person.getId());
person.setAge(person.getAge());
person.setName(person.getName());
person.setSex(person.getSex());
return personRepository.save(person);
}
}
实体类:
package com.example.myproject.domain;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.validation.constraints.Min;
//加上@Entity注解之后,就可以将类映射成数据库中的数据表,将属性映射成字段
@Entity
public class Person {
@Id
@GeneratedValue
private int id;
private String name;
private String sex;
//@Min注解给age属性加一个限制条件,如果年龄小于18岁,就返回给用户提示错误信息:未成年人禁止入内!
@Min(value = 18,message = "未成年人禁止入内!")
private int age;
//必须写空参数的构造函数,否则数据库会报错
public Person() {
}
// 省略getter、setter方法
}
10、@PathVariable
@PathVariable : 用来接收路径参数。
比如对于上面的请求路径:@RequestMapping("/helloName/{name}")
对于请求路径@RequestMapping("/helloName/{name}"),必须使用@PathVariable注解。在浏览器中请求时,如果路径参数名与方法参数名相同,可以不用此注解,如果不同,必须要使用该注解进行绑定。如上面的@RequestMapping("/helloName/{name}")路径参数名是name,方法public String helloName(@PathVariable("name") String name)中参数名也是name,所以此处可以不用加上@PathVariable("name")注解。如果把方法参数名String name改为String myname,此时就需要加上@PathVariable("name")注解。
11、@RequestParam
@RequestParam注解表示接受请求参数,接受在浏览器中url后面用?连接的参数,如http://localhost:8080/say?id=10。@RequestParam中有一些属性,如value、required、defaultValue,这些属性可有可无,如果写的话,value的值必须与url中请求参数名相同。
package com.example.myproject.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
@RequestMapping(value = "/say",method = RequestMethod.GET)
public String say(@RequestParam(value = "id",required = false,
defaultValue = "0") int myId){
return "id:" + myId;
}
}
12、@RequestBody
通过@requestBody可以将请求体中的JSON字符串绑定到相应的bean上,当然,也可以将其分别绑定到对应的字符串上。
@RequestMapping("/login")
public void login(@RequestBody String userName, @RequestBody String pwd){
System.out.println(userName+" :"+pwd);
}
这种情况是将JSON字符串中的两个变量的值分别赋予了两个字符串。假如有一个User类,拥有如下字段:
String userName;
String pwd;
那么上述参数可以改为以下形式:@RequestBody User user 这种形式会将JSON字符串中的值赋予user中对应的属性上。需要注意的是,JSON字符串中的key必须对应user中的属性名,否则是请求不过去的。
13、@Value
@Value : 属性注入,读取properties文件或者Yaml文件中的属性。
(1)创建application.yml文件:
#设置一些属性
name: 倚天照海
age: 18
content: "${name}今年${age}岁了!!!"
(2)创建controller类,并通过@Value注解引用application.yml文件中的属性。
package com.example.myproject.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
@Value("${name}")
private String name;
@Value("${age}")
private int age;
@Value("${content}")
private String content;
@RequestMapping(value = {"/user","/hehe","/heihei"},method = RequestMethod.GET)
public String user(){
return name + " " + age;
//要想返回页面只能在类上使用@Controller注解
//return "index";
}
}
14、@ConfigurationProperties
虽然可以在类中通过@Value注解将配置文件中的属性及其值引入到类中使用,但是当配置文件中属性特别多的时候,就需要多次使用@Value注解,过于繁琐。当配置文件中有很多配置属性的时候,这时我们会把这些属性作为字段来创建一个javabean,并将属性值赋予给他们。于是可以将配置文件中的属性封装到一个类中,通过@ConfigurationProperties注解将配置文件中的属性值一次性引入到封装类的属性中。
(1)创建yaml配置文件:
hero:
name: 倚天照海
age: 18
content: "倚天照海今年18岁了!!!"
(2)创建一个类,并封装配置文件中的属性:
package com.example.myproject.domain;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
//将application.yml文件中的多个属性封装在一个类中,可以避免多次使用@Value()注解。
@Component
@ConfigurationProperties(prefix = "hero")
public class UserProperties {
private String name;
private int age;
private String content;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
(3)然后需要在pom.xml中添加spring-boot-configuration-processor依赖:
<!--使用@ConfigurationProperties注解就要引入这个依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
(4)最后要在使用这些属性的类或者主类上添加@EnableConfigurationProperties注解,同时在类中使用@Autowired注解将封装类对象注入到该类中:
package com.example.myproject.controller;
import com.example.myproject.domain.UserProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
@EnableConfigurationProperties(UserProperties.class)
public class UserController {
/*
@Value("${name}")
private String name;
@Value("${age}")
private int age;
@Value("${content}")
private String content;
*/
//将application.yml文件中的多个属性封装在一个类中,可以避免多次使用@Value()注解。
@Autowired
private UserProperties userProperties;
@RequestMapping(value = {"/user","/hehe","/heihei"},method = RequestMethod.GET)
public String user(){
return userProperties.getContent();
}
}
拓展:如果只使用@ConfigurationProperties或者只使用@RestController和@Value,获取到的属性的值为null,不知道为什么!下面结合使用这几个注解才有效果。
@PropertySource("classpath:sql.yml")
@ConfigurationProperties(prefix = "columnName")
@RestController
@Value
1.创建sql.yml配置文件(此处配置文件名称不是application.yml):
columnInfo:
userColumnName: user_id, username, password
tagColumnName: tag_id, tag_name
2.创建配置类,读取配置文件中的属性值
@Configuration
@PropertySource("classpath:sql.yml")
@ConfigurationProperties(prefix = "columnInfo")
@RestController
public class ColumnInfoConfig {
@Value("${userColumnName}")
private String userColumnName;
@Value("${tagColumnName}")
private String tagColumnName;
}
3.在主函数启动类上加上@EnableConfigurationProperties({ColumnInfoConfig.class})
@EnableConfigurationProperties({ColumnInfoConfig.class})
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
4.最后在需要使用属性值的类中注入ColumnInfoConfig即可。
15、@EnableConfigurationProperties
@EnableConfigurationProperties 开启对@ConfigurationProperties注解配置Bean的支持。开启属性注入。
在上面的UserController 类中,@EnableConfigurationProperties({ UserProperties.class })告知Spring在实例化UserController 之前需要先实例化UserProperties(此类必须带注解@ConfigurationProperties,带此注解的类并不会被Spring实例化,只是会被加载进Environment),然后就可以用@Autowired引用。
16、@EnableScheduling
@EnableScheduling注解会初始化一个Scheduler用于执行定时任务和异步任务。
通过下面两步就可以实现定时任务:
1、在Spring Boot的主类中加入@EnableScheduling注解,启用定时任务的配置
2、创建一个实体类和方法,并在方法上使用@Scheduled注解
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableScheduling
public class MapApplication {
public static void main(String[] args) {
SpringApplication.run(MapApplication.class, args);
}
}
import com.google.common.collect.Lists;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.text.SimpleDateFormat;
import java.util.List;
@Component
public class ScheduleUtil {
private static final Logger logger = LoggerFactory.getLogger(ScheduleUtil.class);
@Scheduled(initialDelay = 2000,fixedDelay = 10000)
private void customSchedule(){
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
logger.info("start_time==========="+sdf.format(System.currentTimeMillis()));
try {
Thread.sleep(1000);
}catch (InterruptedException e){
logger.error(e.getMessage());
}
logger.info("end_time==========="+sdf.format(System.currentTimeMillis()));
}
}
@Scheduled(initialDelay = 2000,fixedDelay = 10000) 表示项目启动之后大约延迟两秒自动执行定时任务,并且以后固定每隔10秒再次执行定时任务。
17、@PostConstruct
被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器调用一次,类似于Servlet的inti()方法。被@PostConstruct修饰的方法会在构造函数之后,init()方法之前运行。
@Documented
@Retention(value=RUNTIME)
@Target(value=METHOD)
public @interface PostConstruct
@PostConstruct 注解用于在依赖关系注入完成之后需要执行的方法上,以执行任何初始化。此方法必须在将类放入服务之前调用。支持依赖关系注入的所有类都必须支持此注解。即使类没有请求注入任何资源,用 PostConstruct 注释的方法也必须被调用。只有一个方法可以用此注解进行注释。应用 PostConstruct 注释的方法必须遵守以下所有标准:该方法不得有任何参数,除非是在 EJB 拦截器 (interceptor) 的情况下,根据 EJB 规范的定义,在这种情况下它将带有一个 InvocationContext 对象 ;该方法的返回类型必须为 void;该方法不得抛出已检查异常;应用PostConstruct的方法可以是 public、protected、default或private;除了应用程序客户端之外,该方法不能是 static;该方法可以是 final;如果该方法抛出未检查异常,那么不得将类放入服务中,除非是能够处理异常并可从中恢复的 EJB。
使用方式如下所示:
@PostConstruct //方式1
public void someMethod(){
...
}
public @PostConstruct void someMethod(){ //方式2
...
}
import java.util.HashMap;
import java.util.Map;
import javax.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import indi.config.IpsIndiConfig;
import indi.service.data.Querier;
import indi.vo.data.IndiDataVo;
import web.core.RespResult;
/**
* RDS 服务数据查询
*/
@Service
public class RdsQuerierService implements Querier {
@Autowired
private RestTemplate restTemplate;
@Autowired
private IpsIndiConfig ipsIndiConfig;
private String serviceUrl;
private static final Logger logger = LoggerFactory.getLogger(RdsQuerierService.class);
@SuppressWarnings("unchecked")
@Override
public Object query(IndiDataVo indi) throws Exception {
Map<String, String> vars = new HashMap<String, String>();
vars.put("indiId", indi.getIndiId());
RespResult<Object> obj = restTemplate.postForObject(serviceUrl, indi.getParameter(), RespResult .class, vars);
return obj;
}
@PostConstruct
private void init() {
this.serviceUrl = ipsIndiConfig.getDataUrlRds();
logger.info("RDS service url [{}]", serviceUrl);
}
}
18、@Transactional
@Transactional注解是保证数据库一致性操作的,该注解用在方法上,使同一个方法中的操作要么都成功,要么都失败。
package com.example.myproject.service;
import com.example.myproject.domain.Person;
import com.example.myproject.domain.PersonRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class PersonService {
@Autowired
private PersonRepository personRepository;
//@Transactional注解使insertTwoPerson()方法中的操作要么都成功,要么都失败
@Transactional
public void insertTwoPerson(){
Person personA = new Person();
personA.setAge(18);
personA.setName("端木嬅");
personA.setSex("女");
personRepository.save(personA);
Person personB = new Person();
personB.setAge(18);
personB.setName("宇文昊");
personB.setSex("男");
personRepository.save(personB);
}
}
19、@SpringBootApplication
@SpringBootApplication开启了spring的组件扫描和自动配置功能。
实际@SpringBootApplication将三个注解合在了一起:
1. spring的@Configuration: 表明该类使用了spring基于Java的配置。
2. Spring的@ComponentScan:启用组件扫描,这样组件可以被扫描到
3. SpringBoot的@EnableAutoConfiguration:开启了springboot的自动配置
@ComponentScan 注解会将带有@Component , @Service , @Repository , @Controller 注解的POJO(普通Java对象)类自动注册为Spring Beans。
@EnableAutoConfiguration 注解:作用在于让Spring Boot根据应用所声明的依赖来对Spring框架进行自动配置。这个注解告诉Spring Boot根据添加的jar依赖猜测你想如何配置Spring。如果在pom.xml文件中添加了spring-boot-starter-web依赖,由于spring-boot-starter-web添加了Tomcat和Spring MVC,所以auto-configuration将假定你正在开发一个web应用并相应地对Spring进行设置。
@EnableAutoConfiguration(开启自动配置)注解通常都放到main所在类(启动类)的上面,启动类一般需要放在根包下面,与其他类所在的包是同级目录(不是与其他类是同级目录,虽然可以与其他类是同级目录,但是不建议这么做),但是不能将启动类直接放在java包下面,否则会报错。@EnableAutoConfiguration可以逐层的往下搜索各个包中加注解的类。
示例一:
//启动类继承SpringBootServletInitializer,使得项目可以部署到Tomcat中
@SpringBootApplication
public class Myproject01Application extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Myproject01Application.class);
}
public static void main(String[] args) {
SpringApplication.run(Myproject01Application.class, args);
}
}
示例二:
package com.example.myproject02;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
@SpringBootApplication
//@MapperScan("com.example.myproject02.mapper")注解表示MapperScannerRegistrar会
// 扫描com.example.myproject02.mapper包下所有Mapper接口的对象
@MapperScan("com.example.myproject02.mapper")
public class Myproject02Application {
private static Logger logger = LoggerFactory.getLogger(Myproject02Application.class);
//DataSource配置
//@ConfigurationProperties(prefix = "spring.datasource")表示将根据
// 前缀“spring.datasource”从application.properties中匹配相关属性值
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource(){
return new DataSource();
}
//提供SqlSeesion
@Bean
public SqlSessionFactory sqlSessionFactoryBean() throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource());
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
sqlSessionFactoryBean.setMapperLocations(resolver.getResources("classpath:/mybatis/*.xml"));
return sqlSessionFactoryBean.getObject();
}
@Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource());
}
//主函数启动程序
public static void main(String[] args) {
SpringApplication.run(Myproject02Application.class, args);
logger.info("SpringBoot Start Success!");
}
}
2、其他注解
1、@Entity、@Table、@Column等注解
@Entity:声明该类为一个实体类。
@Table:对应数据库中的哪一张表,name="数据库中的表名"。
@Id注解用来指定主键,@GeneratedValue注解表示主键自动增长。
package com.example.myproject.domain;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
//加上@Entity、@Table注解之后,就可以将类Person映射成数据库中的数据表t_person,将属性映射成字段
@Entity
@Table(name = "t_person")
public class Person {
//指定主键,自动增长
@Id
@GeneratedValue
private int id;
private String name;
private String sex;
private int age;
//必须写空参数的构造函数,否则数据库会报错
public Person() {
}
//省略getter、setter方法
}
@Column注解表示类中的属性映射表中的字段,该注释的属性定义如下:
@Target({METHOD, FIELD})
@Retention(RUNTIME)
public @interface Column {
String name() default "";
boolean unique() default false;
boolean nullable() default true;
boolean insertable() default true;
boolean updatable() default true;
String columnDefinition() default "";
String table() default "";
int length() default 255;
int precision() default 0;
int scale() default 0;
}
在使用此@Column(name="表中字段名称")标记时,需要注意以下几个问题:
此标记可以标注在getter方法或属性前,例如以下的两种标注方法都是正确的:
1.标注在属性前:
@Entity
@Table(name = "t_user")
public class User{
@Column(name="user_name")
private String username;
}
2.标注在getter方法前:
@Entity
@Table(name = "t_user")
public class User{
private String username;
@Column(name="user_name")
public String getUsername() {
return username;
}
}
@Column注解中属性解释:
1.unique属性表示该字段是否为唯一标识,默认为false。如果表中有一个字段需要唯一标识,则既可以使用该标记,也可以使用@Table标记中的@UniqueConstraint。
2.nullable属性表示该字段是否可以为null值,默认为true。
3.insertable属性表示在使用“INSERT”脚本插入数据时,是否需要插入该字段的值。
4.updatable属性表示在使用“UPDATE”脚本插入数据时,是否需要更新该字段的值。insertable和updatable属性一般多用于只读的属性,例如主键和外键等。这些字段的值通常是自动生成的。
5.columnDefinition属性表示创建表时,该字段创建的SQL语句,一般用于通过Entity生成表定义时使用。
6.table属性表示当映射多个表时,指定表的表中的字段。默认值为主表的表名。有关多个表的映射将在本章的5.6小节中详细讲述。
7.length属性表示字段的长度,当字段的类型为varchar时,该属性才有效,默认为255个字符。
8.precision属性和scale属性表示精度,当字段类型为double时,precision表示数值的总长度,scale表示小数点所占的位数。
下面举几个小例子:
@Entity
@Table(name = "t_user")
public class User {
private Integer id;
private String name;
private BigDecimal monthlyIncome;
private String description;
// 示例一:指定字段“contact_name”的长度是“512”,并且值不能为null。
@Column(name="user_name",nullable=false,length=512)
public String getName() {
return name;
}
// 示例二:指定字段“monthly_income”月收入的类型为double型,精度为12位,小数点位数为2位。
@Column(name="monthly_income",precision=12, scale=2)
public BigDecimal getMonthlyIncome() {
return monthlyIncome;
}
// 示例三:自定义生成CLOB类型字段的SQL语句。
@Column(name="description",columnDefinition="clob not null")
public String getDescription () {
return description;
}
// 示例四:字段值为只读的,不允许插入和修改。通常用于主键和外键。
@Column(name="id",insertable=false,updatable=false)
public Integer getId() {
return id;
}
}
创建的SQL语句如下所示:
CREATE TABLE t_user(
id integer not null,
user_name varchar (512) not null,
monthly_income double(12,2),
description clob(200) not null,
primary key (id)
)
2、@OneToOne
@OneToOne注解表明实体类与实体类之间为一对一关系,@OneToOne注解有六个属性:targetEntity、cascade、fetch、optional 、mappedBy和orphanRemoval。
targetEntity:默认是该成员属性对应的类类型,所以通常不需要提供定义。
cascade:该属性定义类和类之间的级联关系,定义的级联关系将被容器视为对当前类对象及其关联类对象采取相同的操作,而且这种关系是递归调用的。cascade的值只能从CascadeType.PERSIST(级联新建)、CascadeType.REMOVE(级联删除)、CascadeType.REFRESH(级联刷新)、CascadeType.MERGE(级联更新)中选择一个或多个。还有一个选择是使用CascadeType.ALL,表示选择上面全部四项。如果不写该属性,则对关系表不会产生任何影响。
fetch:FetchType类型的属性,可选择项包括:FetchType.EAGER 和FetchType.LAZY。
FetchType.EAGER表示关系类在主类加载的时候同时加载,FetchType.LAZY表示关系类在被访问时才加载。默认值是FetchType.LAZY。比如User类有两个属性:name和address,用户登录之后,用户名是需要立即显示出来的,要马上到数据库查,用急加载;而用户地址大多数情况下不需要显示出来,只有在查看用户资料是才需要显示,需要用的时候才查数据库,用懒加载就好了。所以,并不是一登录就把用户的所有资料都加载到对象中,于是有了这两种加载模式。
optional :表示属性是否可以为null,optional=true表示属性可以为null,optional=false表示不可以为null。
mappedBy:该属性用在双向关联关系中,在单向关系中不用该属性。在双向关系中,拥有关系的一方有建立、解除和更新与另一方关系的能力,而另一方没有,只能被动管理,这个属性被定义在关系的被拥有方。双向@OneToOne,双向@OneToMany,双向@ManyToMany。
比如实体类Husband对应的表为t_husband,该表中有三个字段:hid、name、wife_id(外键,关联t_wife表中的主键),实体类Wife对应的表为t_wife,该表中有两个字段wid、name。
在t_husband表中通过外键wife_id与t_wife表建立关系,通过t_husband表可以影响到两表的关系,但是不能通过t_wife表建立、解除、更新两表的关系,t_husband表是关系维护端,t_wife表是关系被维护端。mappedBy属性用于双向关联实体时,标注在不保存关系的实体中。
双向OneToOne其实是在原来单向的基础上,使得被维护端也可以知道与自己有关那一方的信息,在原来单向工程的基础上只需修改一点,打开Wife类,为其增加一个字段Husband,mappedBy表明了关系被维护端,这样我们就可以通过Wife得到对应的husband。
orphanRemoval:该属性取值为true和false,表示在删除关系维护端时,是否级联删除关系被维护端。orphanRemoval=true表示在删除维护端时,通过外键,级联删除被维护端,orphanRemoval=false表示在删除维护端时,不删除被维护端。cascade 是总开关,如果没有设置CascadeType.ALL或者CascadeType.REMOVE,即使orphanRemoveal设置为true也无法执行删除。
@JoinColumn(name = "外键名称", referencedColumnName ="关联表的主键",unique = true)
比如实体类Husband对应的表为t_husband,该表中有三个字段:hid、name、wife_id(外键,关联t_wife表中的主键wid),实体类Wife对应的表为t_wife,该表中有两个字段wid、name。
在t_husband表中通过外键wife_id与t_wife表建立关系。t_husband表称为主表,t_wife表称为关联表。如果不设置name的值,默认情况下,name的取值规则如下:name=关联表的名称 + "_" + 关联表主键的字段名(比如此处不设置name的值,name的值默认是t_wife_wid)。如果不设置referencedColumnName的值,referencedColumnName的值默认是关联表的主键。如果主表中的外键不是与关联表中的主键进行关联的,假如主表t_husband的外键名称是w_name,与关联表t_wife中的name字段进行关联,此时referencedColumnName的值就必须写成name,即@JoinColumn(name = "w_name", referencedColumnName = "name")
public class Husband{
@Column(name="hid")
private Integer id;
@Column(name="name")
private String name;
@OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "wife_id", referencedColumnName = "wid")
private Integer w_id;
//省略get、set方法
}
3、@Mapper
在dao层,传统方式使用mybatis操作数据库都是通过编写mapper接口和mapper接口对应的XML配置文件,后来mybatis也可以使用注解操作数据库。通过@Mapper注解,结合@Select、@Insert、@Update、@delete等操作数据库。在接口上使用@Mapper注解,在方法上使用@Select、@Insert、@Update、@delete等注解。如下面的示例:
package com.example.myproject02.mapper;
import com.example.myproject02.model.UserEntity;
import org.apache.ibatis.annotations.*;
import java.util.List;
//这种方式是注解的方式使用mybatis,将mapper.xml配置文件中的sql语句都写到了mapper接口中,不需要mapper.xml配置文件了
@Mapper
public interface AnnotationMapper {
@Select("SELECT * FROM users")
@Results({
@Result(property = "userSex", column = "user_sex" ),
@Result(property = "nickName", column = "nick_name")
})
List<UserEntity> getAll();
@Select("SELECT * FROM users WHERE id = #{id}")
@Results({
@Result(property = "userSex", column = "user_sex"),
@Result(property = "nickName", column = "nick_name")
})
UserEntity getOne(Long id);
@Insert("INSERT INTO users(userName,passWord,user_sex) VALUES(#{userName}, #{passWord}, #{userSex})")
void insert(UserEntity user);
@Update("UPDATE users SET userName=#{userName},nick_name=#{nickName} WHERE id =#{id}")
void update(UserEntity user);
@Delete("DELETE FROM users WHERE id =#{id}")
void delete(Long id);
}
- @Select 是查询类的注解,所有的查询均使用这个
- @Result 修饰返回的结果集,关联实体类属性和数据库字段一一对应,如果实体类属性和数据库属性名保持一致,就不需要这个属性来修饰。
- @Insert 插入数据库使用,直接传入实体类会自动解析属性到对应的值
- @Update 负责修改,也可以直接传入对象
- @delete 负责删除
下面这个配置文件是与上面注解操作数据库实现相同的功能:
这种方式是通过xml配置文件使用mybatis,需要编写与之对应的mapper接口。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.myproject02.mapper.UserMapper">
<resultMap id="BaseResultMap" type="com.neo.entity.UserEntity" >
<id column="id" property="id" jdbcType="BIGINT" />
<result column="userName" property="userName" jdbcType="VARCHAR" />
<result column="passWord" property="passWord" jdbcType="VARCHAR" />
<result column="user_sex" property="userSex" javaType="com.neo.enums.UserSexEnum"/>
<result column="nick_name" property="nickName" jdbcType="VARCHAR" />
</resultMap>
<sql id="Base_Column_List" >
id, userName, passWord, user_sex, nick_name
</sql>
<select id="getAll" resultMap="BaseResultMap" >
SELECT
<include refid="Base_Column_List" />
FROM users
</select>
<select id="getOne" parameterType="java.lang.Long" resultMap="BaseResultMap" >
SELECT
<include refid="Base_Column_List" />
FROM users
WHERE id = #{id}
</select>
<insert id="insert" parameterType="com.neo.entity.UserEntity" >
INSERT INTO
users
(userName,passWord,user_sex)
VALUES
(#{userName}, #{passWord}, #{userSex})
</insert>
<update id="update" parameterType="com.neo.entity.UserEntity" >
UPDATE
users
SET
<if test="userName != null">userName = #{userName},</if>
<if test="passWord != null">passWord = #{passWord},</if>
nick_name = #{nickName}
WHERE
id = #{id}
</update>
<delete id="delete" parameterType="java.lang.Long" >
DELETE FROM
users
WHERE
id =#{id}
</delete>
</mapper>
4、@MapperScan
上面使用@Mapper注解需要在每个mapper接口上都要加上该注解,实际开发中肯定不止一个mapper接口,这样就需要多次添加@Mapper注解,太过繁琐。于是可以在启动类上使用@MapperScan来扫描注册mybatis数据库接口,其中basePackages属性表明接口类所在的包,sqlSessionTemplateRef表明接口类使用的SqlSessionTemplate。如果项目需要配置两个数据库,@MapperScan也需要分别配置。
@MapperScan("com.example.myproject02.mapper")注解表示MapperScannerRegistrar会扫描com.example.myproject02.mapper包下所有Mapper接口的对象。
package com.example.myproject02;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@ComponentScan
@MapperScan("com.example.myproject02.mapper")
public class Myproject02Application {
private static Logger logger = LoggerFactory.getLogger(Myproject02Application.class);
//DataSource配置
//@ConfigurationProperties(prefix = "spring.datasource")表示将根据
// 前缀“spring.datasource”从application.properties中匹配相关属性值
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource(){
return new DataSource();
}
// ......
//主函数启动程序
public static void main(String[] args) {
SpringApplication.run(Myproject02Application.class, args);
logger.info("SpringBoot Start Success!");
}
}
5、@CrossOrigin
由于浏览器的同源策略(即协议、域名、端口号三者都分别对应相同),在一个域中的资源不能访问另一个域中的资源,否则就是跨域访问。@CrossOrigin是用来处理跨域请求的注解,让你能访问不是一个域的文件。@CrossOrigin注解可以用在类上和方法上。
http://www.123.com/index.html (协议是http协议,域名是:www.123.com,端口号不写默认是80端口)
http://www.123.com/server.PHP (同源、非跨域)
https://www.123.com/index.html (不同源、跨域,协议不同,此处是https协议)
http://www.456.com/server.php (域名不同:123/456,跨域)
http://abc.123.com/index.html (域名不同:www/abc,跨域)
http://www.123.com:8080/index.html (端口不同:默认80/8080,跨域)
示例1:只用在类上
@CrossOrigin(origins = "http://domain2.com", maxAge = 3600)
@RestController
@RequestMapping("/account")
public class AccountController {
@RequestMapping("/{id}")
public Account retrieve(@PathVariable Long id) {
// ...
}
@RequestMapping(method = RequestMethod.DELETE, path = "/{id}")
public void remove(@PathVariable Long id) {
// ...
}
}
这里指定AccountController
类
中所有的方法都可以处理http://domain2.com
域上的请求。
示例2:只用在方法上
@RestController
@RequestMapping("/user")
public class AppUserController {
private static final Logger logger = LogManager.getLogger(AppUserController.class);
public static Map<String, String> USER_APP_MAP = new HashMap<String, String>();
@CrossOrigin
@RequestMapping(value = "/apps/{userId}/{appName}", method = { RequestMethod.GET })
public ResultEntity updateUserAppName(@PathVariable("userId") String userId,
@PathVariable("appName") String appName) {
ResultEntity result = ResultEntity.success();
logger.info("user [{}] browse app [{}]", userId, appName);
USER_APP_MAP.put(userId, appName);
return result;
}
}
可以同时用在类上和方法上,Spring会合并两个注解的属性一起使用。
6、swagger注解
@Api、@ApiOperation、@ApiParam、@ApiModel和@ApiModelProperty
详见:Swagger注解及SwaggerUI的使用-CSDN博客
7、条件注解
@Conditional注解、@ConditionalOnProperty注解、@ConditionalOnMissingBean注解