MVC 主要任务:

封装请求参数到JavaBean
数据类型转换和验证
调用业务代码
返回数据数据
转向某个页面展示结果

MVC的理解?

MVC是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计思想, 强制性的把应用程序的输入、处理和输出分开。解耦和,任何的重定向都能解耦和

MVC中的模型、视图、控制器它们分别担负着不同的任务。

  1. 视图: 视图是用户看到并与之交互的界面。视图向用户显示相关的数据,并接受用户的输入。视图不进行任何业务逻辑处理。 —获取数据/显示数据
  2. 模型: 模型表示业务数据和业务处理。相当于JavaBean。一个模型能为多个视图提供数据。这提高了应用程序的重用性— 处理数据(model层对容器的依赖性越少越好,model层是多实例还是单例)
  3. 控制器: 当用户单击Web页面中的提交按钮时,控制器接受请求并调用相应的模型去处理请求。 然后根据处理的结果调用相应的视图来显示处理的结果。 —控制流程

MVC的处理过程:首先控制器接受用户的请求,调用(委托)相应的模型来进行业务处理,并返回数据给控制器。控制器调用相应的视图来显示处理的结果。并通过视图呈现给用户。

MVC 优点:(DRY/SRP(单一职责原则)/松耦合/可重用/可维护/便于开发)

  1. 分层有助于管理复杂的应用程序,可以在一个时间内专门关注一个方面。例如,可以在不依赖业务逻辑的情况下专注于视图设计。
  2. 让应用程序的测试更加容易。
  3. 也简化了分组开发。不同的开发人员可同时开发视图、控制器逻辑和业务逻辑。

最典型的MVC就是JSP + servlet + javabean的模式

JAVA五大框架整理 - 图1JAVA五大框架整理 - 图2

三层架构的理解

JAVA五大框架整理 - 图3
JAVA五大框架整理 - 图4
JAVA五大框架整理 - 图5

srtuts2

struts2的理解

Struts 2以WebWork为核心,采用拦截器的机制来处理用户的请求,这样的设计也使得业务逻辑控制器能够与ServletAPI完全脱离开
JAVA五大框架整理 - 图6
大致流程:
(1) 客户端(Client)向Action发用一个请求(Request)
(2) Container通过web.xml映射请求,并获得控制器(Controller)的名字
(3) 容器(Container)调用控制器(StrutsPrepareAndExecuteFilter)。在Struts2.1以前调用FilterDispatcher,Struts2.1以后调用StrutsPrepareAndExecuteFilter
(4) 控制器(Controller)通过ActionMapper获得Action的信息
(5) 控制器(Controller)调用ActionProxy
(6) ActionProxy读取struts.xml文件获取action和interceptor stack的信息。
(7) ActionProxy把request请求传递给ActionInvocation
(8) ActionInvocation根据配置文件加载相关的所有Interceptor拦截器,通过代理模式调用Action和interceptor
(9) 根据action的配置信息,产生result
(10) Result信息返回给ActionInvocation根据struts.xml中配置的result,决定进行下一步输出
(11) 产生一个HttpServletResponse响应
(12) 产生的响应行为发送给客服端。

详细流程:
1.启动服务器(tomcat),StrutsPrepareAndExecuteFilter的init方法执行将会自动加载配置文件
default.properties 在 struts2-core-2.3.7.jar 中 org.apache.struts2包里面(常量的默认值)
struts-default.xml 在 struts2-core-2.3.7.jar(Bean、313/273行18个默认拦截器、结果类型 )
struts-plugin.xml 在struts-Xxx-2.3.7.jar(在插件包中存在 ,配置插件信息 )struts-config-browser-plugin-2.3.7.jar里面有
struts.xml 该文件是web应用默认的struts配置文件 (实际开发中,通常写struts.xml )
struts.properties 该文件是Struts的默认配置文件 (配置常量 )
web.xml 该文件是Web应用的配置文件 (配置常量 )
2.请求经过一系列的过滤器(Filter),StrutsPrepareAndExecuteFilter被调用doFilter方法被执行
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
prepare.setEncodingAndLocale(request, response);//设置编码格式:utf-8,根据default.properties
prepare.createActionContext(request, response);//创建actionContext
prepare.assignDispatcherToThread();//整个的过程都在当前线程中执行
request = prepare.wrapRequest(request);//对request进行了包装
ActionMapping mapping = prepare.findActionMappin(request, response, true);//询问ActionMapper来决定这个请求是否需要调用某个Action。如果是.action:
dispatcher.serviceAction()

3.createAcitonContext(){
//利用容器创建值栈
ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();
//构建值栈的结构,把request,session,application等封装成一些map,再把这些map放入到大map中
stack.getContext().putAll(dispatcher.createContextMap(request, response, null, servletContext));
//把大map的引用指向了ActionContext中的Map context;
ctx = new ActionContext(stack.getContext());
//把整个actionContext放入到了当前线程中,因为actionContext中有valueStack,所以valueStack也在当前线程中,
//这样就保证了数据的安全性并且在一个线程范围内可以共享数据
ActionContext.setContext(ctx);
}
说明:
1、创建actionContext对象
2、创建ValueStack(实现类OnglValueStack)
3、把整个的actionContext放入到了ThreadLocal中
4.serviceAction(){
ValueStack stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
/*
因为在struts2容器中有太多的参数request,response,valueStack,session,application,paramters
所以struts2容器对当前的请求中用到所有的数据封装在了ActionContext中的map中
*/
extraContext.put(ActionContext.VALUE_STACK, valueStackFactory.createValueStack(stack));
String namespace = mapping.getNamespace();
String name = mapping.getName();
String method = mapping.getMethod();
//创建actionProxy
ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(
namespace, name, method, extraContext, true, false);
proxy.execute()方法
prepare.cleanupRequest(request);//把struts2过程中的数据全部清空了
}
5.在createActionProxy中执行了如下的内容:
重点:
执行了DefaultActionInvocation中的init方法
createAction(contextMap)调用ObjectFactory中buildAction 创建了action
stack.push(action); 把action放入到了对象的栈顶
contextMap.put(“action”, action);把action放入到map中
List interceptorList = new ArrayList(proxy.getConfig().getInterceptors());
interceptors = interceptorList.iterator();
获取所有的拦截器,并且返回了迭代器的形式

6.DefaultActionInvocation中的invoke方法
1、按照顺序的方式执行所有的拦截器
2、执行action中的方法
3、执行结果集
4、按照倒序的方式执行拦截器
invoke(){
//调用了拦截器
if (interceptors.hasNext()) {
final InterceptorMapping interceptor = (InterceptorMapping) interceptors.next();
String interceptorMsg = “interceptor: “ + interceptor.getName();
UtilTimerStack.push(interceptorMsg);
try {resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);
}
finally {UtilTimerStack.pop(interceptorMsg);
}
}
resultCode = invokeActionOnly();//执行action
//在执行结果集之前执行PreResultListener
if (preResultListeners != null) {
for (Object preResultListener : preResultListeners) {
PreResultListener listener = (PreResultListener) preResultListener;
String _profileKey = “preResultListener: “;
try {UtilTimerStack.push(_profileKey);
listener.beforeResult(this, resultCode);
}
finally {UtilTimerStack.pop(_profileKey);
}
}
}
executeResult();//执行结果集
}
总结:
在执行过滤器filter的过程中
1、创建ActionContext
1、创建actionContext
2、创建值栈
3、把actionContext加入到当前线程中
2、创建actionProxy
执行DefaultActionInvocation的init方法
1、创建action
2、把action放入到栈顶
3、创建所有的拦截器,并且产生拦截器的迭代器
3、执行Proxy的execute方法
proxy.execute()——>invocaction.invoke方法
1、执行所有的拦截器
2、执行当前请求的action
3、执行RreResultListener
4、执行结果集
4、清除数据 在finally中,这样即便出错,也能清除
struts2的缺点:
1、整个的核心流程写死了,想动态的添加内容那是不可能的
2、struts2的错误处理
action—>throws—->defaultActionInvocation.invoke—->throws—>
StrutsActionProxy.execute—->throws—>Dispatcher.serviceAction
try{

  1. }catch(ConfigurationException e){<br /> sendError(request, response, context, HttpServletResponse.SC_NOT_FOUND, e);<br /> }catch (Exception e) {<br /> sendError(request, response, context, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);<br /> }<br /> 这样的错误处理意味着:在action中的每一个方法中都得try catch,在架构中不是一个聪明的做法<br /> 3ajaxstruts2整合的时候,在ajax的请求过程中,如果后台是servlet,如果后台报错,servlet内部会设置状态码,<br /> 这个时候,会触发ajaxonreadystatechange函数,js的客户端就会得到该值知道:后台出错了<br /> 但是struts2框架是会跳转到错误模板页面,对于状态码并没有进行设置<br /> 所以在ajaxstruts2结合的过程中,因为struts2内部没有设置错误处理的状态码,<br /> 如果想要在客户端的error方法中输出内容,必须在action中通过手动设置<br />拦截器<br /> ExceptionMappingInterceptor在这个拦截器中可以处理,也可以不处理<br /> List<ExceptionMappingConfig> exceptionMappings = invocation.getProxy().getConfig().getExceptionMappings();<br /> 如果没有右边的配置,list将为null,如果为null,继续抛<br /> 如果不为null,则处理。<br /> <global-exception-mappings><br /> <exception-mapping exception="java.lang.Exception" result="errHandler" /><br /> </global-exception-mappings>

Servlet的优点

  1. 是mvc的基础,其他的框架比如struts1,struts2,webwork都是从servlet基础上发展过来的。所以掌握servlet是掌握mvc的关键。
  2. Servlet把最底层的api暴漏给程序员,使程序员更能清楚的了解mvc的各个特点。
  3. 程序员可以对servlet进行封装。Struts2就是从servlet中封装以后得到的结果。
  4. 市场上任何一个mvc的框架都是servlet发展过来的,所以要想学好struts2这个框架,了解servlet的运行机制很关键。

    Servlet的缺点

  5. 每写一个servlet在web.xml中都要做相应的配置。如果有多很servlet,会导致web.xml内容过于繁多。

  6. 这样的结构不利于分组开发。
  7. 在servlet中,doGet方法和doPost方法有HttpServletRequest和HttpServletResponse参数。这两个参数与容器相关,如果想在servlet中作单元测试,则必须初始化这两个参数。
  8. 如果一个servlet中有很多个方法,则必须采用传递参数的形式,分解到每一个方法中。

获取值栈的路径

JAVA五大框架整理 - 图7

