bean注入相关注解
- @Configuration:标注在类上,相当于把该类作为spring的xml配置文件中的,配置spring容器(应用上下文),其实就是替换xml配置文件,被注解的类内部包含有一个或多个被@Bean注解的方法,这些方法将会被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext类进行扫描,并用于构建bean定义,初始化Spring容器;
- @Bean:注解用于创建一个Bean对象,然后这个Bean对象交给Spring管理。产生这个Bean对象的方法Spring只会调用一次,随后这个Spring将会将这个Bean对象放在自己的IOC容器中;
- @component:作用于类上,把java class实例化到spring容器中
- @Import:用来导入其他配置类;
- @ImportResource:用来加载xml配置文件;
- @Value:注入Spring boot application.properties配置的属性的值,写法如下:
@Value("${env}")
public String env;
web服务分层使用注解
- @controller:作用于对外提供访问请求的web服务的交互类上,用于标注控制层
- @service:用于标注服务层,主要用来进行业务的逻辑处理
- @repository:用于标注数据访问层,也可以说用于标注数据访问组件,即DAO组件
- @RestController:@ResponseBody和@Controller的合集
Controller层交互注解
- @RequestMapping:作用于类或者方法上,表示该控制器可以处理的UR L请求路径;
用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。该注解有六个属性:
params:指定request中必须包含某些参数值是,才让该方法处理。
headers:指定request中必须包含某些指定的header值,才能让该方法处理请求。
value:指定请求的实际地址,指定的地址可以是URI Template 模式
method:指定请求的method类型, GET、POST、PUT、DELETE等
consumes:指定处理请求的提交内容类型(Content-Type),如application/json,text/html;
produces:指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回 - @PostMapping:指定method为POST的RequestMapping,作用和RequestMapping类似,只是指定请求类型为post;
- @GetMapping:指定method为GET的RequestMapping;
- @RequestParam:接收的参数是来自HTTP请求url的QueryString中;
- @RequestBody:接收的参数是来自requestBody中,即请求体HttpEntity中的;
- @PathVariable:接收请求路径中占位符的值
注入bean相关注解
- @Autowired:自动导入依赖的bean,只按照byType 注入
- @Resource:默认按byName自动注入,也提供按照byType 注入
相同点:
@Resource的作用相当于@Autowired,均可标注在字段或属性的setter方法上。
不同点:
(1)提供方:@Autowired是由org.springframework.beans.factory.annotation.Autowired提供,换句话说就是由Spring提供;@Resource是由javax.annotation.Resource提供,即J2EE提供,需要JDK1.6及以上。
(2)注入方式:@Autowired只按照byType 注入;@Resource默认按byName自动注入,也提供按照byType 注入;
(3)属性:@Autowired按类型装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它required属性为false。如果我们想使用按名称装配,可以结合@Qualifier注解一起使用。@Resource有两个中重要的属性:name和type。name属性指定byName,如果没有指定name属性,当注解标注在字段上,即默认取字段的名称作为bean名称寻找依赖对象,当注解标注在属性的setter方法上,即默认取属性名作为bean名称寻找依赖对象。需要注意的是,@Resource如果没有指定name属性,并且按照默认的名称仍然找不到依赖对象时, @Resource注解会回退到按类型装配。但一旦指定了name属性,就只能按名称装配了。
Conditional相关注解
所有conditional开头的注解,底层依赖的都是@Conditional注解,@Condition来指定满足一定条件下注册组件对像,所有的条件必须实现Condition或者SpringBootCondition接口,重写匹配计算方法,来决定组件是否注册。
- @ConditionalOnClass:某个class位于类路径上(实际就是引入class依赖的jar包),才会实例化这个Bean,比如下面mybatis的启动类,依赖SqlSessionFactory.class,和SqlSessionFactoryBean.class,其实就是我们必须引入org.mybatis->mybatis才能生效;
@org.springframework.context.annotation.Configuration
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
@ConditionalOnBean(DataSource.class)
@EnableConfigurationProperties(MybatisProperties.class)
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class MybatisAutoConfiguration {
- @ConditionalOnBean:仅仅在当前上下文中存在某个对象时,才会实例化一个Bean;
在spring ioc的过程中,优先解析@Component,@Service,@Controller注解的类。其次解析配置类,也就是@Configuration标注的类。最后开始解析配置类中定义的bean;。但是,同样的配置类或者自定义bean的加载顺序是无法保证的,如果单纯使用ConditionalOnBean,很可能发生依赖的bean确实有,但是注入失败的情况,所以这个注解一般结合AutoConfigureBefore或者AutoConfigureAfter使用; - @ConditionalOnMissingBean:当前上下文中不存在某个对象时,才会实例化一个Bean,与ConditionalOnBean是相反的
- @AutoConfiguteAfter:它的value 是一个class数组,表示在加载value里面的配置类之后再加载当前类,对比上面mybatis的配置类,就是必须先加载DataSourceAutoConfiguration才会加载MybatisAutoConfiguration。这里一定要注意,因为spring只对spring.factories文件下的配置类进行排序,所以这个注解的使用需要将加载类放到META_INF/pring.factories中;
- @AutoConfigureBefore:和AutoConfiguteAfter基本相同,这个表示加载value里面的配置类之前先加载当前类;
- @AutoConfigureOrder:它的value是一个int,代表优先级,执行优先级别根据数字决定,数字越小,越优先加载,负数也可以;
- @ConditionalOnExpression:当表达式为true的时候,才会实例化一个Bean;
- @ConditionalOnNotWebApplication:不是web应用才会加载实例化一个Bean;
- @ConditionalOnProperty:当配置文件包含某个属性或者某个prefix开头的属性的时候才加载这个类;
全局异常处理注解
@ControllerAdvice:定义在类上,可以进行统一异常捕获和处理;
@RestControllerAdvice:@ControllerAdvice 和 @ResponseBody;
@ExceptionHandler(Exception.class):用在方法上面,捕获对应的异常,并执行对应方法;
使用@RestControllerAdvice和@ExceptionHandler实现一个统一的异常捕获和处理类,使用这个机制,可以在做一些参数验证或者服务抛错的时候,无需try catch并识别类型再进行对应的response返回对象的封装,可以直接抛出自定义的异常,直接返回给前端:
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
/**
* 参数数值校验失败或参数缺失导致抛出的异常
*
* @param req http请求信息
* @param e exception信息
* @return 出错信息
*/
@ExceptionHandler({
MissingServletRequestParameterException.class, // 必需参数缺失
IllegalArgumentException.class // 参数值校验失败
})
public Response missingServletRequestParameterExceptionHandler(HttpServletRequest req,
Exception e
) {
log.error("{} occurs: {}, msg: {}, url: {}", e.getClass(), e, e.getMessage(), req.getRequestURL());
return Response.fail(null, ResultCode.INVALID_PARAMETER, e.getMessage());
}
/**
* 参数类型错误导致抛出的异常
*
* @param req http请求信息
* @param e exception信息
* @return 出错信息
*/
@ExceptionHandler({HttpMessageNotReadableException.class})
public Response httpMessageNotReadableExceptionHandler(
HttpServletRequest req,
HttpMessageNotReadableException e
) {
log.error("{} occurs: {}, msg: {}, url: {}", e.getClass(), e, e.getMessage(), req.getRequestURL());
return Response.fail(null, ResultCode.INVALID_PARAMETER, ResultMsg.INVALID_PARAMETER);
}
/**
* 参数类型错误导致抛出的异常
*
* @param req http请求信息
* @param e exception信息
* @return 出错信息
*/
@ExceptionHandler({UserException.class})
public Response userExceptionHandler(
HttpServletRequest req, UserException e
) {
return Response.fail(null, e.getCode(), e.getMessage());
}
/**
* 用于兜底的普通exception处理
*
* @param req http请求信息
* @param e exception信息
* @return 出错信息
*/
@ExceptionHandler({Exception.class})
public Response exceptionHandler(HttpServletRequest req, Exception e) {
log.error("Unknown exception occurs. class: {}, msg: {}, url: {}",
e.getClass(), e.getMessage(), req.getRequestURL());
// 打印堆栈信息
StackTraceElement[] ses = e.getStackTrace();
StringBuilder stackInfo = new StringBuilder();
for (int i = 0, len = ses.length; i < len; i++) {
stackInfo.append(ses[i].toString());
if (i < len - 1) {
stackInfo.append("\nat ");
}
}
log.error("errmsg:{}", e.getMessage());
log.error("stack trace: {}", stackInfo.toString());
return Response.fail(null, ResultCode.INTERNAL_ERROR, ResultMsg.INTERNAL_ERROR);
}
}