【Spring Boot】Spring Boot最核心注解及作用说明

11. @Qualifier VS @Primary

默认情况下,@Autowired 按类型装配 Spring Bean。如果容器中有多个相同类型的 bean,则框架将抛出 NoUniqueBeanDefinitionException, 以提示有多个满足条件的 bean 进行自动装配。程序无法正确做出判断使用哪一个。

  • 通过使用 @Qualifier 注解,我们可以消除需要注入哪个 bean 的问题。
  • @Primary 的注解,我们也可以用来发生依赖注入的歧义时决定要注入哪个 bean。当存在多个相同类型的 bean 时,此注解定义了首选项。除非另有说明,否则将使用与 @Primary 注释关联的 bean 
     @Component
     @Qualifier("fooFormatter")
     public class FooFormatter implements Formatter {
         public String format() {
             return "foo";
         }
     }
 
     @Component
     @Qualifier("barFormatter")
     public class BarFormatter implements Formatter {
         public String format() {
             return "bar";
         }
     }

相关文章地址:https://www.cnblogs.com/felordcn/p/12142471.html

1. @RestController 和@Controller的区别

  • @RestController注解相当于@ResponseBody + @Controller合在一起的作用
  • @Controller 注解,在对应的方法上视图解析器可以解析return 的jsp,html页面,并且跳转到相应页面; 
  • @ResponseBody注解,将会无法返回jsp或html页面,只能返回json等字符串等内容
  • 区别:

(1) 如果只是使用@RestController注解Controller,则Controller中的方法无法返回jsp页面,或者html,配置的视图解析器 InternalResourceViewResolver不起作用,返回的内容就是Return 里的内容

(2) 如果需要返回到指定页面,则需要用 @Controller配合视图解析器InternalResourceViewResolver才行。
    如果需要返回JSON,XML或自定义mediaType内容到页面,则需要在对应的方法上加上@ResponseBody注解。

2. @Service("chineseMan") 

   括号内可以指定自定义的bean的名字

  • @Service服务层组件,用于标注业务层组件,表示定义一个bean,自动根据bean的类名实例化一个首写字母为小写的bean,例如Chinese实例化为chinese,如果需要自己改名字则:@Service("你自己改的bean名")。比如chinese

3. @RequestMapping

  • value: 指定请求的实际地址, 比如 /action/info之类(可以同时指定多个路径)
  • method: 指定请求的method类型, GET、POST、PUT、DELETE等
  • consumes: 指定处理请求的提交内容类型(Content-Type),例如application/json, text/html;
  • produces: 指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回
  • params: 指定request中必须包含某些参数值是,才让该方法处理
  • headers: 指定request中必须包含某些指定的header值,才能让该方法处理请求    

注意:

(1) @RequestParam用于将请求参数区数据映射到功能处理方法的参数上。

  1. value:参数名字,即入参的请求参数名字,如username表示请求的参数区中的名字为username的参数的值将传入;
  2. required:是否必须,默认是true,表示请求中一定要有相应的参数,否则将抛出异常;
  3. defaultValue:默认值,表示如果请求中没有同名参数时的默认值,设置该参数时,自动将required设为false。
  @RequestMapping(value = "/personId")
    String getId(@RequestParam String personId) {
        System.out.println("ID is " + personId);
        return "Get ID from query string of URL without value element";
    }

(2)@PathVariable用于将请求URL中的模板变量映射到功能处理方法的参数上。     

@RequestMapping(value="/users/{userId}/topics/{topicId}")
public String test(
       @PathVariable(value="userId") int userId, 
       @PathVariable(value="topicId") int topicId)   

4. @RequestBody

  • @RequestBody : 允许request的参数在request体中,而不是直接连接在地址后面 次注解放置在参数前

注意:

  1. 若使用RequestBody 用于接收对象实体类型,因为接收到的是JSON串
  2. 若要接收直接连接在地址后面的参数,不能使用RequestBody注解,并且在RequestMapping中指定consumes的Content-Type 类型(相当于表单提交)