JAVA五大框架整理 - 图8
说明:

  1. 值是一样的,说明只有一个对象
  2. 因为有一种是从request域中获取的,所以是一次请求

    值栈的内存总图

    JAVA五大框架整理 - 图9
    说明:
    从上图中可以看出valueStack总共分为两个部分:
    对象栈:root
    Map栈:_values

    值栈放值取值

    ActionContext.getContext().getValueStack().push(Object) ,
    将对象注入到栈顶对象栈 JSP页面通过 javabean 属性名 获取
    ActionContext.getContext().getValueStack().set(k ,v ) ,
    把一个对象变成map存入到对象栈中 JSP页面 “key” 获得
    ActionContext.getContext().put(k ,v )
    把一个数据直接放入到map栈中 , JSP页面“#key” 获取 #相当于ActionContext.getContext()

结果集

在struts-default.xml文件中,如下面所示:











    说明:<br />从上述可以看出总共10种类型<br />默认类型为ServletDispatcherResult即转发。<br />结果类型可以是这10种结果类型的任意一种。  

Struts1-PK-Struts2
Action 类:
• Struts1要求Action类继承一个抽象基类。Struts1的一个普遍问题是使用抽象类编程而不是接口。
• Struts 2 Action类可以实现一个Action接口,也可实现其他接口,使可选和定制的服务成为可能。Struts2提供一个ActionSupport基类去 实现 常用的接口。Action接口不是必须的,任何有execute标识的POJO对象都可以用作Struts2的Action对象。

线程模式:
• Struts1 Action是单例模式并且必须是线程安全的,因为仅有Action的一个实例来处理所有的请求。单例策略限制了Struts1 Action能作的事,并且要在开发时特别小心。Action资源必须是线程安全的或同步的。
• Struts2 Action对象为每一个请求产生一个实例,因此没有线程安全问题。(实际上,servlet容器给每个请求产生许多可丢弃的对象,并且不会导致性能和垃圾回收问题)

Servlet 依赖:
• Struts1 Action 依赖于Servlet API ,因为当一个Action被调用时HttpServletRequest 和 HttpServletResponse 被传递给execute方法。
• Struts 2 Action不依赖于容器,允许Action脱离容器单独被测试。如果需要,Struts2 Action仍然可以访问初始的request和response。但是,其他的元素减少或者消除了直接访问HttpServetRequest 和 HttpServletResponse的必要性。

可测性:
• 测试Struts1 Action的一个主要问题是execute方法暴露了servlet API(这使得测试要依赖于容器)。一个第三方扩展--Struts TestCase--提供了一套Struts1的模拟对象(来进行测试)。
• Struts 2 Action可以通过初始化、设置属性、调用方法来测试,“依赖注入”支持也使测试更容易。

捕获输入:
• Struts1 使用ActionForm对象捕获输入。所有的ActionForm必须继承一个基类。因为其他JavaBean不能用作ActionForm,开发者经 常创建多余的类捕获输入。动态Bean(DynaBeans)可以作为创建传统ActionForm的选择,但是,开发者可能是在重新描述(创建)已经存 在的JavaBean(仍然会导致有冗余的javabean)。
• Struts 2直接使用Action属性作为输入属性,消除了对第二个输入对象的需求。输入属性可能是有自己(子)属性的rich对象类型。Action属性能够通过 web页面上的taglibs访问。Struts2也支持ActionForm模式。rich对象类型,包括业务对象,能够用作输入/输出对象。这种 ModelDriven 特性简化了taglib对POJO输入对象的引用。

表达式语言:
• Struts1 整合了JSTL,因此使用JSTL EL。这种EL有基本对象图遍历,但是对集合和索引属性的支持很弱。
• Struts2可以使用JSTL,但是也支持一个更强大和灵活的表达式语言--”Object Graph Notation Language” (OGNL).

绑定值到页面(view):
• Struts 1使用标准JSP机制把对象绑定到页面中来访问。
• Struts 2 使用 “ValueStack”技术,使taglib能够访问值而不需要把你的页面(view)和对象绑定起来。ValueStack策略允许通过一系列名称相同但类型不同的属性重用页面(view)。

类型转换:
• Struts 1 ActionForm 属性通常都是String类型。Struts1使用Commons-Beanutils进行类型转换。每个类一个转换器,对每一个实例来说是不可配置的。
• Struts2 使用OGNL进行类型转换。提供基本和常用对象的转换器。

校验:
• Struts 1支持在ActionForm的validate方法中手动校验,或者通过Commons Validator的扩展来校验。同一个类可以有不同的校验内容,但不能校验子对象。
• Struts2支持通过validate方法和XWork校验框架来进行校验。XWork校验框架使用为属性类类型定义的校验和内容校验,来支持chain校验子属性

Action执行的控制:
• Struts1支持每一个模块有单独的Request Processors(生命周期),但是模块中的所有Action必须共享相同的生命周期。
• Struts2支持通过拦截器堆栈(Interceptor Stacks)为每一个Action创建不同的生命周期。堆栈能够根据需要和不同的Action一起使用。

拦截器
在AOP(Aspect-Oriented Programming)中用于在某个方法或字段被访问之前,进行拦截然后在之前或之后加入某些操作。拦截器是AOP的一种实现策略。

1、struts2在web.xml配置的Filter叫什么?

org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter<br />    负责初始化整个Struts框架并且处理所有的请求<br />Struts2.1以前调用FilterDispatcher

2、struts2的Action有几种编写方式?

第一种 创建类,这个类不继承任何类,不实现任何的接口<br />    第二种 创建类,实现接口 Action接口<br />    第三种 创建类,继承类 ActionSupport类,ActionSupport类是Action接口的实现类

3、能否在struts2的Action定义多个业务方法?如何做到不同的请求访问Action的不同方法?

1method=...<br />    2使用通配符方式进行操作 ,使用符号 * 星号代表任意内容<br />    3访问Action中指定方法,不进行配置 <br />       1) 在工程中使用 动态方法调用 ,必须保证 struts.enable.DynamicMethodInvocation = true 常量值 为true <br />       2) 在action的访问路径 中 使用 "!方法名" 

4、xml进行struts2请求参数校验时,指定方法的校验和所有方法校验文件命名规则是什么?

对指定的方法校验<br />        1针对动作类中的指定方法进行校验,使用@SkipValidation注解<br />        2格式  Action类名-ActionName(<action>元素name属性)-validation.xml <br />            例如 : 校验AddCustomerAction中execute方法  配置 <action name="addcustomer" .../> <br />            校验文件名字:<br />            AddCusotmerAction-addcustomer-validation.xml<br />    所有方法校验<br />        在Action所在包 编写 Action类名-validation.xml 对Action所有业务方法进行校验

5、struts2的Action中如何使用ServletAPI?

1、 在Action 中解耦合方式 间接访问 Servlet API  --------- 使用 ActionContext 对象<br />        actionContext = ActionContext.getContext();<br />        1) actionContext.getParameters(); 获得所有请求参数Map集合 <br />        2) actionContext.put("company", "传智播客"); / actionContext.get("company") 对request范围存取数据 <br />        3) actionContext.getSession(); 获得session数据Map,对Session范围存取数据<br />        4) actionContext.getApplication(); 获得ServletContext数据Map,对应用访问存取数据 <br />    2、 使用接口注入的方式,操作Servlet API (耦合)<br />        ServletContextAware : 注入ServletContext对象<br />        ServletRequestAware :注入 request对象<br />        ServletResponseAware : 注入response对象<br />    3、 在Action中直接通过 ServletActionContext 获得Servlet API<br />        ServletActionContext.getRequest() : 获得request对象 (session)<br />        ServletActionContext.getResponse() : 获得response 对象<br />        ServletActionContext.getServletContext() : 获得ServletContext对象

6、struts2中有哪些常用结果类型?

1) dispatcher :Action 转发给 JSP<br />    2)  chain :Action转发到另一个Action (同一次请求)<br />    3) redirect : Action重定向到 JSP<br />    4) redirectAction :Action重定向到另一个Action <br />    stream:下载用的(文件上传和下载时再议)<br />    plainText:以纯文本的形式展现内容<br />    

7、struts2中修改常量的方式?

1) struts2 默认常量 在 default.properties 中配置 <br />    2) 开发者自定义常量 <br />        struts.xml (要求)<br />                格式 : <constant name="struts.devMode" value="true" /><br />        struts.properties (要求)<br />                格式 : struts.devMode = true<br />        web.xml <br />                格式 : <br />                <filter><br />                    <filter-name>struts2</filter-name><br />                    <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class><br />                    <init-param><br />                        <param-name>struts.devMode</param-name><br />                        <param-value>true</param-value><br />                    </init-param><br />                </filter>

    <br />        

1、struts2中全局国际化的配置方式?

创建一系列的资源文件 ,命名规范 :基本名称_小写语言_大写的国家.properties<br />        messages_zh_CN.properties <br />        messages_en_US.properties <br />    配置全局国际化<br />        使用struts.xml里面常量进行配置<br />            常量名称:struts.custom.i18n.resources<br />            常量的值:资源文件的基本名称<br />            如果资源文件在src下面,直接写基本名称<br />            如果资源文件在包下面,添加包路径  比如  cn/itcast/messages

2、addFieldError、addActionError 有何区别?

    都是com.opensymphony.xwork2.ActionSupport类下的方法.<br />    addActionError (String  anErrorMessage)<br />        添加一个Action级别的错误消息到Action<br />            anErrorMessage: 错误消息,被存放在List列表中<br />        显示消息的标签是(如放在jsp页面中):<br />            <s:actionerror />  显示全部的 Action级别的错误消息,可以加CSS代码<br />        <br />    addFieldError (String  fieldName, String  errorMessage)<br />        给一个字段(属性) 添加错误消息<br />            fieldName: 字段(属性)名<br />            errorMessage: 错误消息,被存放在一个Map<key, value>中(其中key存放的是fieldName,value存放的是errorMessage)。 <br />        显示消息的标签是(如放在jsp页面中):<br />            <s:fielderror />   显示全部的错误消息(用addFieldError方法添加的 )<br />        <br />    ActionSupport类还有一个 public boolean hasErrors () 方法,  其实他内部实现是这样的:<br />        (hasActionErrors() || hasFieldErrors())  , 分别检查有无Action级别的错误信息, 有无Fidld级别的错误的信息.<br />        只要一个为真, 就跳回input 实图, 并显示错误信息(如果你写了相应的标签)<br />        <br />        

3、简述struts2中自定义拦截器实现步骤?

