Spring 常用注解简介

    Spring IoC容器(ApplicationContext)负责创建Bean,并通过容器将功能类Bean注入到需要的Bean中。Spring提供使用xml、注解、Java配置实现Bean的创建和注入。
    可见,注解在Spring的使用中占据非常重要的地位,虽然Spring 4.x及Spring Boot中推荐使用Java配置,但注解的使用仍然不可完全丢弃。项目中常常根据开发者的习惯和二者优势而选择,或混合使用。通常,涉及到全局配置的如数据库相关配置、MVC相关配置等,使用JAVA配置方式。而涉及业务配置的,使用注解方式。
    本文简单的介绍一些Spring的常用注解,官方文档有更详细准确的介绍,小伙伴们可根据需要详细研读:
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/

一、声明Bean的注解:

1)@Component组件,没有明确的角色
2)@Service在业务逻辑层(Service层)使用
3)@RepoSitory在数据访问层(dao层)使用
4)@Controller在展示层(Spring MVC)使用

  • Bean的scope有以下几种,在新建Bean实例时,可配合@Scope注解设置Bean的Scope。
    1)Singleton:单一实例,全容器共享一个实例,此为Spring的默认配置。
    2)Prototype:每次调用新建一个Bean的实例。
    3)Request:Web项目中,给每一个http request新建一个Bean实例。
    4)Session:Web项目中,黑每一个http session新建一个Bean实例。
    5)GlobalSession:这个只会在portal应用中有用,给每一个global http session 新建一个Bean实例。
  • Bean的初始化和销毁:
    1)Java配置方式:使用@Bean的initMethod和destoryMethod指定
    2)注解方式:使用JSR-250的@PostConstruct和@PreDestroy
    例如:Java配置方式
public class Car {
    public Car() {
        System.out.println("Car's Constructor..");
    }
    public void init(){
        System.out.println("Car's Init...");
    }
    public void destory(){
        System.out.println("Car's Destroy...");
    }
}
@Configuration
public class AppConfig {
    @Bean(initMethod = "init",destroyMethod = "destory")
    public Car car() {
      return new Car();
    }
}

例如:注解方式

public Class AAA {
    @Autowired
    private BBB b;
    public AAA() {
        System.out.println("此时b还未被注入: b = " + b);
    }
    @PostConstruct
    private void init() {
        System.out.println("将在依赖注入完成后被调用: b = " + b);
    }
    @PreDestroy
    private void destroy() {
       //xxx
    }
}
  • 如果需要在项目启动后执行某功能,只需实现CommandLineRunner接口,将启动时执行的业务逻辑放过在run方法中。
@Component
public class MyStartupRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
   System.out.println("服务启动执行...");
}
}
  • 如果有多个类实现CommandLineRunner接口,为保证顺序,需要在实体类上使用一个@Order注解,执行时按value值从小到大顺序。
@Component
@Order(value=2)
public class MyStartupRunner1 implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
   System.out.println("服务启动执行 2222");
 }
}
@Component
@Order(value=1)
public class MyStartupRunner2 implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
  System.out.println("服务启动执行 111111");
 }
}

二、注入Bean的注解:

1)@Autowired:Spring提供的注解
2)@ReSource:JSR-250提供的注解
3)@Inject:JSR-330提供的注解

详细介绍:
1)@Autowired:是spring自带的注解,类型进行自动装配的,如果需要按名称进行装配,则需要配合@Qualifier。@Autowired有个属性为required,可以配置为false,如果配置为false之后,当没有找到相应bean的时候,系统不会报错。@Autowired可以作用在变量、setter方法、构造函数上。
    @Qualifier(“XXX”) 中的 XX是 Bean 的名称,所以 @Autowired 和 @Qualifier 结合使用时,自动注入的策略就从 byType 转变成 byName 了。不过需要注意的是@Autowired 可以对成员变量、方法以及构造函数进行注释,而 @Qualifier 的标注对象是成员变量、方法入参、构造函数入参。
例如:

public void setDataSource(@Qualifier("myDataSource")Datasource dataSource){
    super.setDataSource(dataSource);
}

2)@Resource:是JSR250规范的实现,需要导入javax.annotation实现注入。@Resource是根据名称进行自动装配的,一般会指定一个name属性(也可以不指定,会使用类名首字母小写作为name)。可作用在变量、setter方法上。

public class SimpleMovieListener {
    private MovieFinder movieFinder;
    @Resource(name = "myMovieFinder")
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
}

3)@Inject:是JSR330 (Dependency Injection for Java)中的规范,需要导入javax.inject.Inject;实现注入。@Inject是根据类型进行自动装配的,如果需要按名称进行装配,则需要配合@Named;可作用在变量、setter方法、构造函数上。用法和@Autowired一样。

