|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?注册加入
×
自我介绍:第一章:JSP、Servlet
更多精彩请关注微信“柠檬学园”
1、Servlet生命周期分四个阶段:实例化、初始化、服务和销毁。
当客户端第一次访问一个servlet的时候,服务器判断该servlet对象是否已经创建,如果没有创建,则创建该servlet对象,创建时执行init方法,对servlet执行一些初始化工作,然后准备好ServletRequest和ServletResponse的对象,将其作为参数传给service方法,处理业务后,将结果交给response对象,response对象将信息交给http协议发送到客户端进行解析,当容器停掉或者重启服务器的时候,执行destory方法。
2、servlet四大域对象
PageContext:页面范围。用的较少,范围太小。
ServletRequest:请求范围。用的较多,数据仅存在一次请求中。(需要注意:转发是一次请求)比如:查询记录的结果集
HttpSession:会话范围。用的较多,数据存在一次会话中。比如:购物车、用户登录信息等。
ServletContext:应用范围。用的较少,范围太大,有线程安全问题。注意同步。
Servlet和 Listener 和 Filter分别适合做什么业务
2.1 Servlet:处理客户端请求的核心业务逻辑
例如;
1)接收客户端提交的数据
2)封装成JavaBean对象
3)调用业务层完成具体的业务操作
4)跳转到视图进行结果显示
4.2 Listener
监听ServletContext,HttpSession,ServletRequest三个域对象的创建、初始化和销毁。
ServletContextListener/ServletContextAttributeListener
HttpSessionListener/HttpSessionAttributeListener
ServletRequestListener/ServletRequestAttributeListener
4.3 Filter
处理请求到达web 资源之前(预处理)和处理响应到达浏览器之前(后处理)业务
例如:编码方式转转,权限控制,压缩响应,过滤敏感关健字
大数据分页:
在 test/CustomerTest 编写物理分页和逻辑分页的测试用例
1)、物理分页,mysql提供limit 关键字 ,oracle提供rownum 行号
mysql limit 语法 : select * from customer limit 开始记录索引,长度 ;
* 开始记录索引,数据表记录从0开始,长度,从开始位置查询多少条数据
* 关键点根据每页记录条数和第几页 计算开始记录索引 ------- 开始记录索引 = (页码-1)* 每页记录条数
2) 逻辑分页,一次性查询所有数据,再截取需要的数据
int start = (pageNum - 1) * numPerPage;
int end = pageNum * numPerPage;
3、JSP的9个内置对象分别是:request、response、session、application、config、page、exception、out、pageContext。
1、request对象
该对象封装了用户提交的信息,通过调用该对象相应的方法可以获取封装的信息,即使用该对象可以获取用户提交的信息。
2、response对象
对客户的请求做出动态的响应,向客户端发送数据。
3、session对象
是一个JSP内置对象,它在第一个JSP页面被装载时自动创建,完成会话期管理。从一个客户打开浏览器并连接到服务器开始,到客户关闭浏览器离开这个服务器结束,被称为一个会话。当一个客户访问一个服务器时,可能会在这个服务器的几个页面之间切换,服务器应当通过某种办法知道这是一个客户,就需要session对象。
4、application对象
服务器启动后就产生了这个Application对象,当客户在所访问的网站的各个页面之间浏览时,这个Application对象都是同一个,直到服务器关闭。但是与Session对象不同的是,所有客户的Application对象都是同一个,即所有客户共享这个内置Application对象。
5、out 对象
out对象是一个输出流,用来向客户端输出数据。
6、cookie 对象
cookie是Web服务器保存在用户硬盘上的一段文本。cookie允许一个Web站点在用户电脑上保存信息并且随后再取回它。
7、config 对象
配置对象。
8、exception 对象
在处理异常的网页中可以直接访问exception隐式对象。
9、page 对象
页面对象。
4、说出JSP的6个基本动作指令和作用:
1、Include指令
<jsp:include>标签,表示包含一个静态的或者动态的文件。
2、Forward指令
<jsp:forward>标签,表示重定向一个静态html/jsp的文件,或者是一个程序段。
3、UseBean指令
<jsp:useBean>标签,表示用来在JSP页面中创建一个bean实例并指定它的名字以及作用范围。
4、GetProperty指令
<jsp:getProperty>标签,表示获取BEAN的属性的值并将之转化为一个字符串,然后将其插入到输出的页面中。
5、SetProperty指令
<jsp:setProperty>标签,表示用来设置Bean中的属性值。
6、Plugin指令
<jsp:plugin>标签,表示执行一个applet或Bean,有可能还要下载一个Java插件用于执行它。
如何在JSP中使用标签?
<%@ taglib uri=”标签文件存放路径” prefix=”元素前缀”%>
例如:
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<c:out value="hello" />
4、EL表达式的11个隐式对象:
pageContext、pageScope、requestScope、sessoinScope、applicationScope、param、paramValues、header、headerValues、cookie、initParam
5、自定义标签的开发步骤
1>、写一个类,继承SimpleTagSupport类;
2>、写一个tld文件,配置标签
3>、在页面上引入标签
6、自定义函数的开发步骤
1>、写一个类,写一个自定义方法,这个方法必须是静态的
2>、在WEB-INF下写配置一个tld文件,对其进行配置
3>、在jsp页面上使用
Dbutils:
Common Dbutils是操作数据库的组件,对传统操作数据库的类进行二次封装,可以把结果集转化成List。
补充一下,传统操作数据库的类指的是JDBC(java database connectivity:java数据库连接,java的数据库操作的基础API。)
DBUtils是java编程中的数据库操作实用工具,小巧简单实用,
特色:
1.对于数据表的读操作,他可以把结果转换成List,Array,Set等java集合,便于程序员操作;
2.对于数据表的写操作,也变得很简单(只需写sql语句)
3.可以使用数据源,使用JNDI,数据库连接池等技术来优化性能--重用已经构建好的数据库连接对象,
而不像php,asp那样,费时费力的不断重复的构建和析构这样的对象。
session:
存储需要在整个用户会话过程中保持其状态的信息,例如登录信息或用户浏览 Web应用程序时需要的其它信息。
存储只需要在页重新加载过程中或按功能分组的一组页之间保持其状态的对象。
Session 的作用就是它在 Web服务器上保持用户的状态信息供在任何时间从任何页访问。
因为浏览器不需要存储任何这种信息,所以可以使用任何浏览器,即使是像 PDA 或手机这样的浏览器设备。
servlet:
Servlet是一种服务器端的Java应用程序,具有独立于平台和协议的特性,可以生成动态的Web页面。
它担当客户请求(Web浏览器或其他HTTP客户程序)与服务器响应(HTTP服务器上的数据库或应用程序)的中间层。
Servlet是位于Web 服务器内部的服务器端的Java应用程序,与传统的从命令行启动的Java应用程序不同,
Servlet由Web服务器进行加载,该Web服务器必须包含支持Servlet的Java虚拟机。
Filter: 是过滤器,可以阻止url请求,过滤url请求,当请求符合要求时才会批准请求,进行响应
Listenner:
listener是servlet的监听器,他可以监听客户端的请求,服务端的操作等,通过监听器,可以自动激发一些
操作,比如监听在线用户数量,当增加一个httpsession时,给在线人数加1,编写监听器需要实现相应
的接口,编写完后在web.xml文件中配置一下,就要可以起作用了
拦截器的创建步骤?
持久化对象:
已经被持久化,加入到session的缓存中,对象和session有了关联,对象处于hibernate框架管理中.处于持久化的java对象被称为持久化对象
JDBC:
是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。
JDBC提供了一种基准,据此可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序
数据源:
数据源(Data Source)是提供某种所需要数据的器件或原始媒体。顾名思义,数据的来源。
在数据源中存储了所有建立数据库连接的信息。就象通过指定文件名称可以在文件系统中找到文件一样,
通过提供正确的数据源名称,你可以找到相应的数据库连接。
第二章、Struts2
1、说一说Struts2的优点
Struts2是一个MVC框架,它的作用就是替代我们传统的JSP、Servlet开发,对我们以往开发中出现的重复代码进行了一系列的重构,要说Struts2,首先需要说一下我们以往的开发是怎么个现状,这样才能知道为什么要有Struts2的出现。
以往我们开发web项目的时候,业务层和DAO层我就不说了,WEB层我们用的是MVC框架,这叫做model 1,javabean作为数据传输的对象,Servlet作为控制器,jsp负责显示数据。以往的程序运行和处理流程如下:
1>、当客户端访问我们web应用的时候,服务器根据客户端访问的路径到web.xml中去找到url映射,看客户端请求的是哪个servlet,找到这个servlet对象后,进行判断,如果没有初始化则对这个servlet进行初始化,然后判断客户端的请求方式,到底是get还是post,如果是get就调用doGet方法,如果是post方式就调用doPost方式,一个请求我们一般要写一个servlet,如果想要多个请求共用一个servlet,可以在客户端的请求后面带参数,在servlet的doGet或doPost方法中进行判断,然后调用对应的方法。
2>、在对应的处理方法中,我们先通过request对象设置编码格式,然后获取客户端提交过来的参数,对需要转换类型的数据,比如说日期类型,我们需要手工转化这个类型。
3>、处理业务,对处理业务中产生的异常,我们采用try catch进行捕获,出现异常的时候,我们通过request转发或response重定向到某个错误页面去。
4>、处理完业务后,我们将我们的结果存储到request域中,或者如果是json格式的数据,我们要进行非常复杂的数据转化,然后通过response.getOutputStream.write()方法,将数据输出到浏览器。
通过以上分析我们发现采用jsp/servlet开发项目时的缺点:(servlet开发的缺点)
第一:一个servlet要处理多个请求非常麻烦,不但要在web.xml文件中进行一堆配置,还需要在servlet的doGet或doPost方法中进行判断请求的是哪个方法;
第二:获取前台页面的参数非常麻烦;(采用servlet开发怎么获取前台的参数?)
第三:所有的类型转换需要我们程序员来手动转换;
第四:所有的异常都要一一处理,代码重复度很高;
第五:结果集的处理非常麻烦;
这时候,struts2就出来了,它解决了我们上面出现的所有问题,甚至更优秀。(以下是struts2的优点)
struts2有一个核心过滤器,StrutsPreparedAndExecuteFilter,在容器启动的时候,执行该过滤器中的init方法,在该方法中,加载系统的一些配置,包括struts.xml配置文件,在该方法中,它会解析这个配置文件,将文件中action节点的name作为key,class指定的类的对象作为实例存储到一个Map中,当你客户端发送一个请求的时候,它会从这个map里面去找与你请求匹配的key,这样就解决了我们上面说的servlet请求映射的问题。在初始化方法中,还进行了一些常量的初始化,比如字符集统一设置为UTF-8等,常量是支持自定义的,我们可以在struts.xml配置文件中进行配置。包括请求后缀、struts2的模版等。当客户端发送一个请求的时候,首先会经过struts2核心过滤器,核心过滤器根据你的请求,从一个map集合中取出你要访问的对象,生成一个代理对象,紧接着,调用一系列的拦截器,这些拦截器的调用方式采用的是职责链的方式,拦截器是struts2的一个核心功能,struts2默认提供了一堆拦截器,采用拦截器栈的方式进行了配置,主要作用是
1>、对请求参数进行拦截,采用反射技术,将参数的值注入到VO对象中,如果实现了ModelDriven,则会将值注入到ModelDriven的泛型类的对象的属性中去。
2>、对文件上传进行进行统一处理,简化了文件上传功能
3>、对国际化提供支持
4>、对前台的Checkbox数据进行处理
5>、如果配置了声明式验证,则对请求的数据进行校验
等等。
经过一系列的拦截器后,调用客户端请求的action中的方法,在方法中,我们不需要再用request对象去设置编码和获取参数了,struts2框架已经帮我们将值注入了属性中,我们直接拿来用即可。处理完业务流程后,我们将我们的结果交给struts2的结果集来处理,默认struts2提供了10个结果集,比如dispatcher、chain、redirectAction、stream等。
对于异常的处理,在struts2中,可以使用自定义拦截器来实现。对比struts2和我们传统的servlet、jsp的开发,我们可以发现struts2的很多有点和好处,框架的作用就是帮我们简化开发、优化开发流程。
Struts的缺点:
一、 转到展示层时,需要配置forward,如果有十个展示层的jsp,需要配置十次struts,而且还不包括有时候目录、文件变更,需要重新修改forward,注意,每次修改配置之后,要求重新部署整个项目,而tomcate这样的服务器,还必须重新启动服务器
二、Struts 的Action必需是thread-safe方式,它仅仅允许一个实例去处理所有的请求。所以action用到的所有的资源都必需统一同步,这个就引起了线程安全的问题。
三、 测试不方便. Struts的每个Action都同Web层耦合在一起,这样它的测试依赖于Web容器,单元测试也很难实现。不过有一个Junit的扩展工具Struts TestCase可以实现它的单元测试。
四、 类型的转换. Struts的FormBean把所有的数据都作为String类型,它可以使用工具Commons-Beanutils进行类型转化。但它的转化都是在Class级别,而且转化的类型是不可配置的。类型转化时的错误信息返回给用户也是非常困难的。
五、 对Servlet的依赖性过强. Struts处理Action时必需要依赖ServletRequest 和ServletResponse,所有它摆脱不了Servlet容器。
六、 前端表达式语言方面.Struts集成了JSTL,所以它主要使用JSTL的表达式语言来获取数据。可是JSTL的表达式语言在Collection和索引属性方面处理显得很弱。
七、 对Action执行的控制困难. Struts创建一个Action,如果想控制它的执行顺序将会非常困难。甚至你要重新去写Servlet来实现你的这个功能需求。
八、 对Action 执行前和后的处理. Struts处理Action的时候是基于class的hierarchies,很难在action处理前和后进行操作。
九、 对事件支持不够. 在struts中,实际是一个表单Form对应一个Action类(或DispatchAction),换一句话说:在Struts中实际是一个表单只能 对应一个事件,struts这种事件方式称为application event,application event和component event相比是一种粗粒度的事件
在struts-2.3.15.1apps\struts2-blank.war中,只需要导入 其中所有 jar 包 ---- 11 个 jar 包,可以参考里面的空白例子进行配置。
2、Struts2中的值栈
Struts2中的值栈的作用是简化我们获取数据的方式,采用ValueStack+OGNL表达式,实现数据的存取。ValueStack是一个接口,这个接口有一个默认的实现类OgnlValueStack,这个类在运行的时候,有两个很重要的属性,一个是CompondRoot,叫根对象,一个是OgnlContext,Ognl上下文环境,这个OgnlContext里面又包含两个东西,一个是Map集合,一个是CompoundRoot,Map集合里存储了所有Session的集合、request对象的集合等数据,CompoundRoot里存储的东西跟OgnlValueStack里的CompoundRoot存储的是一样的,这个我觉得Struts2设计的有点多余,搞两个一样的东西,不过人家可能觉得为我们提供了方便,但是另一方面,也增加了我们学习时的难度。细节太多了。
通过ActionContext.getContext().getValueStack().push()方法,我们可以将一个对象数据压入栈顶,CompoundRoot继承了ArrayList这个类,对里面的几个方法进行了扩展,所谓栈顶,就是这个集合的第一个元素,通过把数据放到栈顶,在页面或struts2配置文件中,我们可以非常方便的直接获取出来,不需要再用对象.属性的方式来获取了,这是struts2做的很好的一个地方。
值栈:简单的说,就是存放action的堆栈,当我们提交一个请求道服务器端 action时,就有个堆栈,如果action在服务器端进行跳转,
所有action共用一个堆栈,当需要保存在action中的数据时,首先从栈顶开始 搜索,
若找到相同的属性名(与要获得的数据的属性名相同)时,即将值取出
3、Struts上传下载
上传
1、页面表单设置enctype=”multipart/form-data”,method=”post”
2、在action中定义三个属性,一个File photo,上传文件的文件流;一个String photoContentType,这是上传文件的MIME类型;一个是photoFileName,这是上传文件的文件名。
3、在struts.xml中指定上传处理方法即可。
下载
1、在action中定义两个属性,一个InputStream类型的inputStream用于接收下载文件的输入流;一个String类型的fileName属性,存储下载文件时的名称。这个文件名如果是中文名会出现乱码问题,需要先获取iso8859-1的二进制数据再转化成utf-8的编码,然后将文件名存储到request域中
2、获取下载文件的输入流,赋值给inputStream属性
3、定义stream类型的结果集,设置三个参数,第一个参数contentType的值为application/octet-stream;第二个参数inputName的值为inputStream;第三个参数contentDisposition的值为attachment;filename=${#request.fileName};
4、struts2 漏洞
在2013年6月底发布的Struts 2.3.15版本被曝出存在重要的安全漏洞
1>、可远程执行服务器脚本代码:可以破坏系统让系统无法运行;
2>、重定向漏洞:可以利用其盗取用户密码。
应对方法:省级版本到2.3.15.1即可。
5、过滤器 Filter 和 struts2 拦截器的区别
1、拦截器是基于 java 反射机制的,而过滤器是基于函数回调的。
2、过滤器依赖于 servlet 容器,而拦截器不依赖于 servlet 容器。
3、拦截器只能对 Action 请求起作用,而过滤器则可以对几乎所有请求起作用。
4、拦截器可以访问 Action 上下文、值栈里的对象,而过滤器不能。
5.1 、 struts2 拦截器可以实现哪些功能?
参数解析封装 ParamIntercepter
转换错误 conversionError
校验 validation
控制有错误 跳转input, workflow
防止重复提交 token
文件上传 fileUpload
权限控制、异常处理、统计Action运行时间、记录日志
59、理解Struts2拦截器
1. Struts2拦截器是在访问某个Action或Action的某个方法,字段之前或之后实施拦截,并且Struts2拦截器是可插拔的,拦截器是AOP的一种实现.
2. 拦截器栈(Interceptor Stack)。Struts2拦截器栈就是将拦截器按一定的顺序联结成一条链。
在访问被拦截的方法或字段时,Struts2拦截器链中的拦截器就会按其之前定义的顺序被调用。
60、实现Struts2拦截器原理
Struts2拦截器的实现原理相对简单,当请求struts2的action时,Struts 2会查找配置文件,并根据其配置实例化相对的
拦截器对象,然后串成一个列表,最后一个一个地调用列表中的拦截器
61、定义Struts2拦截器。
Struts2规定用户自定义拦截器必须实现com.opensymphony.xwork2.interceptor.Interceptor接口。该接口声明了3个方法,
其中,init和destroy方法会在程序开始和结束时各执行一遍,不管使用了该拦截器与否,只要在struts.xml中声明了该Struts2拦截器就会被执行。
intercept方法就是拦截的主体了,每次拦截器生效时都会执行其中的逻辑。
6、struts2的工作原理
Struts 2 框架本身大致可以分为 3 个部分:
核心过滤器StrutsPrepareAndExecuterFilter、业务控制器 Action 和用户实现的企业业务逻辑组件。
核心控制器StrutsPrepareAndExecuterFilter是 Struts 2 框架的基础,包含了框架内部的控制流程和处理机制。
业务控制器 Action 和业务逻辑组件是需要用户来自己实现的。
用户在开发 Action 和业务逻辑组件的同时,还需要编写相关的配置文件,供核心控制器StrutsPrepareAndExecuterFilter来使用。
Struts 2 的工作流程相对于 Struts 1 要简单,与 WebWork 框架基本相同。
struts2的工作原理(流程)
1)客户端发出一个HttpServletRequest请求,到web服务器的struts2框架中
》首先经过ActionContextCleanUp过滤器,它主要对ActionContext域对象中的内容进行清理,例如:删除某些错误消息集合
》其它经过其它的一些过滤器,因为struts2框架可以与其它web层框架融合在一起工作,此时就得考虑得其它框架的工作特点,例如:
SiteMesh框架,它主要对web页面进行部局等等
》最后,请求来到struts2框架的核心过滤器,StrutsPrepareAndExecuterFilter
2)创建ActionMapper对象
》如果有不需要处理的资源,例如:html,jpg,或者是程序指定的不需要由struts2框架处理的URL路径,直接放行请求,struts2框架不进行处理
》如果是需要处理的资源,继续交由struts2框架完成后继的操作
3)创建ActionProxy对象
4)创建ConfigurationManager对象
5)加载src/struts.xml程序员写的配置文件
形成一个ActionMapping对象,该对象存有<action>标签的所有配置信息
6)创建ActionInvocation对象
7)将创建好的ActionInvocation对象,交由struts2框架中的默认拦载器栈,依次处理
你将ActionInvocation对象,理解为过滤器链
最好,通过反射调用程序员自己写的目标对象Action的业务方法
8)将Action返回的字符串,与真实的jsp页面对应起来,准备返回
9)返回时,还要依次经过逆序的过滤器链,整个拦载器执行过程,ActionInvocation对象一直存在
10)如果上述操作,都完全正确,web服务器会创建HttpServletResponse对象,响应给客户端浏览器。
Struts的工作流程:
在web应用启动时就会加载初始化ActionServlet,ActionServlet从struts-config.xml文件中读取配置信息,把它们存放到各种配置对象,当ActionServlet接收到一个客户请求时,将执行如下流程.
(1)检索和用户请求匹配的ActionMapping实例,如果不存在,就返回请求路径无效信息;
(2)如果ActionForm实例不存在,就创建一个ActionForm对象,把客户提交的表单数据保存到ActionForm对象中;
(3)根据配置信息决定是否需要表单验证.如果需要验证,就调用ActionForm的validate()方法;
(4)如果ActionForm的validate()方法返回null或返回一个不包含ActionMessage的ActuibErrors对象, 就表示表单验证成功;
(5)ActionServlet根据ActionMapping所包含的映射信息决定将请求转发给哪个Action,如果相应的Action实例不存在,就先创建这个实例,然后调用Action的execute()方法;
(6)Action的execute()方法返回一个ActionForward对象,ActionServlet在把客户请求转发给ActionForward对象指向的JSP组件;
(7)ActionForward对象指向JSP组件生成动态网页,返回给客户;
7、 为什么要使用struts2框架
Struts2 是一个相当强大的Java Web开源框架,是一个基于POJO的Action的MVC Web框架。它基于当年的Webwork和XWork框架,继承其优点,同时做了相当的改进。
1.Struts2基于MVC架构,框架结构清晰,开发流程一目了然,开发人员可以很好的掌控开发的过程。
2、使用OGNL进行参数传递。
OGNL提供了在Struts2里访问各种作用域中的数据的简单方式,你可以方便的获取Request,Attribute,Application,Session,Parameters中的数据。大大简化了开发人员在获取这些数据时的代码量。
3、强大的拦截器
Struts2 的拦截器是一个Action级别的AOP,Struts2中的许多特性都是通过拦截器来实现的,例如异常处理,文件上传,验证等。拦截器是可配置与重用的,可以将一些通用的功能如:登录验证,权限验证等置于拦截器中以完成一些Java Web项目中比较通用的功能。在我实现的的一Web项目中,就是使用Struts2的拦截器来完成了系统中的权限验证功能。
4、易于测试
Struts2的Action都是简单的POJO,这样可以方便的对Struts2的Action编写测试用例,大大方便了Java Web项目的测试。
5、易于扩展的插件机制在Struts2添加扩展是一件愉快而轻松的事情,只需要将所需要的Jar包放到WEB-INF/lib文件夹中,在struts.xml中作一些简单的设置就可以实现扩展。
6、模块化管理
Struts2已经把模块化作为了体系架构中的基本思想,可以通过三种方法来将应用程序模块化:将配置信息拆分成多个文件把自包含的应用模块创建为插件创建新的框架特性,即将与特定应用无关的新功能组织成插件,以添加到多个应用中去。
7、全局结果与声明式异常
为应用程序添加全局的Result,和在配置文件中对异常进行处理,这样当处理过程中出现指定异常时,可以跳转到特定页面。
他的如此之多的优点,是很多人比较的青睐,与spring ,Hibernate进行结合,组成了现在比较流行的ssh框架,当然每个公司都要自己的框架,也是ssh变异的产品。
8、 struts2 有哪些优点
1)在软件设计上Struts2的应用可以不依赖于Servlet API和struts API。 Struts2的这种设计属于无侵入式设计;
2)拦截器,实现如参数拦截注入等功能;
3)类型转换器,可以把特殊的请求参数转换成需要的类型;
4)多种表现层技术,如:JSP、freeMarker、Velocity等;
5)Struts2的输入校验可以对指定某个方法进行校验;
6)提供了全局范围、包范围和Action范围的国际化资源文件管理实现
第三章、hibernate
Hibernate 是轻量级 JavaEE 应用的持久层解决方案,是一个关系型数据库 完全的 ORM 框架。
Hibernate的核心接口一共有6个,分别为:Session、SessionFactory、Transaction、Query、Criteria和Configuration。这6个核心接口在任何开发中都会用到。通过这些接口,不仅可以对持久化对象进行存取,还能够进行事务控制。
Session
Session接口负责执行被持久化对象的CRUD操作(CRUD的任务是完成与数据库的交流,包含了很多常见的SQL语句。)。但需要注意的是Session对象是非线程安全的。同时,Hibernate的session不同于JSP应用中的HttpSession。这里当使用session这个术语时,其实指的是Hibernate中的session,而以后会将HttpSession对象称为用户session。
SessionFactory
SessionFactory接口负责初始化Hibernate。它充当数据存储源的代理,并负责创建Session对象。这里用到了工厂模式。需要注意的是SessionFactory并不是轻量级的,因为一般情况下,一个项目通常只需要一个SessionFactory就够,当需要操作多个数据库时,可以为每个数据库指定一个SessionFactory。
Transaction
Transaction 接口是一个可选的API,可以选择不使用这个接口,取而代之的是Hibernate 的设计者自己写的底层事务处理代码。 Transaction 接口是对实际事务实现的一个抽象,这些实现包括JDBC的事务、JTA 中的UserTransaction、甚至可以是CORBA 事务。之所以这样设计是能让开发者能够使用一个统一事务的操作界面,使得自己的项目可以在不同的环境和容器之间方便地移植。
Query
Query接口让你方便地对数据库及持久对象进行查询,它可以有两种表达方式:HQL语言或本地数据库的SQL语句。Query经常被用来绑定查询参数、限制查询记录数量,并最终执行查询操作。
Criteria
Criteria接口与Query接口非常类似,允许创建并执行面向对象的标准化查询。值得注意的是Query接口也是轻量级的,它不能在Session之外使用。
Configuration
Configuration 接口的作用是对Hibernate 进行配置,以及对它进行启动。在Hibernate 的启动过程中,Configuration 类的实例首先定位映射文档的位置,读取这些配置,然后创建一个SessionFactory对象。虽然Configuration 接口在整个Hibernate 项目中只扮演着一个很小的角色,但它是启动hibernate 时所遇到的每一个对象。
1、实体对象的状态
瞬态:对象刚创建,还没有和session关联,不处于session的缓存中。
持久化状态:对象跟session关联了,处于session的缓存中。
脱管状态(游离态)——已经被持久化,但不处于session的缓存中。
2、检索策略(抓取策略)
hibernate的检索策略分为类级别的检索、关联级别的检索、批量检索三种
类级别的检索:session.load(),默认这个是懒加载一个对象,只有在用的时候才会真正的去加载这个对象。
关联级别的检索:从一的一方检索多的一方,从多的一方检索一的一方,双向检索
3、hibernate处理并发
hibernate中,如果不考虑事务的级别,可能出现脏读、不可重复度,幻读(虚读)和丢失更新的现象。所谓丢失更新,就是后操作的事务覆盖了前面提交的事务的结果。在hibernate中,为了解决丢失更新的现象,可以使用悲观锁和乐观锁,所谓悲观锁,就是认为丢失更新一定会发生,在查询一条数据的时候就对它进行锁定,只有操作完了,别人才能操作,在操作期间,别人哪怕是想查询都不行,在查询对象的时候设置LockOptions.UPGRADE参数即可。所谓乐观锁,通过在数据库添加一个版本号字段,就是我在查询的时候拿到一个版本号,另外一个事务查询的时候也拿到一个版本号,当我进行修改的时候,数据库中的版本号会更改,另外一个事务提交的时候,数据库发现版本号不一致就会报错。在hibernate映射文件中配置一个版本号字段即可。
以上现象如果考虑事务隔离级别的话是不会有问题的,hibernate中默认的事务隔离级别是READ COMMITED,不会造成以上问题。如果需要修改,配置一下即可。
4、hibernate二级缓存
hibernate中的二级缓存是基于sessionFactory级别的缓存,二级缓存分为更新时间戳区、类级别的缓存区、集合级别的缓存去、查询缓存区。
1>.对于类级别的缓存,存放的是对象中散列数据,比如id=1,name=”张三”这样的形式存储。如果配置了hibernate的类级别的缓存,第一次查询出一个对象后,下次就不会到数据库里去查了,直接从二级缓存中取出来用。
2>.对于集合级别的缓存:比如说部门员工的关系,部门的PO对象中引用了员工的Set集合数据,当第一次查询出部门对象的时候,假如说员工都查询出来了,当下次再查询的时候就不需要去数据库里查了,hibernate会直接从二级缓存中取出来。对于集合级别缓存,集合中存储的都是对象的oid,具体的数据都存储在类级别的缓存区中,要用的时候再重新组装数据。当我集合缓存了2条数据,下次我要查3条的时候,hibernate会根据oid判断,如果前面2条数据的oid已经存在集合缓存区里了,就不再去数据库查了,只会去数据库中查询那条没有存在缓存中的数据。
3>.查询缓存区:查询缓存区的原理是以查询的sql语句作为key,查询的结果作为value,存储在一个map集合中,value中,存的都是oid,具体的值也存放在类级别的缓存区中。当下次再查询的时候,会从这个map中去查找这个sql语句,如果又是相同的sql语句,则直接从用sql语句作为key从map集合中取出来。否则就去数据库查询。
4>.更新时间戳区:更新时间戳区的作用是更新二级缓存中的数据,当你数据库中的数据发生变化的时候,hibernate会更新二级缓存中的数据。如果是直接从库里改的,是不会更新二级缓存的,只有从前台更改的数据hibernate才会去更新。
介绍一下Hibernate的二级缓存
提示(1)首先说清楚什么是缓存,(2)再说有了hibernate的Session就是一级缓存,即有了一级缓存,为什么还要有二级缓存,(3)最后再说如何配置Hibernate的二级缓存。
(1)缓存就是把以前从数据库中查询出来和使用过的对象保存在内存中(一个数据结构中),这个数据结构通常是或类似Hashmap,当以后要使用某个对象时,先查询缓存中是否有这个对象,如果有则使用缓存中的对象,如果没有则去查询数据库,并将查询出来的对象保存在缓存中,以便下次使用
2)Hibernate的Session就是一种缓存,我们通常将之称为Hibernate的一级缓存,当想使用session从数据库中查询出一个对象时,Session也是先从自己内部查看是否存在这个对象,存在则直接返回,不存在才去访问数据库,并将查询的结果保存在自己内部。由于Session代表一次会话过程,一个Session与一个数据库连接相关连,所以Session最好不要长时间保持打开,通常仅用于一个事务当中,在事务结束时就应关闭。并且Session是线程不安全的,被多个线程共享时容易出现问题。通常只有那种全局意义上的缓存才是真正的缓存应用,才有较大的缓存价值,因此,Hibernate的Session这一级缓存的缓存作用并不明显,应用价值不大。Hibernate的二级缓存就是要为Hibernate配置一种全局缓存,让多个线程和多个事务都可以共享这个缓存。我们希望的是一个人使用过,其他人也可以使用,session没有这种效果。
(3)二级缓存是独立于Hibernate的软件部件,属于第三方的产品,多个厂商和组织都提供有缓存产品,例如,EHCache和OSCache等等。在Hibernate中使用二级缓存,首先就要在hibernate.cfg.xml配置文件中配置使用哪个厂家的缓存产品,接着需要配置该缓存产品自己的配置文件,最后要配置Hibernate中的哪些实体对象要纳入到二级缓存的管理中。明白了二级缓存原理和有了这个思路后,很容易配置起Hibernate的二级缓存。扩展知识:一个SessionFactory可以关联一个二级缓存,也即一个二级缓存只能负责缓存一个数据库中的数据,当使用Hibernate 的二级缓存后,注意不要有其他的应用或SessionFactory来更改当前数据库中的数据,这样缓存的数据就会与数据库中的实际数据不一致。
什么样的数据适合存放到第二级缓存中?
1 很少被修改的数据
2 不是很重要的数据,允许出现偶尔并发的数据
3 不会被并发访问的数据
4 常量数据
不适合存放到第二级缓存的数据?
1 经常被修改的数据
2 .绝对不允许出现并发访问的数据,如财务数据,绝对不允许出现并发
3 与其他应用共享的数据。
49、Hibernate查找对象如何应用缓存?
当Hibernate根据ID访问数据对象的时候,首先从Session一级缓存中查;查不到,如果配置了二级缓存,那么从二级缓存中查;如果都查不到,再查询数据库,把结果按照ID放入到缓存,删除、更新、增加数据的时候,同时更新缓存。Hibernate管理缓存实例无论何时,当你给save()、update()或saveOrUpdate()方法传递一个对象时,或使用load()、 get()、list()、iterate() 或scroll()方法获得一个对象时, 该对象都将被加入到Session的内部缓存中。 当随后flush()方法被调用时,对象的状态会和数据库取得同步。 如果你不希望此同步操作发生,或者你正处理大量对象、需要对有效管理内存时,你可以调用evict() 方法,从一级缓存中去掉这些对象及其集合。
50.Hibernate的检索方式
答:①导航对象图检索 ②OID检索 ③HQL检索 ④QBC检索 ⑤本地SQL检索
1.导航对象图检索方式。(根据已经加载的对象,导航到其他对象。)
2.OID检索方式。(按照对象的OID来检索对象。)
3.HQL检索方式。(使用面向对象的HQL查询语言。)
4.QBC检索方式。(使用QBC(Qurey By Criteria) API来检索对象。)
5.本地SQL检索方式。(使用本地数据库的SQL查询语句。)
hibernate性能优化
初用HIBERNATE的人也许都遇到过性能问题,实现同一功能,用HIBERNATE与用JDBC性能相差十几倍很正常,如果不及早调整,很可能影响整个项目的进度。 大体上,对于HIBERNATE性能调优的主要考虑点如下: Ø 数据库设计调整
.HQL优化
.API的正确使用(如根据不同的业务类型选用不同的集合及查询API)
.主配置参数(日志,查询缓存,fetch_size, batch_size等)
.映射文件优化(ID生成策略,二级缓存,延迟加载,关联优化)
.一级缓存的管理
.针对二级缓存,还有许多特有的策略
.事务控制策略。
数据库设计
a) 降低关联的复杂性
b) 尽量不使用联合主键
c) ID的生成机制,不同的数据库所提供的机制并不完全一样
d) 适当的冗余数据,不过分追求高范式
Hibernate中怎样实现类之间的关系?(如:一对多、多对多的关系)
类与类之间的关系主要体现在表与表之间的关系进行操作,它们都是对对象进行操作,我们在程序中把所有的表与类都映射在一起,它们通过配置文件中的many-to-one、one-to-many、many-to-many、
HQL优化
HQL如果抛开它同HIBERNATE本身一些缓存机制的关联,HQL的优化技巧同普通的SQL优化技巧一样,可以很容易在网上找到一些经验之谈。
如何优化Hibernate?
使用双向一对多关联,不使用单向一对多
* 灵活使用单向一对多关联
* 不用一对一,用多对一取代
* 配置对象缓存,不使用集合缓存
* 一对多集合使用Bag,多对多集合使用Set
* 继承类使用显式多态
* 表字段要少,表关联不要怕多,有二级缓存撑腰
主配置
a) 查询缓存,同下面讲的缓存不太一样,它是针对HQL语句的缓存,即完全一样的语句再次执行时可以利用缓存数据。但是,查询缓存在一个交易系统(数据变更频繁,查询条件相同的机率并不大)中可能会起反作用:它会白白耗费大量的系统资源但却难以派上用场。
b) fetch_size,同JDBC的相关参数作用类似,参数并不是越大越好,而应根据业务特征去设置
c) batch_size同上。
d) 生产系统中,切记要关掉SQL语句打印。
缓存
a) 数据库级缓存:这级缓存是最高效和安全的,但不同的数据库可管理的层次并不一样,比如,在Oracle中,可以在建表时指定将整个表置于缓存当中。
b) SESSION缓存:在一个HibernateSESSION有效,这级缓存的可干预性不强,大多于HIBERNATE自动管理,但它提供清除缓存的方法,这在大批量增加/更新操作是有效的。比如,同时增加十万条记录,按常规方式进行,很可能会发现OutofMemeroy的异常,这时可能需要手动清除这一级缓存:Session.evict以及 Session.clear
c) 应用缓存:在一个SESSIONFACTORY中有效,因此也是优化的重中之重,因此,各类策略也考虑的较多,在将数据放入这一级缓存之前,需要考虑一些前提条件:
i. 数据不会被第三方修改(比如,是否有另一个应用也在修改这些数据?)
ii. 数据不会太大
iii. 数据不会频繁更新(否则使用CACHE可能适得其反)
iv. 数据会被频繁查询
v. 数据不是关键数据(如涉及钱,安全等方面的问题)。
缓存有几种形式,可以在映射文件中配置:read-only(只读,适用于很少变更的静态数据/历史数据),nonstrict-read- write,read-write(比较普遍的形式,效率一般),transactional(JTA中,且支持的缓存产品较少)
d) 分布式缓存:同c)的配置一样,只是缓存产品的选用不同,oscache, jboss cache,的大多数项目,对它们的用于集群的使用(特别是关键交易系统)都持保守态度。在集群环境中,只利用数据库级的缓存是最安全的。
延迟加载
a) 实体延迟加载:通过使用动态代理实现
b) 集合延迟加载:通过实现自有的SET/LIST,HIBERNATE提供了这方面的支持
c) 属性延迟加载:
十二、Hibernate是如何延迟加载?
当Hibernate在查询数据的时候,数据并没有存在于内存中,当程序真正对数据的操作时,对象才存在于内存中,就实现了延迟加载,他节省了服务器的内存开销,从而提高了服务器的性能。
Hibernate工作原理(编程步骤)
1.读取并解析Hibernate核心配置文件hibernate.cfg.xml
2.读取并解析Hibernate映射文件,创建SessionFactory
3.打开Sesssion sessionFactory.openSession();//得到session。
4.创建事务Transation session.beginTransaction();//开启事务。
5.持久化操作persistent operate;
6.提交事务session.getTransaction().commit();//提交事务
7.关闭Session
8.关闭SesstionFactory
hibernate优点:
1、封装了jdbc,简化了很多重复性代码。
2、简化了DAO层编码工作,使开发更对象化了。
3、移植性好,支持各种数据库,如果换个数据库只要在配置文件中变换配置就可以了,不用改变hibernate代码。
4、支持透明持久化,因为hibernate操作的是纯粹的(pojo,实际就是普通JavaBeans )java类,没有实现任何接口,没有侵入性。所以说它是一个轻量级框架。
hibernate延迟加载:
get不支持延迟加载,load支持延迟加载。
1、hibernate2对 实体对象和集合 实现了延迟加载
2、hibernate3对 提供了属性的延迟加载功能
hibernate延迟加载就是当使用session.load(User.class,1)或者session.createQuery()查询对象或者属性的时候
这个对象或者属性并没有在内存中,只有当程序操作数据的时候,才会存在内存中,这样就实现延迟加载,节省了内存的开销,从而提高了服务器的性能。
Hibernate的缓存机制
一级缓存:session级的缓存也叫事务级的缓存,只缓存实体,生命周期和session一致。不能对其进行管理。
不用显示的调用。
二级缓存:sessionFactory缓存,也叫进程级的缓存,使用第3方插件实现的,也值缓存实体,生命周期和sessionFactory一致,可以进行管理。
首先配置第3方插件,我们用的是EHCache,在hibernate.cfg.xml文件中加入
<propertyname="hibernate.cache.user_second_level_cache">true</property>
在映射中也要显示的调用,<cacheusage="read-only"/>
二级缓存之查询缓存:对普通属性进行缓存。如果关联的表发生了修改,那么查询缓存的生命周期也结束了。
在程序中必须手动启用查询缓存:query.setCacheable(true);
优化Hibernate
1、使用一对多的双向关联,尽量从多的一端维护。
2、不要使用一对一,尽量使用多对一。
3、配置对象缓存,不要使用集合缓存。
4、表字段要少,表关联不要怕多,有二级缓存撑腰。
hibernate 类与类之间关系
关联关系 聚集关系 继承关系
Hibernate继承关系映射策略分为三种:一张表对应一整棵类继承树、一个类对应一张表、每一个具体类对应一张表。
为什么要使用Hibernate框架?它有什么优势?
² Hibernate对JDBC访问数据库的代码做了封装,大大简化了数据访问层繁琐的重复性代码。
² Hibernate是一个基于JDBC的主流持久化框架,是一个优秀的ORM实现,它很大程度的简化了DAO层编码工作。
² Hibernate使用Java的反射机制,而不是字节码增强程序类实现透明性。
² 因为它是一个轻量级框架。映射的灵活性很出色。它支持很多关系型数据库,从一对一到多对多的各种复杂关系。
Hibernate中load方法和get方法区别
区别1:通过get方法加载,如果数据库中没有对应的记录,则返回的是null值;
如果通过load方法加载,则返回一个代理对象,当通过user对象调用某个方法(比如user.getPassword())时,会抛出异常:org.hibernate.ObjectNotFoundException;
区别2:load支持延迟加载,get不支持延迟加载。
第四章、Spring
1、谈谈你对spring的理解
spring类似于一个大的容器,具有非常强大的整合作用,可以整合市面上绝大部分的j2ee框架,spring的核心是依赖注入(DI)、控制反转(IOC)和面向切面编程(AOP)。
1>.所谓依赖注入,就是叫对象之间的依赖关系交给spring容器来管理,比如说A对象中用到了B对象,那么在初始化A对象的时候,spring容器会将B对象进行实例化同时将B对象注入A对象中的B属性。
2>.所谓控制反转就是将单例对象的创建、初始化和销毁工作交给spring来处理,让spring容器来管理对象的生命周期。
3>. 所谓AOP编程就是将业务逻辑代码和非业务逻辑代码分离,每一类非业务逻辑代码(比如事务、日志、权限等)封装成一个独立的切面,当需要对一个目标类中的方法进行事务控制的时候,我们可以通过spring的切面配置和表达式给某些指定的目标类加上事务控制的功能,不需要事务控制的时候,我们卸载事务控制功能,这种AOP的方式做到了非业务逻辑代码的可插拔式编程,不仅有利于团队开发还有利于后期的维护工作。
3>. 另外,spring在对数据库的操作方面提供了一整套的JDBC编程模版,利用这套模版,程序员可以非常方便和简单的实现数据库的访问
IOC:控制反转,就是应用本身不负责依赖对象的创建和维护,依赖对象的创建及维护是由外部容器负责,这样控制权由应用转移到了外部容器
控制权的转移就是所谓的反转,目的为了获得更好的扩展性和良好的可维护性。
2、Spring框架有哪几部分组成?
Spring框架有七个模块组成组成,这7个模块(或组件)均可以单独存在,也可以与其它一个或多个模块联合使用,主要功能表现如下:
☞ Spring 核心容器(Core):提供Spring框架的基本功能。核心容器的主要组件是BeanFactory,她是工厂模式的实现。BeanFactory使用控制反转(Ioc)模式将应用程序的配置和依赖性规范与实际的应用代码程序分开。
☞ Spring AOP:通过配置管理特性,Spring AOP模块直接面向方面的编程功能集成到了Spring框架中,所以可以很容易的使Spring框架管理的任何 对象支持 AOP。Spring AOP模块为基于Spring的应用程序中的对象提供了事务管理服务。通过使用Spring AOP,不用依赖于EJB组 件,就可以将声明性事务管理集成到应用程序中。
☞ Spring ORM:Spring框架集成了若干ORM框架,从而提供了ORM的对象关系工具,其中包括 JDO、Hibernate、iBatis和TopLink。所有这些都遵从 Spring的通用事务和DAO异常层结构。
☞ Spring DAO:JDBC DAO抽象层提供了有意义的异常层次的结构,可用该结构来管理异常处理和不同数据供应商抛出的异常错误信息。异常层次结 构简化了错误处理,并且大大的降低 了需要编写的异常代码数量(例如,打开和关系连接)。Spring DAO的面向JDBC的异常遵从 通 用的DAO异常层结构。
☞ Spring WEB:Web上下文模块建立在上下文模块(Context)的基础之上,为基于Web服务的应用程序提供了上下文的服务。所以Spring框架支持 Jakarta Struts的集成。Web模块还简化了处理多部分请求及将请求参数绑定到域对象的工作。
☞ Spring上下文(Context):Spring上下文是一个配置文件,向Spring框架提供上下文信息。Spring上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化校验和调度功能。
☞ Spring MVC:Spring的MVC框架是一个全功能的构建Web应用程序的MVC实现。通过策略接口,MVC框架变成为高度可配置的,MVC容纳的大 量视图技术,包括JSP、Velocity、Tiles、iText和Pol
28、spring 的优点?
1.降低了组件之间的耦合性 ,实现了软件各层之间的解耦
2.可以使用容易提供的众多服务,如事务管理,消息服务等
3.容器提供单例模式支持
4.容器提供了AOP技术,利用它很容易实现如权限拦截,运行期监控等功能
5.容器提供了众多的辅助类,能加快应用的开发
6.spring对于主流的应用框架提供了集成支持,如hibernate,JPA,Struts等
7.spring属于低侵入式设计,代码的污染极低
8.独立于各种应用服务器
9.spring的DI机制降低了业务对象替换的复杂性
10.Spring的高度开放性,并不强制应用完全依赖于Spring,开发者可以自由选择spring的部分或全部
Spring框架的优点都有什么?
Spring是分层的架构,你可以选择使用你需要的层而不用管不需要的部分
Spring是POJO编程,POJO编程使得可持续构建和可测试能力提高
依赖注入和IoC使得JDBC操作简单化
Spring是开源的免费的
Spring使得对象管理集中化合简单化
如果我自己写一个Spring框架,应该怎么做呢?
1、我需要我的框架使用者定义一个xml配置文件,这个配置文件中,配置使用者项目中哪些对象需要我的Spring容器来进行管理,这个配置文件不能瞎写,所以,我需要写一个dtd文件,用schema约束对使用者的这个xml文件进行约束。
2、我需要写一个监听器,这个监听器,需要使用者在web.xml配置文件中进行注册,在监听器中,我定义一个静态的Map集合,map集合的键用于存储对象的实例名称,map集合的值用于存储具体的对象实例,在监听器的初始化方法中,我加载用户配置的xml配置文件,利用dom4j技术解析该配置文件,利用反射技术和工厂设计模式给配置文件中每个对象中的属性注入值,让我的容器来管理配置文件中所有对象的生命周期。
3、我需要定义一个类,实现InvocationHandler接口,并重写invoke方法,在该方法中,我需要读取xml配置文件中定义的前置通知和后置通知,在调用目标方法前执行xml配置文件中指定切面的前置通知,然后调用目标方法,在调用目标方法后执行xml配置文件中指定切面的后置通知。
什么是 AOP
Aspect Oriented Programming 面向切面编程 ,业界 AOP 实际上 OOP (面向对象编程 ) 延伸 ---- OOP 编程语言、 AOP 设 计思想。
AOP 采取横向抽取机制, 取代了 传统纵向继承体系 重复性代码(性能监视、 事务 管理、安全检查、缓存)
横向抽取代码复用, 基于代理技术, 在不修改原有对象代码情况下, 对原有对象 方法功能进行增强! ---------- AOP 思想
Spring的AOP 的理解:
Spring默认采取的动态代理机制实现AOP,当动态代理不可用时(代理类无接口)会使用CGlib机制。但Spring的AOP有一定的缺点,第一个只能对方法进行切入,不能对接口,字段,静态代码块进行切入(切入接口的某个方法,则该接口下所有实现类的该方法将被切入)。第二个同类中的互相调用方法将不会使用代理类。因为要使用代理类必须从Spring容器中获取Bean。第三个性能不是最好的,从3.3章节我们得知使用自定义类加载器,性能要优于动态代理和CGlib。
面向切面编程(AOP)完善spring的依赖注入(DI),面向切面编程在spring中主要表现为两个方面
1.面向切面编程提供声明式事务管理
2.spring支持用户自定义的切面
Spring AOP编程原理?项目中如何使用的?
将传统继承的代码复用,变为代码的横向抽取机制,通过代理机制,向目标对象织入增强代码。
Spring AOP底层基于 Jdk动态代理和Cglib 动态代理实现。
项目中用到的Spring中的切面编程最多的地方:声明式事务管理。
a、定义一个事务管理器
b、配置事务特性(相当于声明通知。一般在业务层的类的一些方法上定义事务)
c、配置哪些类的哪些方法需要配置事务(相当于切入点。一般是业务类的方法上)
Spring 为了细分组件的功能,在提供@Component 注解,提供三个等价的注解
@Controller 控制器,表示 web 层组件 ---- action
@Service 业务类,表示业务层组件 --- service
@Repository 表示持久层的组件 --- dao
AOP 相关术语
n Joinpoint(连接点) : 所谓连接点是指那些被拦截到的点。 在 spring 中, 这些点指的是方法, 因为 spring 只支持方法类型的连接点.
n Pointcut(切入点): 所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义.
n Advice(通知/增强) : 所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知.
通知分为前置通知, 后置通知, 异常通知, 最终通知, 环绕通知(切面要完成的功能)
n Introduction(引介): 引介是一种特殊的通知在不修改类代码的前提下,Introduction 可以在运行期为类动态地添加一些方法或 Field.
n Target(目标对象): 代理的目标对象
n Weaving(织入): 是指把增强应用到目标对象来创建新的代理对象的过程。
n spring 采用动态代理织入, 而 AspectJ 采用编译期织入和类装载期织入。
n Proxy(代理) : 一个类被 AOP 织入增强后,就产生一个结果代理类。
n Aspect(切面): 是切入点和通知(引介)的结合
execution(* cn.itcast.service..*.*(..)) 拦截 service 包包含子包下所有类 所有方法 execution(* *.s*(..)) 拦截以 s 开头的方法。
小结:
前置通知(权限控制)
后置通知 ---- 不怎么用
环绕通知(权限控制、 性能监控、 缓存技术 )
异常通知 (发生异常后, 记录错误日志 )
最终通知 (释放资源 )
环绕通知 是取代任何通知效果
DEFAULT 每种数据库提供默认隔离级别
mysql REPEATABLE_READ (解决脏读、不可重复读、 引发虚读)、
oracle READ_COMMITTED(解决脏读, 引发(不可重复读、虚读))
解决延迟加载问题:在 struts2 过滤器前,配置 OpenSessionInViewFilter,一定要配置在struts2过滤器前。
AOP实战
说了这么多理论,那AOP到底能做什么呢? AOP能做的事情非常多。
性能监控,在方法调用前后记录调用时间,方法执行太长或超时报警。
缓存代理,缓存某方法的返回值,下次执行该方法时,直接从缓存里获取。
软件破解,使用AOP修改软件的验证类的判断逻辑。
记录日志,在方法执行前后记录系统日志。
工作流系统,工作流系统需要将业务代码和流程引擎代码混合在一起执行,那么我们可以使用AOP将其分离,并动态挂接业务。
权限验证,方法执行前验证是否有权限执行当前方法,没有则抛出没有权限执行异常,由业务代码捕捉。
第五章、mybatis和hibernate的比较
相同点:
资源
hibernate
mybatis
配置文件
hibernate.cfg.xml
sqlMapConfig.xml
实体类
User.java
User.java
实体类映射文件
User.hbm.xml
UserMapper.xml
CRUD操作步骤
1、加载配置文件
2、构建session工厂
3、获取session对象
4、操作数据库获取结果集
5、关闭Session
跟hibernate是一样的
当实体类中的属性和数据库中的字段不对应的时候
可以通过在hbm.xml文件中设置字段和属性的映射关系来解决
可以通过在写sql语句的时候给字段取别名的方式来解决
不同点:
1、hibernate是面向对象的ORM框架,而mybatis是面向结果集的;
2、hibernate的学习难度相对mybatis来说要难一点。
3、性能方面,hibernate每次都是查询所有的字段,虽然可以通过投影查询来查询指定属性,或者通过抓取策略和二级缓存来提高性能,但是想对来说,性能还是差一点,而mybatis都是通过原生态的sql语句来获取结果集的,所以性能肯定要优于hibernate。
4、对于使用hibernate开发,开发人员甚至都不需要非常熟悉sql语言就可以以面向对象的思维去开发程序,但是mybatis需要对sql较为熟练才行。
5、可扩展性:由于hibernate的HQL语句是与数据库无关的,所以在不同数据库之前的迁移是非常方便的,但是mybatis开发的项目一旦需要做数据库迁移,那将是非常大的工作量。
6、两者都有一级缓存和二级缓存,但是mybatis的二级缓存配置想读较为简单。
7、hibernate的dao层开发比mybatis的dao层的开发要快、更简单。
第六章、SpringMVC
第七章:前台技术(ajax、jquery)
1、同步和异步的区别
同步:需要等到一个操作完了才能做另外一个操作
异步:你做一件事情的时候,我可以做另外一件事情,两件事情可以同时做
异步的好处:使web开发的项目页面性能提高了,因为不需要像以前一样,如果要更新页面的部分信息需要整个页面提交,再从服务器端下载整个页面;现在有了ajax异步交互,我可以局部刷新,这样不仅减少了带宽、还增加了web应用的反应速度。
类似的异步技术还有iframe框架、flash和java applet。
2、ajax的核心对象是javascript的XmlHttpRequest
3、ajax不是一项新技术,而是多种技术的综合,ajax=javascript+xhtml+css+dom+xml+XMLHttpRequest
4、ajax开发的步骤:(以下xhr代表XMLHttpRequest对象)
1>.创建XMLHttpRequest对象:不同的浏览器创建对象的方式不一样
//创建XMLHttpRequest对象是固定写法:背下来
function ajaxFunction(){
var xmlHttp;
try{ // Firefox, Opera 8.0+, Safari
xmlHttp=new XMLHttpRequest();
}
catch (e){
try{// Internet Explorer
xmlHttp=new ActiveXObject("Msxml2.XMLHTTP");
}
catch (e){
try{
xmlHttp=new ActiveXObject("Microsoft.XMLHTTP");
}
catch (e){}
}
}
return xmlHttp;
}
2>.监听服务器状态:xhr.onreadystatechange=function(){}
3>.客户端与服务端建立连接:xhr.open(“get”,”abc.do”)
4>.客户端向服务端发送请求:xhr.send()
不过,现在都用jQuery了,上面这种方式很少用了,用jQuery,写个$.ajax();就可以了。
5、jQuery十个选择器
如果想在面试jQuery的时候没问题,这个十大选择器还是必须会的,如果觉得太多了,最少要把属性选择器熟透了。
5.1基本选择器:$(“#mydiv”)、$(“div”)、$(“.myClass”)
5.2.层次选择器:$(“form input”) 表示form表单下的input标签、$(“form>input”)表示form表单下的所有input标签、$(“label+input”)匹配紧接在label标签后的input元素、$(“label~input”)匹配label标签后的所有同辈的input元素
5.3.过滤选择器:该选择器除了属性选择器以外,都以:开头。
5.3.1>、基础过滤选择器:$(“tr:first”)查找第一个tr标签、$(“tr:last”)查找最后一个tr标签、$(“input:not(checked)”)匹配没有选中的input框、$(“tr:even”)匹配所有索引为偶数的tr标签、$(“tr:odd”)匹配所有索引为奇数的tr标签、$(“tr:eq(0)”)获取第一个tr标签、$(“tr:gt(3)”)获取索引大于3的所有tr标签、$(“tr:lt(3)”)获取所有索引小于3的tr标签、$(“:header”)匹配所有h1,h2,h3这样的标签、$(“:animated”)匹配所有正在执行动画效果的元素;
5.3.2>、内容过滤选择器:$(“div:contains(‘zhoule’)”)匹配内容中包含了zhoule的所有div元素、$(“div:empty”)匹配所有不包含子元素或者是文本的空元素、$(“div:has(p)”)匹配所有包含了p标签的div元素、$(“div:parent”)匹配所有含有子元素或者文本的元素
5.3.3>、可见度过滤选择器:$(“div:hidden”)获取所有隐藏的div标签、$(“div:visible”)获取所有可见的div标签。
5.3.4>、属性过滤选择器:
$(“div[id]”)匹配所有带id属性的div标签、$(“input[name=’username’]”)匹配所有name属性为”username”的input标签、$(“input[name!=’username’]”)匹配所有name属性不为”username”的input标签、$(“input[name^=’us’]”)匹配所有name属性的值以”us”开头的input标签、$(“input[name$=’me’]”)匹配所有name属性的值以”me”结尾的input标签、$(“input[name*=’us’]”)匹配所有name属性的值包含”us”的input标签、$(“input[id]input[name^=’us’]”)匹配所有包含id属性且name属性的值以”us”开头的input标签;
在过滤器中,属性过滤选择器是重点,只要问jQuery,这里面的东西都是必问的。
5.3.5>、子元素过滤选择器:这个太麻烦,用的也少,就不写了
5.3.6>、表单对象属性过滤器:$(“input:enable”)匹配所有可用的input元素、$(“input:disable”)匹配所有不可用的input元素、$(“input:checked”)匹配所有选中了的单选框或复选框但不包含select标签中选中的option、$(“select option:selected”)匹配所有select标签下选中了的option
5.3.7>、表单选择器:$(“:input”)匹配所有的input元素、$(“:text”)匹配所有的text元素、$(“:password”)匹配所有的password元素、$(“:radio”)匹配所有的radio元素、$(“:checkbox”)匹配所有的checkbox元素、$(“:file”)匹配所有file元素。
第八章:数据库
1、oracle数据库
1) Having和WHERE的区别?
where是先从表中按条件查询,having对查询结果表再进行条件过滤
2)如何使用Like关键字实现模糊查询?
like 关键字用于 where子句中来完成模糊查询功能,经常配合关键字%、_使用,%表示任意一个字符串,_表示任意的一个字符。例如
select * from users where name like ‘张%’; -- 查询users表中所有name以张开头的数据
select * from articles where content not like ‘%java%’; -- 查询文章表中内容不包含java的文章
select * from users where name like ‘刘_’; -- 查询users表中name以张开头,并且姓名两个字的数据记录。
2、mysql数据库
2.1>、explain命令:sql语句的执行查询计划,通过此命令我们可以知道mysql优化器是如何执行sql语句的。命令不给调整建议,但可以帮助我们去做调优决策。
2.2>、当数据库中数据量过大时,有什么方案进行优化?
a、在数据库端,可以进行分库、分表、分区操作。
所谓分库,就是将数据分别存储在多个数据库上,搞几台服务器,每台服务器上的数据库都是一样的表结构,假如A服务器上的A表里存100W条用户数据,超过100W的数据我就存储到B服务器上的B表里,在查询用户数据的时候进行逻辑判断,如果要查询前100W的数据则连接A服务器,要查询后面的数据则连接B服务器。
所谓分表,就是将数据分多张表存储。分表技术有横向分表和纵向分表两种,先说说横向分表,横向分表就是将表中的多条记录分装在多个表里,比如A表存100W,B表存100万条,假如说现在有一个全国人口信息表,有14亿的数据,都存储到一张表里显然是不切实际的,这时候可以考虑分表技术,将人口按省份分表存储,比如原来的人口信息表叫personinfo_c,现在按省份来存储,我们需要针对每个省份建一张表,湖南省的人口信息存储到personinfo_hunan_c,北京市的人口信息存储到personinfo_beijing_c。这种做法,每个表存储在硬盘上的文件是不一样的,当新增人口信息的时候要进行逻辑判断,判断是哪个地方的人就存储到哪个表里去。所谓纵向分表,就是将A表中的几个字段放到一个表中,A表中的另外几个字段放到另外的一个表中,这种情况发生在当一个表中有些字段查询多,有些字段执行新增次数多的情况下。
所谓分区,就是将一张表分成多个区,比如说新浪的新闻信息都存储在一张表里,有上亿的数据,如果不分表或者分区的话,每次查询都是灾难性的,查一条数据可能得好几天才能查出来,我们可以将新闻数据按天进行分区,这样每一天的数据都会存储在硬盘上不同的文件里,查询起来会比较快,跟分表是一样的原理,只不过分区的话所有的数据还是在一个表里,分表的话就是两张表了。
b、建立索引
建立索引的好处是避免每次都进行全表扫描,当表中数据过多时,进行全表扫描是非常耗时的,建立索引有助于提升查询性能。
mysql的索引分为普通索引、唯一索引、主键索引(当给一个表建立主键时,默认就给这个表的这个字段建立了主键索引了)和组合索引四种。
什么时候使用索引呢?一般来说,在WHERE和JOIN中出现的列需要建立索引,但也不完全如此,MySQL只对<、<=、=、>、>=、BETWEEN、IN、LIKE才会使用索引。比如我的sql语句为:SELECT NAME,AGE,ADDRESS FROM USERS WEHRE NAME=’ZHANGSAN’ AND ADDRESS LIKE ‘bei%’,在这条语句中,由于where后面使用了name和address作为查询过滤条件,这时候,我们就可以给这两个列增加索引了。
索引的缺点:索引能够提高查询性能,但是降低了更新和删除的性能,再一个就是索引占用了磁盘空间。
使用索引的经验:
1、使用短索引(局部索引),比如说你要查询一个全世界的地理信息表,addressname字段有255的长度,但是前面的一个字符已经可以获取你的信息了,比如我要查询所有中国的地区,这时候,我 like ‘中%’就行了,不需要like ’中华人民共和国%’,这时候我这个addressname的字段在建立索引的时候,就不需要整列都设置索引,设置前1个字符索引就行了。
2、like语句操作:尽量避免使用like,如果非要使用,不要用like ‘%aaa%’的方式,而要用like ‘aaa%’,因为前者不会使用索引,后者会使用到索引。
3、不要在索引列上进行计算,因为索引列上进行计算将会进行全表扫描。比如upper(name),这样写就会影响性能了。
4、优化order by语句:SELECT [column1],[column2],.... FROM [TABLE] WHERE
[columnX] = [value] ORDER BY [sort] LIMIT[offset],[LIMIT]; 对于上面的sql语句,最高效的方法是建立一个联合索引[columnX,sort]
c、SQL语句优化的分析方法
1、设置慢查询日志:设置一些语句,如果查询的结果在多长时间之后才出来就把它记录到一个文件中,这样我们查看这个文件时,就知道哪些sql语句性能不行了。
2、利用maatkit工具分析sql
3、使用explain、show_status、show variables来总和分析sql语句
4、尽量避免在查询语句的where条件后面使用or来连接条件,否则将导致引擎(InnoDB或MyISAM)放弃使用索引而使用全表扫描
如果非得有or这样的关键字,可以换个方式写,比如:
select name,age from users where id=10 or id=20 可以改成:
select name,age from users where id=10
union all
select name,age from users where id=20;
5、尽量不要使用子查询,用表连接查询替代,一般情况下,表连接要比子查询要快。
6、Join的优化:如果你的程序中有很多join查询,你应该确认两个表中的join字段是被建过索引的。这样,mysql内部会启动为你优化join的sql语句的机制。而且这些被用来join的字段,应该是类型相同的。
7、exists代替in
8、尽量简化sql语句,比如select col,val from table where col=’a’,这个语句中,这个col就是多余的,后面的条件中已经指定了col=’a’了,所以,在应用中直接拿’a’做col这一列的值就可以了,不需要再从数据库中去查询了。
本科 本科 本科 河南财经政法大学 本科-河南财经政法大学
d、利用主从复制+读写分离来提高数据库的并发负载能力。
读写分离:将读取较为频繁的数据放到一台服务器上,将写操作较为频繁的数据放到另一台服务器上,两台服务器做集群和负载均衡,一台机器的数据更新时会同步到另外一台机器,实现数据的同步,这样能极大提升数据库整体性能。
主从复制:这个是如何复制的呢?一般分为三步?
1>.master将改变记录到二进制日志中
2>.slave将master的日志拷贝到它的中继日志
3>.slave重做中继日志中的事件,将改变反应到它自己的数据。
JDBC知识:
1、如何利用JDBC获取数据表结构信息?
DatabaseMetaData dmd = conn.getMetaData();
dmd.getDatabaseProductName(); // 数据库名称
dmd.getUserName(); //数据库用户名
第九章:MongoDB
第十章:JBPM工作流
你用过工作流啊?是啊
说一说工作流
嗯,我们项目中有个货运管理模块,一个合同信息登记完毕后,要交给公司法务部进行审核,法务部审核完毕后需要交给公司老总看,后期可能还有其他人员会看,考虑到这个业务流程是可能会变化的,为了提高系统后期的可扩展性,我们采用了JBPM工作流技术,这个目前成熟的版本是4.4,所以我们系统也采用的这个版本。这块工作是我开发的,由于之前没学习过工作流技术,所以花了一个多礼拜时间学习了一下,对工作流的常用API进行了了解、表结构进行了了解,然后做了几个小Demo,然后后来就整合到我们项目中来了。我们项目用到了spring框架,所以对于工作流的整合只需要将工作流的核心对象ProcessEngine对象交给spring容器来管理就可以了。然后需要根据具体业务设计流程图,然后将设计后的jpdl.xml文件和png图片利用RepositoryService对象的deploy方法将这两个文件部署起来,实际上是将这两个文件存到了数据库中去了。然后在销售专责填写好合同信息,选择好需要处理合同的法务办的工作人员,并提交合同时,后台调用ExecutionService的startProcessInstanceByKey方法启动工作流,这时候,可以在启动时,通过流程变量存储下一个任务的办理人。
当法务办工作人员登录后,我们弄了个弹框效果,提示当前登录用户有哪些待办事项,当法务办工作人员点击这个需要处理的任务信息后,直接进入到合同审批页面,如果法务办人员审核通过,流程流转到总经理进行处理,总经理处理完毕后流程结束,如果不同意,流程直接结束。
对于销售专责,它是可以动态的看到合同的审批进程的,每次销售专责登录后,进入到合同报审的页面,能够看到每个合同的审核结果,点击一个合同后面的流程进度查询,可以在流程图上看到具体的进度,这里我们是通过获取流程图中每个节点的坐标,通过来控制一个层的位置覆盖到当前处理的节点上,这样就能够让当前处理的节点显示高亮效果。
JBPM简要过程:
1、定义流程(利用JPDL)
2、部署流程(部署到数据库)
3、创建公文并与流程实例绑定
4、可通过JBPM的接口,触发流程向下流动
5、可通过JBPM的接口,获得流动到某个用户那里的文档(即待处理任务列表)
6、可通过JBPM的接口,结束某个用户的任务(这将触发流程继续向下流动)
7、如此,直到结束
第十一章:Lucene
1、Lucene知识总结
Lucene是一个高效的、基于Java的全文检索库,所谓全文检索就是计算机通过扫描文章中的每一个词,对每一个词建立一个索引,指明该词在文章中出现的次数和位置,当用户查询的时候根据建立的索引查找,类似于通过字典的目录来查找这个字。
全文检索以文本作为检索对象,找出含有指定词汇的文本,全面、准确和快速是衡量全文检索系统的关键指标。对于结构化的数据,windows的搜索功能和Linux的grep命令搜索起来都比较快,但是仅限于少量数据,数据量大了,就慢了,结构化数据之所以搜索起来快,是因为它根据了一定的算法来加快搜索,对于非结构化的数据它没有这种结构,它存储的信息和我们要搜索的信息是不一致的,我们可以通过将非结构化的数据弄成结构化,这样就可以加快搜索速度了,这种思路也是全文检索的基本思路。在全文检索里,这种做法叫做索引,非结构化的数据建立索引后,就可以像结构化的数据一样使用了,查询起来更快了。
那么这就有个问题了:
1>、索引中存的什么东西?
2>、怎么创建索引?
3>、怎么对索引进行搜索?
下面我就讲讲我对这几个问题的理解。
1)、索引中存储了词典和倒排表
2)、怎样创建索引?
1、需要提供要创建索引的文档;
2、将我们的源文档传给分词组件(Tokenizer),Tokenizer会将源文档中的标点符号去掉、将文档分成一个一个的词并且去掉停词,停词就是不需要作为词典的词;
3、将得到的词元(Token)传给语言处理组件(Linguistic Processor),语言处理组件会做一些处理,比如英语的话,它会将词元变为小写、将单词缩减为词根(cars到car)、将单词转变为词根形式(token到take);
4、将得到的词传给索引组件,索引组件会利用得到的词创建一个字段、对字典按字母顺序进行排序、合并相同的词成为文档倒排链表;
3)、如何对索引进行搜索?
建立完索引后我们是可以迅速找到我们需要的文档了,但是当返回的结果特别多的时候,如何将我最需要的几个文档排在最前面呢?
1、输入查询语句:比如zhangsan and lisi not wangwu,说明你要找一个包含zhangsan、lisi但不包含wangwu的文档;
2、对查询语句进行词法分析、语法分析及语言处理,词法分析主要用来识别单词和关键字、语法分析主要是用来根据查询语句的语法规则来形成一棵语法树;
3、搜索索引,得到符合语法树的文档,这个它会现在反向索引表中找出符合zhangsan、lisi、wangwu的所有文档链表,然后对包含zhangsan、lisi的链表进行合并操作,得到既包含 zhangsan又包含lisi的链表,然后将这个链表与包含了wangwu的链表进行差操作,去除包含了wangwu的链表,从而得到既包含zhangsan、lisi又不包含wangwu的文档链表,这个结果就是我们需要的结果;
4、根据得到的文档和查询语句的相关性,对结果进行排序。排序的依据是根据查询语句判断哪些文档中的哪些词与查询语句相关度更高一些,这叫计算权重,权重的计算是依据一些数学公式进行计算的。
全文检索注意要点
1、只处理文本
2、不处理语义
3、搜索时英文不区分大小写
4、结果列表有相关度排序
Lucene的CURD:
创建索引:IndexWriter对象的addDocument方法
查询索引:IndexSeacher对象的search方法和Query对象获取匹配的ScoreDoc数组,然后遍历数组组装列表数据返回给客户端
优化索引库:
1>、合并索引:可以设置当索引文件达到多少个之后就合并,默认是10,最小为2,这个设置后不会立即生效,而是在有改变索引库的操作后才会检测与执行。
2>、创建Directory对象的时候使用RAMDirectory对象,将索引文件一次性加载到内存中,提升检索速度,但是这个很耗内存,而且程序退出后,内存中的索引就没了。
2、JEECMS系统源码研究心得
第十二章:WebService
1、webservice是个什么东西?webservice就是实现远程调用的一种技术,它可以实现跨平台、跨语言的调用,比如常见的天气预报信息调用、手机号码归属地查询等。
2、WSDL是什么:webservice description language,web服务描述语言,它是描述webservice的一个xml文件,通过这个文件,我们可以知道这个webservice服务的调用地址、方法名称、方法需要传递的参数、方法的返回值等信息。
3、SOAP是什么:simple object access protocal,简单对象访问协议,它是作为一个基于xml格式的协议,用于在网络上传输数据。底层是基于http协议的,可以这么理解:soap=http+xml,而http协议的底层又是基于TCP/IP协议的。soap现有两个版本1.1和1.2。
4、SOAP的组成如下:
Envelope – 必须的部分。以XML的根元素出现。
Headers – 可选的。
Body – 必须的。在body部分,包含要执行的服务器的方法。和发送到服务器的数据。
5、java调用webserivce的四种房四海
1>.利用wsimport命令生成客户端调用的java代码
2>.利用HttpUrlConnection对象,手工拼接soap协议报文,向服务器发送请求获取数据。
3>.通过ajax调用js+xml跨域请求,这个浏览器是不支持的,但是我们可以绕过去
4>.
第十三章:中科商贸物流管理系统
面试题:说一下你最近做的一个系统。
最近我们开发的一套系统是给中科商贸公司开发的,这公司是做小型电器出口的,最近几年业务做到海外去了,随着他们公司不断壮大,原来那套系统已经没办法满足他们的发展需要了,所以他们公司领导就想搞一套物流系统,提高他们的办公效率。整个系统有仓储管理(包括:采购单、仓库、货物、条形码、入库、出库、退货、盘点、库存、库存上限报警、统计查询)、展会管理(包括:展会管理、出单管理)、货运全流程管理,包括购销合同、出货表月统计、出口报运、装箱单、海运委托单、发票、财务统计、决策分析(包括:成本分析图、销售情况统计、重点客户、经营情况同期比对统计、工作绩效等)这么些功能。我在这个项目中,负责货运管理部分模块的开发以及决策分析报表的开发。
货运管理模块里,我负责开发合同管理、报运管理、装箱管理、生产厂家模块。这三个模块之间是紧密联系的,所谓合同管理就是对中科商贸公司所有的合同进行CRUD的操作、合同下的货物管理、货物下的附件管理。在这个模块里,最复杂的地方是需要理清合同、货物、附件、生产厂家之间的关系以及货物信息的导出处理。一个合同包含多个货物,一个货物可以有多个附件,一个生产厂家可以生产多个货物,所以合同跟货物之间是一对多的关系,货物跟附件是一对多的关系,生产厂家跟货物之间是一对多的关系。理清了这一层关系后,开发起来就简单了。
在我们物流项目中,有很多的设计技巧和开发亮点,比如说:
1、在操作货物信息和附件信息的时候,在合同列表中,为了方便用户录入货物信息,我们在每条合同信息后面,加了一个货物的超链接,点击货物的超链接,可以进入货物信息管理页面,对货物进行CRUD操作,由于货物信息每次录入的比较多,为了方便用户快速录入,每次录入一条货物信息后,我们还是跳转到该货物信息录入界面,同时,了解到用户每次录入货物信息的时候都是一次性录入多个厂家的多个信息,所以我们在跳到该页面的时候,进行了生产厂家等信息的回显,这样极大的提高了用户操作效率。
2、在录入附件信息的时候,用户以往的系统是点击一条合同信息进入到该合同对应的货物列表页面,再点击货物列表中的一个超链接进入到附件列表页面,当用户想直接录入附件信息的时候,需要点几次才能到达附件页面,所以我们在设计这个的时候,直接在合同列表页面加了一个附件按钮,点击这个超链接直接进入到附件录入页面,在这个录入页面,给用户提供一个货物信息的下拉框进行选择,这样又方便了用户的附件信息的录入了。
3、在合同管理界面,我们提供了合同的批量复制功能。
4、在导出出货表的时候,为了避免多线程安全问题,后下载的文件覆盖前下载的文件,我们采用了按照时间点自动分目录的方式,文件名采用UUID算法命名。
5、当系统上线时间长了,系统的历史合同数据、历史货物信息、历史附件信息数据越来越多,严重影响了系统性能,我们对系统提供了历史数据归档功能,当用户点击归档按钮的时候,将每年数百万的历史数据导出到excel文件中。
6、在生产厂家信息维护界面,为了方便用户录入,我们采用了自定义的一个js控件进行了批量新增和批量修改,这样加快了用户操作的速度。
7、系统上线后,出现了很多性能问题,反应速度不是很快,用户不太满意,为此,我们做了一个系统性能的分析,利用struts2的拦截器,在拦截器中,对所有用户访问的哪个类的哪个方法、方法执行消耗的时间等进行记录,并存储到数据库中,然后用amFlash技术做性能分析报表,找到系统的性能瓶颈,然后针对性的进行优化,两个礼拜后,我们对系统的性能报告进行分析,发现,用户主要访问的模块在上午8:30-9:30,主要访问的模块是货物信息录入、查询以及附件信息的录入查询,因为这个点是上班时间点,而且录入货物信息和附件信息较多,所以,针对这几个方法,我们采取了以下优化策略:
1>、利用hibernate抓取策略进行优化,尽量减少增加、和查询的sql语句;
2>、报表查询时,采用原生态的sql语句,不用hibernate的hql语句,使用Spring的JdbcTemplate模版,为此,我们单独又写了一个spring配置文件,这个配置文件的作用是配置数据库连接池和给JdbcTemplate模版注入属性,对于加载Spring配置文件的这个类,我们采用单例设计模式,避免多次创建对象实例,做到了性能最优和线程安全。
3>、对数据字典进行二级缓存的配置,避免多次与数据库的交互。
8、在这个项目中,我们还用到了JBPM工作流技术,在合同录入完毕后,需要中科商贸公司的法务部进行审核,然后提交到总经理进行审核,这是一个典型的工作流程。
9、导出出货表的技术我们使用了POI,能够导出图文并茂的出货表,还能够对商品价格进行自动计算,不需要购买第三方报表,给用户省去了买报表的钱,得到了用户的肯定。
第十四章:设计模式总结
设计模式是前人经验的总结,前辈们解决问题的一些好的方法,它是用来解决一些重复出现的问题、特定问题的解决方案,抽象出来就成了设计模式了。比如单例设计模式、工厂设计模式、代理设计模式、模版方法设计模式、装饰设计模式、观察者设计模式、策略设计模式。我们项目中用到的设计模式有如下几个:
1、单例设计模式
所谓单例设计模式,就是说内存中只有一份对象的实例,单例模式呢,有三个要素,第一个就是要求对象的构造函数是私有化的,这样,外部就不能直接创建你对象的实例了,第二个是提供一个私有的静态的类类型的变量;第三个是提供一个静态的public的方法,这个方法用于向外部提供你这个对象的实例;对于那个私有的静态的类类型的变量,你可以直接给它赋值,这种方式叫做饿汉式加载,因为变量是静态的嘛,所以在加载这个类的字节码的时候就会实例化这个类对象,也可以不赋值,在getInstance方法中给它赋值,这种方式叫懒汉式加载,这种方式有个缺陷,就是当高并发访问的时候会出现安全问题,我们可以通过在getInstance方法上加synchronized关键字来解决这个安全问题,但是直接在方法上加同步关键字,有一点点性能影响,我们可以在方法内部,通过两个if判断+同步关键字的方式,来同时解决线程安全问题和性能问题。
我们项目中有用到这个单例模式,我做的那个物流管理项目,采用的是混合架构,在操作数据库的时候,对于普通的对性能要求不高的方法我们采用hibernate来做,对于像报表统计和大批量数据的查询操作,我们采用Spring框架的JdbcTemplate模版,单独配置了一个配置文件,然后定义了一个AppContext类,这个类就是单例的,它的作用是加载这个配置文件,这个配置文件只加载一次就可以了,我们是将这个加载工作放在AppContext这个类的构造函数中加载的,所以我们这个AppContext类自然而然就设计成单例模式了。
2、工厂设计模式
所谓工厂设计模式就是说我们对象的创建依让另外一个对象帮我们来创建java编程中,我们通常用面向接口来编程,就是说我们要创建一个对象的时候,不直接创建对象,而是在等号左边用接口类型来接收,等号右边用具体的实现类,
比如:Person p = new American();这样做的好处是让我们的封装隔离,就是说接口只负责定义一个具体的骨架,具体的算法和实现在具体的实现类中去做。但是像上面我说的这种Person p = new American()的写法,客户端调用这个p中的方法的时候还是依赖了具体的实现类,虽然外部不知道你里面怎么写的,但是内部你还是依赖了这个具体的实现类,这样的代码耦合度还是太高,所以我们通过采用另外一个类来创建这个Person接口类型的一个对象,这个类就是工厂类,它负责创建这个Person接口类型的对象,在创建的时候他可以进行逻辑判断,然后决定创建哪个具体的实现类给你。
我们的这个Spring框架就是这个原理,在我们进行三层架构开发的时候,业务层依赖DAO层,Action依赖Service层,为了降低三层之间的依赖,我们采用spring框架来帮我们解耦,只需要在Action中定义service组件的引用,在service层定义DAO层的引用即可,具体的实现类我们不给,在容器初始化的时候,Spring容器获取配置文件中的信息,利用工厂给每个组件创建具体的实现类,这样降低了各组件之间的依赖,增加了程序的可扩展性。你比如说一个银行系统,建设银行和农业银行调用的数据都是从一个数据库来的,他们的DAO层和表现层都是一样的代码,但是每个银行具体业务不一样,如果用以往的面向接口的方式开发业务层,我们每个银行不但要开发各自的业务逻辑层代码,还要修改Action层的代码,这样的维护量是非常大的,但是如果用工厂方式来创建所有的对象,每个银行只需要开发自己的业务层代码即可,然后修改配置文件配置各层之间的依赖关系即可。这样是工厂模式的好处。
3、责任链设计模式
4、代理设计模式
5、装饰设计模式
6、模版方法设计模式
解决问题:提高功能的扩展性。
什么时候用模板方法设计模式:
当定义功能时,功能中一部分可以确定,一部分不确定的时候。就使用模板方法设计模式。
可抽象可不抽象,因为可以有默认的实现。
abstract class Template
{
void template()
{
code...;
method();
code...;
}
abstract void methdo();
}
示例:
abstract class GetTimeTamplate
{
public final void getTime()
{
long start = System.currentTimeMillis();
code();
long end = System.currentTimeMillis();
System.out.println(end-start);
}
abstract void code();
}
第十五章、面试经验总结
1、jdk中的享元设计模式
Long a = 5;
Long b = 5;
System.out.println(a==b);//结果是啥?结果是true
Long a = 200;
Long b = 200;
System.out.println(a==b);//结果是啥?结果是false
为啥呢?因为在jdk1.5以后,对于基本类型的包装类型使用了享元模式,对于-128到127之间的数据,jdk只会创建一次,不会创建多个对象,因为这个区间的数据是经常用的。
注意:这种享元设计模式只对Byte、Short、Integer、Long、Character有效,对于浮点类型的包装类型是无效的,Double a = 5; Double b = 5; System.out.println(a==b);结果是false
2、十万条数据从一端传送到另一端,有什么方式减少带宽、提升传输速度?
答:采用json数据格式传输
3、Struts2 2.3.15版本中有两个漏洞,是利用的ognl验证的漏洞跳过了ognl的验证来攻击服务器的,通过升级版本我们可以解决问题,如果不升级我们还想解决这个问题怎么办?
答:写一个过滤器,过滤掉request对象中所有接收到的参数中的非法字符。
4、svn中分支、合并的用法
svn中,我们一般都分三个文件夹:branches、trunk、tags,
branches存放需要修改的版本
trunk存放正在开发的版本
tags存放稳定的版本
当一个项目,开发成功后,我们从trunk下进行分支,发布一个版本V1.0到tags中
如果这时候用户说需要开发一个V2.0的版本,我们需要在trunk下进行V2.0的开发
如果这时候用户用的V1.0出问题了,我们需要从tags下V1.0的版本进行分支,移动到branches下,命名为V1.0_fixup,然后切换版本库的路径到V1.0_fixup,进行bug修复,修复后提交,然后做分支标记,发布新的版本V1.1到tags下。
svn的作用是什么?
以往不用svn时,会遇到以下一些问题:
1、项目代码混乱
2、每次都备份多个版本的项目,占用磁盘空间大
3、解决代码冲突困难
4、容易引发bug
5、难于追溯问题代码的修改人和修改时间
6、难于恢复到以前正确的版本
7、无法进行权限控制
5、Maven是什么?它和svn有什么区别
Maven是一个项目管理工具,它负责管理这个项目的生命周期,包括清理、编译、测试和打包、部署等,它可以通过pom.xml文件中一小段描述信息(jar包坐标)来管理项目的构建、依赖。
6、线程间是怎么通信的?
多线程之间是怎么通信的呢?比如说一个线程做完了一件事情之后要告诉另外一个线程继续干活,这个怎么做呢?
1>、通过标识符来处理,如果多个线程都能够访问一个标识符,我们可以通过更改标识符的值来控制线程的运行,比如A线程计算完了,我让A线程将标识符的值进行更改,B线程不停的判断这个标识符,一旦是某个值时就执行某段代码。
2>、通过notify()方法唤醒线程池中的一个线程
7、你们用的Lucene中,分词器的词库是怎么做的?
这个我们是用的第三方的IKAnalyzer,这个框架是Lucene框架中Analyzer接口的一个实现,Lucene中的Analyzer是一个分词器接口,IKAnalyzer对其进行了实现,并通过一个配置文件存储了27万常用汉语词典,每个汉语单词以换行符分隔,在客户端调用该分词器的分词方法时,该方法内部会去读取该词典文件,并根据该文件进行分词,另外该分词器的分词方法还会去读取用户自定义的词语文件,并据此进行单词分割。
8>、在Lucene中,关键词高亮显示是怎么做的?
这个就是把关键词找出来,然后给关键词加一个html标签,在页面上显示的时候,这个关键字的样式就改变了。
9>、编写一个程序,判断字符串是否为回文?比如(“121”,”abcdcba”)
public static void main(String[] args) {
String s = "";
System.out.println(isPalindrooms(s));
}
private static boolean isPalindrooms(String s) {
if(s==null || s.equals("")){
throw new RuntimeException("需要判断的字符串不能为空!");
}
char[] chs = s.toCharArray();
int halfIndex = chs.length/2;
for(int i=0,j=chs.length-1; i<halfIndex; i++,j--){
if(chs != chs[j]){
return false;
}
}
return true;
}
10、 |
|