Please help me
I am new spring mvc user. In controller i call a singleton bean like that:
#RequestMapping(value = "/student", method = RequestMethod.GET)
public ModelAndView student(#RequestParam(required = false) String name) {
ApplicationContext context =
new ClassPathXmlApplicationContext(new String[] { "mvc-dispatcher-servlet.xml" });
Student student = (Student) context.getBean("student");
if (name != null && name.length() > 1) {
student.setName(name);
}
System.out.println("name:" + student.getName());
return new ModelAndView("result", "student", student);
}
The first time, i enter url in browser: http://localhost:8080/example/student?name=myname
The system print result like that: name:myname=> it's ok
The second time, i enter url in browser: http://localhost:8080/example/student
The system print result like that: name:null
Why? you said that a single bean instance be created for every request?
So the first time the name of student was set is "myname". The second time, when i request again, if a single bean instance was created, the name of student must be "myname", because it was set in first time request?But in my case, the second time request, seem that a new bean instance be created? So the name value is null
Thanks very much
Spring is doing what you have exactly asked for.
ApplicationContext context =
new ClassPathXmlApplicationContext(new String[] { "mvc-dispatcher-servlet.xml" });
Everytime you are making a request, you are creating a new instance of parent context. And hence you are not getting the bean with a singleton scope.
When you use ,
#Autowired private Student student
you are not creating context for every request. Hence your bean is getting created with a singleton scope.
Singleton instance means through out the applications only single instance of bean will be created by the spring containers like BeanFactory or ApplicationContext.
When ever you supplied your *.xml configuration file to BeanFactory or ApplicationContext containers then they will read specified bean declarations and starts instantiate them as singleton by default.
In your case you are specifying your *.xml to containers for each request to "/student" so, for every request of "/student" instances will be created.
study this link to configure your xml files to spring container for web applications
http://docs.spring.io/autorepo/docs/spring/3.2.x/spring-framework-reference/html/mvc.html
Related
I'm trying a new development method.
In mybatis3, I write mapper.java and mapper.xml usually.
I know, the sql statements is corresponded by sqlId(namespace+id).
I want to execute the sql statement like this :
SqlSession sqlSession = sessionFactory.openSession();
return sqlSession.selectList(sqlId, param);
but I get a error:
Cause: java.lang.IllegalArgumentException: Mapped Statements collection does not contain value for mapper.JinBoot.test
at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:150)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:141)
at cn.tianyustudio.jinboot.dao.BaseDao.select(BaseDao.java:20)
at cn.tianyustudio.jinboot.service.BaseService.select(BaseService.java:10)
at cn.tianyustudio.jinboot.controller.BaseController.test(BaseController.java:21)
here is my BaseDao.java
public class BaseDao {
private static SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
public static List<Map> select(String sqlId, Map param) {
try {
factoryBean.setDataSource(new DruidDataSource());
SqlSessionFactory sessionFactory = factoryBean.getObject();
SqlSession sqlSession = sessionFactory.openSession();
return sqlSession.selectList(sqlId, param);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
here is UserMapper.xml
<mapper namespace="mapper.JinBoot">
<select id="test" parameterType="hashMap" resultType="hashMap">
select * from user
</select>
</mapper>
the application.properties
mybatis.mapperLocations=classpath:mapper/*.xml
I start the project, the send a http request, after controller and service ,the param 'sqlId' in BaseDao is 'mapper.JinBoot.test' (see error info).
In method 'BaseDao.select', both the parameter and the result type is Map.
So I don't want to create UserMapper.java, I want try it.
How can I resolve it? What's the malpractice of this way?
This does not work because spring boot creates its own SqlSessionFactory. And the option in application.properties that specifies where mappers should be looked for is only set for that SqlSessionFactory. You are creating unrelated session factory in your DAO and it does not know where to load mappers definition.
If you want to make it work you need that you DAO is spring managed so that you can inject mybatis session factory into it and use it in select. This would also require that you convert select into non static method.
As I understand you want to have only one method in you base DAO class and use it in individual specific DAO classes. I would say it makes little sense. If the method returns Map there will be some place that actually maps this generic type to some application specific types. This would probably be in the child DAOs. So you still need to create the API of the child DAO with the signature that uses some input parameters and returns some domain objects. And that's exactly what you want to avoid by not creating mybatis mapper classes.
The thing is that you can treat your mytabis mappers as DAOs. That is you mappers would be your DAOs. And you don't need another layer. As I understand now you have two separate layers - DAO and mappers and you want to remove boilerplate code. I think it is better to remove DAO classes. They are real boilerplate and mybatis mapper can serve as DAO perfectly. You inject it directly to you service and service depends only on the mapper class. The logic of the mapping is in the mapper xml file. See also answer to this question Can Spring DAO be merged into Service layer?
In a call sent over a websocket connection in a Spring MVC 4 application, I can get a java.security.Principal object when adding this as a parameter in the method.
However, this basicly only has the username in it, and I need the (extended) UserDetails object created during logging in.
SecurityContextHolder.getContext().getAuthentication() returns null. As I understood because spring creates a special session for handling the websocket connections.
The Question: Is there a way to access the securitycontext from inside the websocket session/thread?
(Being able to access a session bean would suffice as well).
#AuthenticationPrincipal is specifically for REST/MVC methods.
However, you can get pretty close by adding an argument of Type java.security.Principle to your #MessageMapping annotated method and then taking the real UserDetails object from it as follows:
FooUserDetails currentUser = (FooUserDetails) ((Authentication) user).getPrincipal();
This works for me for Spring Boot 1.5.4
I suppose you have extended AbstractWebSocketHandler
the you could have principal name :
#Override
public void afterConnectionEstablished ( WebSocketSession session ) throws Exception {
logger.info ( "afterConnectionEstablished = " + session.getPrincipal () );
}
I've not tested it, but you should be able to access your user adding a parameter like
#AuthenticationPrincipal CustomUser customUser
to your #MessageMapping method.
You can find more here: http://docs.spring.io/spring-security/site/docs/4.0.x/reference/htmlsingle/#mvc-authentication-principal
I need to update my model attribute inside a resource method. The thing is that, after update it, I'd need to set this new modified bean in the model, in order to get the right timestamp to do the next operation when some action method be called.
I have my bean with
#SessionAttributes(ServletContextKeys.MY_BEAN)
and
#ModelAttribute(ServletContextKeys.MY_BEAN)
anotations, so I get my bean from the session in the resource method, but after update it, I've no idea how to access the model in the resource phase or any other possible workarround to solve this issue.
#ResourceMapping(ServletContextKeys.MY_RESOURCE_METHOD)
public final void updateDoc(){
MyBean myBean = getBeanFromSession();
MyBean myNewBean = myService.updateDocs(myBean); //This mehod will change the timestamp and return the updated bean
//Now I'd like to set the bean in the model. How could I access it?
model.addAttribute(ServletContextKeys.MY_BEAN, myNewBean);
}
I need to pass a bean object from MVC to webFlow. Currently, I am achieving it this way:
Storing my bean object as request attribute in controller.
Forwarding to flow.
Accessing the object from flowRequestContext on-start of my flow and setting it in flowScope.
#RequestMapping(value = "/ProcessUser", method=RequestMethod.POST)
public String processForm(LoginUser loginUser, HttpServletRequest request){
....
request.setAttribute("registrationDetails", registrationDetails);
return "forward:/chineseFlow"; //Call to flow
}
chineseFlow.xml
<on-start>
<evaluate expression="userDetailsService.getRegistrationDetails(flowRequestContext)" result="flowScope.registrationDetails"/>
</on-start>
UserDetailsService
public RegistrationDetails getRegistrationDetails(RequestContext requestContext){
HttpServletRequest httpRequest = (HttpServletRequest) requestContext.getExternalContext().getNativeRequest();
RegistrationDetails registrationDetails = (RegistrationDetails)httpRequest.getAttribute("registrationDetails");
return registrationDetails;
}
I don't want to pass multiple request parameters as input to my flow. Is this the correct way to pass the bean to SWF or is there any other better way to achieve the same?
There are not many options. Proper way would be to redesign your application so that whole process happens within the same flow, then you can store your values in flowscope to begin with. The only alternatives would be either a request attribute (which you are doing already), or session-scoped bean/session attribute. Out of these request attribute(s) is preferred as otherwise you will end up polluting your session scope, and introduce potential bugs that stem from leftover values in session scope.
I have a Spring MVC controller with an action that's called using AJAX.
#SessionAttributes({"userContext"})
public class Controller
{
...
#RequestMapping(value = "/my-url", method= { RequestMethods.POST })
public ModelAndView doSomething(#ModelAttribute("userContext") UserContext context,
SessionStatus sessionStatus)
{
BusinessObject obj = doSomeBusinessLogic(context.getUserName());
sessionStatus.setComplete();
ModelAndView mav = new ModelAndView("jsonView");
mav.addObject("someInt", obj.getId());
return mav;
}
}
When I run this action, I get the following exception:
net.sf.json.JSONException: There is a cycle in the hierarchy!
at t.sf.json.util.CycleDetectionStrategy$StrictCycleDetectionStrategy.handleRepeatedReferenceAsObject(CycleDetectionStrategy.java:97)
at net.sf.json.JSONObject._fromBean(JSONObject.java:833)
at net.sf.json.JSONObject.fromObject(JSONObject.java:168)
at org.springframework.web.servlet.view.json.writer.jsonlib.PropertyEditorRegistryValueProcessor.processObjectValue(PropertyEditorRegistryValueProcessor.java:127)
at net.sf.json.JSONObject._fromMap(JSONObject.java:1334)
Truncated. see log file for complete stacktrace
After doing some debugging I found out that Spring is placing the UserContext object onto the ModelAndView that I am returning. If I hard-code my user name and remove the context object from the method's parameters, the action runs successfully. Is there a way to configure Spring to omit the ModelAttribute-annotated parameters from the returned ModelAndView? As you can see, sessionStatus.setComplete() has no effect.
I've had similar problems in the past with #SessionAttributes. By declaring #SessionAttributes({"userContext"}) you're telling Spring that you want "userContext" to always be available in the model, and so Spring has no choice but to send your UserContext object out to the model, just in case you're going to be redirecting or doing something else which might end up at another Controller.
The "solution" (and I didn't like it much, but it worked) was to omit the #SessionAttributes annotation on the controller, add an HttpSession parameter to the necessary methods and "manually" manage what's in it.
I'm interested to see if there's a better way, because it seems #SessionAttributes has tremendous potential to tidy up controller-level code.
I registered a WebArgumentResolver to get to my session variable. This allowed me to keep this session variable out of the response while keeping my action unit testable.
Along with #ModelAttribute, pass #ModelMap as a method argument.
Based on business logic, error conditions -- if you do not need the attribute for certain scenarios, then remove it from the map.
public ModelAndView foo(#ModelAttribute("userContext") UserContext, #ModelMap map){
if(success){
return success.jsp
}
else{
map.remove("userContext");
return "error.jsp"
}
}
Not totally satisfied with having to pass the ModelMap as well, but I did not find any other easier way of doing it.
Cheers!!