// 表单形式提交 不能使用 @RequestBody
@PostMapping(value = "user",consumes = "application/x-www-form-urlencoded;charset=UTF-8")
public String getUser(User user, @RequestParam("code")String code){}

// 对象形式提交 在body体中
@PostMapping(value = "user")
public String getUser(@RequestBody User user){}

5.  @RequestParam详解

  1. @CookieValue绑定Cookie数据值
  2. @RequestHeader绑定请求头数据
  3. @ModelAttribute绑定请求参数到命令对象

5.1 处理数组

public  JSONObject getInfo(
      String searchText, 
     @RequestParam(value = "meetingIds",required = false) List<Long> meetingIds){}
    

如果传递参数的格式是   meetingIds = 1,23,4,5 这样的 则直接写参数名meetingIds 即可;

有几点需要注意:

  •  如果是post传参,前端直接传数组类型的数据,后台接参需要吧value改为  meetingIds[]   注意括号
  •  如果是参数有可能不传,则需要设置required = false  , 如果不写,则默认会是true,也就是必然有参数,如果没参数会报错,不进入方法。

5.3 @PathVariable

@RequestMapping(value="/user/{username}")
    public String userProfile(@PathVariable(value="username") String username) {
    	return "user"+username;
 }

 @RequestParam和@PathVariable都能够完成类似的功能——因为本质上,它们都是用户的输入,只不过输入的部分不同,一个在URL路径部分,另一个在参数部分。要访问一篇博客文章,这两种URL设计都是可以的:

  • 通过@PathVariable,例如/blogs/1
  • 通过@RequestParam,例如blogs?blogId=1

那么究竟应该选择哪一种呢?建议:

1、当URL指向的是某一具体业务资源(或资源列表),例如博客,用户时,使用@PathVariable

2、当URL需要对资源或者资源列表进行过滤,筛选时,用@RequestParam

例如我们会这样设计URL:

  • /blogs/{blogId}
  • /blogs?state=publish而不是/blogs/state/publish来表示处于发布状态的博客文章

相关文章:@RequestParam和@PathVariable的用法与区别

6. @PostConstruct 

@PostConstruct注解好多人以为是Spring提供的。其实是Java自己的注解。

Java中该注解的说明:@PostConstruct该注解被用来修饰一个非静态的void()方法。被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器执行一次。

PostConstruct在构造函数之后执行,init()方法之前执行。

通常我们会是在Spring框架中使用到@PostConstruct注解 该注解的方法在整个Bean初始化中的执行顺序:

Constructor(构造方法) -> @Autowired(依赖注入) -> @PostConstruct(注释的方法)

相关文章

  1. @PostConstruct注解

7. @Autowire和@Resource

今天使用Idea写代码的时候,看到之前的项目中显示有warning的提示,去看了下,是如下代码?

@Autowire
private JdbcTemplate jdbcTemplate;

提示的警告信息

Field injection is not recommended Inspection info: Spring Team recommends: "Always use constructor based dependency injection in your beans. Always use assertions for mandatory dependencies".

这段是Spring工作组的建议,大致翻译一下:
属性字段注入的方式不推荐,检查到的问题是:Spring团队建议:"始终在bean中使用基于构造函数的依赖项注入,
始终对强制性依赖项使用断言"

7.1 注入方式

虽然当前有关Spring Framework(5.0.3)的文档仅定义了两种主要的注入类型,但实际上有三种:

  • 基于构造函数的依赖注入
    public class UserServiceImpl implents UserService{
        private UserDao userDao;
        
        @Autowire
        public UserServiceImpl(UserDao userDao){
            this.userDao = userDao;
        }
    }
    复制代码
  • 基于Setter的依赖注入
    public class UserServiceImpl implents UserService{
         private UserDao userDao;
         
         @Autowire
         public serUserDao(UserDao userDao){
             this.userDao = userDao;
         }
     }
    复制代码
  • 基于字段的依赖注入
    public class UserServiceImpl implents UserService{
         @Autowire
         private UserDao userDao;
     }
    复制代码