第一步 创建类 ,继承AbstractInterceptor类或继承MethodFilterInteceptor类<br />    第二步 重写AbstractInterceptor类里面 intercept方法,在这个方法中写逻辑<br />            最后执行下一个操作return invocation.invoke();<br />    第三步 注册拦截器<br />            在要拦截的action所在的package里面声明拦截器<br />            在要拦截的action里面使用拦截器<br />            如果使用自定义的拦截器,默认的拦截器不会执行的,手动使用默认的拦截器<br />            <package name="demo2" namespace="/" extends="struts-default"><br />                <interceptors><br />                    <interceptor name="pre" class="cn.itcast.demo2.Invacaton"></interceptor><br />                </interceptors><br />                <action name="demo2" class="cn.itcast.demo2.DemoAction" ><br />                    <result>/index.jsp</result>    <br />                    <result name="login">/demo100.jsp</result><br />                    <interceptor-ref name="pre"></interceptor-ref><br />                    <interceptor-ref name="defaultStack"></interceptor-ref><br />                </action><br />            </package><br />            

4、简述struts2 的拦截器执行原理?

1.struts2中的拦截器的实现原理是AOP思想.<br />    2.struts2中的拦截器采用的是责任链模式<br />    <br />    当Action请求到来的时候,会由系统的代理生成一个Action的代理对象,<br />    由这个代理对象调用Action的execute()或指定的方法前,<br />    在struts.xml中查找与该Action对应的拦截器。<br />    如果有对应的拦截器,就在Action的方法执行前(后)调用这些拦截器;<br />    如果拦截器堆栈中还有其他的Interceptor,那么invocation.invoke()将调用堆栈中下一个Interceptor的执行。<br />    如果拦截器堆栈中只有Action了,那么invocation.invoke()将调用Action执行。<br />    <br />    一个有序链表,通过递归调用,变成了一个堆栈执行过程,<br />    将一段有序执行的代码变成了2段执行顺序完全相反的代码过程,<br />    从而巧妙地实现了AOP。这也就成为了Struts2的Action层的AOP基础<br />    <br />    2个非常重要的推论:<br />        1. 如果在拦截器中,我们不使用invocation.invoke()来完成堆栈中下一个元素的调用,<br />        而是直接返回一个字符串作为执行结果,那么整个执行将被中止。<br />        2. 我们可以以invocation.invoke()为界,将拦截器中的代码分成2个部分,<br />        在invocation.invoke()之前的代码,将会在Action之前被依次执行,<br />        而在invocation.invoke()之后的代码,将会在Action之后被逆序执行。<br />        由此,我们就可以通过invocation.invoke()作为Action代码真正的拦截点,从而实现AOP

5、使用struts2如何实现多文件上传?

1第一步 上传表单页面,满足三个要求,提交到action里面,要求:多个文件上传项name属性值必须要一样<br />    2创建action,在action实现多文件的上传,在action中使用数组形式得到多个文件的信息<br />        private File[] uploadImages;//得到上传的文件<br />        private String[] uploadImagesContentType;//得到文件的类型<br />        private String[] uploadImagesFileName;//得到文件的名称<br />    3遍历数组,得到每一个文件的信息,一个一个上传到服务器中<br />        if(uploadImages!=null&&uploadImages.length>0){<br />           for(int i=0;i<uploadImages.length;i++){<br />                File destFile = new File(realpath,uploadImageFileNames[i]);<br />                FileUtils.copyFile(uploadImages[i], destFile);<br />           }<br />        }<br />        <br />        

1、什么是值栈?

贯穿整个 Action 的生命周期(每个 Action 类的对象实例都拥有一个ValueStack 对象).<br />    相当于一个数据的中转站. 在其中保存当前 Action 对象和其他相关对象<br />    <br />    ValueStack 是 struts2 提供一个接口,实现类 OgnlValueStack ---- 值栈对象 (OGNL是从值栈中获取数据的 )<br />    每个Action实例都有一个ValueStack对象 (一个请求 对应 一个ValueStack对象 )<br />    在其中保存当前Action 对象和其他相关对象 (值栈中 是有Action 引用的 )<br />    Struts 框架把 ValueStack 对象保存在名为 “struts.valueStack” 的请求属性中,request中 (值栈对象 是 request一个属性)

2、值栈的内部结构?

 值栈由两部分组成 <br />        root :compoundRoot其实就是一个ArrayList.<br />        context    :OgnlContext其实就是一个Map.context中有root的引用,request/session/application/parameters/attr对象引用.<br />        ValueStack中 存在root属性 (CompoundRoot) 、 context 属性 (OgnlContext )<br />            * CompoundRoot 就是ArrayList<br />            * OgnlContext 就是 Map<br />        context 对应Map 引入 root对象 <br />            * context中还存在 request、 session、application、 attr、 parameters 对象引用 <br />            * OGNL表达式,访问root中数据时 不需要 #, 访问 request、 session、application、 attr、 parameters 对象数据 必须写 # <br />            * 操作值栈 默认指 操作 root 元素 <br />            

3、值栈ValueStack 和 ActionContext关系? 

每一个Action都有一个属于自己的ValueStack对象.ActionContext是Action的上下文.从ActionContext中获取到ValueStack值栈对象.<br />    值栈对象是请求时创建的 <br />    doFilter中 prepare.createActionContext(request, response); <br />        * 创建ActionContext 对象过程中,创建 值栈对象ValueStack <br />        * ActionContext对象 对 ValueStack对象 有引用的 (在程序中 通过 ActionContext 获得 值栈对象 ) <br />    Dispatcher类 serviceAction 方法中 将值栈对象保存到 request范围<br />        request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());<br />    

4、如何获得值栈对象?

获得值栈对象 有两种方法<br />    ValueStack valueStack = (ValueStack) ServletActionContext.getRequest().getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);<br />    ValueStack valueStack2 = ActionContext.getContext().getValueStack();<br />    

5、向值栈保存数据?

1// 将数据保存root的索引0位置,放置到第一个元素 ArrayList add(0,element);<br />        valueStack.push("itcast");<br />    2// 在值栈创建参数map, 将数据保存到map中<br />        valueStack.set("company", "传智播客");<br />      在jsp中 通过 <s:debug /> 查看值栈的内容 <br />    3Action这个类默认就在栈中!!!如果Action有一个属性必然会在值栈中.<br />    操作的时候只需要在Action中提供一个属性.并且对这个属性提供get方法就可以在页面中获得。<br />    

6、在jsp中获取值栈内容?

访问root中数据 不需要#<br />    访问 其它对象数据 加 #<br />    通过下标获取root中对象<br />   <s:property value="[0].top"/> //取值栈顶对象<br />    直接在root中查找对象属性 (自上而下自动查找)<br />    valueStack:<s:property value="username"/><br />    (1)对象保存到值栈<br />    <s:property value="user.username"/><br />    (2)  集合保存到值栈<br />    <s:property value="list[0].username"/><br />    在OgnlContext中获取数据<br />    request:<s:property value="#request.username"/><br />    session:<s:property value="#session.username"/><br />    application:<s:property value="#application.username"/><br />    attr:<s:property value="#attr.username"/><br />    parameters:<s:property value="#parameters.cid"/>

7、EL为什么能访问值栈中的数据?

StrutsPreparedAndExecuteFilter的doFilter代码中 <br />request = prepare.wrapRequest(request);     <br />    * 对Request对象进行了包装 ,StrutsRequestWrapper <br />    * 重写request的 getAttribute <br />        Object attribute = super.getAttribute(s);<br />        if (attribute == null) {<br />           attribute = stack.findValue(s);<br />        }<br />      访问request范围的数据时,如果数据找不到,去值栈中找 <br />    request对象 具备访问值栈数据的能力 (查找root的数据)

Hibernate

一级缓存

1、概念<br />        一级缓存是session级别的缓存,是一次会话的缓存<br />        一级缓存存放的是私有数据(因为session存放在ThreadLocal中,这样就保证了session中数据存放的安全性的问题)<br />    2、生命周期<br />        和session的生命周期是一致的<br />    3、关于操作<br />        利用sesion.get,load,save,update,evict,clear等方法都可以操作session缓存<br />        可以利用两种方式来验证是否操作了一级缓存<br />            1、利用session.get,load方法可以得到数据,看是否发出了SQL语句<br />            2、利用session.getStatistics()方法可以获得统计一级缓存的类SessionStatistics<br />                getCollectionCount   访问一级缓存中集合缓存的次数<br />                getEntityCount         访问一级缓存中对象的次数<br />            <br />    4、session缓存的意义:session.flush方法<br />        1、场景:<br />            现在有班级和学生这两个类,有这样一个需求:<br />                1、根据班级的ID号查询该班级<br />                2、遍历该班级中的每一个学生,把每一个学生的成绩进行修改<br />        2、jdbc解决方案<br />            发出这样的SQL语句<br />                1、查询班级和学生的语句<br />                2、针对每一个不同的学生发出update语句<br />            说明:和数据库交互的次数太多,所以性能不高<br />        3、用hibernate解决<br />            1、利用session.get方法把该班级查询出来                    发出sql语句<br />            2、利用classes.getStudents()方法把所有的学生查询出来      发出sql语句<br />            3、遍历所有的学生对象,把每一个学生对象的成绩属性进行修改<br />            4、提交事务<br />            说明:通过这个例子可以看出,在提交事务的时候,执行了session.flush方法,把所有的学生同时发出update语句,把变化的数据更新到了数据库中<br />                整个数据修改的过程和数据库只交互了一次<br />        4、意义:<br />            hibernate可以利用session的一级缓存,在执行保存对象和修改对象或者删除对象操作的时候,先把对象保存在一级缓存中(这个时候并没有发出SQL语句)<br />            当进行事务提交的事务统一发出SQL语句,这样做,和数据库交互的次数最少,效率最高<br />    

二级缓存