public class SimpleMovieListener {
    private MovieFinder movieFinder;
    @Inject
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
}

    @Named(“XXX”) 中的 XX是 Bean 的名称,所以 @Inject和 @Named结合使用时,自动注入的策略就从 byType 转变成 byName 了。

public class SimpleMovieListener {
    private MovieFinder movieFinder;
    @Inject
    public void setMovieFinder(@Named("main") MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
}

三、配置类注解:

1)@Configuration:声明当前类是一个配置类
2)@ComponentScan:自动扫描包下所有使用@Service、@Component、@Repository、@Controller的类,并注册为Bean

四、Java配置注解:

1)@Configuration:声明当前类是一个配置类,相当于一个Spring配置的xml文件
2)@Bean注解在方法上,声明当前方法的返回值为一个Bean
例如:

@Configuration 
public class AppConfig {
    @Bean
    public MyBean myBean() {
        // instantiate, configure and return bean ...
    }}

还可以配合@Profile注解使用,如下:

@Configurationpublic
public class AppConfig {

    @Profile("english")
    @Bean
    public English getEnglish() { return new English(); }

    @Profile("chinese")
    @Bean
public Chinese getChinese() { return new Chinese(); }}

    // 测试类public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        applicationContext.getEnvironment().setActiveProfiles("chinese");
        applicationContext.register(AppConfig.class);
        applicationContext.refresh();
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames){
            System.out.println(beanDefinitionName); 
        }
    }}

    也可以配合@Lazy使用,没加注解之前主要容器启动就会实例化bean,加上@Lazy注解则必须在第一次调用的时候才会加载,其作用主要是减少springIOC容器启动的加载时间。例如:

@Configuration
public class MainConfig {
    @Lazy
    @Bean("user")
    public User user() {
        System.out.println("给容器中添加user....");
        return new User("张三", 28);
    }}

五、资源注入类注解:@Value ,用于注入不同的资源

常见用法如下:
1)基本数值:

@Value("张三")
private String name;

2)SpringEL:#{}

//注入操作系统属性
@Value("#{systemProperties['os.name']}")
private String osName;

//注入表达式结果
@Value("#{ T(java.lang.Math).random()*100.0 }")
private double randomNumber;

//注入其他Bean的属性
@Value("#{demoService.another}")
private String fromAuther;

3)文件&网站资源:

//注入文件资源
@Value("classpath:com/xxx/xxx.txt")
private Resource testFile;

//注入网址资源
@Value("http://www.baidu.com")
private Resource testFile;

4)配置文件资源:${}

@Value("${person.nickName}")
private String nickName;

    如果配置文件不是默认的application.properties,需要使用@PropertySource注解指定。

@Component
@PropertySource("classpath:user.properties")
@ConfigurationProperties(prefix = "user")
public class User {
  private String userName;
  private Integer age;

  // set get省略...
}

六、多线程注解:

1)@EnableAsync:在配置类中通过该注解开启对异步任务的支持
2)@Async:在Bean中使用该注解声明一个异步任务

    使用时,编写自定义配置类,实现AsyncConfigurer接口,覆盖其getAsyncExecutor()、getAsyncUncaughtExceptionHandler()方法,分别用来获取Executors及进行异步执行的异常处理。并在该类上使用@EnableAsync开启对异步的支持。
例如:

package ds.watsons.app.label.config;
import java.util.concurrent.Executor;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
  
@Configurable
@EnableAsync
public class TreadPoolConfigTest implements AsyncConfigurer{
@Override
public Executor getAsyncExecutor() {
 // TODO Auto-generated method stub
 ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
       //核心线程池数量,方法: 返回可用处理器的Java虚拟机的数量。
       executor.setCorePoolSize(Runtime.getRuntime().availableProcessors());
//最大线程数量
       executor.setMaxPoolSize(Runtime.getRuntime().availableProcessors()*5);
//线程池的队列容量
executor.setQueueCapacity(Runtime.getRuntime().availableProcessors()*2);
//线程名称的前缀
executor.setThreadNamePrefix("this-excutor-");
// setRejectedExecutionHandler:当pool已经达到max size的时候,如何处理新任务
// CallerRunsPolicy:不在新线程中执行任务,而是由调用者所在的线程来执行
//executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
     }
     /*异步任务中异常处理*/
     @Override
     public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
         // TODO Auto-generated method stub
         return new SimpleAsyncUncaughtExceptionHandler();
     }       
  }  
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
 
@Component
public class TreadTasks {
    @Async
    public void startMyTreadTask() {
       System.out.println("my async task");
    }
  }

    调用异步线程任务:

@Controller
public class AsyncTaskUse {
   @Autowired
   private TreadTasks treadTasks;
   @GetMapping("/startMysync")
   public void useMySyncTask() {
     treadTasks.startMyTreadTask();
   }
}

七、重试注解::

1)@EnableRetry:在配置类或启动类上加上@EnableRetry注解开启重试机制
2)@Retryable:在需要失败重试的方法上使用该注解
例如:

//使用@EnableRetry开启重试机制
@SpringBootApplication
@EnableRetry
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class,args);
    }
}
//定义一个简单的Controller,调用minGoodsnum方法
@RestController
public class HelloController {

    Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private PayService payService;

    @GetMapping("/createOrder")
    public String createOrder(@RequestParam int num) throws Exception{
        int remainingnum = payService.minGoodsnum(num == 0 ? 1: num);
        logger.info("剩余的数量==="+remainingnum);
        return "库库存成功";
    }
}
@Service
public class PayService {

    private Logger logger = LoggerFactory.getLogger(getClass());

    private final int totalNum = 100000;

    //value值表示当哪些异常的时候触发重试,maxAttempts表示最大重试次数默认为3,delay表示重试的延迟时间,multiplier表示上一次延时时间是这一次的倍数。
   @Retryable(value = Exception.class,maxAttempts = 3,backoff = @Backoff(delay = 2000,multiplier = 1.5))
   public int minGoodsnum(int num) throws Exception{
        logger.info("minGoodsnum开始"+ LocalTime.now());
        if(num <= 0){
            throw new Exception("数量不对");
        }
        logger.info("minGoodsnum执行结束");
        return totalNum - num;
    }
    //达到重试上限依然异常,会触发该方法
    @Recover
    public int recover(Exception e){
        logger.warn("减库存失败!!!");
        //记日志到数据库
        return totalNum;
    }
}

    笔者恰好最近在项目中遇到一个申请接入的接口,需要在接口中进行写库、改写配置文件、发送邮件通知等操作。这个场景就比较适合(六)中的异步注解配合(七)中的重试注解使用。完成写库操作后即可给用户返回处理结果,后续修改配置文件和发邮件等相对比较耗时的操作异步来完成,异常则重试1次,重试仍然异常则发送邮件给管理员手工处理。这样基本可以在提升用户体验的基础上,保证流程的完整性,代码实现起来也相对简洁易读。

八、计划任务注解:

1)@EnableScheduling :开启对计划任务的支持
2)@Scheduled:在计划任务方法上使用,支持多种计划任务类型,包括:cron、fixDelay、fixRate

    @EnableScheduling在启动类或配置类上使用:

@SpringBootApplication
@EnableScheduling //开启定时任务
public class MainApplication {
    public static void main(String[] args) {
        SpringApplication.run(MainApplication.class, args);
    }
}
@Component
public class Jobs {
    //表示方法执行完成后5秒
    @Scheduled(fixedDelay = 5000)
    public void fixedDelayJob() throws InterruptedException {
        System.out.println("fixedDelay 每隔5秒" + new Date());
    }

    //表示每隔3秒
    @Scheduled(fixedRate = 3000)
    public void fixedRateJob() {
        System.out.println("fixedRate 每隔3秒" + new Date());
    }

    //表示每天8时30分0秒执行
    @Scheduled(cron = "0 0,30 0,8 ? * ? ")
    public void cronJob() {
        System.out.println(new Date() + " ...>>cron....");
    }
}

九、条件注解:

  • @Conditional:在满足一个特定条件时创建一个Bean。该注解需搭配 Condition接口使用,重写其matches方法来构造判断条件。
    例如:当配置文件中有create.bean时则会创建Product,否则不创建。
public class ProductCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
  //从配置文件中获取属性
  String property=conditionContext.getEnvironment().getProperty("create.bean");
    if (property != null){
           return property.contains("product");
    } else {
           return false;
    }
 }
}
@Configuration
public class ProductConfig {
    @Conditional(ProductCondition.class)
    @Bean(name = "product")
    public Product createProd(){
        return Product.builder().id(12312).categoryId(12).
                productName("Mac Book Pro").productImg("prod.png")
                .productPrice(18000).build();
    }
}
  • @ConditionalOnClass:某个类存在,则创建Bean。
@Configurationpublic 
class ProductConfig {
    @ConditionalOnClass(name = "com.roseduan.demo.entity.Product")
    @Bean(name = "product")
    public Product createProd(){
        return Product.builder().id(12312).categoryId(12).
                productName("Mac Book Pro").productImg("prod.png")
                .productPrice(18000).build();
    }
}
  • @ConditionalOnProperty:直接从配置文件中获取属性,做为是否创建 bean 的依据,为true则创建。