基于字段的依赖注入方式会在Idea当中吃到黄牌警告,但是这种使用方式使用的也最广泛,因为简洁方便.您甚至可以在一些Spring指南中看到这种注入方法,尽管在文档中不建议这样做.(有点执法犯法的感觉)

7.2 基于字段的依赖注入缺点

  • 对于有final修饰的变量不好使   

    Spring的IOC对待属性的注入使用的是set形式,但是final类型的变量在调用class的构造函数的这个过程当中就得初始化完成,这个是基于字段的依赖注入做不到的地方.只能使用基于构造函数的依赖注入的方式

  • 掩盖单一职责的设计思想

    我们都知道在OOP的设计当中有一个单一职责思想,如果你采用的是基于构造函数的依赖注入的方式来使用Spring的IOC的时候,当你注入的太多的时候,这个构造方法的参数就会很庞大,类似于下面.当你看到这个类的构造方法那么多参数的时候,你自然而然的会想一下:这个类是不是违反了单一职责思想?.但是使用基于字段的依赖注入不会让你察觉,你会很沉浸在@Autowire当中

public class VerifyServiceImpl implents VerifyService{
    
  private AccountService accountService;
  private UserService userService;
  private IDService idService;
  private RoleService roleService;
  private PermissionService permissionService;
  private EnterpriseService enterpriseService;
  private EmployeeService employService;
  private TaskService taskService;
  private RedisService redisService;
  private MQService mqService;

  public SystemLogDto(AccountService accountService, 
                      UserService userService, 
                      IDService idService, 
                      RoleService roleService, 
                      PermissionService permissionService, 
                      EnterpriseService enterpriseService, 
                      EmployeeService employService, 
                      TaskService taskService, 
                      RedisService redisService, 
                      MQService mqService) {
      this.accountService = accountService;
      this.userService = userService;
      this.idService = idService;
      this.roleService = roleService;
      this.permissionService = permissionService;
      this.enterpriseService = enterpriseService;
      this.employService = employService;
      this.taskService = taskService;
      this.redisService = redisService;
      this.mqService = mqService;
  }
}
  • 与Spring的IOC机制紧密耦合

      当你使用基于字段的依赖注入方式的时候,确实可以省略构造方法和setter这些个模板类型的方法,但是,你把控制权全给Spring的IOC了,别的类想重新设置下你的某个注入属性,没法处理(当然反射可以做到).本身Spring的目的就是解藕和依赖反转,结果通          过再次与类注入器(在本例中为Spring)耦合,失去了通过自动装配类字段而实现的对类的解耦,从而使类在Spring容器之外无效.  

  • 隐藏依赖性

    当你使用Spring的IOC的时候,被注入的类应当使用一些public类型(构造方法,和setter类型方法)的方法来向外界表达:我需要什么依赖.但是基于字段的依赖注入的方式,基本都是private形式的,private把属性都给封印到class当中了.

  • 无法对注入的属性进行安检

    基于字段的依赖注入方式,你在程序启动的时候无法拿到这个类,只有在真正的业务使用的时候才会拿到,一般情况下,这个注入的都是非null的,万一要是null怎么办,在业务处理的时候错误才爆出来,时间有点晚了,如果在启动的时候就暴露出来,那么bug就可以很快得到修复(当然你可以加注解校验).如果你想在属性注入的时候,想根据这个注入的对象操作点东西,你无法办到.我碰到过的例子:一些配置信息啊,有些人总是会配错误,等到了自己测试业务阶段才知道配错了,例如线程初始个数不小心配置成了3000,机器真的是狂叫啊!这个时候就需要再某些Value注入的时候做一个检测机制.

7.3 结论

通过上面,我们可以看到,基于字段的依赖注入方式有很多缺点,我们应当避免使用基于字段的依赖注入.推荐的方法是使用基于构造函数基于setter的依赖注入.对于必需的依赖项,建议使用基于构造函数的注入,以使它们成为不可变的,并防止它们为null。对于可选的依赖项,建议使用基于Setter的注入