1、二级缓存的作用<br />        1、因为二级缓存是公开的缓存,是进程级别的缓存,所以二级缓存存放公开的数据,私有的数据是不能存放在这里的<br />        2、当把一个数据放入到二级缓存中以后,只要sessionFactory没有关闭,二级缓存中的数据就能够存在<br />        3、hibernate本身对于二级缓存并没有实现<br />    <br />    3、二级缓存的读写策略<br />        在配置文件中,usage为读写策略,一般情况下会有两个值:read-only/read-write<br />            read-only<br />                允许把一个对象存放到二级缓存中,但是只要存入了二级缓存中,不能修改<br />            read-write<br />                允许把一个对象存放到二级缓存中,并且能修改二级缓存中的数据,所以一般不选这个,因为二级缓存中的数据一般不修改<br />    4、二级缓存和一级缓存的交互<br />        1、当执行session.update方法,并且把数据提交以后,这个时候,如果操作的对象的二级缓存开放,而且没有做处理,会引起一级缓存和<br />            二级缓存的交互。<br />            注:程序员可以通过session.update方法,事务提交把二级缓存中的数据改变<br />        2、如果把二级缓存的读写策略改为read-write,也可以通过其他的手段不修改或者修改二级缓存中的内容<br />        3、交互<br />            1、二级缓存和一级缓存的交互是通过CacheModel这个类的设置来限定的<br />            2、二级缓存和一级缓存的交互是通过session来实现的<br />            3、例子:<br />                1、session.setCacheMode(CacheMode.GET);<br />                    如果这样设置,说明可以从二级缓存中取数据,但是不能存放数据<br />                2、session.setCacheMode(CacheMode.IGNORE);<br />                    如果这样设置,说明session既不从二级缓存中提取数据,也不把数据存在二级缓存中<br />                3、session.setCacheMode(CacheMode.PUT);<br />                    如果这样设置,说明session可以把对象放入到二级缓存中,但是并不从二级缓存中提取对象<br />         说明:我们可以通过CacheMode的设置来控制二级缓存和一级缓存的交互<br />    5、二级缓存的维护<br />        1、通过sessionFactory.getCache()该方法可以得到二级缓存<br />        2、API<br />            containsEntity(entityClass, identifier)  判断一个bean对象是否存在二级缓存中<br />            evictEntity(entityClass, identifier)     从二级缓存中清空一个对象<br />    6、二级缓存对于大量数据的处理<br />        1、场景:如果二级缓存存放的数据特别多的时候,肯定也是非常消耗内存的<br />        2、解决方案:<br />            可以把一部分数据存在磁盘上<br />        3、步骤:<br />            1、在src下创建一个文件ehcache.xml<br />                <diskStore path="C:\\TEMP1"/>  如果把数据存在磁盘上,指向的路径<br />                <Cache<br />                    name="com.itheima.hibernate.domain.Classes"   //作用的对象是Classes类<br />                    maxElementsInMemory="5"   //在内存中最多能存的对象数量 <br />                    eternal="false"<br />                    timeToIdleSeconds="120"<br />                    timeToLiveSeconds="120"<br />                    overflowToDisk="true"    //是否在磁盘上存储<br />                    maxElementsOnDisk="10000000"<br />                    diskPersistent="false"<br />                    diskExpiryThreadIntervalSeconds="120"<br />                    memoryStoreEvictionPolicy="LRU"<br />                    /><br />            2、在执行下面的代码的时候<br />                List<Classes> classesList = session.createQuery("from Classes").list();<br />                System.out.println(sessionFactory.getStatistics().getSecondLevelCachePutCount());<br />               如果查询出来的数据的个数大于5个,则会把数据存到指定的文件夹里面

延迟加载

set上lazy默认值为true
many-to-one上为 proxy
1、概念
当需要数据的时候,才加载。
2、当遇到这样的场景如何提供性能呢?
当访问的数据特别大的时候,不需要该数据很早就加载到内存中,而是希望用到的时候加载
这样的情况用懒加载解决这个问题。
3、研究的是
什么时候发出SQL语句加载数据
4、分类
1、类的延迟加载
1、当执行session.load方法的时候,发生类的延迟加载
2、在相对应的映射文件中class元素中lazy属性设置为true(默认值为true)
2、集合的延迟加载
1、先加载一的一方,再加载多的一方的时候
例如:先加载一个班级,再得到一个班级的所有的学生
2、集合的延迟加载有三个值
true
当迭代集合的时候发出SQL语句
false
当加载班级的时候,发出加载学生集合的SQL语句
extra
更进一步的延迟加载,在求集合的min,max,avg,sum的时候用

    3、单端关联的延迟加载<br />            1、根据多的一方加载一的一方<br />                例子:知道一个学生,得到该学生所在的班级<br />            2、值为<br />                false<br />                no-proxy(true)  默认值<br />                proxy<br />            3、说明<br />                这个一般保持默认就可以了<br />    5、延伸<br />        在企业级应用的时候,解决懒加载的问题有可能会引起异常:<br />            当数据还没有加载出来的时候,session已经关闭了,这个时候会引起no session的异常,<br />            解决这个异常的办法:在session关闭之前把数据提取出来

抓取策略

fetch的默认值是select
1、研究的对象是集合
2、抓取策略研究是怎么样发出SQL语句获取Set集合的数据
3、有三个值
join
1、左外连接,一次性把一端的数据和多端的数据全部提取出来
2、当需求分析含有子查询的时候,join不再适用
3、集合的延迟加载不再适用
select
1、默认的查询方式
2、当集合在迭代的时候才要发出sql语句
3、会发出n+1条SQL语句(有时候)
subselect
1、如果需求分析中含有子查询,用这样的查询效率比较高
2、集合什么时候发出SQL语句,要看集合的延迟加载

3、延迟加载和抓取策略的结合
1、研究的是集合
2、情况
1、如果fetch为join,则延迟加载失去作用,一次性加载数据,并且是在加载一的一方的时候,直接加载多的一方
2、如果fetch为”select/subselect”,当set集合的lazy为true时,迭代的时候发出SQL语句

持久化类:

  Hibernate采用普通、传统的Java对象(POJO),作为持久化类persistent object,与数据表进行映射
  编写规则:

  • 提供一个无参数 public访问控制符的构造器
  • 提供一个标识属性,映射数据表主键字段
  • 所有属性提供public访问控制符的 set get 方法
  • 标识属性应尽量使用基本数据类型的包装类型
  • 不要用final修饰 (将无法生成代理对象。进行优化)

    Hibernate运行过程如下:

    1、应用程序先调用Configuration类读取Hibernate配置文件及映射文件中的信息,一个Configeration实例代表java类到sql数据库映射的集合。
    2、并用这些信息生成一个SessionFactory对象,SessionFactory是实例代表一个数据库的存储源
    3、然后从SessionFactory对象生成一个Session对象,
    4、并用Session对象生成Transaction对象;
    A、可通过Session对象的get(),load(),save(),update(),delete()和saveOrUpdate()等方法对PO进行加载、保存、更新、删除、等操作;
    B、在查询的情况下,可通过Session对象生成一个Query对象,然后利用Query对象执行查询操作;如果没有异常,Transaction对象将提交这些操作到数据库中。
    5.关闭Session
    6.关闭SesstionFactory

为什么要用:

  • 对JDBC访问数据库的代码做了封装,大大简化了数据访问层繁琐的重复性代码。
  • Hibernate是一个基于JDBC的主流持久化框架,是一个优秀的ORM实现。他很大程度的简化DAO层的编码工作
  • hibernate使用Java反射机制,而不是字节码增强程序来实现透明性。
  • hibernate的性能非常好,因为它是个轻量级框架。映射的灵活性很出色。它支持各种关系数据库,从一对一到多对多的各种复杂关系

inverse

在映射一对多的双向关联关系时,应该在one方把inverse属性设为true,这可以提高性能。在一的一方设值inverse为TRUE表明一的一方不维护其关系,这样就会发出一条update语句,这样效率也就提高了

session缓存操作

 **flush:** 进行清理缓存(此时缓存中的数据并不丢失)的操作,让缓存和数据库同步 执行一些列sql语句,但不提交事务,;<br />     **commit:**先调用flush() 方法,然后提交事务. 则意味着提交事务意味着对数据库操作永久保存下来。<br />     **reresh**:刷新,让session和数据库同步,执行查询,把数据库的最新信息显示出来,更新本地缓存的对象状态.<br />     **clear**:清空缓存,等价于list.removeAll(); 

Session执行批量操作

可以写一个for循环,Session可以批量插入上万条数据。如下面的代码:
For(int i=0;i<10000;i++){
Session.save(object);
}
这样写的代码会产生一个什么问题?会使一万个对象的缓存全部存在于内存中,这样做加大了内存的压力。所以应该定期清理session的缓存,也就是flush一下,这样内存才能保证足够的空间。

一级缓存-PK-二级缓存

JAVA五大框架整理 - 图10JAVA五大框架整理 - 图11
JAVA五大框架整理 - 图12
JAVA五大框架整理 - 图13

二级缓存使用注意点

JAVA五大框架整理 - 图14

hibernate性能优化

JAVA五大框架整理 - 图15
JAVA五大框架整理 - 图16
JAVA五大框架整理 - 图17

检索策略的选择

JAVA五大框架整理 - 图18

HQL-PK-QBC

JAVA五大框架整理 - 图19

JAVA五大框架整理 - 图20

JAVA五大框架整理 - 图21

Hibernate缺点:

它限制您所使用的对象模型。(例如,一个持久性类不能映射到多个表)其独有的界面和可怜的市场份额也让人不安,尽管如此,Hibernate 还是以其强大的发展动力减轻了这些风险。其他的开源持久性框架也有一些,不过都没有 Hibernate 这样有市场冲击力。
补充几点我的意见:
Hibernate是一个和JDBC密切关联的框架,所以Hibernate的兼容性和JDBC驱动,和数据库都有一定的关系,但是和使用它的Java程序,和App Server没有任何关系,也不存在兼容性问题。
Hibernate不能用来直接和Entity Bean做对比,只有放在整个J2EE项目的框架中才能比较。并且即使是放在软件整体框架中来看,Hibernate也是做为JDBC的替代者出现的,而 不是Entity Bean的替代者出现的,让我再列一次我已经列n次的框架结构:
传统的架构:
1) Session Bean <-> Entity Bean <-> DB
为了解决性能障碍的替代架构:
2) Session Bean <-> DAO <-> JDBC <-> DB
使用Hibernate来提高上面架构的开发效率的架构:
3) Session Bean <-> DAO <-> Hibernate <-> DB
就上面3个架构来分析:
1、内存消耗:采用JDBC的架构无疑是最省内存的,Hibernate的架构次之,EB的架构最差。
2、运行效率:如果JDBC的代码写的非常优化,那么JDBC架构运行效率最高,但是实际项目中,这一点几乎做不到,这需要程序员非常精通JDBC,运用 Batch语句,调整PreapredStatement的Batch Size和Fetch Size等参数,以及在必要的情况下采用结果集cache等等。而一般情况下程序员是做不到这一点的。因此Hibernate架构表现出最快的运行效率。 EB的架构效率会差的很远。
3、开发效率:在有JBuilder的支持下以及简单的项目,EB架构开发效率最高,JDBC次之,Hibernate最差。但是在大的项目,特别是持久层关系映射很复杂的情况下,Hibernate效率高的惊人,JDBC次之,而EB架构很可能会失败。
Hibernate难在哪里?不在Hibernate本身的复杂,实际上Hibernate非常的简单,难在Hibernate太灵活了。
Hibernate它太灵活了,相同的问题,你至少可以设计出十几种方案来解决,所以特别的犯难,究竟用这个,还是用那个呢?这些方案之间到底有什 么区别呢?他们的运行原理有什么不同?运行效率哪个比较好?光是主键生成,就有七八种方案供你选择,你为难不为难?集合属性可以用Set,可以用 List,还可以用Bag,到底哪个效率高,你为难不为难?查询可以用iterator,可以用list,哪个好,有什么区别?你为难不为难?复合主键你 可以直接在hbm里面配置,也可以自定义CustomerType,哪种比较好些?你为难不为难?对于一个表,你可以选择单一映射一个对象,也可以映射成 父子对象,还可以映射成两个1:1的对象,在什么情况下用哪种方案比较好,你为难不为难?
这个列表可以一直开列下去,直到你不想再看下去为止。当你面前摆着无数的眼花缭乱的方案的时候,你会觉得幸福呢?还是悲哀呢?如果你是一个负责的程序员, 那么你一定会仔细研究每种方案的区别,每种方案的效率,每种方案的适用场合,你会觉得你已经陷入进去拔不出来了。如果是用EB,你第一秒种就已经做出了决 定,根本没得选择,比如说集合属性,你只能用Collection,如果是Hibernate,你会在Bag,List和Set之间来回犹豫不决,甚至搞 不清楚的话,程序都没有办法写。

Hibernate是如何延迟加载?

  • Hibernate2延迟加载实现:a)实体对象 b)集合(Collection)
  • Hibernate3 提供了属性的延迟加载功能,当Hibernate在查询数据的时候,数据并没有存在与内存中,当程序真正对数据的操作时,对象才存在与内存中,就实现了延迟加载,他节省了服务器的内存开销,从而提高了服务器的性能。

Hibernate的缓存机制

  • 内部缓存存在Hibernate中又叫一级缓存,属于应用事物级缓存Session 有一个内置的缓存,其中存放了被当前工作单元加载的对象。每个Session 都有自己独立的缓存,且只能被当前工作单元访问。

  • 二级缓存:
    a)应用级缓存,SessionFactory的内置缓存:存放了映射元数据,预定义的Sql语句
    b)分布式缓存
    条件:数据不会被第三方修改、数据大小在可接受范围、数据更新频率低、同一数据被系统频繁使用、非关键数据
    c) 第三方缓存的实现,SessionFactory的外置的可插拔的缓存插件。其中的数据可被多个Session共享访问

Hibernate的查询方式

(1)导航对象图查询
(2)OID查询
(3)HQL
(4)QBC
(5)本地SQL

hibernate分页查询

例如:从数据库中的第20000条数据开始查后面100条记录
Query q = session.createQuery(“from Cat as c”);;
q.setFirstResult(20000);
q.setMaxResults(100);
List l = q.list();

Hibernate优化?

  1. 使用双向一对多关联,不使用单向一对多
  2. 灵活使用单向一对多关联
  3. 不用一对一,用多对一取代
  4. 配置对象缓存,不使用集合缓存
  5. 一对多集合使用Bag,多对多集合使用Set
  6. 继承类使用显式多态
  7. 表字段要少,表关联不要怕多,有二级缓存撑腰

1.建索引
2.减少表之间的关联
3.优化sql,尽量让sql很快定位数据,不要让sql做全表查询,应该走索引,把数据量大的表排在前面
4.简化查询字段,没用的字段不要,已经对返回结果的控制,尽量返回少量数据

Hibernate中对象的状态

  • A、临时状态(transient) 无ID,不与session关联

         特征: <br />        1】不处于Session 缓存中 <br />        2】数据库中没有对象记录 <br />Java如何进入临时状态 <br />        1】通过new语句刚创建一个对象时 <br />        2】当调用Session 的delete()方法,从Session 缓存中删除一个对象时。  
    
  • B、.持久化状态(persisted) 有ID 与session关联

          特征: <br />        1】处于Session 缓存中 <br />        2】持久化对象数据库中设有对象记录 <br />        3】Session 在特定时刻会保持二者同步 <br />Java如何进入持久化状态 <br />        1】Session 的save()把临时-》持久化状态 <br />        2】Session 的load(),get()方法返回的对象 <br />        3】Session 的find()返回的list集合中存放的对象 <br />        4】Session 的update(),saveOrupdate()使游离-》持久化 
    
  • C、.游离状态(detached) 有ID 不与session关联

         特征: <br />        1】不再位于Session 缓存中 <br />        2】游离对象由持久化状态转变而来,数据库中可能还有对应记录。 <br />Java如何进入持久化状态-》游离状态 <br />        1】Session 的close()方法 <br />        2】Session 的evict()方法,从缓存中删除一个对象。提高性能。少用。
    

Hibernate的并发机制?怎么去处理并发问题?

Hibernate并发机制:
a、Hibernate的Session对象是非线程安全的,对于单个请求,单个会话,单个的工作单元(即单个事务,单个线程),它通常只使用一次,然后就丢弃。
如果一个Session 实例允许共享的话,那些支持并发运行的,例如Http request,session beans将会导致出现资源争用。
如果在Http Session中有hibernate的Session的话,就可能会出现同步访问Http Session。只要用户足够快的点击浏览器的“刷新”, 就会导致两个并发运行的线程使用同一个Session。
b、多个事务并发访问同一块资源,可能会引发第一类丢失更新,脏读,幻读,不可重复读,第二类丢失更新一系列的问题。

解决方案:设置事务隔离级别。
Serializable:串行化。隔离级别最高
Repeatable Read:可重复读
Read Committed:已提交数据读
Read Uncommitted:未提交数据读。隔离级别最差
设置锁:乐观锁和悲观锁。
乐观锁:使用版本号或时间戳来检测更新丢失,在的映射中设置 optimistic-lock=”all”可以在没有版本或者时间戳属性映射的情况下实现 版本检查,此时Hibernate将比较一行记录的每个字段的状态 行级
悲观锁:Hibernate总是使用数据库的锁定机制,从不在内存中锁定对象!只要为JDBC连接指定一下隔 离级别,然后让数据库去搞定一切就够了。
类LockMode 定义了Hibernate所需的不同的锁定级别:LockMode.UPGRADE,LockMode.UPGRADE_NOWAIT,LockMode.READ;
管理Session(Session与本地线程绑定):

  • 尽管让程序自主管理 Session 对象的生命周期也是可行的, 但是在实际 Java 应用中, 把管理 Session 对象的生命周期交给 Hibernate 管理, 可以简化 Java 应用程序代码和软件架构
  • Hibernate 3 自身提供了三种管理 Session 对象的方法
    • Session 对象的生命周期与本地线程绑定
    • Session 对象的生命周期与 JTA 事务绑定
    • Hibernate 委托程序管理 Session 对象的生命周期
  • 在 Hibernate 的配置文件中, hibernate.current_session_context_class 属性用于指定 Session 管理方式, 可选值包括
    • thread: Session 对象的生命周期与本地线程绑定
    • jta*: Session 对象的生命周期与 JTA 事务绑定
    • managed: Hibernate 委托程序来管理 Session 对象的生命周期

不再调用sessionFactory.openSession().而是调用sessionFactory. getCurrentSession().获取session对象.从当前的线程提取session,
当前线程如果存在session对象,取出直接使用
当前线程如果不存在session对象,获取一个新的session对象和当前的线程绑定

Hibernate与jdbc的联系

hibernate是jdbc的轻量级封装,包括jdbc的与数据库的连接(用hibernate.property的配置文件实现当然本质是封装了jdbc的forname),和查询,删除等代码,都用面向对象的思想用代码联系起来,hibernate通过hbm 配置文件把po类的字段和数据库的字段关联起来比如数据库的id,<br />    在po类中就是pravite Long id; public Long getId() ;public setId(Long id);<br />    然后hql语句也是面向对象的,它的查询语句不是查询数据库而是查询类的,这些实现的魔法就是xml文件,其实hibernate=封装的jdbc+xml文件

Hibernate与spring的联系

hibernate中的一些对象可以给Spring来管理,让Spring容器来创建hibernate中一些对象实例化。例如:SessionFactory,HibernateTemplate等。<br />    Hibernate本来是对数据库的一些操作,放在DAO层,而Spring给业务层的方法定义了事务,业务层调用DAO层的方法,很好的将Hibernate的操作也加入到事务中来了。

Hibernate自带的分页机制是什么?如果不使用Hibernate自带的分页,则采用什么方式分页?

1、hibernate自带的分页机制:获得Session对象后,从Session中获得Query对象。用Query.setFirstResult():设置要显示的第一行数据,<br />       Query.setMaxResults():设置要显示的最后一行数据。<br />    2、不使用hibernate自带的分页,可采用sql语句分页,<br />       如:5:为每页显示的记录,2为当前页: select * top 5 from table where tabId not in (select tabId top (2-1)*5 from table);<br />   

Hibernate拒接连接、服务器崩溃的原因

  1. db没有打开
    2. 网络连接可能出了问题
    3. 连接配置错了
    4. 驱动的driver,url是否都写对了
    5. LIB下加入相应驱动,数据连接代码是否有误
    6. 数据库配置可能有问题
    7. 当前连接太多了,服务器都有访问人数限制的
    8. 服务器的相应端口没有开,即它不提供相应的服务

Hibernate的主键生成机制