@Configuration
public class ProductConfig {
    @ConditionalOnProperty(value = "create.product.bean")
    @Bean(name = "product")
    public Product createProd(){
        return Product.builder().id(12312).categoryId(12).
                productName("Mac Book Pro").productImg("prod.png")
                .productPrice(18000).build();
    }
}

十、测试注解:

1)@RunWith(SpringJUnit4ClassRunner.class):使用Junit测试框架
2) 加载配置,使用配置文件和Java配置方式分别如下:
@ContextConfiguration(locations={“classpath*:spring-mybatis.xml”})
@ContextConfiguration(class={“TestConfig.class”})
3) @ActiveProfiles(“prod”):声明活动的profile
4) @Test:标注测试方法

@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration(classes = {SpringConfiguration.class})
public class SpringJunitTest { 
    @Autowired 
    private UserService userService; 
    @Test 
    public void testUserService(){ 
        userService.save(); 
    } 
}  

十一、Spring MVC常用注解:

1)@Controller:在类上使用,该类为SpringMVC Controller 对象
2)@RequestMapping:用来映射web请求(访问路径和参数)、处理类和方法的。可注解在类或方法上。注解在方法上的@RequestMapping路径会继承注解在类上的路径。

  • @RequestMapping注解有6大属性:
    ① value:指定请求的实际地址,指定的地址可以是URI Template 模式
    ② method:指定请求的method类型, GET、POST、PUT、DELETE等
    ③ consumes:指定处理请求的提交内容类型(Content-Type),例如application/json, text/html
    ④ produces: 指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回
    ⑤ params: 指定request中必须包含某些参数值是,才让该方法处理
    ⑥ headers: 指定request中必须包含某些指定的header值,才能让该方法处理请求
@RequestMapping(value="/{day}", method = RequestMethod.GET)
public Map<String, Appointment> getForDay(@PathVariable @DateTimeFormat(iso=ISO.DATE) Date day, Model model) {
        return appointmentBook.getAppointmentsForDay(day);
}
//处理request Content-Type为“application/json”类型的请求
@Controller
@RequestMapping(value = "/pets", method = RequestMethod.POST, consumes="application/json")
public void addPet(@RequestBody Pet pet, Model model) {    
    // implementation omitted
}
//仅处理request请求中Accept头中包含了"application/json"的请求,同时暗示了返回的内容类型为application/json
@Controller
@RequestMapping(value = "/pets/{petId}", method = RequestMethod.GET, produces="application/json")
@ResponseBody
public Pet getPet(@PathVariable String petId, Model model) {    
    // implementation omitted
}
//仅处理请求中包含了名为“myParam”,值为“myValue”的请求
@Controller
@RequestMapping("/owners/{ownerId}")
public class RelativePathUriTemplateController {
   @RequestMapping(value = "/pets/{petId}", method = RequestMethod.GET, params="myParam=myValue")
   public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) {    
    // implementation omitted  }
}
//仅处理request的header中包含了指定“Refer”请求头和对应值为“http://www.ifeng.com/”的请求
@Controller
@RequestMapping("/owners/{ownerId}")
public class RelativePathUriTemplateController {
   @RequestMapping(value = "/pets", method = RequestMethod.GET,headers="Referer=http://www.ifeng.com/")
   public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) {    
   // implementation omitted 
}
}

3)ResponseBody:指定接口返回数据,而不是对象
4)@RequestBody:用在参数前,指定请求参数放在请求体中,而不是链接在地址后面。一般用来处理content-type为application/json类型。

@PostMapping("/2")
public String user2String(@RequestBody User user){
    return user.toString();
}

5)@PathVariable:放在参数前,用来接收路径参数

@RequestMapping(value="/product/{productId}",method = RequestMethod.GET)  
public String getProduct(@PathVariable("productId") String productId){  
  System.out.println("Product Id : " + productId);  
  return "hello";  
} 

6)@RestController:相当于@ResponseBody + @Controller合在一起的作用,用于定义一个返回数据(而不是页面)的web接口。

十二、Spring Boot常用注解:

  • @SpringBootApplication:该注解包含了@ComponentScan、@Configuration和@EnableAutoConfiguration。
    其中,@ComponentScan让spring Boot扫描到Configuration类并把它加入到程序上下文。
    @Configuration 等同于spring的XML配置文件;使用Java代码可以检查类型安全。
    @EnableAutoConfiguration 开启自动配置。
        该注解通常用在Spring Boot启动类上:
@SpringBootApplication
public class MySpringbootApplication {
    public static void main(String[] args) {
        SpringApplication.run(MySpringbootApplication.class, args);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值