1.Servlet底层原理总结
底层通过浏览器(程序)访问服务器(程序),实际是通过(操作)系统底层TCP/IP层的(主机)地址和端口
建立计算机底层间的连接,实现程序间访问,响应。Servlet是服务器程序在收到第一次访问时(Socket级)
运行class文件(并加载配置文件数据)在内存中创建(new)和调用的,同时创建(new)request,response对象传递给它,都包含着相关
请求和响应数据,服务器调用Servlet的init方法初始化,doGet,doPost方法处理和响应请求
一个应用的Servlet实现多线程并发访问,不同的应用存储不同的目录(虚拟目录映射),运行不同的应用程序
实际最终还是像同一台计算机上最底层的java程序间的相互访问
程序示例:只开启服务器,直接通过底层程序访问主机地址和端口,获取服务端数据,URL地址为服务器下一个应用(虚拟目录映射)
public class RangeDemo { public static void main(String[] args) throws Exception{ //中文文件名怎么传?内容是中文怎么防乱码? //原始的读写,只开启服务器,利用IP地址,端口,虚拟目录映射,用浏览器访问此资源 //懂原理后不要每次去想底层细节!直接抽象到高层对象和程序流程调用去自然迅速地理解!! URL url=new URL("http://localhost:8080/workday16/index.jsp"); HttpURLConnection conn=(HttpURLConnection) url.openConnection(); //设置请求头,断点续传方式 conn.setRequestProperty("Range", "bytes=5"); //这些API直接查直接用,没有技术含量!重在项目,架构,算法,新技术,经验!!!快过这些基础!! InputStream in=conn.getInputStream(); int len=0; byte buffer[]=new byte[1024]; FileOutputStream out=new FileOutputStream("c:\\1qa.txt",true);//append方式 while((len=in.read(buffer))>0){ out.write(buffer,0,len); } //注意去练finally,关资源!! in.close(); out.close(); } }
源码细节:
<pre name="code" class="java">public abstract class GenericServlet implements Servlet, ServletConfig, java.io.Serializable { private transient ServletConfig config; public GenericServlet() { } public void destroy() { } public String getInitParameter(String name) { return getServletConfig().getInitParameter(name); } public Enumeration getInitParameterNames() { return getServletConfig().getInitParameterNames(); } public ServletConfig getServletConfig() { return config; } public ServletContext getServletContext() { return getServletConfig().getServletContext(); } public String getServletInfo() { return ""; } public void init(ServletConfig config) throws ServletException { this.config = config; this.init(); } public void init() throws ServletException { } ... }
例子:
web.xml中的配置:
<servlet> <servlet-name>ServletDemo6</servlet-name> <servlet-class>cn.itcast.ServletDemo6</servlet-class> <init-param> <param-name>data</param-name> <param-value>xxxxxx</param-value> </init-param> <init-param> <param-name>data1</param-name> <param-value>yyyyyy</param-value> </init-param> <init-param> <param-name>data2</param-name> <param-value>zzzzz</param-value> </init-param> </servlet>
//ServletConfig对象:用于封装servlet的配置信息 //在实际开发中,有一些东西不适合在servlet中写死,可以通过配置方式配给servlet.比如 //采用哪个码表,连接哪个库,使用哪个配置文件 public class ServletDemo6 extends HttpServlet { //private ServletConfig config;//记住服务器传递过来的ServletConfig信息 public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //处理ServletConfig信息 //String value=config.getInitParameter("data"); //直接获得ServletConfig对象 String value=this.getServletConfig().getInitParameter("data"); System.out.println(value); //得到所有初始化数据 Enumeration e=this.getServletConfig().getInitParameterNames(); while(e.hasMoreElements()){ String name=(String) e.nextElement();//名称 //名称对应的值 String value1=this.getServletConfig().getInitParameter(name); System.out.println(name+"="+value1); } } //服务器将web.xml中的初始化信息封装在ServletConfig对象中,传递给init方法 @Override //public void init(ServletConfig config) throws ServletException { //this.config=config; //} public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } }
3.ServletContext:代表web应用,管理web资源
setAttribute,getAttribute共享资源,getContext获取(别的)web应用
getInitParameter得到web应用的配置信息,而上面的ServletConfig对象是得到Servlet的配置信息
getRealPath返回资源绝对路径,getResource返回资源URL,getResourceAsStream把资源作为流返回
示例:
web.xml中代表整个应用的参数配置:
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <context-param> <param-name>data</param-name> <param-value>xxx</param-value> </context-param> ...得到应用参数的方法:
String value=this.getServletContext().getInitParameter("data"); System.out.println(value);
程序:ServletDemo8访问ServletDemo7在ServletContext中设置的参数
public class ServletDemo7 extends HttpServlet { //ServletContext:代表Web应用对象 //同一个应用中不同的Servlet可通过它共享数据 public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //得到ServletContext //方法1 //ServletContext context=this.getServletConfig().getServletContext(); //方法2 //context=this.getServletContext(); String data="aaa"; this.getServletContext().setAttribute("data", data); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request,response); } }
public class ServletDemo8 extends HttpServlet { //ServletContext域:1.它是一个容器2.它的作用范围是整个应用 //先访问ServletDemo7设置,再访问ServletDemo8,同一个应用,共享数据--->ServletContext域,整个应-用-程-序范围内数据共享! //实际应用:聊天室----->都发给ServletContext,别人共享 public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String value=(String)this.getServletContext().getAttribute("data"); System.out.println(value); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request,response); } }
public class ServletDemo10 extends HttpServlet { //ServletContext创建时间:服务器启动时创建所有应用的ServletContext //转发数据:不同于重定向,一次客户端请求,而重定向是两次 public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //转发给jsp String data="aaaaaaaaa"; //但通过ServletContext共享会出现多线程安全问题,又是多线程访问同一资源! //所以实际开发中要通过request域来转发 this.getServletContext().setAttribute("data", data); //转发对象 RequestDispatcher rd=this.getServletContext().getRequestDispatcher("/1.jsp"); rd.forward(request, response);//转过去 } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request,response); } }
ServletContext管理web资源:
代码:注-意-看-注-释-中-的-知-识-点!!!
//读取资源文件 public class ServletDemo11 extends HttpServlet { //ServletContext管理web资源 //应用的配置使用资源文件,两种类型:xml和properties //xml应用于数据彼此相关的情况,比如标签间的嵌套关系,properties用于数据彼此无关的情况:比如数据库配置 public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //读取资源文件 //搞清目录,包就是classes下的文件夹,WebRoot就是应用根目录/,这里是服务器调用,要写相对于此应用,该文件的路径 //在Eclipse中开发完后是发送到服务器中去,要遵循服务器中应用的组织结构,服务器中没有src InputStream in=this.getServletContext().getResourceAsStream("/WEB-INF/classes/db.properties"); //如果用FileInputStream--->文件是相对路径,相对于谁:这是java工程,虚拟机调用服务器程序,服务器程序调用web应用 //这个路径是相对于java虚拟机(想想玩命令行时,是java虚拟机的java命令在当前目录下,直接操作当前目录下的文件,虚拟机在哪个目录下启动就相对于哪个目录写相对路径) //服务器程序是在bin下开启的(虚拟机在此目录下执行服务器程序),所以要相对于Tomcat的bin目录写路径 //而这里的web工程,是相对于服务器编程!是服务器调用,要写服务器中应用的路径! //所以不能再以Java工程的传统方式读!!!---->这个可用ServletContext的getRealPath方法得到绝对路径: //String path=this.getServletContext().getRealPath("/WEB-INF/classes/db.properties"); //模板代码(背) Properties props=new Properties(); props.load(in);//load进Properties对象,用Map来保存数据 String url=props.getProperty("url"); String username=props.getProperty("username"); String password=props.getProperty("password"); System.out.println(url); System.out.println(username); System.out.println(password); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } }
代码:
public class ServletDemo12 extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //获取绝对路径,用于获取资源名称(比如下载时,或用户传的资源,客户机带来的资源文件名,下载时需要回显给用户资源文件名)--->ServletContext只能获取流 String path=this.getServletContext().getRealPath("/WEB-INF/classes/db.properties"); String filename=path.substring(path.lastIndexOf("\\")+1); System.out.println("当前读取的资源名称是:"+filename); FileInputStream in=new FileInputStream(path); Properties props=new Properties(); props.load(in); String url=props.getProperty("url"); String username=props.getProperty("username"); String password=props.getProperty("password"); System.out.println(url); System.out.println(username); System.out.println(password); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request,response); } }
实际应用中,Dao层中读取配置文件,拿不到ServletContext的情况下(如果非要拿到ServletContext,就需要Web层以参数的方式传递其引用过来,这样就破坏了设计原则,Web层侵入了Dao层,造成耦合)读取配置文件的办法:通过类装载器读取---------->UserDao.class.getClassLoader():获取装-载-该-类-的类装载器!!
通过流读取的方式:
//创建对象原理:反射,类装载器装载类字节码,创建对象 //服务器用类装载器装载、调用类目录下的类文件,如果配置文件在类目录下,也可通过类装载器装载 //得到装载class目录下类文件的类装载器 public class UserDao { public void update() throws IOException{ Properties dbconfig=new Properties(); InputStream in=UserDao.class.getClassLoader().getResourceAsStream("db.properties"); dbconfig.load(in); System.out.println(dbconfig.getProperty("url")); } public static void main(String[] args) throws IOException{ new UserDao().update(); } }
解决办法:不通过类装载器读,但仍可通过它获得文件位置,通过传统方式读文件
代码:
public class UserDao { public void update() throws IOException{ /*Properties dbconfig=new Properties(); InputStream in=UserDao.class.getClassLoader().getResourceAsStream("db.properties"); dbconfig.load(in); System.out.println(dbconfig.getProperty("url"));*/ //getResource获取URL String path=UserDao.class.getClassLoader().getResource("db.properties").getPath(); FileInputStream in=new FileInputStream(path); Properties dbconfig=new Properties(); //从传统方式的流中获取数据,以便获取更新后的数据 dbconfig.load(in); System.out.println(dbconfig.getProperty("password")); } public static void main(String[] args) throws IOException{ new UserDao().update(); } }
注意实际开发中用装载器读取的文件不能太大,因为是一次性加载进内存,太大会造成内存溢出!!
4.Request获取数据的几种方法及需要注意的
注:要加上Values非空的严谨判断!
注:要加上Value非空及去空格后非空的判断,检查用户不填写或只输入全空格的情况!!(用于表单校验等,比如非必填项,判断是非空值才校验格式!!)
注:获取数据Map,注意键值对的值为String[]类型,因为会有同名数据多个值的情况;注意用BeanUtils用Map集合填充Bean,BeanUtils拷贝Bean(同名同类型)属性时只支持8种基本数据类型,对象类型需要注册类转换器!!
注:流方式获取,主要用于二进制数据,比如文件上传。而post提交的表单数据也会在请求体中,也可通过流获取,如图:
表单提交项为空的情况,不加判断的后果:
注:空指针异常!!
正确的做法:
另:超链接提交请求的方式,url中如果有中文数据,需要经过URL编码方式提交:表单页面,用到jsp中jstl和el表达式!!
Request乱码原理:(注:表单页面提交的码表取决于html头设置,控制浏览器以什么码表传输数据)
request.setCharacterEncoding方式对get方式提交无效!!那么我们就需要用原始的方法反向查iso8859-1,用正确的码表把乱码恢复回来:
超链接提交的中文,想不乱码也要手工处理:
测试题:下面方式不会乱码:
相当于:
以下代码会导致异常:
解决办法:记得跳转完后加上return!!
各种地址的写法:
注:一般只要写地址都先写/,不写/的是相对路径,相对于虚拟机启动目录!!
防盗链:判断是不是从本网站的网站点过来的(referer)
注:记得return!!
注:用复制过来的具体地址直接访问也属盗链,因为是直接访问过来的,没有通过任何本网站的页面!!