1) assigned
主键由外部程序负责生成,无需Hibernate参与。
2) hilo
通过hi/lo 算法实现的主键生成机制,需要额外的数据库表保存主键生成历史状态。
3) seqhilo
与hilo 类似,通过hi/lo 算法实现的主键生成机制,只是主键历史状态保存在Sequence中,适用于支持Sequence的数据库,如Oracle。
4) increment
主键按数值顺序递增。此方式的实现机制为在当前应用实例中维持一个变量,以保存着当前的最大值,之后每次需要生成主键的时候将此值加1作为主键。这种方式可能产生的问题是:如果当前有多个实例访问同一个数据库,那么由于各个实例各自维护主键状态,不同实例可能生成同样的主键,从而造成主键重复异常。因此,如果同一数据库有多个实例访问,此方式必须避免使用。
5) identity
采用数据库提供的主键生成机制。如DB2、SQL Server、MySQL中的主键生成机制。
6) sequence
采用数据库提供的sequence 机制生成主键。如Oralce 中的Sequence。
7) native
由Hibernate根据底层数据库自行判断采用identity、hilo、sequence其中一种作为主键生成方式。
8) uuid.hex
由Hibernate基于128 位唯一值产生算法生成16 进制数值(编码后以长度32 的字符串表示)作为主键。
9) uuid.string
与uuid.hex 类似,只是生成的主键未进行编码(长度16)。在某些数据库中可能出现问题(如PostgreSQL)。
10) foreign
使用外部表的字段作为主键。一般而言,利用uuid.hex方式生成主键将提供最好的性能和数据库平台适应性。
这10中生成OID标识符的方法,increment 比较常用,把标识符生成的权力交给Hibernate处理.但是当同时多个Hibernate应用操作同一个数据库,甚至同一张表的时候.就推荐使用identity 依赖底层数据库实现,但是数据库必须支持自动增长,当然针对不同的数据库选择不同的方法.如果你不能确定你使用的数据库具体支持什么的情况下.可以选择用native 让Hibernate来帮选identity,sequence,或hilo.
另外由于常用的数据库,如Oracle、DB2、SQLServer、MySql 等,都提供了易用的主键生成机制(Auto-Increase 字段或者Sequence)。我们可以在数据库提供的主键生成机制上,采用generator-class=native的主键生成方式。
不过值得注意的是,一些数据库提供的主键生成机制在效率上未必最佳,大量并发insert数据时可能会引起表之间的互锁。数据库提供的主键生成机制,往往是通过在一个内部表中保存当前主键状态(如对于自增型主键而言,此内部表中就维护着当前的最大值和递增量),之后每次插入数据会读取这个最大值,然后加上递增量作为新记录的主键,之后再把这个新的最大值更新回内部表中,这样,一次Insert操作可能导致数据库内部多次表读写操作,同时伴随的还有数据的加锁解锁操作,这对性能产生了较大影响。因此,对于并发Insert要求较高的系统,推荐采用uuid.hex 作为主键生成机制

Hibernate中Criteria和DetachedCriteria的作用是什么

Criteria c=session.createCriteria(Customer.class);
//设置条件
c.add(Expression.ge(“字段名”,”值对象”))
ge:>=
gt:>
le:<=
lt:<
eq:=
//排序
c.addOrder(Order.asc(“字段名”))
//分页
c.setFirstResult(1)//从第2行开始提取
c.setMaxResults(5)//返回5行
DetachedCriteria产生时不需要session
DetachedCriteria dc= DetachedCriteria.forClass(Customer.class)
Criteria c=Dc.getExecutableCriteria(session)

Hibernate如何实现数据表映射的继承关系

1、两个表,子类重复父类的属性。
2、一个表,子类父类共用一个表
3、两个表,子类引用父类的主键,享用公共的字段或属性。

说说Hibernate中的update()和saveOrUpdate()的区别,session的load()和get()的区别。

摘自hibernate说明文档:
saveOrUpdate()做下面的事:
如果对象已经在本session中持久化了,不做任何事
如果另一个与本session关联的对象拥有相同的持久化标识(identifier),抛出一个异常
如果对象没有持久化标识(identifier)属性,对其调用save()
如果对象的持久标识(identifier)表明其是一个新实例化的对象,对其调用save()
如果对象是附带版本信息的(通过 ) 并且版本属性的值表明其是一个新实例化的对象,save()它。 否则update() 这个对象

Session.load/get方法均可以根据指定的实体类和id从数据库读取记录,并返回与之对应的实体对象。其区别在于:
如果未能发现符合条件的记录,get方法返回null,而load方法会抛出一个ObjectNotFoundException;load方法可返回实体的代理类实例,而get方法永远直接返回实体类;load方法可以充分利用内部缓存和二级缓存中的现有数据,而get方法则仅仅在内部缓存中进行数据查找,如没有发现对应数据,将越过二级缓存,直接调用SQL完成数据读取。

Hibernate里面sorted collection和ordered collection有什么区别

sorted collection是在内存中通过java比较器进行排序的
ordered collection是在数据库中通过order by进行排序的

Hibernate都支持哪些缓存策略

Read-only: 这种策略适用于那些频繁读取却不会更新的数据,这是目前为止最简单和最有效的缓存策略
Read/write:这种策略适用于需要被更新的数据,比read-only更耗费资源,在非JTA环境下,每个事务需要在session.close和session.disconnect()被调用
Nonstrict read/write: 这种策略不保障两个同时进行的事务会修改同一块数据,这种策略适用于那些经常读取但是极少更新的数据
* Transactional: 这种策略是完全事务化得缓存策略,可以用在JTA环境下
如何查看Hibernate生成并执行的sql
在定义数据库和数据库属性的文件applicationConfig.xml里面,把hibernate.show_sql 设置为true
这样生成的SQL就会在控制台出现了
注意:这样做会加重系统的负担,不利于性能调优

比较Hibernate的三种检索策略优缺点

1立即检索;
优点:对应用程序完全透明,不管对象处于持久化状态,还是游离状态,应用程序都可以方便的从一个对象导航到与它关联的对象;
缺点:1.select语句太多;2.可能会加载应用程序不需要访问的对象白白浪费许多内存空间;
2延迟检索:
优点:由应用程序决定需要加载哪些对象,可以避免可执行多余的select语句,以及避免加载应用程序不需要访问的对象。因此能提高检索性能,并且能节省内存空间;
缺点:应用程序如果希望访问游离状态代理类实例,必须保证他在持久化状态时已经被初始化;
3 迫切左外连接检索
优点:1对应用程序完全透明,不管对象处于持久化状态,还是游离状态,应用程序都可以方便地冲一个对象导航到与它关联的对象。2使用了外连接,select语句数目少;
缺点:1 可能会加载应用程序不需要访问的对象,白白浪费许多内存空间;2复杂的数据库表连接也会影响检索性能;

hibernate的延迟加载和openSessionInView

延迟加载要在session范围内,用到的时候再加载;opensessioninview是在web层写了一个
filter来打开和关闭session,这样就表示在一次request过程中session一直开着,保证了延迟
加载在session中的这个前提。

spring

JAVA五大框架整理 - 图22
JAVA五大框架整理 - 图23

JAVA五大框架整理 - 图24

JAVA五大框架整理 - 图25

JAVA五大框架整理 - 图26

JAVA五大框架整理 - 图27

JAVA五大框架整理 - 图28

JAVA五大框架整理 - 图29

JAVA五大框架整理 - 图30

spring是什么?根据你的理解详细谈谈你的见解。

◆目的:解决企业应用开发的复杂性
  ◆功能:使用基本的JavaBean代替EJB,并提供了更多的企业应用功能
  ◆范围:任何Java应用
  简单来说,Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。
  ◆轻量——从大小与开销两方面而言Spring都是轻量的。完整的Spring框架可以在一个大小只有1MB多的JAR文件里发布。并且Spring所需的处理开销也是微不足道的。此外,Spring是非侵入式的:典型地,Spring应用中的对象不依赖于Spring的特定类。
  ◆控制反转——Spring通过一种称作控制反转(IoC)的技术促进了松耦合。当应用了IoC,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖对象。你可以认为IoC与JNDI相反——不是对象从容器中查找依赖,而是容器在对象初始化时不等对象请求就主动将依赖传递给它。
  ◆面向切面——Spring提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务(例如审计(auditing)和事务()管理)进行内聚性的开发。应用对象只实现它们应该做的——完成业务逻辑——仅此而已。它们并不负责(甚至是意识)其它的系统级关注点,例如日志或事务支持。
  ◆容器——Spring包含并管理应用对象的配置和生命周期,在这个意义上它是一种容器,你可以配置你的每个bean如何被创建——基于一个可配置原型(prototype),你的bean可以创建一个单独的实例或者每次需要时都生成一个新的实例——以及它们是如何相互关联的。然而,Spring不应该被混同于传统的重量级的EJB容器,它们经常是庞大与笨重的,难以使用。
  ◆框架——Spring可以将简单的组件配置、组合成为复杂的应用。在Spring中,应用对象被声明式地组合,典型地是在一个XML文件里。Spring也提供了很多基础功能(事务管理、持久化框架集成等等),将应用逻辑的开发留给了你。
  所有Spring的这些特征使你能够编写更干净、更可管理、并且更易于测试的代码。它们也为Spring中的各种模块提供了基础支持。

项目中如何体现Spring中的切面编程,具体说明。

面向切面编程:主要是横切一个关注点,将一个关注点模块化成一个切面。在切面上声明一个通知(Advice)和切入点(Pointcut); 通知: 是指在切面的某个特定的连接点(代表一个方法的执行。通过声明一个org.aspectj.lang.JoinPoint类型的参数可以使通知(Advice)的主体部分获得连接点信息。)上执行的动作。通知中定义了要插入的方法。切入点:切入点的内容是一个表达式,以描述需要在哪些对象的哪些方法上插入通知中定义的方法。
项目中用到的Spring中的切面编程最多的地方:声明式事务管理。
a、定义一个事务管理器
b、配置事务特性(相当于声明通知。一般在业务层的类的一些方法上定义事务)
c、配置哪些类的哪些方法需要配置事务(相当于切入点。一般是业务类的方法上)

spring的事务如何配置

spring的声明式事务配置:
1.
class=”org.springframework.orm.hibernate3.LocalSessionFactoryBean”>

/WEB-INF/classes/hibernate.cfg.xml


2. 配置事务管理器

class=”org.springframework.orm.hibernate3.HibernateTransactionManager”>




3. 配置事务特性








4. 配置哪些类的哪些方法配置事务



isolation设定事务的隔离级别,事务管理器根据它来控制另外一个事务可以看到本事务内的哪些数据。
定义的5个不同的事务隔离级别:
DEFAULT:默认的隔离级别,使用数据库默认的事务隔离级别
READ_COMMITTED:保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。这种事务隔离级别可以避免脏读出现,但是可能会出现不可重复读和幻像读。
READ_UNCOMMITTED:这是事务最低的隔离级别,它充许别外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻像读。
REPEATABLE_READ:这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免不可重复读。
SERIALIZABLE:这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读,不可重复读外,还避免了幻像读。
propagation定义了7个事务传播行为
REQUIRED: 如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务。
SUPPORTS: 如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行。但是对于事务同步的事务管理器,SUPPORTS与不使用事务有少许不同。
REQUIRES_NEW 总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起。
NOT_SUPPORTED 总是非事务地执行,并挂起任何存在的事务。
NEVER 总是非事务地执行,如果存在一个活动事务,则抛出异常
NESTED:如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行。
嵌套事务一个非常重要的概念就是内层事务依赖于外层事务。外层事务失败时,会回滚内层事务所做的动作。而内层事务操作失败并不会引起外层事务的回滚。

