MVC : Model View Controller
SpringMVC控制器层的框架,主要包含以下组件:
(1)前端控制器 DispatcherServlet(核心入口):配置在web.xml文件中的,拦截匹配的请求,把拦截下来的请求,
依据相应的 规则分发到目标Controller来处理,是配置spring MVC的第一步
作用:接收请求、响应结果 相当于转发器,有了DispatcherServlet 就减少了其它组件之间的耦合度。
(2)处理器映射器HandlerMapping(不需要程序员开发)
作用:根据请求的URL来查找Handler
(3)处理器适配器HandlerAdapter
注意:在编写Handler的时候要按照HandlerAdapter要求的规则去编写,这样适配器HandlerAdapter才可以正确的去执行Handler。
(4)后端控制器Handler(需要程序员开发)
(5)视图解析器 ViewResolver(不需要程序员开发)
作用:进行视图的解析 根据视图逻辑名解析成真正的视图(view)
(6)视图View(需要程序员开发jsp)
View是一个接口, 它的实现类支持不同的视图类型(jsp,freemarker,pdf等等)
SpringMVC的流程:
(1)用户发送请求至前端控制器DispatcherServlet(核心控制器:过滤请求);
(2) DispatcherServlet收到请求后,调用HandlerMapping处理器映射器,请求获取Handle;
(3)处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet;
(4)DispatcherServlet通过HandlerAdapter处理器适配器调用处理器;
(5)执行处理器(Handler,也叫后端控制器);
(6)Handler执行完成返回ModelAndView;
(7)HandlerAdapter将Handler执行结果ModelAndView返回给DispatcherServlet;
(8)DispatcherServlet将ModelAndView传给ViewResolver视图解析器进行解析;
(9)ViewResolver解析后返回具体View;
(10)DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)
(11)DispatcherServlet响应用户。
SpringMvc怎么和AJAX相互调用的:
通过Jackson框架就可以把Java里面的对象直接转化成Js可以识别的Json对象。具体步骤如下 :
(1)加入Jackson.jar
(2)在配置文件中配置json的映射
(3)在接受Ajax方法里面可以直接返回Object,List等,但方法前面要加上@ResponseBody注解。
(通过使用@ResponseBody注解, 可以自动将控制器方法的返回值转换成json字符串。)
1、springMVC和struts2的区别有哪些?
(1)springmvc的入口是一个servlet即前端控制器(DispatchServlet),而struts2入口是一个filter过虑器 (StrutsPrepareAndExecuteFilter)。
(2)springmvc是基于方法开发(一个url对应一个方法),请求参数传递到方法的形参,可以设计为单例或多例(建议单例),
struts2是 基于类开发,传递参数是通过类的属性,只能设计为多例。
(3)Struts采用值栈存储请求和响应的数据,通过OGNL存取数据,springmvc通过参数解析器是将request请求内容解析,并给方 法形参赋值,将数据和视图封装成ModelAndView对象,最后又将ModelAndView中的模型数据通过reques域传输到页面。Jsp 视图解析器默认使用jstl。
2、SpringMvc的核心入口类是什么,Struts1,Struts2的分别是什么:
答:SpringMvc的是DispatchServlet,
Struts1的是ActionServlet,
Struts2的是StrutsPrepareAndExecuteFilter。
3、SpringMvc的控制器是不是单例模式,如果是,有什么问题,怎么解决?
答:Java里有个API叫做ThreadLocal,spring单例模式下用它来切换不同线程之间的参数。用ThreadLocal是为了保证线程安全,实际上ThreadLoacal的key就是当前线程的Thread实例。单例模式下,spring把每个线程可能存在线程安全问题的参数值放进了ThreadLocal。这样虽然是一个实例在操作,但是不同线程下的数据互相之间都是隔离的,因为运行时创建和销毁的bean大大减少了,所以大多数场景下这种方式对内存资源的消耗较少,而且并发越高优势越明显。
单利模式因为大大节省了实例的创建和销毁,有利于提高性能,而ThreadLocal用来保证线程安全性。
单例模式是spring推荐的配置,它在高并发下能极大的节省资源,提高服务抗压能力。spring IOC的bean管理器是“绝对的线程安全”。
最佳实践:
1、不要在controller中定义成员变量。
2、万一必须要定义一个非静态成员变量时候,则通过注解@Scope("prototype"),将其设置为多例模式
springmvc成员方法也是共享的,为什么就不会出现问题呢?
java 里,每个线程都有自己独享的空间,也就是栈内存。线程在调用方法的时候,会创建一个栈帧。也就是说调用一个方法的时候,也就是一个栈帧的入栈过程,该方法执行完毕,栈帧也就出栈了。
换句话讲,成员方法对于每个线程事实上是私有的,而不是你表面看上去的那样是 "共享" 的。
那么为什么springmvc成员变量会出问题呢?
如你所知道的,每个新建对象都存放在堆中,每个持有该对象引用的线程,都可以访问到它(只要你有那个权限)。
这也就是说,成员变量对于每个线程,事实上是共享的。
栈、堆、方法区
JAVA的JVM的内存可分为5个区:
堆(heap)、栈(stack)和方法区(method)、程序计数器、本地方法栈()
堆区:
提供所有类实例和数组对象存储区域
jvm只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身
栈区:
每个线程包含一个栈区,栈中只保存基本数据类型的对象和自定义对象的引用(不是对象),对象都存放在堆区中
每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问。
方法区:
又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的class和static变量。
方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量。
运行时常量池都分配在 Java 虚拟机的方法区之中。
程序计数器:
程序计数器 在 运行时数据区 只占据非常小的内存空间,它用来存储下一个即将执行的字节码指令的地址
本地方法栈:
(本地方法栈)与Java Stacks(Java 栈)非常类似,它用于存储调用本地方法(C/C++)所涉及到的局部变量表、操作栈等信息。
4、SpringMvc中的控制器的注解一般用那个,有没有别的注解可以替代?
答:一般用@Conntroller注解,表示是控制层,不能用用别的注解代替。
5、 @RequestMapping注解用在类上面有什么作用?
答:是一个用来处理请求地址映射的注解,可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路 径。
6、怎么样把某个请求映射到特定的方法上面?
答:直接在方法上面加上注解@RequestMapping,并且在这个注解里面写上要拦截的路径。
7、如果在拦截请求中,我想拦截get方式提交的方法,怎么配置?
答:可以在@RequestMapping注解里面加上method=RequestMethod.GET。
8、怎么样在方法里面得到Request,或者Session?
答:直接在方法的形参中声明request,SpringMvc就自动把request对象传入。
18、如果想在拦截的方法里面得到从前台传入的参数,怎么得到?
答:直接在形参里面声明这个参数就可以,但必须名字和传过来的参数一样。
9、如果前台有很多个参数传入,并且这些参数都是一个对象的,那么怎么样快速得到这个对象?
答:直接在方法中声明这个对象,SpringMvc就自动会把属性赋值到这个对象里面。
10、SpringMvc中函数的返回值是什么?
答:返回值可以有很多类型,有String, ModelAndView,但一般用String比较好。
11、SpringMvc用什么对象从后台向前台传递数据的?
答:向request作用域存数据,通过ModelMap对象,可以在这个对象里面用put方法,把对象加到里面,前台就可以通过el表达式拿到。
向session作用域存入数据
12、SpringMvc中有个类把视图和数据都合并的一起的,叫什么?
答:叫ModelAndView。
13、怎么样把ModelMap里面的数据放入Session里面?
答:可以在类上面加上@SessionAttributes注解,里面包含的字符串就是要放入session里面的key。
@Controller
@SessionAttributes({“name”}) // 将model中为name命名属性存入session作用域(里的key)
Public class xxxController{
Public String test1(Model model){
Model.addAttribute(String name,Object value);
}
// 移除
Public String test2(SessionStatus sessionStatus){
sessionStatus.setComplete();// 移除通过model方式存入session作用域的命名属性
}
}
14、当一个方法向AJAX返回特殊对象,譬如Object,List等,需要做什么处理?
答:要加上@ResponseBody注解。
(通过使用@ResponseBody注解, 可以自动将控制器方法的返回值转换成json字符串。)
15、spring 事务管理有几种方式?有什么区别?
Spring支持如下两种方式的事务管理:
编程式事务管理:这意味着你可以通过编程的方式管理事务,这种方式带来了很大的灵活性,但很难维护。
声明式事务管理:这种方式意味着你可以将事务管理和业务代码分离。你只需要通过注解或者XML配置管理事务。
一般选择声明式事务管理,因为这种方式和应用程序的关联较少。
16、在SpringAOP中,在同一个service中,A方法有事务,B方法没事务,A方法调用B方法,会不会破坏A方法的事务,为什么?
1:内部调用时,被调用方法的事务声明将不起作用 2:换句话说,你在某个方法上声明它需要事务的时候,如果这个类还有其他开发者,你将不能保证这个方法真的会在事务环境中 3:再换句话说,Spring的事务传播策略在内部方法调用时将不起作用。不管你希望某个方法需要单独事务,是RequiresNew,还是要嵌套事务,要Nested,等等,统统不起作用。 4:不仅仅是事务通知,所有你自己利用Spring实现的AOP通知,都会受到同样限制。。。。
我们知道Spring中事务管理是使用AOP代理技术实现的,目标对象自身并没有事务管理功能的,而是通过代理对象
动态增强功能对事务进行增强的。因此当我们在同一个service类中通过一个方法调用另一个方法时,是通过目
标对象this对象调用的,目标对象自身并没有事务管理功能,因此事务不能生效。
解决方案:
1.使用xml配置方式暴漏代理对象,然后在service中通过代理对象AopContext.currentProxy()去调用方法。
xml配置
<aop:aspectj-autoproxy expose-proxy="true"/>
1
service调用
@Service
public class HelloWorldServiceImpl implements HelloWorldService {
@Autowired
private BlogRepository blogRepository;
@Override
public void a(BlogEntity blogEntity) throws Exception {
((HelloWorldService) AopContext.currentProxy()).b(blogEntity);
}
@Transactional(rollbackFor = Exception.class)
@Override
public void b(BlogEntity blogEntity) throws Exception {
blogRepository.save(blogEntity);
throw new Exception("错误");
}
}
2.在java配置类上添加注解@EnableAspectJAutoProxy(exposeProxy = true)方式暴漏代理对象,然后在service中通过代理对象AopContext.currentProxy()去调用方法。
java配置类
/**
* @Author: PanChao
* @Description:
* @Date: Created in 13:14 2018/9/27
*/
@Configuration
@EnableAspectJAutoProxy(exposeProxy = true)
public class AppConfig {
}
service调用
@Service
public class HelloWorldServiceImpl implements HelloWorldService {
@Autowired
private BlogRepository blogRepository;
@Override
public void a(BlogEntity blogEntity) throws Exception {
((HelloWorldService) AopContext.currentProxy()).b(blogEntity);
}
@Transactional(rollbackFor = Exception.class)
@Override
public void b(BlogEntity blogEntity) throws Exception {
blogRepository.save(blogEntity);
throw new Exception("错误");
}
}