由剔除Intellij中Mybatis的Mapper自动注入警告引入对注解的学习

起源

相信使用Mybatis的小伙们一定经常编写类似如下的代码:
img

可以看到 userMapper 下有个红色警告。虽然代码本身并没有问题,能正常运行,但有个警告总归有点恶心。先看下警告信息:

Spring team recommends: “Always use constructor based dependency injection in your beans. Always use assertions for mandatory dependencies.”

原因

众所周知,IDEA是非常智能的,它可以理解Spring的上下文。然而 UserMapper 这个接口是Mybatis的,IDEA理解不了。

@Autowired 注解,默认情况下要求依赖对象(也就是 userMapper )必须存在。而IDEA认为这个对象的实例/代理是个null,所以就友好地给个提示。按照提示修改如下:

1
2
3
4
5
6
private final UserMapper userMapper;

@Autowired
public UserServiceImpl(UserMapper userMapper) {
this.userMapper = userMapper;
}

此时仍然存在一个问题:

1
Could not autowire.

自动注入 bean, spring帮助我们完成了,但是同时Spring提供了一些注解来显式的注明bean之间的引用关系,其中最为熟知的自然是@Controller,@Service,@Repository,@Component等。
这里其实给UserMapper接口加上@Repository,@Component就可以解决,那么他们之间有什么区别?

区别

Spring不但支持自己定义的@Autowired注解,还支持几个由JSR-250规范定义的注解,它们分别是@Resource、@PostConstruct以及@PreDestroy。

@Resource的作用相当于@Autowired,只不过@Autowired按byType自动注入,而@Resource默认按 byName自动注入罢了。@Resource有两个属性是比较重要的,分是name和type,Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不指定name也不指定type属性,这时将通过反射机制使用byName自动注入策略。

@Resource装配顺序

  1. 如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常
  2. 如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常
  3. 如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常
  4. 如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配;

@Autowired 与@Resource的区别

1、@Autowired与@Resource都可以用来装配bean. 都可以写在字段上,或写在setter方法上。

2、@Autowired默认按类型装配(这个注解是属于spring的),默认情况下必须要求依赖对象必须存在,如果要允许null值,可以设置它的required属性为false,如:@Autowired(required=false) ,如果我们想使用名称装配可以结合@Qualifier注解进行使用,如下:

1
2
@Autowired()@Qualifier("baseDao")
privateBaseDao baseDao;

3、@Resource(这个注解属于J2EE的),默认按照名称进行装配,名称可以通过name属性进行指定,如果没有指定name属性,当注解写在字段上时,默认取字段名进行装配查找,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。

1
2
@Resource(name="baseDao")
privateBaseDao baseDao;

推荐使用:@Resource注解在字段上,这样就不用写setter方法了,并且这个注解是属于J2EE的,减少了与spring的耦合。这样代码看起就比较优雅。

Spring @Qualifier注解

@Autowired是根据类型进行自动装配的。如果当Spring上下文中存在不止一个UserDao类型的bean时,就会抛出BeanCreationException异常;如果Spring上下文中不存在UserDao类型的bean,也会抛出BeanCreationException异常。我们可以使用@Qualifier配合@Autowired来解决这些问题。如下:

  1. 可能存在多个UserDao实例
1
2
3
@Autowired   
@Qualifier("userServiceImpl")   
public IUserService userService;

或者

1
2
3
4
@Autowired   
public void setUserDao(@Qualifier("userDao") UserDao userDao) {   
    this.userDao = userDao;   
}

这样Spring会找到id为userServiceImpl和userDao的bean进行装配。

  1. 可能不存在UserDao实例
1
2
@Autowired(required = false)   
public IUserService userService

总结

@Autowired 默认按type注入
@Qualifier(“baseDao”) 一般作为@Autowired()的修饰用
@Resource(name=”baseDao”) 默认按name注入,可以通过name和type属性进行选择性注入

一般@Autowired和@Qualifier一起用,@Resource单独用。当然没有冲突的话@Autowired也可以单独用。

常用注解

声明Bean的注解

1
2
3
4
//定义控制层Bean,如Action
@Controller

@Controller("Bean的名称")
1
2
3
4
//定义业务逻辑层Bean
@Service          

@Service("Bean的名称")
1
2
3
4
//定义DAO层/数据访问层Bean
@Repository   

@Repository("Bean的名称")
1
2
//定义Bean, 不好归类时使用.
@Component

自动装配Bean