创建对象的方式

无参构造函数

<bean id=“personService” class=”cn.itcast.bean.impl.PersonServiceImpl”/>

静态工厂


public class PersonServiceFactory {
public static PersonService createPersonService(){
return new PersonServiceImpl();
}
}

实例工厂

记住概念即可

对象的scope

singleton(默认值)

在每个Spring IoC容器中一个bean定义只有一个对象实例(共享)。
默认情况下会在容器启动时初始化bean,但我们可以指定Bean节点的lazy-init=“true”来延迟初始化bean,这时候,只有第一次获取bean会才初始化bean。如:

如果想对所有bean都应用延迟初始化,可以在根节点beans设置default-lazy-init=“true“,如下:
<beans default-lazy-init=”true“ …>
JAVA五大框架整理 - 图31

prototype

  允许bean可以被多次实例化(使用一次就创建一个实例) . Spring不能对一个prototype bean的整个生命周期负责.这就意味着清楚prototype作用域的对象并释放任何prototype bean所持有的昂贵资源都是客户端的责任。

Request

JAVA五大框架整理 - 图32

Session

JAVA五大框架整理 - 图33

Global session

JAVA五大框架整理 - 图34

依赖注入(DI)

xml形式

使用构造器注入

使用属性setting方法进行注入

代理

JDK的动态代理

必须具备四个条件:
目标接口
目标类
拦截器
代理类
总结:1、因为利用JDKProxy生成的代理类实现了接口,所以目标类中所有的方法在代理类中都有。
2、生成的代理类的所有的方法都拦截了目标类的所有的方法。而拦截器中invoke方法的内容正好就是代理类的各个方法的组成体。
3、利用JDKProxy方式必须有接口的存在。
4、invoke方法中的三个参数可以访问目标类的被调用方法的API、被调用方法的参数、被调用方法的返回类型。

CGLIB做代理

  1. CGlib是一个强大的,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java接口。
  2. 用CGlib生成代理类是目标类的子类。
  3. 用CGlib生成 代理类不需要接口
  4. 用CGLib生成的代理类重写了父类的各个方法。
  5. 拦截器中的intercept方法内容正好就是代理类中的方法体

    spring有两种代理方式:

  6. 若目标对象实现了若干接口,spring使用JDK的java.lang.reflect.Proxy类代理。

    优点:因为有接口,所以使系统更加松耦合
    缺点:为每一个目标类创建接口

  7. 若目标对象没有实现任何接口,spring使用CGLIB库生成目标对象的子类。

    优点:因为代理类与目标类是继承关系,所以不需要有接口的存在。
    缺点:因为没有使用接口,所以系统的耦合性没有使用JDK的动态代理好。

    AOP概念:

  8. Aspect(切面)

比如说事务、权限等,与业务逻辑没有关系的部分

  1. joinpoint(连接点)

目标类的目标方法。(由客户端在调用的时候决定)

  1. Pointcut(切入点)

所谓切入点是指我们要对那些拦截的方法的定义.
被纳入spring aop中的目标类的方法。

  1. Advice(通知)

所谓通知是指拦截到joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)

  1. Target(目标对象):

代理的目标对象

  1. Weaving(织入)

是指把切面应用到目标对象来创建新的代理对象的过程.切面在指定的连接点织入到目标对象

JDKProxy代理 SpringAop
目标对象 目标对象
拦截器类 切面
拦截器类中的方法 通知
被拦截到的目标类中方法的集合 切入点
在客户端调用的方法(目标类目标方法) 连接点
代理类 AOP代理
代理类的代理方法生成的过程 织入

通知根据拦截目标类中的目标方法的位置不一样可以分为:前置通知、后置通知、最终通知、环绕通知、异常通知

aop

代理对象的方法体就把事务和目标方法结合在一起了,这样做的目的就是为了让目标类的目标方法和事务的方法松耦合

Aop的概念

切面

事务、日志、安全性的框架,权限等就是切面

通知

切面中的方法就是通知

切入点

只有符合切入点的条件,才能让通知和目标方法结合在一起

织入

形成代理对象方法体的过程<br />好处:<br />   事务、日志、安全性框架、权限、目标方法之间完全是松耦合的

JAVA五大框架整理 - 图35

springAOP的具体加载步骤:

1、当spring容器启动的时候,加载了spring的配置文件
2、为配置文件中所有的bean创建对象
3、spring容器会解析aop:config的配置
1、解析切入点表达式,用切入点表达式和纳入spring容器中的bean做匹配
如果匹配成功,则会为该bean创建代理对象,代理对象的方法=目标方法+通知
如果匹配不成功,不会创建代理对象
4、在客户端利用context.getBean获取对象时,如果该对象有代理对象则返回代理对象,如果代理对象,则返回目标对象

说明:如果目标类没有实现接口,则spring容器会采用cglib的方式产生代理对象,如果实现了接口,会采用jdk的方式

通知:

1、前置通知
1、在目标方法执行之前执行
2、无论目标方法是否抛出异常,都执行,因为在执行前置通知的时候,目标方法还没有执行,还没有遇到异常
2、后置通知
1、在目标方法执行之后执行
2、当目标方法遇到异常,后置通知将不再执行
3、后置通知可以接受目标方法的返回值,但是必须注意:
后置通知的参数的名称和配置文件中returning=”var”的值是一致的
3、最终通知:
1、在目标方法执行之后执行
2、无论目标方法是否抛出异常,都执行,因为相当于finally
4、异常通知
1、接受目标方法抛出的异常信息
2、步骤
在异常通知方法中有一个参数Throwable ex
在配置文件中

5、环绕通知
1、如果不在环绕通知中调用ProceedingJoinPoint的proceed,目标方法不会执行
2、环绕通知可以控制目标方法的执行

OpenInSessionView

  由于使用的是spring的声明式事务处理方式,所以在调用this.getHibernateTemplate().load方法时,使用了hibernate的懒加载技术。当把一个实体Bean从数据库中加载完以后,只能加载其ID值。这个时候spring的声明式事务处理方式已经把session给关闭掉了。所以当值在页面输出时会产生异常。<br />处理方式为:OpenSessionInview模式。

ssh的整合的目的

问题
  1. struts2的action为什么必须交给spring容器产生?

Action与service要做到完全的松耦合,所以在action中的service必须由spring容器进行注入,那么要完成该注入,action必须在spring容器中。所以action必须由spring容器产生。

  1. 在整合的整个过程中,spring容器用到了哪些知识点?
    1. 为了松耦合action与service,service与dao,使用了ioc和di
    2. 为了不让程序员接触到事务,使用了声明式的事务处理
    3. 有可能会用springaop处理权限、日志等内容
  2. 在整合的过程中,struts2用到了哪些知识点?
    1. mvc的作用
    2. 使用插件的机制使得struts2与spring整合在一起了,实际上就是把

Struts2中的action交给spring处理了。

  1. 在整合的过程中,hibernate充当了什么角色?

数据库的操作由hibernate充当

三大框架整合原理

1、三大框架的作用
struts2是一个mvc框架
spring容器
1、利用ioc和di做到了完全的面向接口编程
2、由于spring的声明式事务处理,使程序员不再关注事务
3、dao层和service层的类是单例的,但是action层是多例
hibernate
就是一个数据库的ormapping的框架
2、整合原理
1、当tomcat启动时,做的事情
1、因为在web.xml中,

org.springframework.web.context.ContextLoaderListener


contextConfigLocation
classpath:spring/applicationContext.xml


struts2
org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter


struts2
/

所以在启动的时候,执行的是
ContextLoaderListener
contextInitialized
this.contextLoader = createContextLoader();
加载spring的配置文件
这里有一个固定的参数con的textConfigLocation
可以指定classpath路径下的spring的配置文件
也可以任意位置指定配置文件 spring
.xml WEB-INF/任意多个任意文件夹/spring-*.xml
如果没有指定固定参数,则查找默认的加载路径:WEB-INF/applicationContext.xml
this.contextLoader.initWebApplicationContext(event.getServletContext());
启动spring容器
总结:当tomcat启动的时候,spring容器就启动了,这个时候service层和dao层所有的单例类就创建对象了
struts2容器:
加载了default.properties,struts-default.xml,struts-plugin.xml,struts.xml

2、请求一个url时,发生的事情:
1、在引入jar包时,导入了struts2-spring-plugin-2.1.8.1.jar包,该jar中有一个文件struts-plugin.xml
class=”org.apache.struts2.spring.StrutsSpringObjectFactory” />

2、由于上面的配置改变了action的生成方式,action由StrutsSpringObjectFactory生成,经过查找是由SpringObjectFactory中的buidBean方法
生成的
try {
o = appContext.getBean(beanName);
} catch (NoSuchBeanDefinitionException e) {
Class beanClazz = getClassInstance(beanName);
o = buildBean(beanClazz, extraContext);
}
3、由上面的代码可以看出,先从spring容器中查找相应的action,如果没有找到,再根据反射机制创建action,
beanName就是struts配置文件class属性的值,所以class属性的值和spring中ID的值保持一致

springmvc

Springmvc严格按照mvc设计模式设计一套框架:
Struts2严格按照mvc设计模式设计框架:

  • 前端控制器strutsPrepareAndExcuteFilter(ssh/list.action)
    • 接受请求
    • 转发请求
  • 请求获取Action对象

Public class UserAction(
@resource
UserService userService;
Public String list(){
userService.list()
}
)

  • ActionMapping去寻找Action
  • 创建Action代理对象。ActionProxy。

Springmvc:前端控制器(Controller/DispatcherServlet)
JAVA五大框架整理 - 图36
处理器映射器:什么样的请求交给controller
处理器适配器:实现什么接口
视图解析器:解析视图逻辑名对应的真实路径

Spring web mvc 架构

架构图

JAVA五大框架整理 - 图37