8. @EnableApolloConfig

@Configuration
@EnableApolloConfig(value = "redis")
public class ConfigLoader {
}

上述类是一个空类,功能全部由@EnableApolloConfig注解实现了。这个注解会在示例工程启动时,将Apollo服务端的redis命名空间中的配置注入Spring容器,也就相当于加载传统的redis.properties配置文件。

注意@EnableApolloConfig注解要和@Configuration注解一起使用,否则不会生效。

9. @Profile(value = {"prod"})

有时候我们可能还会有这样的需求,生产环境或者开发环境我们所看到的功能是不同的,也需要我们根据配置项来激活不同的功能,所以使用上面的注解,可以根据value 的值不同,激活不同的环境。

@Service
@Profile({"prod","default"})
public class ProdServiceImpl implements ProfileService {

    public ProdServiceImpl() {
        System.out.println("我是生产环境。。。。。");
    }

    @Override
    public String getMsg() {
        StringBuilder sb = new StringBuilder();
        sb.append("我在生产环境,").append("我可以吃鸡鸭鱼牛羊肉。。。。");
        return sb.toString();
    }
}

10. @EnableAutoConfiguration

@EnableAutoConfiguration可以帮助SpringBoot应用将所有符合条件的@Configuration配置都加载到当前SpringBoot创建并使用的IoC容器。

在AutoConfigurationImportSelector类中可以看到通过 SpringFactoriesLoader.loadFactoryNames()
把 spring-boot-autoconfigure.jar/META-INF/spring.factories中每一个xxxAutoConfiguration文件都加载到容器中,spring.factories文件里每一个xxxAutoConfiguration文件一般都会有下面的条件注解:

  • @ConditionalOnClass : classpath中存在该类时起效
  • @ConditionalOnMissingClass : classpath中不存在该类时起效
  • @ConditionalOnBean : DI容器中存在该类型Bean时起效
  • @ConditionalOnMissingBean : DI容器中不存在该类型Bean时起效
  • @ConditionalOnSingleCandidate : DI容器中该类型Bean只有一个或@Primary的只有一个时起效
  • @ConditionalOnExpression : SpEL表达式结果为true时
  • @ConditionalOnProperty : 参数设置或者值一致时起效
  • @ConditionalOnResource : 指定的文件存在时起效
  • @ConditionalOnJndi : 指定的JNDI存在时起效
  • @ConditionalOnJava : 指定的Java版本存在时起效
  • @ConditionalOnWebApplication : Web应用环境下起效
  • @ConditionalOnNotWebApplication : 非Web应用环境下起效

相关文章

常用注解

  1. @Configuration把一个类作为一个IoC容器,它的某个方法头上如果注册了@Bean,就会作为这个Spring容器中的Bean。 
  2. @Scope注解 作用域 
  3. @Lazy(true) 表示延迟初始化 
  4. @Service用于标注业务层组件、 
  5. @Controller用于标注控制层组件(如struts中的action) 
  6. @Repository用于标注数据访问组件,即DAO组件。 
  7. @Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。 
  8. @Scope用于指定scope作用域的(用在类上) 
  9. @PostConstruct用于指定初始化方法(用在方法上) Servlet只加载一次 构造函数之后执行,init()方法之前执
  10. @PreDestory用于指定销毁方法(用在方法上) 
  11. @Resource 默认按名称装配,当找不到与名称匹配的bean才会按类型装配。 
  12. @DependsOn:定义Bean初始化及销毁时的顺序 
  13. @Primary:自动装配时当出现多个Bean候选者时,被注解为@Primary的Bean将作为首选者,否则将抛出异常 
  14. @Autowired 默认按类型装配,如果我们想使用按名称装配,可以结合@Qualifier注解一起使用 
  15. @Autowired @Qualifier(“personDaoBean”) 存在多个实例配合使用
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值