Java Web篇
Servlet API中forward()与redirect()的区别?
从地址栏显示来说
forward是服务器请求资源,服务器直接访问目标地址的URL,把那个URL的响应内容读取过来,然后把这些内容再发给浏览器。浏览器根本不知道服务器发送的内容从哪里来的,所以它的地址栏还是原来的地址。
redirect是服务端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址,所以地址栏显示的是新的URL。redirect等于客户端向服务器端发出两次request,同时也接受两次response。
从数据共享来说
forward:转发页面和转发到的页面可以共享request里面的数据。
redirect:不能共享数据。
redirect不仅可以重定向到当前应用程序的其他资源,还可以重定向到同一个站点上的其他应用程序中的资源,甚至是使用绝对URL重定向到其他站点的资源。forward方法只能在同一个Web应用程序内的资源之间转发请求,forward 是服务器内部的一种操作。
redirect 是服务器通知客户端,让客户端重新发起请求。
从运用地方来说
forward:一般用于用户登陆的时候,根据角色转发到相应的模块。
redirect:一般用于用户注销登陆时返回主页面和跳转到其它的网站等。
Servlet的生命周期
Servlet有良好的生存期的定义,包括加载和实例化、初始化、处理请求以及服务结束。这个生存期由javax.servlet.Servlet接口中的init、service、destroy方法表达。
web容器加载servlet,生命周期开始。通过调用servlet的init()方法进行servlet的初始化。通过调用service()方法实现根据请求的不同调用不同的do**()方法。结束服务,web容器调用servlet的destroy()方法。
init()只会调用一次。
Servlet的多线程机制
Servlet体系结构是建立在Java多线程机制之上的,它的生命周期是由Web容器负责的。当客户端第一次请求某个Servlet时,Servlet容器将会根据web.xml配置文件实例化这个Servlet类。当有新的客户端请求该Servlet时,一般不会再实例化该Servlet类,也就是有多个线程在使用这个实例。Servlet容器会自动使用线程池等技术来支持系统的运行,如图1所示。
这样,当两个或多个线程同时访问同一个Servlet时,可能会发生多个线程同时访问同一资源的情况,数据可能会变得不一致。所以在用Servlet构建的Web应用时如果不注意线程安全的问题,会使所写的Servlet程序出现难以发现的错误。
虽然使用synchronized可以解决Servlet的线程安全问题,但是会造成线程的等待,不是很好的办法。所以最好不要在Servlet实例中定义任何成员变量,同时只读属性最好定义为final类型的。
Request对象的主要方法有哪些?
- setAttribute(String name,Object):设置名字为name的request 的参数值
- getAttribute(String name):返回由name指定的属性值
- getAttributeNames():返回request 对象所有属性的名字集合,结果是一个枚举的实例
- getCookies():返回客户端的所有 Cookie 对象,结果是一个Cookie 数组
- getCharacterEncoding() :返回请求中的字符编码方式 = getContentLength() :返回请求的 Body的长度
- getHeader(String name) :获得HTTP协议定义的文件头信息
- getHeaders(String name) :返回指定名字的request Header 的所有值,结果是一个枚举的实例
- getHeaderNames() :返回所以request Header 的名字,结果是一个枚举的实例
- getInputStream() :返回请求的输入流,用于获得请求中的数据
- getMethod() :获得客户端向服务器端传送数据的方法
- getParameter(String name) :获得客户端传送给服务器端的有 name指定的参数值
- getParameterNames() :获得客户端传送给服务器端的所有参数的名字,结果是一个枚举的实例
- getParameterValues(String name):获得有name指定的参数的所有值
- getProtocol():获取客户端向服务器端传送数据所依据的协议名称
- getQueryString() :获得查询字符串
- getRequestURI() :获取发出请求字符串的客户端地址
- getRemoteAddr():获取客户端的 IP 地址
- getRemoteHost() :获取客户端的名字
- getSession([Boolean create]) :返回和请求相关 Session
- getServerName() :获取服务器的名字
- getServletPath():获取客户端所请求的脚本文件的路径
- getServerPort():获取服务器的端口号
- removeAttribute(String name):删除请求中的一个属性
request.getAttribute()和 request.getParameter()有何区别?
- request.getParameter()获取的类型是String;request.getAttribute()获取的类型是Object。
- request.getPrameter()获取的是POST/GET传递的参数值和URL中的参数;request.getAttribute()获取的是对象容器中的数据值/对象。
- request.setAttribute()和request.getAttribute()可以发送、接收对象;request.getParamter()只能接收字符串,没有request.setParamter()这个方法。
setAttribute()和getAttribute()的传参原理:setAttribute()是应用服务器把这个对象放在该页面所对应的一块内存中去,当你的页面服务器重定向到另外一个页面时,应用服务器会把这块内存拷贝到另一个页面所对应的那块内存中。这个就可以通过getAttribute()获取到相应的参数值或者对象。
JSP有哪些内置对象?作用分别是什么?
JSP一共有9个内置对象:
- request:负责得到客户端请求的信息,对应类型:javax.servlet.http.HttpServletRequest
- response:负责向客户端发出响应,对应类型:javax.servlet.http.HttpServletResponse
- session:负责保存同一客户端一次会话过程中的一些信息,对应类型:javax.servlet.http.httpsession
- out:负责管理对客户端的输出,对应类型:javax.serlvet.jsp.jspwriter
- application:表示整个应用环境的信息,对应类型:javax.servlet.servletcontext
- config:表示ServletConfig,对应类型:javax.servlet.servletconfig
- exception:表示页面中发生的异常,可以通过它获得页面异常信息,对应类型:java.lang.exception
- pagecontext:表示这个JSP页面上下文,对应类型:javax.servlet.jsp.pagecontext
- page:表示当前JSP页面本身。
JSP中的四种作用域
JSP中的四种作用域包括page、request、session和application,具体来说:
- page是代表一个页面相关的对象和属性。一个页面由一个编译好的java servlet类(可以带有include指令,但不可以带有include动作)表示。这既包括servlet又包括编译成servlet的jsp页面。
- request是代表与web客户机发出的一个请求相关的对象和属性。一个请求可能跨越多个页面,涉及多个web组件(由于forware指令和include动作的关系)
- session是代表与用于某个web客户机的一个用户体验相关的对象和属性。一个web回话也可以经常跨域多个客户机请求。
- application是代表与整个web应用程序相关的对象和属性。这实质上是跨域整个web应用程序,包括多个页面、请求和回话的一个全局作用域。
URL 重写
在URL中添加用户会话的信息作为请求的参数,或者将唯一的会话ID添加到URL结尾以标识一个会话。
- 优点: 在Cookie被禁用的时候依然可以使用
- 缺点: 必须对网站的URL进行编码,所有页面必须动态生成,不能用预先记录下来的URL进行访问。
JSP, Servlet 和 JSP引擎
我们常说,Tomcat是一个Servlet容器,而不说它是JSP容器,因为JSP实质是被转换为Servlet后再工作的。
那我们为什么不直接使用Servlet呢?因为在Servlet中写视图层代码(HTML)非常麻烦。但是Tomcat又只“认识”Servlet,因此就需要JSP引擎做一个转换工作。
如果客户端请求的是一个JSP,则该JSP文件传递给JSP引擎,JSP引擎将JSP文件转译为Servlet的java文件,其实质就是这个Servlet来处理客户端的请求。
JSP的生命周期
- 如果JSP文件是新的,则转译为Servlet java文件,然后编译为class文件。加载类到内存,创建一个(仅仅一个)Servlet对象。并执行jspInit,表示这个servlet被启用。如果不是,则直接调用驻留在内存上的实例的_jspService 方法。
- 通过_jspService方法处理请求。多个请求同时请求同一个JSP的servlet实例,则这些请求会使用独立的线程去调用_jspService 方法。
- 当此Servlet实例不再被使用、或者服务器关机时,调用jspDestroy,GC
jsp静态包含和动态包含的区别
<%@include file="xxx.jsp"%>为jsp中的编译指令,其文件的包含是发生在jsp向servlet转换的时期,而<jsp:include page="xxx.jsp">是jsp中的动作指令,其文件的包含是发生在编译时期,也就是将java文件编译为class文件的时期。
使用静态包含只会产生一个class文件,而使用动态包含会产生多个class文件。
使用静态包含,包含页面和被包含页面的request对象为同一对象,因为静态包含只是将被包含的页面的内容复制到包含的页面中去;而动态包含包含页面和被包含页面不是同一个页面,被包含的页面的request对象可以取到的参数范围要相对大些,不仅可以取到传递到包含页面的参数,同样也能取得在包含页面向下传递的参数。
JSP转换为Servlet的细节
JSP按如下规则转换为Servlet:
- 所有的 非 JSP 文本 ( 如HTML代码,XML代码),都将在生成的_jspService方法中以字符串的形式使用out对象输出。
-
所有的<% %> 和 <%= %>脚本,将在他所在的地方原原本本对应插入到_jspService方法中去。所有的<%! %>都将成为Servlet的类级别的成员。因此<%! %>写在JSP页面代码的任何地方都没有任何区别。 <%-- --%> JSP注释 将只保留在JSP代码中,不会存在转换后的servlet代码中。
- EL,JSTL等被JSP引擎使用特殊转换。
Cookie和Session的的区别?
由于HTTP协议是无状态的协议,所以服务端需要记录用户的状态时,就需要用某种机制来识别具体的用户,这个机制就是Session。典型的场景比如购物车,当你点击下单按钮时,由于HTTP协议无状态,所以并不知道是哪个用户操作的,所以服务端要为特定的用户创建了特定的Session,用于标识这个用户,并且跟踪用户,这样才知道购物车里面有几本书。这个Session是保存在服务端的,有一个唯一标识。在服务端保存Session的方法很多,内存、数据库、文件都有。集群的时候也要考虑Session的转移,在大型的网站,一般会有专门的Session服务器集群,用来保存用户会话,这个时候 Session 信息都是放在内存的,使用一些缓存服务比如Redis之类的来放 Session。
思考一下服务端如何识别特定的客户?这个时候Cookie就登场了。每次HTTP请求的时候,客户端都会发送相应的Cookie信息到服务端。实际上大多数的应用都是用 Cookie 来实现Session跟踪的,第一次创建Session的时候,服务端会在HTTP协议中告诉客户端,需要在 Cookie 里面记录一个Session ID,以后每次请求把这个会话ID发送到服务器,我就知道你是谁了。有人问,如果客户端的浏览器禁用了 Cookie 怎么办?一般这种情况下,会使用一种叫做URL重写的技术来进行会话跟踪,即每次HTTP交互,URL后面都会被附加上一个诸如 sid=xxxxx 这样的参数,服务端据此来识别用户。
Cookie其实还可以用在一些方便用户的场景下,设想你某次登陆过一个网站,下次登录的时候不想再次输入账号了,怎么办?这个信息可以写到Cookie里面,访问网站的时候,网站页面的脚本可以读取这个信息,就自动帮你把用户名给填了,能够方便一下用户。这也是Cookie名称的由来,给用户的一点甜头。
所以,总结一下:
Session是在服务端保存的一个数据结构,用来跟踪用户的状态,这个数据可以保存在集群、数据库、文件中;Cookie是客户端保存用户信息的一种机制,用来记录用户的一些信息,也是实现Session的一种方式。
JDBC访问数据库的基本步骤是什么?
- 加载驱动
- 通过DriverManager对象获取连接对象Connection
- 通过连接对象获取会话,有2种方式Statement、PreparedStatement;
- 通过会话进行数据的增删改查,封装对象
- 关闭资源、关闭会话、关闭连接。
说说preparedStatement和Statement的区别
- PreparedStatement 接口继承 StatementPreparedStatement 实例包含已编译的 SQL 语句,所以其执行速度要快于 Statement 对象
- 安全性:可以有效的避免sql注入攻击!sql注入攻击就是从客户端输入一些非法的特殊字符,而使服务端在构造sql语句的时候仍然能够正确构造,从而收集程序和服务器的信息和数据。
数据库连接池的原理。为什么要使用连接池
- 数据库连接是一个费时的操作,连接池可以使多个操作共享一个连接。
- 数据库连接池的基本思想就是为数据库连接建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。我们可以通过设定连接池最大连接数来防止系统无尽的与数据库连接。更为重要的是我们可以通过连接池的管理机制监视数据库的连接的数量、使用情况,为系统开发,测试及性能调整提供依据。
- 使用连接池是为了提高对数据库连接资源的管理
最小连接数是连接池一直保持的数据连接。如果应用程序对数据库连接的使用量不大,将会有大量的数据库连接资源被浪费掉。
最大连接数是连接池能申请的最大连接数。如果数据连接请求超过此数,后面的数据连接请求将被加入到等待队列中,这会影响之后的数据库操作。
如果最小连接数与最大连接数相差太大,那么,最先的连接请求将会获利,之后超过最小连接数量的连接请求等价于建立一个新的数据库连接。不过,这些大于最小连接数的数据库连接在使用完不会马上被释放,它将被放到连接池中等待重复使用或是空闲超时后被释放。
什么是幻读,哪种隔离级别可以防止幻读?
幻读是指一个事务多次执行一条查询返回的却是不同的值。假设一个事务正根据某个条件进行数据查询,然后另一个事务插入了一行满足这个查询条件的数据。之后这个事务再次执行了这条查询,返回的结果集中会包含刚插入的那条新数据。这行新数据被称为幻行,而这种现象就叫做幻读。
只有TRANSACTION_SERIALIZABLE(串行化)隔离级别才能防止产生幻读。
JDBC的DriverManager是用来做什么的?
JDBC的DriverManager是一个工厂类,我们通过它来创建数据库连接。当JDBC的Driver类被加载进来时,它会自己注册到DriverManager类里面。然后我们会把数据库配置信息传成DriverManager.getConnection()方法,DriverManager会使用注册到它里面的驱动来获取数据库连接,并返回给调用的程序。