1
2
//默认按类型匹配,自动装配(Srping提供的),可以写在成员属性上,或写在setter方法上
@Autowired
1
2
//一定要找到匹配的Bean,否则抛异常。 默认值就是true
@Autowired(required=true)
1
2
3
//按名称装配Bean,与@Autowired组合使用,解决按类型匹配找到多个Bean问题。
@Autowired
@Qualifier("bean的名字")
1
2
3
4
5
6
//JSR-250提供的,默认按名称装配,当找不到名称匹配的bean再按类型装配.可以写在成员属性上,或写在setter方法上
@Resource  

//可以通过@Resource(name="beanName") 指定被注入的bean的名称, 要是未指定name属性, 默认使用成员属性的变量名,一般不用写name属性.

@Resource(name="beanName")
1
2
//是JSR-330提供的,按类型装配,功能比@Autowired少,没有使用的必要。
@Inject

Java配置类

@Configuration 声明当前类为配置类,相当于xml形式的Spring配置(类上)

@Bean 注解在方法上,声明当前方法的返回值为一个bean,替代xml中的方式(方法上)

@Configuration 声明当前类为配置类,其中内部组合了@Component注解,表明这个类是一个bean(类上)

@ComponentScan 用于对Component进行扫描,相当于xml中的(类上)

@WishlyConfiguration 为@Configuration与@ComponentScan的组合注解,可以替代这两个注解

切面(AOP)

Spring支持AspectJ的注解式切面编程。

@Aspect 声明一个切面(类上)

使用@After、@Before、@Around定义建言(advice),可直接将拦截规则(切点)作为参数。

@After 在方法执行之后执行(方法上)

@Before 在方法执行之前执行(方法上)

@Around 在方法执行之前与之后执行(方法上)

@PointCut 声明切点

在java配置类中使用@EnableAspectJAutoProxy注解开启Spring对AspectJ代理的支持(类上)

定义Bean的作用域和生命过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//设置Spring容器如何新建Bean实例(方法上,得有@Bean)
@Scope("prototype")

其设置类型包括:

Singleton (单例,一个Spring容器中只有一个bean实例,默认模式),

Protetype (每次调用新建一个bean),

Request (web项目中,给每个http request新建一个bean),

Session (web项目中,给每个http session新建一个bean),

GlobalSession(给每一个 global http session新建一个Bean实例)
1
2
//相当于init-method,使用在方法上,当Bean初始化时执行。
@PostConstruct
1
2
//相当于destory-method,使用在方法上,当Bean销毁时执行。
@PreDestroy

声明式事务

1
@Transactional

@Value

@Value 为属性注入值(属性上)支持如下方式的注入:

  1. 注入普通字符
1
2
@Value("Tom")
String name;
  1. 注入操作系统属性
1
2
@Value("#{systemProperties['os.name']}")
String osName;
  1. 注入表达式结果
1
2
@Value("#{T(java.lang.Math).random() * 100}")
String randomNumber;
  1. 注入其他bean属性
1
2
@Value("#{demoClass.name}")
String name;
  1. 注入文件资源
1
2
@Value("classpath:com/springboot/study/hello/test.txt")
String resourceFile;
  1. 注入网站资源
1
2
@Value("http://www.baiduc.om")
Resource url;
  1. 注入配置文件
1
2
@Value("${book.name}")
String bookName;

环境切换

@Profile 通过设定Environment的ActiveProfiles来设定当前context需要使用的配置环境。(类或方法上)

@Conditional Spring4中可以使用此注解定义条件化的bean,通过实现Condition接口,并重写matches方法,从而决定该bean是否被实例化。(方法上)

异步

@EnableAsync 配置类中,通过此注解开启对异步任务的支持,叙事性AsyncConfigurer接口(类上)

@Async 在实际执行的bean方法使用该注解来申明其是一个异步任务(方法上或类上所有的方法都将异步,需要@EnableAsync开启异步任务)

定时任务

@EnableScheduling 在配置类上使用,开启计划任务的支持(类上)

@Scheduled 来申明这是一个任务,包括cron,fixDelay,fixRate等类型(方法上,需先开启计划任务的支持)

@Enable*

这些注解主要用来开启对xxx的支持。

@EnableAspectJAutoProxy 开启对AspectJ自动代理的支持

@EnableAsync 开启异步方法的支持

@EnableScheduling 开启计划任务的支持

@EnableWebMvc 开启Web MVC的配置支持

@EnableConfigurationProperties 开启对@ConfigurationProperties注解配置Bean的支持

