[JavaWeb]Servlet,Request,Response知识点补充

16 篇文章 0 订阅

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();
	}
}

2.ServletConfig:

在Servlet的配置文件中,可以使用一个或多个<init-param>标签为servlet配置一些初始化参数。
当servlet配置了初始化参数后,web容器在创建servlet实例对象时,会自动将这些初始化参数封装到ServletConfig对象中,并在调用servlet的init方法时,将ServletConfig对象传递给servlet。进而,程序员通过ServletConfig对象就可以得到当前servlet的初始化参数信息。

为了得到ServletConfig对象,可以复写init方法,在Servlet类中增加字段保存,但实际上通过this.getServletConfig即可获取(this代表Servlet对象.看源码可知其实现原理就是前面的,不过是在父类中增加了字段,实现了init方法,如此在Servlet对象中保存了服务器创建并传递过来的ServletConfig

源码细节:

<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资源

WEB容器在启动时,它会为每个WEB应用程序都创建一个对应的ServletContext对象,它代表当前web应用。

ServletConfig对象中维护了ServletContext对象的引用,开发人员在编写servlet时,可以通过ServletConfig.getServletContext方法获得ServletContext对象。

由于一个WEB应用中的所有Servlet共享同一个ServletContext对象,因此Servlet对象之间可以通过ServletContext对象来实现通讯。ServletContext对象通常也被称之为 context 对象

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带,则会出现多用户间的线程安全问题,因为ServletContext域的数据被整个应用共享!!!-->而应该用request域,为每个用户创建一个request对象。


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 {

		
	}

}

传统方式FileInputStream读取文件:注意读上面注-释-中-的-知-识-点!!-------->获取文件在服务器中的绝对路径!!

代码:

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();
	}
}

实际开发中只读取一次,可以放到静态代码块中。Dao层的异常可以抛到上层去,读取文件失败应该是一个错误,可以抛出错误ExceptionInInitializerError.

注意实际开发中用装载器读取的文件不能太大,因为是一次性加载进内存,太大会造成内存溢出!!


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!!

注:用复制过来的具体地址直接访问也属盗链,因为是直接访问过来的,没有通过任何本网站的页面!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值