最近在帮企业做微信企业号的项目,关于用户查询工资条这一块的问题。只要之前有一个人查询了工资,我再查询的时候老是可以查询到他的工资信息。没有太想明白问题的原因啊。貌似我通过加上@SessionAttributes就没有发生查询到别人的工资的问题了。另外我也把 微信的 serviceimpl 不在作为静态的方法了,这样每次进入都重新实例化一下,防止引起并发问题。我是参考了如下的博文,受到了启发。感谢网友的知识共享。
Spring @SessionAttributes @ModelAttribute
package me.chanjar.weixin.cp.controller; import java.io.IOException; import java.io.UnsupportedEncodingException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import me.chanjar.weixin.common.bean.result.WxCpBatchUpdateUserResult; import me.chanjar.weixin.common.bean.result.callback; import me.chanjar.weixin.common.exception.WxErrorException; import me.chanjar.weixin.cp.api.Writelog; import me.chanjar.weixin.cp.api.WxCpInMemoryConfigStorage; import me.chanjar.weixin.cp.api.WxCpServiceImpl; import me.chanjar.weixin.cp.po.UserInfo; import me.chanjar.weixin.cp.service.UserInfoService; import me.chanjar.weixin.cp.util.crypto.MD5Util; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.SessionAttributes; /** * 单纯实现OAuth2验证,不使用注解及拦截器 * * * */ @Controller @SessionAttributes("Userid") public class SimpleOAuth2Controller { String accsstoken = ""; public static WxCpInMemoryConfigStorage config; public WxCpServiceImpl wxserv; public static callback callback; public static WxCpBatchUpdateUserResult wxcpbur; static { /*** 实例化static类 ***/ config = new WxCpInMemoryConfigStorage(); callback=new callback(); wxcpbur=new WxCpBatchUpdateUserResult(); callback.setUrl(""); callback.setEncodingaeskey(""); callback.setToken(""); wxcpbur.setcallback(callback); config.setCorpId(""); // 设置微信企业号的appid config.setCorpSecret(""); // 设置微信企业号的app // corpSecret config.setAgentId("2"); // 设置微信企业号应用ID=2,工资查询 config.setToken(""); // 设置微信企业号应用的token config.setAesKey(""); // 设置微信企业号应用的EncodingAESKey } /** * 拼接网页授权链接 此处步骤也可以用页面链接代替 * * @return * @throws IOException */ @RequestMapping(value = "/oauth2wx.do", method = RequestMethod.GET) public String Oauth2API(HttpServletRequest request) throws IOException { // 获取项目域名 String reqUrl = ""; Writelog.writetolog("获得项目域名:" + reqUrl); // 拼接微信回调地址 String backUrl = "http://" + reqUrl + "/oauth2me.do"; String redirect_uri = "";// 带上oauth2me.do返回 try { redirect_uri = java.net.URLEncoder.encode(backUrl, "utf-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } String oauth2Url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + "&redirect_uri=" + redirect_uri + "&response_type=code&scope=snsapi_base&state=sunlight#wechat_redirect"; Writelog.writetolog("返回oath2url:" + oauth2Url); return "redirect:" + oauth2Url; } /** * 授权回调请求处理 * * @return */ @RequestMapping(value = { "/oauth2me.do" }) public String oAuth2Url(@RequestParam String code, Model model) { //HttpSession session = request.getSession(); try { String Userid = "";// 用于获得用户的userid wxserv = new WxCpServiceImpl(); wxserv.setWxCpConfigStorage(config); String userinfo[] = wxserv.oauth2getUserInfo(code); Userid = userinfo[0];// 获得userid Writelog.writetolog("获得usercode:" + code); Writelog.writetolog("获得userid" + Userid); model.addAttribute("Userid",Userid); } catch (WxErrorException e) { // TODO Auto-generated catch block e.printStackTrace(); } return "/WEB-INF/user"; // 返回页面 } /***** * * @param service */ /*** * 业务逻辑 */ @Autowired private UserInfoService service; public void setService(UserInfoService service) { this.service = service; } }
Using @SessionAttributes to store model attributes in the HTTP session between requests
The type-level @SessionAttributes annotation declares session attributes used by a specific
handler. This will typically list the names of model attributes or types of model attributes which should
be transparently stored in the session or some conversational storage, serving as form-backing beans
between subsequent requests.
The following code snippet shows the usage of this annotation, specifying the model attribute name:
@Controller @RequestMapping("/editPet.do") @SessionAttributes("pet") //必须在class 外面申明 public class EditPetForm { // ... }
此外,@SessionAttributes 还可以通过属性类型指定要 session 化的 ModelMap 属性,如 @SessionAttributes(types = User.class),当然也可以指定多个类,如 @SessionAttributes(types = {User.class,Dept.class}),还可以联合使用属性名和属性类型指定:@SessionAttributes(types = {User.class,Dept.class},value={“attr1”,”attr2”})。通过value来指定多个属性的名称。
The @ModelAttribute annotation can be used on methods or on method arguments. This section
explains its usage on methods while the next section explains its usage on method arguments.
An @ModelAttribute on a method indicates the purpose of that method is to add one or more model
attributes. Such methods support the same argument types as @RequestMapping methods but cannot
be mapped directly to requests. Instead @ModelAttribute methods in a controller are invoked before
@RequestMapping methods, within the same controller. A couple of examples:
// Add one attribute // The return value of the method is added to the model under the name "account" // You can customize the name via @ModelAttribute("myAccount") @ModelAttribute public Account addAccount(@RequestParam String number) { return accountManager.findAccount(number); } // Add multiple attributes @ModelAttribute public void populateModel(@RequestParam String number, Model model) { model.addAttribute(accountManager.findAccount(number));//通过model的addAttribute添加属性。 // add more ... }
// 关于 @ModelAttribute在方法上面的作用-用于预先获取一些数据。
@ModelAttribute methods are used to populate the model with commonly needed attributes for example to fill a drop-down with states or with pet types, or to retrieve a command object like Account in order to use it to represent the data on an HTML form. The latter case is further discussed in the next section.
Note the two styles of @ModelAttribute methods. In the first, the method adds an attribute implicitly by returning it. In the second, the method accepts a Model and adds any number of model attributes to it. You can choose between the two styles depending on your needs.
A controller can have any number of @ModelAttribute methods. All such methods are invoked before @RequestMapping methods of the same controller.
@ModelAttribute methods can also be defined in an @ControllerAdvice-annotated class and such methods apply to many controllers. See the the section called “Advising controllers with the @ControllerAdvice annotation” section for more details.
[Tip]-小技巧,如果没有属性的名称没有显示定义,那么默认的显示名为 返回类型的首字母小写。
What happens when a model attribute name is not explicitly specified? In such cases a default name is assigned to the model attribute based on its type. For example if the method returns an object of type Account, the default name used is "account". You can change that through the value of the @ModelAttribute annotation. If adding attributes directly to the Model, use the appropriate overloaded addAttribute(..) method - i.e., with or without an attribute name.
The @ModelAttribute annotation can be used on @RequestMapping methods as well. In that case the return value of the @RequestMapping method is interpreted as a model attribute rather than as a view name. The view name is derived from view name conventions instead much like for methods returning void — see Section 17.13.3, “The View - RequestToViewNameTranslator”.
As explained in the previous section @ModelAttribute can be used on methods or on method arguments. This section explains its usage on method arguments.
An @ModelAttribute on a method argument indicates the argument should be retrieved from the model. If not present in the model, the argument should be instantiated first and then added to the model. Once present in the model, the argument's fields should be populated from all request parameters that have matching names. This is known as data binding in Spring MVC, a very useful mechanism that saves you from having to parse each form field individually.
@RequestMapping(value="/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST) public String processSubmit(@ModelAttribute Pet pet) { }// pet 属性是如何实例化的呢,请看如下说明。
Given the above example where can the Pet instance come from? There are several options:
It may already be in the model due to use of @SessionAttributes — see the section called “Using @SessionAttributes to store model attributes in the HTTP session between requests”.
It may already be in the model due to an @ModelAttribute method in the same controller — as explained in the previous section.
It may be retrieved based on a URI template variable and type converter (explained in more detail below).
It may be instantiated using its default constructor.
An @ModelAttribute method is a common way to to retrieve an attribute from the database, which may optionally be stored between requests through the use of @SessionAttributes. In some cases it may be convenient to retrieve the attribute by using an URI template variable and a type converter. Here is an example:
@RequestMapping(value="/accounts/{account}", method = RequestMethod.PUT) public String save(@ModelAttribute("account") Account account) { }
In this example the name of the model attribute (i.e. "account") matches the name of a URI template variable. If you register Converter<String, Account> that can turn the String account value into an Account instance, then the above example will work without the need for an @ModelAttribute method.
The next step is data binding. The WebDataBinder class matches request parameter names — including query string parameters and form fields — to model attribute fields by name. Matching fields are populated after type conversion (from String to the target field type) has been applied where necessary. Data binding and validation are covered in Chapter 7, Validation, Data Binding, and Type Conversion. Customizing the data binding process for a controller level is covered in the section called “Customizing WebDataBinder initialization”.
As a result of data binding there may be errors such as missing required fields or type conversion errors. To check for such errors add a BindingResult argument immediately following the @ModelAttribute argument:
@RequestMapping(value="/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST) public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result) { if (result.hasErrors()) { return "petForm"; } // ... }
With a BindingResult you can check if errors were found in which case it’s common to render the same form where the errors can be shown with the help of Spring’s <errors> form tag.
In addition to data binding you can also invoke validation using your own custom validator passing the same BindingResult that was used to record data binding errors. That allows for data binding and validation errors to be accumulated in one place and subsequently reported back to the user:
@RequestMapping(value="/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST) public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result) { new PetValidator().validate(pet, result); if (result.hasErrors()) { return "petForm"; } // ... }
Or you can have validation invoked automatically by adding the JSR-303 @Valid annotation:
@RequestMapping(value="/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST) public String processSubmit(@Valid @ModelAttribute("pet") Pet pet, BindingResult result) { if (result.hasErrors()) { return "petForm"; } // ... }