@EnableJpaRepositories 开启对SpringData JPA Repository的支持

@EnableTransactionManagement 开启注解式事务的支持

@EnableCaching 开启注解式的缓存支持

测试

@RunWith 运行器,Spring中通常用于对JUnit的支持

1
@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration 用来加载配置ApplicationContext,其中classes属性用来加载配置类

1
@ContextConfiguration(classes={Test.class})

SpringMVC

@EnableWebMvc 在配置类中开启Web MVC的配置支持,如一些ViewResolver或者MessageConverter等,若无此句,重写WebMvcConfigurerAdapter方法(用于对SpringMVC的配置)。

@Controller 声明该类为SpringMVC中的Controller

@RequestMapping 用于映射Web请求,包括访问路径和参数(类或方法上)

@ResponseBody 支持将返回值放在response内,而不是一个页面,通常用户返回json数据(返回值旁或方法上)

@RequestBody 允许request的参数在request体中,而不是直接连接在地址后面。(放在参数前)

@PathVariable 用于接收路径参数,比如@RequestMapping(“/hello/{name}”)申明的路径,将注解放在参数前,即可获取该值,通常作为Restful的接口实现方法。

@RestController 该注解为一个组合注解,相当于@Controller和@ResponseBody的组合,注解在类上,意味着,该Controller的所有方法都默认加上了@ResponseBody。

@ControllerAdvice 通过该注解,我们可以将对于控制器的全局配置放置在同一个位置,注解了@Controller的类的方法可使用@ExceptionHandler、@InitBinder、@ModelAttribute注解到方法上,这对所有注解了 @RequestMapping的控制器内的方法有效。

@ExceptionHandler 用于全局处理控制器里的异常

@InitBinder 用来设置WebDataBinder,WebDataBinder用来自动绑定前台请求参数到Model中。

@ModelAttribute 本来的作用是绑定键值对到Model里,在@ControllerAdvice中是让全局的@RequestMapping都能获得在此处设置的键值对。

为什么建议构造器注入

构造器注入与域注入 热门文章 Why field injection is evil 给出总结:
Field injection:

  • less code to write;
  • unsafe code;
  • more complicated to test;

Constructor injection:

  • safe code;
  • more code to write (see the hint to Lombok);
  • easy to test;

Spring 的博客上指出 Setter injection versus constructor injection and the use of @Required

解决方案

书归正传,说下如何剔除Intellij中Mybatis的Mapper自动注入警告。

方法1:为 @Autowired 注解设置required = false

使用 @Autowired 注解时,若希望允许null值,可设置required = false,像这样:

1
2
@Autowired(required = false)
private UserMapper userMapper;

这样就不会有警告了。原因很好理解:IDEA认为userMapper是个null,给了警告;加上required = false后,使用 @Autowired 注解不再去校验userMapper是否存在了。也就不会有警告了。

方法2:用 @Resource 替换 @Autowired

1
2
@Resource
private UserMapper userMapper;

方法3:在Mapper接口上加上@Repository注解

1
2
3
@Repository
public interface UserMapper extends Mapper<User> {
}

这样也能让你的

1
2
@Autowired
private UserMapper userMapper;

不再报错。当然,如果你用@Component替换@Repository也是可以的。原理大致:IDEA不是认为 userMapper 是个null嘛…加个@Repository注解骗一下IDEA就OK了……

方法4:用Lombok

1
2
3
4
5
6
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class TestService {
private final UserMapper userMapper;
...
}

Lombok生成的代码是这样的:

1
2
3
4
5
6
7
8
9
@Service
public class TestService {
private final UserMapper userMapper;
@Autowired
public TestService(final UserMapper userMapper) {
this.userMapper = userMapper;
}
...
}

但如果自己手写成Lombok生成的代码,IDEA依然会给你报警告 。我猜,应该是IDEA的Lombok插件把IDEA搞懵逼了…所以不提示了…

方法5:把IDEA的警告关闭掉
个人没试过。

方法6:安装mybatis plugin
据说安装mybatis plugin可以解决该问题。

本文标题:由剔除Intellij中Mybatis的Mapper自动注入警告引入对注解的学习

文章作者:王洪博

发布时间:2019年01月20日 - 10:01

最后更新:2019年09月12日 - 10:09

原始链接:http://whb1990.github.io/posts/8b9fb30a.html

▄︻┻═┳一如果你喜欢这篇文章,请点击下方"打赏"按钮请我喝杯 ☕
0%