@repository
@repository跟@Service,@Compent,@Controller这4种注解是没什么本质区别,都是声明作用,取不同的名字只是为了更好区分各自的功能.下图更多的作用是mapper注册到类似于以前mybatis.xml中的mappers里.
也是因为接口没办法在spring.xml中用bean的方式来配置实现类吧(接口配不了),所以只能用注解或者mybatis.xml中扫描bean的方式来生成实现类吧
一,首先:@repository是用来注解接口,如下图:这个注解是将接口BookMapper的一个实现类(具体这个实现类的name叫什么,还需要再分析源码找找看)交给spring管理(在spring中有开启对@repository注解的扫描),当哪些地方需要用到这个实现类作为依赖时,就可以注入了.当然我们也可以主动给这个实现类命名,如下图
二,为什么有时候我们不用@repository来注解接口,我们照样可以注入到这个接口的实现类呢?如下图,下图是在接口没有用
@repository注解的情况下,依然可以实现注入它的实现类.
上面是在idea中报了红线警告,说找不到这个实现类,但依然是可以运行,没有问题(只是单纯的警告),而在myeclipse中,是连警告都没有的,运行完全没问题.这是因为如下图:
是因为我们在mybatis的xml文件配置了上图这个bean,它会去将dao这个层中的mapper(也就是我们的接口)都生成实现类,然后交给spring管理(因为mybatis.xml文件我们最终还是导入了spring容器中),所以我们这里不对这些接口用@repository注解,也是一样可以用它的实现类,(这也是我们写项目时,有时感觉完全是没用到@repository注解的原因,因为没有什么必要)而idea报红线警告,可能是idea自己的原因,这个在我们对它对应的接口用@repository注解后,红线警告会消失,运行也完全没问题
@RequestBody:
作用:
主要用来接收前端传递给后端的json字符串中的数据的(请求体中的数据的);
要求:
GET方式无请求体,所以使用@RequestBody接收数据时,前端不能使用GET方式提交数据,而是用POST方式进行提交。
在后端的同一个接收方法里,@RequestBody与@RequestParam()可以同时使用,@RequestBody最多只能有一个,而@RequestParam()可以有多个。
简言之:
一个请求——》只有一个@RequestBody;
一个请求——》可以有多个@RequestParam。
******①同时使用@RequestParam()和@RequestBody
@RequestParam( )指定的参数可以是普通元素、数组、集合、对象等等
(即: @RequestBody 与@RequestParam()可以同时使用时,原SpringMVC接收参数的机制不变,只不过RequestBody 接收的是请求体里面的数据(get是默认的请求体,post是提交表单需要的请求体);而RequestParam接收的是key-value里面的参数,所以它会被切面进行处理从而可以用普通元素、数组、集合、对象等接收)。
因此:
1、如果参数时放在请求体中,传入后台的话,那么后台要用@RequestBody才能接收到
否则就会在数据库中不能完成curd操作;
2、如果不是放在 请求体中的话,那么后台接收前台传过来的参数时,要用@RequestParam来接收,或则形参前什么也不写也能接收。
******②参数前写了@RequestParam(xxx)
1、前端必须有对应的xxx名字才行(不管是否有值,可通过设置该注解的required属性来调节是否必须传)2、如果没有xxx名的话,那么请求会出错,报400。
******③参数前不写@RequestParam(xxx)
1、前端是否有对应的xxx名字都行,如果有xxx名的话,那么就会自动匹配
2、没有的话,请求也能正确发送。
&&&追注:这里与feign消费服务时不同;feign消费服务时,如果参数前什么也不写,那么会被默认@RequestBody的。
******④如果后端参数是一个对象,且该参数前是以@RequestBody修饰的,那么前端传递json参数时,必须满足以下要求:
后端@RequestBody注解对应的类在将HTTP的输入流(含请求体)装配到目标类
(也就是:@RequestBody后面的类)时,会根据json字符串中的key来匹配对应实体类的属性,如果匹配一致且json中的该key对应的值符合)
(或者说:实体类的对应属性的类型要求时,会调用实体类的setter方法将值赋给该属性。)
1、json字符串中,如果value为 “” 的话(空串),后端对应属性如果是String类型的,那么接受到的就是 “”
如果是后端属性的类型是Integer、Double等类型,那么接收到的就是null。
2、json字符串中,如果value为null的话,后端对应收到的就是null。
3、如果某个参数没有value的话,在传json字符串给后端时,要么干脆就不把该字段写到json字符串中;要么写value时, 必须有值,null 或""都行。
千万不能有类似"stature":,这样的写法,如:
总结:
结论①:@JsonAlias注解,实现:json转模型时,使json中的特定key能转化为特定的模型属性;但是模型转json时,
对应的转换后的key仍然与属性名一致
结论②:@JsonProperty注解,实现:json转模型时,使json中的特定key能转化为指定的模型属性;同样的,模
型转json时,对应的转换后的key为指定的key
结论③:@JsonAlias注解需要依赖于setter、getter,而@JsonProperty注解不需要。
结论④:在不考虑上述两个注解的一般情况下,key与属性匹配时,默认大小写敏感。
结论⑤:有多个相同的key的json字符串中,转换为模型时,会以相同的几个key中,排在最后的那个key的值给模
型属性复制,因为setter会覆盖原来的值。见示例中的gender属性。
结论⑥:后端@RequestBody注解对应的类在将HTTP的输入流(含请求体)装配到目标类(即:@RequestBody后面
的类)时,会根据json字符串中的key来匹配对应实体类的属性,如果匹配一致且json中的该key对应的值
符合(或可转换为)实体类的对应属性的类型要求时,会调用实体类的setter方法将值赋给该属性。
@ComponentScan
- @ComponentScan注解是什么
其实很简单,@ComponentScan主要就是定义扫描的路径从中找出标识了需要装配的类自动装配到spring的bean容器中
- @ComponentScan注解的详细使用
做过web开发的同学一定都有用过@Controller,@Service,@Repository注解,查看其源码你会发现,他们中有一个共同的注解@Component,没错@ComponentScan注解默认就会装配标识了@Controller,@Service,@Repository,@Component注解的类到spring容器中,好下面咱们就先来简单演示一下这个例子
在包com.zhang.controller下新建一个UserController带@Controller注解如下:
package com.zhang.controller;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
}
在包com.zhang.service下新建一个UserService带@Service注解如下:
package com.zhang.service;
import org.springframework.stereotype.Service;
@Service
public class UserService {
}
在包com.zhang.dao下新建一个UserDao带@Repository注解如下:
package com.zhang.dao;
import org.springframework.stereotype.Repository;
@Repository
public class UserDao {
}
新建一个配置类如下:
/*** 主配置类 包扫描com.zhang** @author zhangqh* @date 2018年5月12日*/
@ComponentScan(value="com.zhang")
@Configuration
public class MainScanConfig {
}
新建测试方法如下:
AnnotationConfigApplicationContext applicationContext2 = new AnnotationConfigApplicationContext(MainScanConfig.class);String[] definitionNames = applicationContext2.getBeanDefinitionNames();for (String name : definitionNames) {System.out.println(name);
}
运行结果如下:
mainScanConfig
userController
userDao
userService
怎么样,包扫描的方式比以前介绍的通过@Bean注解的方式是不是方便很多,这也就是为什么web开发的同学经常使用此方式的原因了
上面只是简单的介绍了@ComponentScan注解检测包含指定注解的自动装配,接下来让我们来看看@ComponentScan注解的更加详细的配置,在演示详细的配置之前,让我们先看看@ComponentScan的源代码如下:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {/*** 对应的包扫描路径 可以是单个路径,也可以是扫描的路径数组* @return*/@AliasFor("basePackages")String[] value() default {};/*** 和value一样是对应的包扫描路径 可以是单个路径,也可以是扫描的路径数组* @return*/@AliasFor("value")String[] basePackages() default {};/*** 指定具体的扫描的类* @return*/Class<?>[] basePackageClasses() default {};/*** 对应的bean名称的生成器 默认的是BeanNameGenerator* @return*/Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;/*** 处理检测到的bean的scope范围*/Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;/*** 是否为检测到的组件生成代理* Indicates whether proxies should be generated for detected components, which may be* necessary when using scopes in a proxy-style fashion.* <p>The default is defer to the default behavior of the component scanner used to* execute the actual scan.* <p>Note that setting this attribute overrides any value set for {@link #scopeResolver}.* @see ClassPathBeanDefinitionScanner#setScopedProxyMode(ScopedProxyMode)*/ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;/*** 控制符合组件检测条件的类文件 默认是包扫描下的 **/*.class* @return*/String resourcePattern() default ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN;/*** 是否对带有@Component @Repository @Service @Controller注解的类开启检测,默认是开启的* @return*/boolean useDefaultFilters() default true;/*** 指定某些定义Filter满足条件的组件 FilterType有5种类型如:* ANNOTATION, 注解类型 默认ASSIGNABLE_TYPE,指定固定类ASPECTJ, ASPECTJ类型REGEX,正则表达式CUSTOM,自定义类型* @return*/Filter[] includeFilters() default {};/*** 排除某些过来器扫描到的类* @return*/Filter[] excludeFilters() default {};/*** 扫描到的类是都开启懒加载 ,默认是不开启的* @return*/boolean lazyInit() default false;
}
a,演示basePackageClasses参数,如我们把配置文件改成如下:
@ComponentScan(value="com.zhang.dao",useDefaultFilters=true,basePackageClasses=UserService.class)
@Configuration
public class MainScanConfig {
}
测试结果如下:
mainScanConfig
userDao
userService
b,演示includeFilters参数的使用如下:
在com.zhang.service包下新建一个UserService2类如下:注意没有带@Service注解
package com.zhang.service;
public class UserService2 {
}
配置类改成:
@ComponentScan(value="com.zhang",useDefaultFilters=true,includeFilters={@Filter(type=FilterType.ANNOTATION,classes={Controller.class}),@Filter(type=FilterType.ASSIGNABLE_TYPE,classes={UserService2.class})})
@Configuration
public class MainScanConfig {
}
运行结果如下:
mainScanConfig
userController
userDao
userService
userService2
userService2同样被加入到了spring容器
新增一个自定义的实现了TypeFilter的MyTypeFilter类如下:
/*** 自定义过滤** @author zhangqh* @date 2018年5月12日*/
public class MyTypeFilter implements TypeFilter {public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)throws IOException {AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();ClassMetadata classMetadata = metadataReader.getClassMetadata();Resource resource = metadataReader.getResource();String className = classMetadata.getClassName();System.out.println("--->"+className);// 检测名字包含Service的beanif(className.contains("Service")){return true;}return false;}
}
修改主配置如下:
@ComponentScan(value="com.zhang",useDefaultFilters=true,includeFilters={@Filter(type=FilterType.ANNOTATION,classes={Controller.class}),@Filter(type=FilterType.CUSTOM,classes={MyTypeFilter.class})})
@Configuration
public class MainScanConfig {
}
运行结果如下:
mainScanConfig
userController
userDao
userService
userService2
可以发现同样userService2被加入到了spring容器中
好了includeFilters参数就演示到这,另外一个参数excludeFilters和includeFilters用户一摸一样,只是他是过滤出不加入spring容器中,感兴趣的同学可以自己试试,我这边就不演示了
总结一下@ComponentScan的常用方式如下
- 自定扫描路径下边带有@Controller,@Service,@Repository,@Component注解加入spring容器
- 通过includeFilters加入扫描路径下没有以上注解的类加入spring容器
- 通过excludeFilters过滤出不用加入spring容器的类
- 自定义增加了@Component注解的注解方式