起源
相信使用Mybatis的小伙们一定经常编写类似如下的代码:
可以看到 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 | private final 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装配顺序
- 如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常
- 如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常
- 如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常
- 如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配;
@Autowired 与@Resource的区别
1、@Autowired与@Resource都可以用来装配bean. 都可以写在字段上,或写在setter方法上。
2、@Autowired默认按类型装配(这个注解是属于spring的),默认情况下必须要求依赖对象必须存在,如果要允许null值,可以设置它的required属性为false,如:@Autowired(required=false) ,如果我们想使用名称装配可以结合@Qualifier注解进行使用,如下:
1 | "baseDao") () ( |
3、@Resource(这个注解属于J2EE的),默认按照名称进行装配,名称可以通过name属性进行指定,如果没有指定name属性,当注解写在字段上时,默认取字段名进行装配查找,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。
1 | "baseDao") (name= |
推荐使用:@Resource注解在字段上,这样就不用写setter方法了,并且这个注解是属于J2EE的,减少了与spring的耦合。这样代码看起就比较优雅。
Spring @Qualifier注解
@Autowired是根据类型进行自动装配的。如果当Spring上下文中存在不止一个UserDao类型的bean时,就会抛出BeanCreationException异常;如果Spring上下文中不存在UserDao类型的bean,也会抛出BeanCreationException异常。我们可以使用@Qualifier配合@Autowired来解决这些问题。如下:
- 可能存在多个UserDao实例
1 |
|
或者
1 |
|
这样Spring会找到id为userServiceImpl和userDao的bean进行装配。
- 可能不存在UserDao实例
1 | false) (required = |
总结
@Autowired 默认按type注入
@Qualifier(“baseDao”) 一般作为@Autowired()的修饰用
@Resource(name=”baseDao”) 默认按name注入,可以通过name和type属性进行选择性注入
一般@Autowired和@Qualifier一起用,@Resource单独用。当然没有冲突的话@Autowired也可以单独用。
常用注解
声明Bean的注解
1 | //定义控制层Bean,如Action |
1 | //定义业务逻辑层Bean |
1 | //定义DAO层/数据访问层Bean |
1 | //定义Bean, 不好归类时使用. |
自动装配Bean
1 | //默认按类型匹配,自动装配(Srping提供的),可以写在成员属性上,或写在setter方法上 |
1 | //一定要找到匹配的Bean,否则抛异常。 默认值就是true |
1 | //按名称装配Bean,与@Autowired组合使用,解决按类型匹配找到多个Bean问题。 |
1 | //JSR-250提供的,默认按名称装配,当找不到名称匹配的bean再按类型装配.可以写在成员属性上,或写在setter方法上 |
1 | //是JSR-330提供的,按类型装配,功能比@Autowired少,没有使用的必要。 |
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 | //设置Spring容器如何新建Bean实例(方法上,得有@Bean) |
1 | //相当于init-method,使用在方法上,当Bean初始化时执行。 |
1 | //相当于destory-method,使用在方法上,当Bean销毁时执行。 |
声明式事务
1 |
@Value
@Value 为属性注入值(属性上)支持如下方式的注入:
- 注入普通字符
1 | "Tom") ( |
- 注入操作系统属性
1 | "#{systemProperties['os.name']}") ( |
- 注入表达式结果
1 | "#{T(java.lang.Math).random() * 100}") ( |
- 注入其他bean属性
1 | "#{demoClass.name}") ( |
- 注入文件资源
1 | "classpath:com/springboot/study/hello/test.txt") ( |
- 注入网站资源
1 | "http://www.baiduc.om") ( |
- 注入配置文件
1 | "${book.name}") ( |
环境切换
@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 | (SpringJUnit4ClassRunner.class) |
@ContextConfiguration 用来加载配置ApplicationContext,其中classes属性用来加载配置类
1 | (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 | false) (required = |
这样就不会有警告了。原因很好理解:IDEA认为userMapper是个null,给了警告;加上required = false后,使用 @Autowired 注解不再去校验userMapper是否存在了。也就不会有警告了。
方法2:用 @Resource 替换 @Autowired
1 |
|
方法3:在Mapper接口上加上@Repository注解
1 |
|
这样也能让你的
1 |
|
不再报错。当然,如果你用@Component替换@Repository也是可以的。原理大致:IDEA不是认为 userMapper 是个null嘛…加个@Repository注解骗一下IDEA就OK了……
方法4:用Lombok
1 |
|
Lombok生成的代码是这样的:
1 |
|
但如果自己手写成Lombok生成的代码,IDEA依然会给你报警告 。我猜,应该是IDEA的Lombok插件把IDEA搞懵逼了…所以不提示了…
方法5:把IDEA的警告关闭掉
个人没试过。
方法6:安装mybatis plugin
据说安装mybatis plugin可以解决该问题。