Servlet
- Servlet
- Request
- Response
一.Servlet
- server applet
1. 概念
运行在服务器端的小程序
* Servlet就是一个接口,定义了Java类被浏览器访问到(tomcat识别)的规则。
* 将来我们自定义一个类,实现Servlet接口,复写方法。
2. 快速入门
1. 创建JavaEE项目
2. 定义一个类,实现Servlet接口
* public class ServletDemo1 implements Servlet
3. 实现接口中的抽象方法
4. 配置Servlet
在web.xml中配置:
<!--配置Servlet -->
<servlet>
<servlet-name>demo1</servlet-name>
<servlet-class>cn.hp.web.servlet.ServletDemo1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>demo1</servlet-name>
<url-pattern>/demo1</url-pattern>
</servlet-mapping>
- 执行原理
- 当服务器接受到客户端浏览器的请求后,会解析请求URL路径,获取访问的Servlet的资源路径
- 查找web.xml文件,是否有对应的标签体内容。
- 如果有,则在找到对应的全类名
- tomcat会将字节码文件加载进内存,并且创建其对象
- 调用其方法
3.Servlet的生命周期方法
- init 方法
servlet被创建时,执行init方法,只执行一次
- Servlet什么时候被创建?
*默认情况下,第一次被访问时,Servlet被创建
* 可以配置执行Servlet的创建时机。
* 在标签下配置
1.第一次被访问时,创建
* 的值为负数
2.在服务器启动时,创建 * <load-on-startup>的值为0或正整数
Servlet的init方法,只执行一次,说明一个Servlet在内存中只存在一个对象,Servlet是单例的
*多个用户同时访问时,可能存在线程安全问题。
* 解决:尽量不要在Servlet中定义成员变量
。即使定义了成员变量,也不要去修改值
- service 方法
提供服务:
执行service方法,可执行多次
每次访问Servlet时,Service方法都会被调用一次,即为用户提供一次服务。
- destroy方法
被销毁前:
执行destroy方法,只执行一次
- Servlet
被销毁时执行
。服务器关闭时,Servlet被销毁
只有服务器正常关闭时,才会执行destroy方法
。- destroy方法在Servlet被销毁
之前执行
,一般用于释放资源
public class Cservlet implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("出生");
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("服务");
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
4.Servlet3.0
- 使用步骤
- 创建JavaEE项目,选择Servlet的版本3.0以上,可以不创建web.xml
- 定义一个类,实现
Servlet接口
- 复写方法
- 在类上使用
@WebServlet注解
,进行配置
* @WebServlet(“资源路径”)- urlpartten:Servlet访问路径
- 一个Servlet可以定义多个访问路径 :@WebServlet({"/d4","/dd4","/ddd4"})
- 路径定义规则:
1. /xxx:路径匹配
2. /xxx/xxx:多层路径,目录结构
3. *.do:扩展名匹配
5.Servlet体系结构
Servlet -- 接口
|
GenericServlet -- 抽象类
|
HttpServlet -- 抽象类
* GenericServlet:将Servlet接口中其他的方法做了默认空实现,只将service()方法作为抽象
* 将来定义Servlet类时,可以继承GenericServlet,实现service()方法即可
* HttpServlet:对http协议的一种封装,简化操作
1. 定义类继承HttpServlet
二.Request
1.request和response
- request和response对象是由服务器创建的。我们来使用它们
- request对象是来
获取请求消息
,response对象是来设置响应消息
2.request的继承结构
ServletRequest -- 接口
| 继承
HttpServletRequest -- 接口
| 实现
org.apache.catalina.connector.RequestFacade 类(tomcat提供)
3.request的功能
1.获取请求消息数据
1. 获取请求路径url的方法,如:/day14/demo1?name=zhangsan HTTP/1.1
1. 获取请求方式 :GET/POST
* String getMethod()
2. (*)获取虚拟目录:/day14
* String getContextPath()
3. 获取Servlet路径: /demo1
* String getServletPath()
4. 获取get方式请求参数:name=zhangsan
* String getQueryString()
5. (*)获取请求URI:/day14/demo1
* String getRequestURI(): /day14/demo1 (/虚拟路径/servlet路径)
* StringBuffer getRequestURL() :http://localhost/day14/demo1
* URL:统一资源定位符: http://localhost/day14/demo1 中华人民共和国
* URI:统一资源标识符: /day14/demo1 共和国
6. 获取协议及版本:HTTP/1.1
* String getProtocol()
7. 获取客户机的IP地址:
* String getRemoteAddr()
2. 获取请求头的方法:
* String getHeader(String name):通过请求头的名称获取请求头的值
* Enumeration<String> getHeaderNames():获取所有的请求头名称
3. 获取请求体数据:
* 请求体:只有POST请求方式,才有请求体,在请求体中封装了POST请求的请求参数
* 步骤:
1. 获取流对象
* BufferedReader getReader():获取字符输入流,只能操作字符数据
* ServletInputStream getInputStream():获取字节输入流,可以操作所有类型数据
* 在文件上传知识点后讲解
2. 再从流对象中拿数据
@WebServlet("/as")
public class AServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("post");
//请求体主要是请求参数
BufferedReader reader = request.getReader();
String s=null;
while ((s=reader.readLine())!=null){
System.out.println(s);
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("get");
//先获取所有请求头的名称
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()){
String name = headerNames.nextElement();
//根据请求头获取请求头的值
String value = request.getHeader(name);
System.out.println(name+" ---- "+value);
}
}
}
2.获取请求参数
1.不论get还是post请求方式都可以使用下列方法来获取请求参数:
1. String getParameter(String name):根据参数名称获取参数值
2. String[] getParameterValues(String name):根据参数名称获取参数值的数组
3. Enumeration<String> getParameterNames():获取所有请求的参数名称
4. Map<String,String[]> getParameterMap() :获取所有参数的map集合
2.中文乱码问题:
* get方式:tomcat 8 已经将get方式乱码问题解决了
* post方式:会乱码
*在获取参数前,设置request的编码:request.setCharacterEncoding("utf-8");
@WebServlet("/bs")
public class BServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
//1.根据参数名后去参数值
String name = request.getParameter("name");
String age = request.getParameter("age");
//2.获取一个参数名对应多个参数值
String[] likes = request.getParameterValues("like");
System.out.println(Arrays.toString(likes));
//2.获取所有的参数名
Enumeration<String> names = request.getParameterNames();
while (names.hasMoreElements()){
String s = names.nextElement();
System.out.println(s+" ---- "+request.getParameter(s));
}
//4.获取所有的参数名和参数值 形成键值对 封装带map结合
Map<String, String[]> map = request.getParameterMap();
Set<Map.Entry<String, String[]>> entries = map.entrySet();
for (Map.Entry<String, String[]> entry : entries) {
System.out.println(entry.getKey()+"---"+Arrays.toString(entry.getValue()));
}
}
3.请求转发
*属于服务器行为
1. 步骤:
1. 获取请求转发器对象:RequestDispatcher getRequestDispatcher(String path)
2. 使用转发器对象来进行转发:forward(ServletRequest request, ServletResponse response)
2. 特点:
1. 浏览器地址栏路径不发生变化
2. 只能转发到当前服务器内部资源中。
3. 对原请求的转发不能算是第二次请求,一共只能算是一次请求
@WebServlet("/cs")
public class CServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("c中的request:"+request);
//存参数值
request.setAttribute("name","阿辉");
request.setAttribute("age",18);
//转发到DServlet
request.getRequestDispatcher("/ds").forward(request,response);
}
}
@WebServlet("/ds")
public class DServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("d中的request:"+request);
System.out.println("d中的name1:"+request.getAttribute("name")+"\n"+
"d中的age1:"+request.getAttribute("age"));
//存参数
request.setAttribute("name","威威");
request.setAttribute("age",55);
System.out.println("d中的name2:"+request.getAttribute("name")+"\n"+
"d中的age2:"+request.getAttribute("age"));
//转发到EServlet
request.getRequestDispatcher("/es").forward(request,response);
}
}
@WebServlet("/es")
public class EServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("e中的request:"+request);
System.out.println("e中的参数:"+request.getAttribute("name")+" "+request.getAttribute("age"));
}
}
4.域对象
* 域对象:一个有作用范围的对象,可以在范围内共享数据
* request域:代表一次请求的范围,一般用于请求转发的多个资源中共享数据
* request作为域对象使用的方法:
1. void setAttribute(String name,Object obj):存储数据
2. Object getAttitude(String name):通过键获取值
3. void removeAttribute(String name):通过键移除键值对
*获取最大域对象:ServletContext:
* ServletContext getServletContext()
三.Response对象
1.功能
设置响应消息
1.设置响应行
格式
:HTTP/1.1 200 ok设置状态码
:setStatus(int sc)
2.
设置响应头
:setHeader(String name, String value)
3.
设置响应体
:
* 使用步骤:
1.获取输出流
*字符输出流
:PrintWriter getWriter()
*字节输出流
:ServletOutputStream getOutputStream()
2.使用输出流,将数据输出到客户端浏览器
2.完成重定向
//1. 设置状态码为302
response.setStatus(302);
//2.设置响应头location
response.setHeader("location","/day15/responseDemo2");
//简单的重定向方法
response.sendRedirect("/day15/responseDemo2");
- 重定向和转发
1.
重定向(redirect)的特点:
地址栏发生变化
- 重定向可以访问其他站点(服务器)的资源
重定向是两次请求
。不能使用request对象来共享数据
2.
转发(forward)的特点:
转发地址栏路径不变
- 转发只能访问当前服务器下的资源
转发是一次请求,可以使用request对象来共享数据
forward 和 redirect 区别
* forward 和 redirect 区别
* 路径写法:
1. 路径分类
1. 相对路径:通过相对路径不可以确定唯一资源
* 如:./index.html
* 不以/开头,以.开头路径
* 规则:找到当前资源和目标资源之间的相对位置关系
* ./:当前目录
* ../:后退一级目录
2. 绝对路径:通过绝对路径可以确定唯一资源
* 如:http://localhost/day15/responseDemo2 /day15/responseDemo2
* 以/开头的路径
* 规则:判断定义的路径是给谁用的?判断请求将来从哪儿发出
* 给客户端浏览器使用:需要加虚拟目录(项目的访问路径)
* 建议虚拟目录动态获取:request.getContextPath()
* <a> , <form> 重定向...
* 给服务器使用:不需要加虚拟目录
* 转发路径
3.服务器输出字符数据到浏览器
获取字符输出流
PrintWriter pw = response.getWriter()
输出数据
response.getWriter().write(“字符串”);
4.服务器输出字节数据到浏览器
获取字节输出流
响应字节数据
response.getOutputStream().write(字节或字节数组);
验证码
public class CheckCode {
//使用到Algerian字体,系统里没有的话需要安装字体,字体只显示大写,去掉了1,0,i,o几个容易混淆的字符
public static final String VERIFY_CODES = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";
private static Random random = new Random();
/**
* 使用系统默认字符源生成验证码
*
* @param verifySize 验证码长度
* @return
*/
public static String generateVerifyCode(int verifySize) {
return generateVerifyCode(verifySize, VERIFY_CODES);
}
/**
* 使用指定源生成验证码
*
* @param verifySize 验证码长度
* @param sources 验证码字符源
* @return
*/
public static String generateVerifyCode(int verifySize, String sources) {
if (sources == null || sources.length() == 0) {
sources = VERIFY_CODES;
}
int codesLen = sources.length();
Random rand = new Random(System.currentTimeMillis());
StringBuilder verifyCode = new StringBuilder(verifySize);
for (int i = 0; i < verifySize; i++) {
verifyCode.append(sources.charAt(rand.nextInt(codesLen - 1)));
}
return verifyCode.toString();
}
/**
* 生成随机验证码文件,并返回验证码值
*
* @param w
* @param h
* @param outputFile
* @param verifySize
* @return
* @throws IOException
*/
public static String outputVerifyImage(int w, int h, File outputFile, int verifySize) throws IOException {
String verifyCode = generateVerifyCode(verifySize);
outputImage(w, h, outputFile, verifyCode);
return verifyCode;
}
/**
* 输出随机验证码图片流,并返回验证码值
*
* @param w
* @param h
* @param os
* @param verifySize
* @return
* @throws IOException
*/
public static String outputVerifyImage(int w, int h, OutputStream os, int verifySize) throws IOException {
String verifyCode = generateVerifyCode(verifySize);
outputImage(w, h, os, verifyCode);
return verifyCode;
}
/**
* 生成指定验证码图像文件
*
* @param w
* @param h
* @param outputFile
* @param code
* @throws IOException
*/
public static void outputImage(int w, int h, File outputFile, String code) throws IOException {
if (outputFile == null) {
return;
}
File dir = outputFile.getParentFile();
if (!dir.exists()) {
dir.mkdirs();
}
try {
outputFile.createNewFile();
FileOutputStream fos = new FileOutputStream(outputFile);
outputImage(w, h, fos, code);
fos.close();
} catch (IOException e) {
throw e;
}
}
/**
* 输出指定验证码图片流
*
* @param w
* @param h
* @param os
* @param code
* @throws IOException
*/
public static void outputImage(int w, int h, OutputStream os, String code) throws IOException {
int verifySize = code.length();
BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
Random rand = new Random();
Graphics2D g2 = image.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
Color[] colors = new Color[5];
Color[] colorSpaces = new Color[]{Color.WHITE, Color.CYAN,
Color.GRAY, Color.LIGHT_GRAY, Color.MAGENTA, Color.ORANGE,
Color.PINK, Color.YELLOW};
float[] fractions = new float[colors.length];
for (int i = 0; i < colors.length; i++) {
colors[i] = colorSpaces[rand.nextInt(colorSpaces.length)];
fractions[i] = rand.nextFloat();
}
Arrays.sort(fractions);
g2.setColor(Color.GRAY);// 设置边框色
g2.fillRect(0, 0, w, h);
Color c = getRandColor(200, 250);
g2.setColor(c);// 设置背景色
g2.fillRect(0, 2, w, h - 4);
//绘制干扰线
Random random = new Random();
g2.setColor(getRandColor(160, 200));// 设置线条的颜色
for (int i = 0; i < 20; i++) {
int x = random.nextInt(w - 1);
int y = random.nextInt(h - 1);
int xl = random.nextInt(6) + 1;
int yl = random.nextInt(12) + 1;
g2.drawLine(x, y, x + xl + 40, y + yl + 20);
}
// 添加噪点
float yawpRate = 0.05f;// 噪声率
int area = (int) (yawpRate * w * h);
for (int i = 0; i < area; i++) {
int x = random.nextInt(w);
int y = random.nextInt(h);
int rgb = getRandomIntColor();
image.setRGB(x, y, rgb);
}
shear(g2, w, h, c);// 使图片扭曲
g2.setColor(getRandColor(100, 160));
int fontSize = h - 4;
Font font = new Font("Algerian", Font.ITALIC, fontSize);
g2.setFont(font);
char[] chars = code.toCharArray();
for (int i = 0; i < verifySize; i++) {
AffineTransform affine = new AffineTransform();
affine.setToRotation(Math.PI / 4 * rand.nextDouble() * (rand.nextBoolean() ? 1 : -1), (w / verifySize) * i + fontSize / 2, h / 2);
g2.setTransform(affine);
g2.drawChars(chars, i, 1, ((w - 10) / verifySize) * i + 5, h / 2 + fontSize / 2 - 10);
}
g2.dispose();
ImageIO.write(image, "png", os);
}
private static Color getRandColor(int fc, int bc) {
if (fc > 255) {
fc = 255;
}
if (bc > 255) {
bc = 255;
}
int r = fc + random.nextInt(bc - fc);
int g = fc + random.nextInt(bc - fc);
int b = fc + random.nextInt(bc - fc);
return new Color(r, g, b);
}
private static int getRandomIntColor() {
int[] rgb = getRandomRgb();
int color = 0;
for (int c : rgb) {
color = color << 8;
color = color | c;
}
return color;
}
private static int[] getRandomRgb() {
int[] rgb = new int[3];
for (int i = 0; i < 3; i++) {
rgb[i] = random.nextInt(255);
}
return rgb;
}
private static void shear(Graphics g, int w1, int h1, Color color) {
shearX(g, w1, h1, color);
shearY(g, w1, h1, color);
}
private static void shearX(Graphics g, int w1, int h1, Color color) {
int period = random.nextInt(2);
boolean borderGap = true;
int frames = 1;
int phase = random.nextInt(2);
for (int i = 0; i < h1; i++) {
double d = (double) (period >> 1)
* Math.sin((double) i / (double) period
+ (6.2831853071795862D * (double) phase)
/ (double) frames);
g.copyArea(0, i, w1, 1, (int) d, 0);
if (borderGap) {
g.setColor(color);
g.drawLine((int) d, i, 0, i);
g.drawLine((int) d + w1, i, w1, i);
}
}
}
private static void shearY(Graphics g, int w1, int h1, Color color) {
int period = random.nextInt(40) + 10; // 50;
boolean borderGap = true;
int frames = 20;
int phase = 7;
for (int i = 0; i < w1; i++) {
double d = (double) (period >> 1)
* Math.sin((double) i / (double) period
+ (6.2831853071795862D * (double) phase)
/ (double) frames);
g.copyArea(i, 0, 1, h1, 0, (int) d);
if (borderGap) {
g.setColor(color);
g.drawLine(i, (int) d, i, 0);
g.drawLine(i, (int) d + h1, i, h1);
}
}
}
public static void main(String[] args) throws IOException {
String s = CheckCode.generateVerifyCode(4);
System.out.println(s);
}
}
@WebServlet("/imgCode")
public class ImgCodeServlet extends HttpServlet {
//相应验证码图片
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
CheckCode.outputVerifyImage(200,70,response.getOutputStream(),4);
}
}
用户登录
实体类
public class Users {
private int id;
private String name;
private String psw;
@Override
public String toString() {
return "Users{" +
"id=" + id +
", name='" + name + '\'' +
", psw='" + psw + '\'' +
'}';
}
public Users() {
}
public Users(int id, String name, String psw) {
this.id = id;
this.name = name;
this.psw = psw;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPsw() {
return psw;
}
public void setPsw(String psw) {
this.psw = psw;
}
}
Dao操作层 UserDao类
public interface UserDao {
boolean login(Users users);
}
/**
* 登录方法
* @param user对象,只有用户名和密码
* @return user包含用户全部数据,没有查询到,返回null
*/
public class UserDaoImpl implements UserDao {
@Override
public boolean login(Users users) {
JdbcTemplate template = new JdbcTemplate(JdbcUtil.getDs());
Users users1 = null;
try{
users1 = template.queryForObject("select *from users where name=? and psw=?",
new BeanPropertyRowMapper<Users>(Users.class), users.getName(), users.getPsw());
}catch (Exception e){
System.out.println("差不到");
return false;
}
return users1!=null;
}
}
Service
public class UserDaoImpl implements UserDao {
@Override
public boolean login(Users users) {
JdbcTemplate template = new JdbcTemplate(JdbcUtil.getDs());
Users users1 = null;
try{
users1 = template.queryForObject("select *from users where name=? and psw=?",
new BeanPropertyRowMapper<Users>(Users.class), users.getName(), users.getPsw());
}catch (Exception e){
System.out.println("差不到");
return false;
}
return users1!=null;
}
}
public class UserServiceImpl implements UserService {
//dao
private UserDao dao = new UserDaoImpl();
@Override
public boolean login(Users users) {
return dao.login(users);
}
}
web层 LoginServlet类
//控制层 ---- 代码清晰简洁
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
private UserService service = new UserServiceImpl();
@Override //取 调 存 转
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
//取参数
Map<String, String[]> map = req.getParameterMap();
//map-->bean
Users users = JdbcUtil.mapToBean(map, Users.class);
//调用service
boolean isLogin = service.login(users);
//成功
if (isLogin) {
req.getRequestDispatcher("/success.html").forward(req,resp);
}else { //失败
req.getRequestDispatcher("/login.html").forward(req,resp);
}
}
}
工具类 JDBCUtils
public class JdbcUtil {
private static DataSource ds;
/**
* 文件的读取,只需要读取一次即可拿到这些值。使用静态代码块
*/
static{
//读取资源文件,获取值。
try {
FileInputStream is = new FileInputStream("E:\\Java-servlet\\day15-servlet02\\src\\druid.properties");
Properties p = new Properties();
p.load(is);
//创建数据源 给成员属性 ds赋值
ds = DruidDataSourceFactory.createDataSource(p);
//System.out.println(p);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
System.out.println(getDs());
}
/**
* 获取连接
* @return 连接对象
*/
public static Connection getConnection() {
try {
return ds.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
//获取连接池对象 ds
public static DataSource getDs(){
return ds;
}
/**
* 释放资源
* @param stmt
* @param conn
*/
public static void close(ResultSet rs, Statement stmt, Connection conn){
if( rs != null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if( stmt != null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if( conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
//2.封装map-->bean BeanUtils使用过程
public static <T>T mapToBean(Map map,Class<T> c) {
T t = null;
try {
t = c.newInstance();
BeanUtils.populate(t, map);
} catch (Exception e) {
e.printStackTrace();
}
return t;
}
}