架构流程

  1. 用户发送请求至前端控制器DispatcherServlet
  2. DispatcherServlet收到请求调用HandlerMapping处理器映射器。
  3. 处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
  4. DispatcherServlet通过HandlerAdapter处理器适配器调用处理器
  5. 执行处理器(Controller,也叫后端控制器)。
  6. Controller执行完成返回ModelAndView
  7. HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet
  8. DispatcherServlet将ModelAndView传给ViewReslover视图解析器
  9. ViewReslover解析后返回具体View
  10. DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)。
  11. DispatcherServlet响应用户jsp页面、json数据。。。。

组件说明

以下组件通常使用框架提供实现:

  • DispatcherServlet:前端控制器

用户请求到达前端控制器,它就相当于mvc模式中的c,dispatcherServlet是整个流程控制的中心,由它调用其它组件处理用户的请求,dispatcherServlet的存在降低了组件之间的耦合性。

  • HandlerMapping:处理器映射器 默认BeanNameUrlHandlerMapping

HandlerMapping负责根据用户请求找到Handler即处理器,springmvc提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。

  • Handler:处理器

Handler 是继DispatcherServlet前端控制器的后端控制器,在DispatcherServlet的控制下Handler对具体的用户请求进行处理。
由于Handler涉及到具体的用户业务请求,所以一般情况需要程序员根据业务需求开发Handler。

  • HandlAdapter:处理器适配器 默认SimpleControllerHandlerAdapter

通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。

  • ViewResolver:视图解析器

ViewResolver负责将处理结果生成View视图,ViewResolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。 springmvc框架提供了很多的View视图类型,包括:jstlView、freemarkerView、pdfView等。
一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开发具体的页面。

DispatcherServlet前端控制器加载 DispatcherServlet.properoties 配置文件,从而默认加载各各组件,
如果在springmvc.xml中配置了处理器映射器和适配器,以sprintmvc.xml中配置的为准

springmvc处理流程源码分析

  1. 用户发送请求到DispatherServlet前端控制器
  2. DispatherServlet调用HandlerMapping(处理器映射器)根据url查找Handler

    JAVA五大框架整理 - 图38
    JAVA五大框架整理 - 图39

  3. DispatherServlet调用HandlerAdapter(处理器适配器)对HandlerMapping找到Handler进行包装、执行。HandlerAdapter执行Handler完成后,返回了一个ModleAndView(springmvc封装对象)

     DispatherServlet 找一个合适的适配器:<br />        ![](https://cdn.nlark.com/yuque/0/2019/png/300802/1553672998373-b8d0bfe3-68ba-47da-9275-a6d583eba636-image40.png#align=left&display=inline&height=87&originHeight=58&originWidth=623&status=done&width=934)<br />        适配器执行Hanlder<br />        ![](https://cdn.nlark.com/yuque/0/2019/png/300802/1553672998391-c58df649-d407-4f6d-84c4-3d7cdc4d2d56-image41.png#align=left&display=inline&height=77&originHeight=51&originWidth=660&status=done&width=990)
    
  4. DispatherServlet拿着ModelAndView调用ViewResolver(视图解析器)进行视图解析,解析完成后返回一个View(很多不同视图类型的View)

JAVA五大框架整理 - 图40

视图解析:
JAVA五大框架整理 - 图41

  1. DispatcherServlet进行视图渲染,将Model中数据放到request域,在页面展示

JAVA五大框架整理 - 图42
将model数据放在request域:
JAVA五大框架整理 - 图43

Springmvc-PK-struts2

实现机制:
Struts2:过滤器。
Springmvc:servlet。
底层是servlet,更底层的东西速度更快。
运行效率:
单例和多例:
Struts2:多例
NNN ( n个Action对象,n个valueStack对象,n个ActionContext,
N个javaBean对象。
)
Springmvc:单例
1*N(方法里面对象)
参数封装:
Struts2:基于属性封装
Public class UserAction{
Private user user;
}
对象生命周期比较长,占用内存空间。

Springmvc:基于方法封装。
方法里面参数生命周期随着方法结束就销毁。
JAVA五大框架整理 - 图44
1、Struts2是类级别的拦截, 一个类对应一个request上下文,SpringMVC是方法级别的拦截,一个方法对应一个request上下文,而方法同时又跟一个url对应,所以说从架构本身上SpringMVC就容易实现restful url,而struts2的架构实现起来要费劲,因为Struts2中Action的一个方法可以对应一个url,而其类属性却被所有方法共享,这也就无法用注解或其他方式标识其所属方法了。
2、由上边原因,SpringMVC的方法之间基本上独立的,独享request response数据,请求数据通过参数获取,处理结果通过ModelMap交回给框架,方法之间不共享变量,而Struts2搞的就比较乱,虽然方法之间也是独立的,但其所有Action变量是共享的,这不会影响程序运行,却给我们编码 读程序时带来麻烦,每次来了请求就创建一个Action,一个Action对象对应一个request上下文。
3、由于Struts2需要针对每个request进行封装,把request,session等servlet生命周期的变量封装成一个一个Map,供给每个Action使用,并保证线程安全,所以在原则上,是比较耗费内存的。
4、 拦截器实现机制上,Struts2有以自己的interceptor机制,SpringMVC用的是独立的AOP方式,这样导致Struts2的配置文件量还是比SpringMVC大。
5、SpringMVC的入口是servlet,而Struts2是filter(这里要指出,filter和servlet是不同的。以前认为filter是servlet的一种特殊),这就导致了二者的机制不同,这里就牵涉到servlet和filter的区别了。
6、SpringMVC集成了Ajax,使用非常方便,只需一个注解@ResponseBody就可以实现,然后直接返回响应文本即可,而Struts2拦截器集成了Ajax,在Action中处理时一般必须安装插件或者自己写代码集成进去,使用起来也相对不方便。
7、SpringMVC验证支持JSR303,处理起来相对更加灵活方便,而Struts2验证比较繁琐,感觉太烦乱。
8、Spring MVC和Spring是无缝的。从这个项目的管理和安全上也比Struts2高(当然Struts2也可以通过不同的目录结构和相关配置做到SpringMVC一样的效果,但是需要xml配置的地方不少)。
9、 设计思想上,Struts2更加符合OOP的编程思想, SpringMVC就比较谨慎,在servlet上扩展。
10、SpringMVC开发效率和性能高于Struts2。
11、SpringMVC可以认为已经100%零配置。
12、jsonp

经过实际测试,发现struts标签解析速度比较慢,建议在实际开发时使用jstl。

JAVA五大框架整理 - 图45

Mybatis

JAVA五大框架整理 - 图46
JAVA五大框架整理 - 图47

Mybatis-PK-Hibernate

JAVA五大框架整理 - 图48

Mybatis解决jdbc编程的问题

  1. 数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库链接池可解决此问题。

解决:在SqlMapConfig.xml中配置数据链接池,使用连接池管理数据库链接。

  1. Sql语句写在代码中造成代码不易维护,实际应用sql变化的可能较大,sql变动需要改变java代码。

解决:将Sql语句配置在XXXXmapper.xml文件中与java代码分离。

  1. 向sql语句传参数麻烦,因为sql语句的where条件不一定,可能多也可能少,占位符需要和参数一一对应。

解决:Mybatis自动将java对象映射至sql语句,通过statement中的parameterType定义输入参数的类型。

  1. 对结果集解析麻烦,sql变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成pojo对象解析比较方便。

解决:Mybatis自动将sql执行结果映射至java对象,通过statement中的resultType定义输出结果的类型。
jdbc开发
1)优点:简单易学,上手快,非常灵活构建SQL,效率高
2)缺点:代码繁琐,难以写出高质量的代码(例如:资源的释放,SQL注入安全性等)
开发者既要写业务逻辑,又要写对象的创建和销毁,必须管底层具体数据库的语法
(例如:分页)。
3)适合于超大批量数据的操作,速度快
hibernate单表开发
1)优点:不用写SQL,完全以面向对象的方式设计和访问,不用管底层具体数据库的语法,(例如:分页)便于理解。
2)缺点:处理复杂业务时,灵活度差, 复杂的HQL难写难理解,例如多表查询的HQL语句
3)适合于中小批量数据的操作,速度慢

mybatis和hibernate区别

Hibernate:hibernate是一个标准的ORM框架,不需要写sql语句,维护关系比较复杂,sql语句自动生成,对sql语句优化,修改比较困难。
Hibernate的优缺点:
优点:面向对象开发,不需要自己写sql语句。如果进行数据库迁移不需要修改sql语句,只需要修改一下方言。
缺点:hibernate维护数据表关系比较复杂。完全是有hibernate来管理数据表的关系,对于我们来说完全是透明的,不易维护。
Hibernate自动生成sql语句,生成sql语句比较复杂,比较难挑错。
Hibernate由于是面向对象开发,不能开发比较复杂的业务。
应用场景:
适合需求变化较少的项目,比如ERP,CRM等等
Mybatis框架对jdbc框架进行封装,屏蔽了jdbc的缺点,开发简单。
Mybatis只需要程序员关注sql本身,不需要过多的关注业务。对sql的优化,修改比较容易
适应场景:
适合需求变化多端的项目,比如:互联网项目

mybatis与hibernate重要区别

企业开发进行技术选型 ,考虑mybatis与hibernate适用场景。

mybatis:入门简单,程序容易上手开发,节省开发成本 。mybatis需要程序员自己编写sql语句,是一个不完全 的ORM框架,对sql修改和优化非常容易实现 。
mybatis适合开发需求变更频繁的系统,比如:互联网项目。

hibernate:入门门槛高,如果用hibernate写出高性能的程序不容易实现。hibernate不用写sql语句,是一个 ORM框架。
hibernate适合需求固定,对象数据模型稳定,中小型项目,比如:企业OA系统。

总之,企业在技术选型时根据项目实际情况,以降低成本和提高系统 可维护性为出发点进行技术选型。

架构图

JAVA五大框架整理 - 图49
JAVA五大框架整理 - 图50

项目中为什么使用SSH

1. 使用Struts是因为struts是基于MVC模式的,很好的将应用程序进行了分层,使开发者更关注于业务逻辑的实现;第二,struts有着丰富的taglib,如能灵活运用,则能大大提高开发效率。<br />    2. 使用Hibernate:因为hibernate为Java应用提供了一个易用的、高效率的对象关系映射框架。hibernate是个轻量级的持久性框架,功能丰富。<br />    3. 使用Spring:因为spring基于IoC(Inversion of Control,反向控制)和AOP构架多层j2ee系统的框架,但它不强迫你必须在每一层中必须使用Spring,因为它模块化的很好,允许你根据自己的需要选择使用它的某一个模块;采用IoC使得可以很容易的实现bean的装配,提供了简洁的AOP并据此实现事务管理(Transcation Managment),等等