-
2016-12-06 11:15:51
在Spring MVC中我们会发现一个特别便利的一个小功能,那就是在Controller层映射的方法上
Spring会自动帮我们注入参数,帮我们初始化对象。
比如常用的:
@RequestParam :取querystring 当中的参数
@PathVariable :取 在@RequestMapping 中定义的占位符中的参数(/test/{id})
@RequestBody : 取request 这个消息体 一般用(String,byte[] 来接)
等等,更多的就不一一列举了,可以参看 这个包下的注解
(org.springframework.web.bind.annotation)
那么还有一些不需要注解的:如
- HttpServletRequest
- HttpServletResponse
- MultipartFile
- MultipartRequest
Form(此Form 为 任意JavaBean 对象,Spring 会将相关请求参数自动注入) 等等。如此智能的体验,实在是太方便了。那么它们又是怎么工作的呢?
在比如一个业务场景,要是我想通过这种方式来自动注入登录用户,那么Spring 的参数注入支不支持自定义注入呢?带着这些好奇心我们来看它的大致工作流程。
其大致流程是 DispaterServlet 接收请求,并开始确定本次请求的 handler。Handler 中确定一个 ServletInvocableHandlerMethod,并初始化一些上下文参数。
ServletInvocableHandlerMethod 开始执行调用,在调用前确定和实例化该方法需要的参数。确定和实例化调用前需要的参数,使用到了一个List<HandlerMethodArgumentResolver>.OK. 那么Spring 就是通过 HandlerMethodArgumentResolver 来识别和处理它能识别的参数了。
那我们来看看这个接口
public interface HandlerMethodArgumentResolver { boolean supportsParameter(MethodParameter parameter); Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception; }
为了节约篇幅,我将注释给删掉了。这个接口由两个类构成,一个方法 supportsParameter用来确定方法上的参数,当前这个Resolver 可不可以处理它,如果支持那么就去处理。
另一个方法 resolveArgument 就很明显了 就是在 supportsParameter 为true 的时候,执行相关处理业务。
看到这里我们可以看出Spring MVC 中方法参数的自动注入是由实现了 HandlerMethodArgumentResolver 的类来完成的,那么文章开始提到的内嵌功能 又是怎么来的呢?聪明的朋友 不难想到,Spring 中肯定初始化了一些默认的resolver。对的,就是这样。我们可在
RequestMappingHandlerAdapter 中找到一个私有方法 getDefaultArgumentResolvers();里面展示了Spring内嵌的Resolver。
先把这段代码贴一下,大家就知道哪些功能是内嵌的了
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() { List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>(); // Annotation-based argument resolution resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false)); resolvers.add(new RequestParamMapMethodArgumentResolver()); resolvers.add(new PathVariableMethodArgumentResolver()); resolvers.add(new PathVariableMapMethodArgumentResolver()); resolvers.add(new MatrixVariableMethodArgumentResolver()); resolvers.add(new MatrixVariableMapMethodArgumentResolver()); resolvers.add(new ServletModelAttributeMethodProcessor(false)); resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters())); resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters())); resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory())); resolvers.add(new RequestHeaderMapMethodArgumentResolver()); resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory())); resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory())); // Type-based argument resolution resolvers.add(new ServletRequestMethodArgumentResolver()); resolvers.add(new ServletResponseMethodArgumentResolver()); resolvers.add(new HttpEntityMethodProcessor(getMessageConverters())); resolvers.add(new RedirectAttributesMethodArgumentResolver()); resolvers.add(new ModelMethodProcessor()); resolvers.add(new MapMethodProcessor()); resolvers.add(new ErrorsMethodArgumentResolver()); resolvers.add(new SessionStatusMethodArgumentResolver()); resolvers.add(new UriComponentsBuilderMethodArgumentResolver()); // Custom arguments if (getCustomArgumentResolvers() != null) { resolvers.addAll(getCustomArgumentResolvers()); } // Catch-all resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true)); resolvers.add(new ServletModelAttributeMethodProcessor(true)); return resolvers; }
OK,那么怎么实现刚刚提到的自动注入登录用户这个功能呢?
首先我们来思考要实现这个功能需要哪几步,答案是两步,
1.我们需要有一个 处理这个业务的 resolver
2. 把这个resolver 给Spring 容器
好我们先来完成第一步,创建一个类 继承 HandlerMethodArgumentResolver ,如:
public class LoginUserArugmentResolver implements HandlerMethodArgumentResolver { @Override public boolean supportsParameter(MethodParameter parameter) { /** * 在这里可以是 通过注解方式(使用注解的或更加的灵活,但需要多创建一个相应的注解) * ,也可以是直接通过判断class 的方式 */ return parameter.hasParameterAnnotation(UserLogined.class)//通过注解方式 || parameter.getParameterType() == User.class;//直接判断 类型方式 } @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer , NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { /** * 到了这里说明 :方法参数中有匹配的情况那么我们可以 获得用户了 * ,这里只是简单的演示,取用户的操作 */ Object user = webRequest.getNativeRequest(HttpServletRequest.class). getSession().getAttribute("currentUser"); //如果有必要的话,可以在 用户为空的情况下抛出异常 /** * if(user==null){ * throw new UserNotFoundException(); * } */ /** * Tips: 在自定义参数注入中,有时也需要在路径中取出参数的情况,就像使用@PathVariable一样, * 我们可以使用以下这行代码来取,Spring把路径中的参数和值封装到了一个Map里面 * ,并放进了Request中 * Map<String, String> uriTemplateVars = * (Map<String, String>) webRequest.getAttribute( * HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST); */ return user; } }
Ok 我们有了上面这个Resolver 后,就可以开始第二步了 将它交给Spring。
这里面提供两个版本的方式 第一种是常见的用到xml的方式,可以在 ServletContext.xml(Servlet级别的上下文) 中进行配置:
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <mvc:annotation-driven> <mvc:argument-resolvers> <bean class="com.myconpany.factory.LoginUserArugmentResolver"/> </mvc:argument-resolvers> </mvc:annotation-driven> </beans>
还是一种就是 Spring4.X 中通过 class 来配置 Sevlet 相关信息的方式 :
@EnableWebMvc@Configuration@EnableAspectJAutoProxy(proxyTargetClass = true) @ComponentScan({"com.mycompany.controller"}) public class ServletContextBoot extends WebMvcConfigurerAdapter{ @Override public void addArgumentResolvers (List<HandlerMethodArgumentResolver> argumentResolvers) { //此行 需要调用方法,这样Spring会帮你完成装配工作 argumentResolvers.add(loginUserArugmentResolver()); } @Bean public LoginUserArugmentResolver loginUserArugmentResolver() { return new LoginUserArugmentResolver(); } }
原文来自
更多相关内容 - HttpServletRequest
-
参数注入:最简方法,直接注入
2019-03-15 13:31:42文章目录把参数写进默认文件里application.properties直接注入,会直接把默认文件里prefix打头的文件加载进去,这样有个缺点就是都是完全载入。 把参数写进默认文件里application.properties 直接注入,会直接把...展开全文 -
SpringBoot 控制器方法自定义参数注入
2019-10-23 15:17:22controller 方法中拿到header中的userId,然后需要根据用户id去取到User的完整信息。在多个controller就会存在同样的重复代码--根据用户id取得user对象。 统一对请求json报文进行解密等操作。 … 类似这样的操作,...1.需求场景
- 在实际开发中,经常会遇到一下场景:
- controller 方法中拿到header中的userId,然后需要根据用户id去取到User的完整信息。在多个controller就会存在同样的重复代码--根据用户id取得user对象。
- 统一对请求json报文进行解密等操作。
- …
类似这样的操作,可以通过自定义注解统一处理。
2.Talk is poor.
以场景1为基础,写一个demo。首先要给出环境:
- Spring Boot 2.1.0.RELEASE.版本很重要1.x和2.x还是有区别的。
- 自定义注解
@Target({ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface CurrentUser { /** * 当前用户在request中的名字 * * @return */ String value() default "user"; }
- 定义实体类
public class UserBean implements Serializable { private String id; private String username; public UserBean(String id, String username) { this.id = id; this.username = username; } public UserBean() { } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } }
- 定义注解解析类,其中HandlerMethodArgumentResolver是springframework提供的接口,实现即可
public class CurrentUserMethodArgumentResolver implements HandlerMethodArgumentResolver { @Override public boolean supportsParameter(MethodParameter parameter) { if (parameter.getParameterType().isAssignableFrom(UserBean.class) && parameter.hasParameterAnnotation(CurrentUser.class)) { return true; } return false; } @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { CurrentUser currentUserAnnotation = parameter.getParameterAnnotation(CurrentUser.class); String userId = webRequest.getHeader("userId"); //从数据库中根据用户id取用户信息,此处直接写死了。 UserBean user = null; if("1001".equals(userId)){ user = new UserBean(userId,"hendy"); } if(user == null){ throw new RuntimeException("用户不存在"); } return user; } }
- 定义controller 方法,使用注解
@RequestMapping(value = "/user/detail", method = RequestMethod.GET) @ResponseBody public Object test(@CurrentUser UserBean userBean) { String username = userBean.getUsername(); System.out.println(username); return userBean; }
网上有很多博客都是这样,到这一步就完成了。开始我也是按照网上那些来写,结果日常踩坑,比如:这篇,还有这篇,都没有把自定义的resolver接入到spring框架中,导致根本无效,还有一些是1.x版本的过时的配置方法。
- 关键的一步,addArgumentResolvers
@Configuration public class WebAppConfig extends WebMvcConfigurationSupport { @Override protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) { argumentResolvers.add(new CurrentUserMethodArgumentResolver()); } }
- Test
把自己踩的坑记录一下,demo给出的也未必是实际的应用需求,学习交流,仅此。有问题欢迎留言。
-
SpringBoot自定义注解参数注入
2020-04-29 09:28:33根据自定义注解拦截请求,利用header的token机制从缓存中获取信息,实现参数注入,控制器Controller层自动获取用户信息 流程图 核心代码 @Target({ElementType.METHOD, ElementType.TYPE}) @Retention...场景
根据自定义注解拦截请求,利用header的token机制从缓存中获取信息,实现参数注入,控制器Controller层自动获取用户信息
流程图
核心代码
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface AuthToken { boolean required() default true; }
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface UnAuthToken { boolean required() default true; }
@Target({ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Documented @ApiIgnore public @interface AuthUser { /** * 当前用户在request中的名字 * * @return */ boolean value() default true; }
/** * 用户登录权限校验 */ @Component public class AuthUserInterceptor implements HandlerInterceptor { private static Logger log = LoggerFactory.getLogger(AuthUserInterceptor.class); @Autowired UserService userService; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { if (!(handler instanceof HandlerMethod)) { return true; } if (response.getStatus() == 404) { throw new PathNotFoundException(); } HandlerMethod handlerMethod = (HandlerMethod) handler; Method method = handlerMethod.getMethod(); if (method.isAnnotationPresent(UnAuthToken.class)) { return true; } // 如果不是映射到方法查看controller层注解 if (method.isAnnotationPresent(AuthToken.class) || handlerMethod.getBeanType().isAnnotationPresent(AuthToken.class)) { //登录验证 AuthToken authToken = method.isAnnotationPresent(AuthToken.class) ? method.getAnnotation(AuthToken.class) : handlerMethod.getBeanType().getAnnotation(AuthToken.class); if (authToken.required()) { // 从 http 请求头中取出 token String token = request.getHeader("token"); // token认证 if (null == token) { throw new JWTVerificationException("token为空"); } if (JWT.decode(token).getExpiresAt().compareTo(new Date()) < 0) { throw new JWTVerificationException("token过期"); } User user = userService.cacheUser(token); TokenUtil.validateToken(user.getPassword(), token); if(null == user){ throw new JWTVerificationException("账户已失效,请重新登录!"); } log.warn("tokenExpires:" + DateUtil.toDate(JWT.decode(token).getExpiresAt(), "yyyy-MM-dd HH:mm:ss")); } } return true; } }
/** * 请求参数注解 @AuthUser user */ @Component public class AuthUserMethodArgumentResolver implements HandlerMethodArgumentResolver { @Autowired private UserService userService; @Override public boolean supportsParameter(MethodParameter parameter) { if (parameter.hasParameterAnnotation(AuthUser.class)) { return true; } return false; } @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) { if (parameter.hasParameterAnnotation(AuthUser.class)) { return userService.cacheUser(webRequest.getHeader("token")); } } }
@RestController @RequestMapping("/api/area") @Api(tags = "区域管理") @AuthToken public class AreaController { @Autowired private AreaService areaService; @GetMapping("/list") @ApiOperation("用户区域列表") public JsonResult<List<Area>> list(@AuthUser User user) { return JsonResult.success(areaService.getUserAreaList(user.getId())); } }
-
使用Springboot注入带参数的构造函数实例
2020-08-19 06:44:47主要介绍了使用Springboot注入带参数的构造函数实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧 -
@Bean修饰的方法参数的注入方式
2019-09-02 15:24:50@Bean修饰的方法参数的注入方式: 方法参数默认注入方式类型Autowired: 1:复杂类型可以通过@Qualifier(value=“XXX”)限定; 2:对于普通类型使用@Value(XXX)指定; @PropertySource("classpath:db.properties") ... -
springboot请求参数注入的几种方式
2020-05-17 17:01:44springboot请求参数注入的几种方式支持的请求模式参数注入方式注意事项源码 支持的请求模式 GET方式:主要用于简单查询,使用注解:@GetMapping("/hello")或者@RequestMapping(value = “hello”, method = Request... -
SpringMvc通过自定义注解在方法的参数中注入数据
2019-01-26 18:47:30SpringMvc通过自定义注解在方法的参数中注入数据 说一个场景,有时候我们做后台业务的时候,经常需要取session中存储的用户信息,所以免不了一直需要写下面这一段又长又无聊的代码: MemberCommand memberCommand = ... -
基于高频电压注入的永磁同步电机参数辨识方法_秦灿华.pdf
2020-10-14 19:25:53提出了一种基于曲线拟合的定子电阻计算方法, 研究了基于高频注 入的永磁电机电感参数辨识方法, 重点分析了不同注入信号选择对参数辨识精度的影响, 为注入信号的选择提供 了依据。 -
jfinal中拦截器(Interceptor)的参数注入
2018-02-26 16:05:12jfinal中拦截器(Interceptor)的参数注入jfinal中拦截器(Interceptor)的参数注入 -
springboot构造注入方法
2020-07-02 09:23:412:@Autowired修饰构造方法,参数于对象名一致,存在多个实现类时,使用@Qualifier在参数位置进行选择, 参考代码如下 package com.cy.pj.common.service; importorg.springframework.beans.factory.annotation.Autowired... -
argument-injector:JavaScript 的函数参数注入器。 它允许任何函数“绑定”到注入器对象,该对象劫持函数...
2021-06-15 21:43:54argument-injector是 JavaScript 的函数参数注入器。 它允许任何函数“绑定”到注入器对象,该对象劫持函数调用并在函数执行时注入参数。 用法 首先,您首先需要创建一个Injector的新实例: var Injector = ... -
13. SpringBoot Beans定义 + 参数注入 + 连接池
2018-01-20 14:08:11SpringBoot Beans定义 ...原有Spring框架,参数注入方法如下 常用的参数注入有注入基本值/对象 xml配置@Value、@Autowired、@Resource等 SpringBoot框架,定义Bean方法如下 利用@Configuration -
参数注入:使用java配置参数的方法,springboot的方法
2019-03-15 11:48:22文章目录把需要需要注入的参数,写到默认文件application.properties:为需要注入的数据专门写一个类:定义一个使用该数据的方法,实际上应该可以和上面的那个类合并,只不过一般数据封装和方法的封装使用不同的类:... -
SpringBoot 如何将配置文件中的参数注入到静态变量里
2020-08-02 18:53:20我们在启动项目的时候,往往需要给某个配置类注入默认的参数,如果开发环境和生产环境所需的参数都不一样,这时候,我们就需要利用配置文件给我们的配置类中注入参数了!一般我们在给变量注入时,是很容易的,这里就不多... -
SpringMVC-请求参数注入-基本类型参数注入源码探讨
2018-08-12 20:02:20这篇文章分享SringMVC处理请求流程中的一个环节,注入请求参数值,请求参数注入是DispatcherServlet接收并处理请求的流程其中的一个环节,最后选取最基础的参数解析器RequestParamMethodArgumentResolver,了解注入... -
Spring DI 依赖注入案例(带参数构造方法依赖注入、setter方法依赖注入、p名称空间注入)
2017-10-27 21:00:08Spring DI 依赖注入案例(带参数构造方法依赖注入和setter方法依赖注入) DI 依赖注入: 简单的说是指对象中的属性的值设置方式不再是调用setter方法进行传值,而是由配置实现经过框架传值。 DI操作可以采用... -
springboot的四种注入方式
2021-01-11 19:44:05springboot的属性注入 以注入dataSource为例 1.springboot默认读取的文件是放在resources目录下的名为application.properties或application.yml的文件 在application.properties中写入以下属性 jdbc.... -
关于spring ioc 注解注入参数的问题
2017-10-23 08:16:05问下大神们spring ioc 注解注入如何传参数?像@controller或@Service这种注解 如果其注解的类构造函数必须要传参的话 注解是否能将其实例化并放入容器中? -
springMVC方法参数值注入简要源码分析
2017-04-15 18:20:01懒惰是前进道路上最大的敌人,懒着懒着心就飘远了,荒废了许久的博客今天稍微重新拾起来,今天带了一篇springMVC源码分析之参数注入。 2. 步入正题 稍微了解springMVC的同学知道,在springMVC的应用中,它采用的中心... -
Spring三种方法的注解自动注入
2019-10-16 14:35:06@Autowired是Spring提供的自动注入的方法,该注解可以放在变量和方法上,在bean+返回值类型的注解中,@Autowired还可以放在参数前;@Autowired默认的注入是根据类型注入,如果相同类型的bean有多个,可以配合@... -
静态类在初始化时注入参数
2018-02-24 10:00:17让spring通过注解的方式在初始化的时候给参数注入1. 指定的类需要添加 @Component 让该类受spring管理2.@Autowired 需要注入的参数上加上该注解 让spring注入该参数3.@PostConstruct在初始化的方法上添加该注解,使... -
Spring boot中参数注入,@Value失效以及解决方案
2018-08-09 10:15:06项目中我们都要要尽量避免将参数直接写进程序里,这样一旦需要需要修改配置,我们可以只需要在配置文件里做修改,而不必在程序里找,这样可以避免很多错误,个人项目可能不会注意这一点,但是需要上线发布的项目,... -
Springmvc controller 自定义参数注入
2017-12-15 11:08:00* Controller 方法参数注入 * @param argumentResolvers */ @Override public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) { argumentResolvers.add(new ... -
@Bean+方法参数&有参构造器注入方式
2021-02-05 23:35:57@Autowired:自动注入,从容器中获取参数组件的值 这里我们首先叉开话题,先讨论一下使用 @Autowired 自动注入时,有可能会遇到以下问题: 当一个接口有 2 个不同实现时,使用 @Autowired 注解时会报 org.spring... -
请问springMVC controller方法中的参数是如何注入的?
2018-06-14 01:39:36@RequestMapping(value = "/login", method = RequestMethod.POST) public String login(String userid, String password, ...为什么请求过来进入这个方法后,可以直接使用这些类,请问这些类是怎么被注入的? -
@Bean 注解方法时,参数自主选择注入的bean
2019-07-02 18:59:25众所周知 当@Bean 注解使用在方法上面是,会被...方法参数,会通过spring自动注入 问题是: 方法参数的自动注入原理 @Component public class Test{ @Bean public MyBean mytest(){ retun new MyBean(); }... -
如何防止sql注入?防止sql注入方法介绍
2021-07-21 10:17:19SQL注入是较为普遍的互联网攻击方法,它并不是通过电脑操作系统的BUG来完成攻击,而是对于程序编写时的疏漏,利用SQL语句,达到无帐号登录,乃至改动数据库的目的。 SQL注入产生的原因便是:没经查验或是未充分检验... -
Spring - 依赖注入:构造函数注入、set方法注入和注解注入
2020-02-29 20:49:571.2 依赖注入(Dependency Injection) 依赖注入的数据类型: 基本类型和String类型 bean类型 ... 复杂类型/集合类型 ...当java类中写了自己的构造函数(如下面所给代码,构造函数有三个参数),则...