周总结
01_Servlet三种方式实现
A_继承HttpServlet
在HttpServlet中,重写GenricServlet类中的service方法,会由GenericServlet给service方法提供请求对象(ServletRequest)、响应对象(ServletResponse),不针对Http协议!!所以需要强转成HttpServletRequest、HttpServletResponse。
紧接着,调用另外一个service方法,传入HttpServletRequest、HttpServletResponse
获取到请求方式,如果是get请求,就调用doGet方法,如果是post请求,就调用doPost方法!!
B_继承GenericServlet类
如果要针对Http协议,需要手动强转
C_实现Servlet接口
如果要针对Http协议,需要手动强转
02_Servlet的生命周期
A_Servlet源码
public interface Servlet {
void init(ServletConfig var1) throws ServletException;
ServletConfig getServletConfig();
void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
String getServletInfo();
void destroy();
}
init(ServletConfig var1):
监听Servlet的初始化
service(ServletRequest var1, ServletResponse var2):
处理请求
destroy():
监听Servlet的销毁
B_生命周期
一个事物,从开始、存活、结束!说白了,就是需要看Servlet的初始化、存活、销毁!!
C_Servlet的生命周期特点
servlet什么时候初始化?
Servlet默认不是随着服务器的启动而初始化,当第一次访问Servlet时才初始化,后面访问就执行处理请求
会发现Servlet是一个单例!!
servlet什么时候销毁?
服务器关闭的时候会销毁Servlet
### 03_load on startup配置
A_概念
根据Servlet生命周期,可知,servlet默认不会随着服务器的启动而初始化!
如果,就想让指定的Servlet跟随服务器启动而初始化,我们如何做?需要用到load on startup
load on startup可以让servlet随着服务器的启动而初始化
B_load on startup
有10个优先级:1~10;数字越小,优先级越高!
比如:Demo01Servlet,load-on-startup:1 ;Demo02Servlet , load-on-startup :2;
当服务器启动时,Demo01Servlet先初始化,然后,Demo02Servlet再初始化!
04_Servlet配置
A_概念
自定义Servlet,第一步,声明该Servlet;第二步,给该Servlet配置访问路径
B_问题一
一个Servlet是否可以有多个访问名称?
可以有多个访问名称,只需要给对应的Servlet多个<servlet-mapping>标签即可!
C_问题二
<url-parttern>书写规则
完全匹配:
要求网址上的访问名称完全和<url-parttern>一致
必须以"/"开头,否则会报错:IllegalArgumentException : Invalid <url-pattern>
目录匹配:
要求网址上的访问名称中的目录和<url-parttern>一致
必须以"/"开头,以"*"结尾
比如:/a/b/* , 目录必须是/a/b,后面随便写
后缀名匹配:
要求网址上的访问名称中的后缀和<url-parttern>一致
不能以"/"开头,以"*"开头,后缀名根据业务写
比如:*.xyz。后缀名必须是xyz,其他随意写!!!
05_缺省Servlet
- A_概念
url-parttern的值为"/"就是缺省Servlet - B_作用
tomcat容器自带缺省Servlet
处理匹配不到的资源,返回404页面
处理静态资源,io流读写 - C_自定义缺省Servlet
在当前工程中自定义Servlet,将url-parttern设置为"/",就覆盖了tomcat容器中的缺省Servlet - D_应用
SpringMVC框架中,用于放行静态资源!
06_服务器中路径问题
- 路径分类
带协议的绝对路径:http://localhost:8080/day51/img/girl.jpg
不带协议的绝对路径:/day51/img/girl.jpg
相对路径:
当前目录:./ ,可以省略
上一级目录:…/ - 总结
开发中,用的比较多:不带协议的绝对路径、相对路径
07_ServletConfig对象
- 概述
ServletConfig是由tomcat容器创建,通过init方法传入给Servlet - 作用
- 获取Servlet的名称标签内容
- 获取Servlet的初始化参数
- 随着Servlet的初始化而初始化
<servlet>
<servlet-name>Demo11Servlet</servlet-name>
<servlet-class>com.qfedu.servlet.Demo11Servlet</servlet-class>
<!--Servlet的初始化参数-->
<init-param>
<param-name>username</param-name>
<param-value>root</param-value>
</init-param>
<init-param>
<param-name>password</param-name>
<param-value>root123</param-value>
</init-param>
</servlet>
getInitParameter(String parameterName):根据参数名称获取指定的参数值
getInitParameterNames():获取所有的参数名称
- 获取域对象:ServletContext
08_ServletContext对象
- 概述
- 相当于是整个应用程序对象
- 作用
- ServletContext是一个域对象,可以用来存储数据
- 在应用程序中的任何位置都能够访问
- getAttribute(String parameterName) : 获取ServletContext域中指定名称的参数值
- setAttribute(String paramterName,Object parameterValue):存储参数到ServletContext域中
- removeAttribute(String parameterNam):将ServletContext域中指定名称的参数移除!
- 获取全局初始化参数
- 会随着服务器的启动而初始化
- ServletContext是一个域对象,可以用来存储数据
<context-param>
<param-name>username</param-name>
<param-value>root456</param-value>
</context-param>
- 获取服务器真实磁盘路径
- getRealPath:依据当前项目去生成真实磁盘路径,
比如:servletContext.getRealPath(“upload”)
“当前项目的服务器磁盘路径/upload”
比如:servletContext.getRealPath(“upload/img”):
“当前项目的服务器磁盘路径/upload/img”
- getRealPath:依据当前项目去生成真实磁盘路径,
09_站点访问次数案例
- 使用ServletContext域对象
- 步骤
-
判断是否是第一次请求
Integer count = servletContext.getAttribute(“count”);
if(count == null){
…
} else {
…
} -
如果是第一次请求
int count = 1;
System.out.println("访问次数 : " + count);
servlet.setAttribute(“count”,count); -
如果不是第一次请求
count++;
System.out.println("访问次数 : " + count);
servlet.setAttribute(“count”,count);
-
10_Servlet3.0
-
概念
Servlet2.5,不支持注解开发。
Servlet3.0,支持注解开发。 -
常用注解
@Test:单元测试
@Override:方法重写 -
@WebServlet:
-
声明配置Servlet,取代了web.xml配置
@WebServlet(name = “Demo18Servlet”,urlPatterns = “/demo18”)
public class Demo18Servlet extends HttpServlet {}
以上配置代码,相当于web.xml配置如下:
-
<servlet>
<servlet-name>Demo18Servlet</servlet-name>
<servlet-class>com.qfedu.servlet.Demo18Servlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Demo18Servlet</servlet-name>
<url-pattern>/demo18</url-pattern>
</servlet-mapping>
登录案例
使用Servlet、html、HttpServletRequest、JDBC完成登录需求
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>登录</title>
<link rel="stylesheet" href="../css/form.css"/>
</head>
<body>
<div id="container">
<div id="title_nav"></div>
<div id="content">
<div id="form_sub">
<p id="manage_tx">登录</p>
<form id="form_sub_a" action="<%=request.getContextPath()%>/mylogin" method="post">
<p class="tx_01">账号: <input class="input_va" type="text" name="user" /></p>
<p class="tx_01">密码: <input class="input_va" type="password" name="password" /></p>
<input class="btn_s" type="submit" value="登录" />
<input class="btn_s" type="reset" value="取消" />
</form>
</div>
</div>
</div>
</body>
</html>
Servlet
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
<display-name>py01</display-name>
<servlet>
<servlet-name>LoginTest</servlet-name>
<servlet-class>com.mero.test.LoginServlet.LoginTest</servlet-class>
</servlet>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<servlet-mapping>
<servlet-name>LoginTest</servlet-name>
<url-pattern>/mylogin</url-pattern>
</servlet-mapping>
</web-app>
JDBC
package com.qf.day51.homework;
import java.io.IOException;
import java.sql.*;
import javax.servlet.*;
import javax.servlet.http.*;
import com.mero.test.DbDao;
public class LoginTest extends HttpServlet {
public Connection conn=null;
public static String username;
public static String password;
private static final long serialVersionUID = 1L;
public LoginTest() {
super();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(req, resp);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
username=request.getParameter("user");
password=request.getParameter("password");
System.out.println(username);
System.out.println(password);
System.out.println("请求参数成功");
try {
conn=DbDao.getConnection();
Statement statement=conn.createStatement();
ResultSet set=statement.executeQuery("select username,password from userInfo where username=1");
while(set.next()){
String name=set.getString(1);
String pwd=set.getString(2);
if(username!=null&&password!=null&&username.equals(name)&&password.equals(pwd)){
String forward="/01/success.jsp";
RequestDispatcher df=request.getRequestDispatcher(forward);
df.forward(request, response);
}else{
String forward="/01/failed.jsp";
RequestDispatcher df=request.getRequestDispatcher(forward);
df.forward(request, response);
}
}
} catch (ClassNotFoundException e1) {
e1.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
package com.qf.day51.t1;
import sun.plugin2.gluegen.runtime.CPU;
import java.sql.*;
public class DBUtils {
static {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
//硬编码
//获得连接
public static Connection getConnection() {
Connection connection = null;
try {
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/companydb?useUnicode=true&characterEncoding=UTF8", "root", "1234");
} catch (Exception e) {
e.printStackTrace();
}
return connection;
}
//释放资源
public static void closeAll(Connection connection, Statement statement, ResultSet resultSet) {
try {
if (resultSet != null) {
resultSet.close();
}
if (statement != null) {
statement.close();
}
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
<!DOCTYPE html >
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>登陆成功</title>
</head>
<body>
登陆成功 <br/>
<a href="<%=request.getContextPath()%>/01/login.jsp">返回登陆界面</a>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>登陆失败</title>
</head>
<body>
登陆失败 <br/>
<a href="<%=request.getContextPath()%>/01/login.jsp">返回登陆界面</a>
</body>
</html>
01_Servlet3.0
- 概念
支持注解开发,由注解配置来代替web.xml配置 - 用法
在Servlet类上直接使用@WebServlet注解 - 常用属性
- name:String
- 设置Servlet名称
- urlPatterns:String[]
- 设置Servlet的访问路径
- loadOnStartup:int
- 设置Servlet的load-on-startup属性
- initParams:WebInitParam[]
- 设置Servlet的初始化参数
- name:String
02_类加载
- 概念
在class文件加载到jvm中时,会对应创建一个Class对象;分为三个步骤:加载、连接、初始化- 加载
- 将class文件加载到内存区域,对应生成一个Class对象
- 连接
- 验证:验证类的结构是否正确
- 准备:初始化静态成员
- 解析:将字节转换成jvm能够执行的引用(对象、变量、方法)
- 初始化
- 将对象中的成员变量初始化
- 加载
- 加载时机
- Class.forName(“com.mysql.jdbc.Driver”) : 将Driver类加载到jvm中的方法区
- 初始化Fahter类的子类Son类:Father类也会加载
- Son类会使用到Father类中的成员变量,Father类中成员变量就需要进行初始化,就需要将Father加载到内存,子类要调用父类的构造方法。
03_类加载器
- 概念
将class文件加载进内存,并生成对应的Class对象 - 分类
- 根类加载器
- 加载java中的核心类,主要加载F:\Program Files\Java\jdk1.8.0_202\jre\lib\rt.jar中的类
- 扩展类加载器
- 加载java中的扩展类,主要加载F:\Program Files\Java\jdk1.8.0_202\jre\lib\ext所有jar包中的类
- 系统类加载器
- 加载开发人员编写的自定义类、以及一些第三方的jar包中类
- 根类加载器
04_类反射机制
- 概念
通过类的Class对象,动态去调用类中的属性和方法 - 获取Class对象方式
- 全类名=包名.类名
- Class.forName(“全类名”)
- 编译期
- 类名.class
- 运行时
- 对象名.getClass()
- 全类名=包名.类名
05_反射结合工厂模式
- 使用继承
- 解决Apple类、Banana类的代码复用性
- Banana、Apple继承Fruit类
- 使用工厂模式
- 解决Banana、Apple和其他模块耦合较高
- 使用反射机制
- 解决工厂的getFruit方法中if…else代码过多
06_反射操作构造器
- 概念
- 使用反射机制操作public、protected、默认、private的构造器
- 应用场景
- 访问public修饰的构造器
Class<?> clazz = Class.forName(“com.qfedu.bean.User”);
Constructor<?> c1 = clazz.getConstructor();
Object obj1 = c1.newInstance();
System.out.println(obj1); - 访问非public修饰的构造器
- 使用暴力反射
Class<?> clazz = Class.forName(“com.qfedu.bean.User”);
Constructor<?> c3 = clazz.getDeclaredConstructor(String.class, String.class);
c3.setAccessible(true);
Object obj3 = c3.newInstance(“张国静”, “20000101”);
System.out.println(obj3);
- 使用暴力反射
- 访问public修饰的构造器
07_反射操作成员变量
- 概念
- 使用反射机制操作public、protected、默认、private的成员变量
- 应用场景
- 操作public修饰的成员变量
Class clazz = User.class;
User user = clazz.newInstance();
Field idField = clazz.getField(“id”);
//设置成员变量
idField.set(user,250);
System.out.println(user);
//获取成员变量
Object idValue = idField.get(user);
System.out.println(idValue); - 操作非public修饰的成员变量
Class clazz = User.class;
User user = clazz.newInstance();
Field usernameField = clazz.getDeclaredField(“username”);
//暴力反射
usernameField.setAccessible(true);
//设置成员变量
usernameField.set(user,“坤坤”);
//获取成员变量
Object usernameValue = usernameField.get(user);
System.out.println(usernameValue);
- 操作public修饰的成员变量
08_反射操作成员方法
- 概念
- 使用反射机制操作public、protected、默认、private的成员方法
- 应用场景
-
操作public修饰的成员方法
Class clazz = User.class;
User user = clazz.newInstance();
//获取public修饰的成员方法
Method method01 = clazz.getMethod(“setPassword”, String.class);
//使用方法对象
//obj:哪个对象在执行该方法
//args:方法执行时所需的参数值
method01.invoke(user,“123456”); -
操作非public修饰的成员方法
Method method02 = clazz.getDeclaredMethod(“show”);
method02.setAccessible(true);
Object result = method02.invoke(user);
System.out.println(result);- invoke方法后的返回值就是原有方法执行之后的返回值!!
-
09_反射越过泛型检查
- 概述
- java中的泛型的作用范围在编译期,也就是说在运行时,是没有泛型的!
- 反射技术,可以在程序运行时,动态地调用List类中add方法,往集合中添加任意类型的元素。
- 代码实现
List list = new ArrayList();
list.add(1);
list.add(2);
list.add(3);
System.out.println(list);
//反射操作List的add方法
Class<? extends List> clazz = list.getClass();
Method add = clazz.getDeclaredMethod(“add”, Object.class);
add.setAccessible(true);
Object result = add.invoke(list, “hello , generic type !”);
System.out.println(list);
10_反射通用方法案例
- 需求
- 给指定对象的指定字段设置指定值
- 代码实现
- 获取指定字段对应的set方法名称
- 获取set方法对象
- 获取指定字段类型
- 执行set方法
public static void setValue(Object obj , String fieldName , Object value) throws Exception {
Class<?> clazz = obj.getClass();
//根据属性名称获取对应的set方法名称
String methodName = “set” + fieldName.substring(0,1).toUpperCase() + fieldName.substring(1);
Field field = clazz.getDeclaredField(fieldName);
//获取字段的数据类型
Class<?> fieldType = field.getType();
//获取到set方法对象
Method method = clazz.getMethod(methodName, fieldType);
//执行set方法
method.invoke(obj , value);
}
11_反射结合配置文件
- 需求
- 编写bean.properties,配置对象的唯一标识及对象的全类名,根据这段配置创建一个对象
- 代码实现
-
读取配置文件
bean01=com.qfedu.bean.User
bean02=com.qfedu.bean.Banana -
根据配置文件中的className生成对象
Properties properties = new Properties();
//将bean.properties中的数据存储到inputStream中
InputStream inputStream = Demo11.class.getClassLoader().getResourceAsStream(“bean.properties”);
//将bean.properties中的数据绑定到了Properties中!
properties.load(inputStream);
//获取全类名
String className = properties.getProperty(“bean01”);
//根据上述全类名创建了一个对象!
Object obj = Class.forName(className).newInstance();
System.out.println(obj);
//根据上述全类名创建了一个对象!
String className2 = properties.getProperty(“bean02”);
Object obj2 = Class.forName(className2).newInstance();
System.out.println(obj2);
-
12_静态代理设计模式
-
概念
- 增强被代理类的功能
-
步骤
- 自定义类实现和被代理类相同的接口
- 在代理类中声明被代理类的对象
- 在代理类的方法中使用被代理类调用方法
-
代码实现
//1,自定义一个代理类(增强类)实现和被代理类(被增强类)相同的接口
public class UserDaoImplProxy implements UserDao{//2,在代理类中声明被代理类的引用 private UserDao userDao ; public UserDaoImplProxy(){ userDao = new UserDaoImpl(); } @Override public void addUser() { //3,在代理类的方法中使用被代理类调用方法 System.out.println("权限校验"); userDao.addUser(); System.out.println("日志记录"); } @Override public void deleteUser() { userDao.deleteUser(); } @Override public void updateUser() { userDao.updateUser(); } @Override public void selectUser() { userDao.selectUser(); } }
-
特点
- 缺点:必须要重写被代理类接口的所有的方法(包括不需要增强的方法,增高了耦合性)
- 作用:增强被代理类的功能
- 特点:可以控制被代理类对象
package com.qf.bean;
public class User {
public Integer id;
private String username;
private String password;
public User() {
}
/**
* 使用private修饰有参构造,当前构造只能在本类中使用
* @param username
* @param password
*/
private User(String username,String password){
this.username = username;
this.password = password;
}
public User(Integer id, String username, String password) {
this.id = id;
this.username = username;
this.password = password;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
private String show(){
System.out.println("id : "+getId()+" , username : "+getUsername()+" , password : "+ getPassword());
return "helloworld";
}
}
package com.qf.bean;
public class Fruit {
private String fruitName;
public Fruit() {
}
public Fruit(String fruitName) {
this.fruitName = fruitName;
}
public String getFruitName() {
return fruitName;
}
public void setFruitName(String fruitName) {
this.fruitName = fruitName;
}
@Override
public String toString() {
return "Fruit{" +
"fruitName='" + fruitName + '\'' +
'}';
}
}
package com.qfedu.bean;
/**
* 香蕉
*/
public class Banana extends Fruit{
private int width;
public Banana() {
}
public Banana(String fruitName,int width) {
super(fruitName);
this.width = width;
}
}
package com.qfedu.bean;
/**
* 苹果
*/
public class Apple extends Fruit{
private int height;
public Apple() {
}
public Apple(String fruitName , int height) {
super(fruitName);
this.height = height;
}
}
package com.qfedu.dao;
/**
* 被代理类(被增强类)
* 主要功能
* 添加用户
* 删除用户
* 修改用户
* 查询用户
*/
public class UserDaoImpl implements UserDao {
@Override
public void addUser() {
System.out.println("UserDaoImpl addUser");
}
@Override
public void deleteUser() {
System.out.println("UserDaoImpl deleteUser");
}
@Override
public void updateUser() {
System.out.println("UserDaoImpl updateUser");
}
@Override
public void selectUser() {
System.out.println("UserDaoImpl selectUser");
}
}
package com.qfedu.dao;
//1,自定义一个代理类(增强类)实现和被代理类(被增强类)相同的接口
/**
* 代理类(增强类)
* 完成非必要功能
* 权限校验
* 日志记录
*/
public class UserDaoImplProxy implements UserDao{
//2,在代理类中声明被代理类的引用
private UserDao userDao ;
public UserDaoImplProxy(){
userDao = new UserDaoImpl();
}
@Override
public void addUser() {
//3,在代理类的方法中使用被代理类调用方法
System.out.println("权限校验");
userDao.addUser();
System.out.println("日志记录");
}
@Override
public void deleteUser() {
userDao.deleteUser();
}
@Override
public void updateUser() {
userDao.updateUser();
}
@Override
public void selectUser() {
userDao.selectUser();
}
}
package com.qfedu.factory;
import com.qfedu.bean.Apple;
import com.qfedu.bean.Banana;
import com.qfedu.bean.Fruit;
/**
* 水果工厂
* 工厂模式:降低耦合
* 结合使用多态:可扩展性强
*/
public class FruitFactory2 {
public static Fruit getFruit(String fruitName){
if ("apple".equals(fruitName)) {
return new Apple("红富士",10);
} else if ("banana".equals(fruitName)) {
return new Banana("南亚小香蕉",10);
}
return null;
}
}
package com.qfedu.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(name = "Demo01Servlet" ,
urlPatterns = {"/demo01" ,"/mydemo01"},
loadOnStartup = 1,
initParams ={ @WebInitParam(name = "username",value = "root") ,
@WebInitParam(name = "password",value = "root123") })
public class Demo01Servlet extends HttpServlet {
@Override
public void init() throws ServletException {
System.out.println("Demo01Servlet init");
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("Demo01Servlet");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
package com.qfedu.demo;
import com.qfedu.bean.User;
/**
* 获取Class对象的三种方式
*/
public class Demo01 {
public static void main(String[] args) throws Exception {
//只知道包名+类名,可以获取到User对应的Class对象
Class<?> clazz01 = Class.forName("com.qfedu.bean.User");
//编译期,已经有了Class对象
Class clazz02 = User.class;
//运行时
User user = new User();
Class<? extends User> clazz03 = user.getClass();
//clazz01、clazz02、clazz03有什么特点?
//都是同一个对象,同一个类的Class对象(运行时对象/字节码文件对象)只有一个对象!
System.out.println(clazz01 == clazz02);
System.out.println(clazz02 == clazz03);
}
}
package com.qfedu.demo;
import com.qfedu.bean.Apple;
import com.qfedu.bean.Banana;
public class Demo02 {
public static void main(String[] args) {
//获取一个苹果
Apple apple = new Apple("红富士",10);
//获取一个香蕉
Banana banana = new Banana("黄金大香蕉",10);
//耦合性太高!
//Apple类和其他组件的耦合性太高!!
}
}
package com.qfedu.demo;
import com.qfedu.bean.Apple;
import com.qfedu.bean.Banana;
import com.qfedu.bean.Fruit;
import com.qfedu.factory.FruitFactory;
import com.qfedu.factory.FruitFactory1;
public class Demo03 {
public static void main(String[] args) {
//获取一个苹果
Apple apple = FruitFactory1.getApple();
//获取一个香蕉
Banana banana = FruitFactory1.getBanana();
}
}
package com.qfedu.demo;
import com.qfedu.bean.User;
import java.lang.reflect.Field;
/**
* 反射操作成员变量
*/
public class Demo07 {
public static void main(String[] args) throws Exception {
//获取User类的Class对象
Class<User> clazz = User.class;
User user = clazz.newInstance();
//操作public修饰的成员变量
Field idField = clazz.getField("id");
//设置该成员变量值
//obj:需要设置的对象
//value:需要设置的值
//给user对象的id属性设置值为250
idField.set(user,250);
System.out.println(user);
//获取该成员变量值
Object idValue = idField.get(user);
System.out.println(idValue);
//操作非public修饰的成员变量
Field usernameField = clazz.getDeclaredField("username");
usernameField.setAccessible(true);
usernameField.set(user,"峰峰");
System.out.println(user);
Object usernameValue = usernameField.get(user);
System.out.println(usernameValue);
}
}
package com.qfedu.demo;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
public class Demo09 {
public static void main(String[] args) throws Exception {
//创建List集合对象
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(3);
System.out.println(list);
//泛型只在编译期有效!!!
//反射越过泛型检查
//反射可以在程序运行时,动态地调用List中的add方法去添加元素
Class<? extends List> clazz = list.getClass();
Method add = clazz.getDeclaredMethod("add", Object.class);
add.setAccessible(true);
Object result = add.invoke(list, "hello , generic type !");
System.out.println(list);
}
}
package com.qfedu.demo;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
/**
* 反射结合配置文件
*/
public class Demo11 {
public static void main(String[] args) throws Exception {
//需求:编写bean.properties,配置对象的唯一标识及对象的全类名,根据这段配置创建一个对象
Properties properties = new Properties();
//将bean.properties中的数据存储到inputStream中
InputStream inputStream = Demo11.class.getClassLoader().getResourceAsStream("bean.properties");
//将bean.properties中的数据绑定到了Properties中!
properties.load(inputStream);
//获取全类名
String className = properties.getProperty("bean01");
//根据上述全类名创建了一个对象!
Object obj = Class.forName(className).newInstance();
System.out.println(obj);
String className2 = properties.getProperty("bean02");
Object obj2 = Class.forName(className2).newInstance();
System.out.println(obj2);
}
}
01_装饰者设计模式
-
开发步骤
- 定义装饰类(增强类)实现和被装饰类(被增强类)相同的接口
- 在装饰类中声明被装饰类的引用
- 在装饰类的方法中,使用被装饰调用原方法
-
代码实现
//1,定义装饰类(增强类)实现和被装饰类(被增强类)相同的接口
public class UserDaoWrapper implements UserDao {// 2,在装饰类中声明被装饰类的引用 private UserDao userDao ; public UserDaoWrapper(){ } public UserDaoWrapper(UserDao userDao) { this.userDao = userDao; } @Override public void addUser() { System.out.println("权限校验"); userDao.addUser(); System.out.println("日志记录"); } @Override public void deleteUser() { userDao.deleteUser(); } @Override public void updateUser() { userDao.updateUser(); } @Override public void selectUser() { userDao.selectUser(); } }
-
作用
- 在不侵入被装饰类源码的前提下,增强某个功能!
-
特点
- 不能控制被装饰类对象
-
缺点
- 需要重写接口中的所有方法,破坏了单一职责原则
02_Proxy动态代理
- 概念
- 基于接口的方法增强
- 代码实现
- 方式一:定义类实现InvocationHandler接口
-
增强addUser方法
public static void main(String[] args) {UserDao userDao = new UserDaoImpl();//被代理类对象 UserDao userDaoProxy = (UserDao) Proxy.newProxyInstance( userDao.getClass().getClassLoader(), userDao.getClass().getInterfaces(), new MyInvocationHandler(userDao)); userDaoProxy.addUser(); userDaoProxy.deleteUser(); userDaoProxy.updateUser(); userDaoProxy.selectUser();
}
/**
-
增强代理类
*/
class MyInvocationHandler implements InvocationHandler{//声明一个被代理类的引用
private UserDao userDao;public MyInvocationHandler(UserDao userDao) {
this.userDao = userDao;
}@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// Method method : 被代理类的原方法
// Object[] args :被代理类的方法的实际参数
//执行被代理类中的原方法
String methodName = method.getName();
Object returnValue = null;
if (“addUser”.equals(methodName)) {
//只有addUser需要增强
System.out.println(“权限校验”);
returnValue = method.invoke(userDao, args);//执行被代理类中的原方法
System.out.println(“日志记录”);
} else {
//其他的方法不增强
method.invoke(userDao,args);//执行被代理类中的原方法
}return returnValue;
}
}
-
-
- 方式二:使用InvocationHandler接口的匿名内部类对象
-
增强deleteUser方法
public static void main(String[] args) {
UserDao userDao = new UserDaoImpl();
//使用匿名内部类对象
UserDao userDaoProxy = (UserDao) Proxy.newProxyInstance(
userDao.getClass().getClassLoader(),
userDao.getClass().getInterfaces(),
new InvocationHandler() {//处理增强
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
Object returnValue = null;
if (“deleteUser”.equals(methodName)) {
System.out.println(“权限校验”);
returnValue = method.invoke(userDao, args);
System.out.println(“日志记录”);
} else {
returnValue = method.invoke(userDao, args);
}
return returnValue;
}
});
userDaoProxy.deleteUser();userDaoProxy.addUser(); }
-
- 方式一:定义类实现InvocationHandler接口
- 优点
- 不需要重写接口所有的方法!
03_连接池概念
- 在不使用连接池的情况下, 如果有100个用户要操作数据库,对应要创建100个连接对象,操作数据库完毕,还需要销毁100个连接对象,创建连接和销毁连接是非常浪费系统性能!
- 如果使用连接池,连接的创建,只在连接池初始化时才进行,当用户要操作数据库时,只需要从连接池中取出已经创建好的连接对象即可,操作数据库完毕,不需要销毁连接,只需将连接对象归还到连接池
- 总结
- 连接池提高了操作数据库的性能!
04_自定义连接池基础版
- 代码实现
-
初始化时,创建一些连接对象,并存储到LinkedList集合中
-
从连接池中取出连接
-
把连接归还连接池
public class MyDataSource {int initPoolSize = 3;//连接池中初始连接数 LinkedList<Connection> connections = new LinkedList<>(); public MyDataSource(){ //当连接池初始化时,创建一些连接,并存储起来! createConnections(); } public void createConnections(){ for (int i = 1 ; i <= initPoolSize ; i++) { try { Connection connection = JDBCUtils.getConnection(); //存储连接,要么数组,要么集合(单列、双列) //单列集合:ArrayList、LinkedList、HashSet、TreeSet //ArrayList:查询修改快,增删慢 //LinkedList:查询修改慢,增删快 //HashSet:去重 //TreeSet:排序 //到底是使用ArrayList还是LinkedList,取决于连接池的特性,移除连接,添加添加! 应该使用LinekdList connections.add(connection); } catch (Exception e) { e.printStackTrace(); } } } /** * 取出连接 * @return */ public Connection getConnection(){ //连接池中的连接够用 if (connections.size() == 0 || connections.isEmpty()) { //连接池中的连接不够用 , 新建一些连接存储连接池中 createConnections(); } Connection connection = connections.remove(0); return connection; } /** * 归还连接 * @param connection */ public void addBack(Connection connection){ connections.add(connection); }
}
-
- 存在的问题
- 自己定义了归还连接的方法addBack。维护困难!
05_自定义连接池优化版
- 解决问题思路
- 一般开发都能记住Connection类中close方法
- 将该close方法功能改变为归还连接
- 代码实现
-
中间类ConnectionWrapper,专注于非close方法的调用
public class ConnectionWrapper implements Connection {//protected:只能在子父类进行访问 protected Connection connection; public ConnectionWrapper() { } public ConnectionWrapper(Connection connection) { this.connection = connection; } @Override public Statement createStatement() throws SQLException { return connection.createStatement(); } //......
}
-
连接装饰类MyConnectionWrapper
public class MyConnectionWrapper extends ConnectionWrapper {//声明被代理类的引用 private List<Connection> connectionList; public MyConnectionWrapper(Connection connection, List<Connection> connectionList) { super(connection); this.connectionList = connectionList; } @Override public void close() throws SQLException { //增强close:归还连接 //把Connection规划给连接池 connectionList.add(connection); }
}
-
- 存在的问题
- 必须要重写接口中的所有方法
06_自定义连接池的终极版
-
结合动态代理进行使用
-
代码实现
public class MyDataSource {int initPoolSize = 3; LinkedList<Connection> connections = new LinkedList<>(); public MyDataSource(){ createConnections(); } public void createConnections(){ for (int i = 1 ; i <= initPoolSize ; i++) { try { Connection connection = JDBCUtils.getConnection(); connections.add(connection); } catch (Exception e) { e.printStackTrace(); } } } /** * 取出连接 * @return */ public Connection getConnection(){ //连接池中的连接够用 if (connections.size() == 0 || connections.isEmpty()) { //连接池中的连接不够用 , 新建一些连接存储连接池中 createConnections(); } Connection connection = connections.remove(0);//普通连接对象 //根据普通连接对象 获取 一个连接代理对象(增强连接对象) Connection connectionProxy = (Connection) Proxy.newProxyInstance( connection.getClass().getClassLoader(), connection.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //处理增强close方法 String methodName = method.getName(); Object returnValue = null; if ("close".equals(methodName)) { //如果是close方法,归还连接 returnValue = connections.add(connection); } else { //如果不是close方法,就执行原有功能 returnValue = method.invoke(connection, args); } return returnValue; } }); return connectionProxy; } }
-
总结
使用动态代理方式解决装饰者设计模式中的弊端:必须要重写接口中的所有方法!!!
07_注解介绍
- 概念
- 就是一个修饰符
- 特点
- 是JDK5.0之后引入的特性。以“@注解名”形式存在
- 作用
- 跟踪代码依赖性
- 执行编译时格式检查
- 代替已有的配置文件
08_java内置注解
- @Overirde
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}- 标记指定方法是一个重写方法,否则报错
- @Deprecated
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}- 标记一个类、字段、方法是一个过时的!
- @SuppressWarings
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();
}- value : 注解所压制的警告的类型
unchecked 未检查的转化,如集合没有指定类型还添加元素
unused 未使用的变量
resource 有泛型未指定类型
path 在类路径,原文件路径中有不存在的路径
deprecation 使用了某些不赞成使用的类和方法
fallthrough switch语句执行到底没有break关键字
rawtypes 没有写泛型,比如: List list = new ArrayList();
all 全部类型的警告
用的最多是all
- value : 注解所压制的警告的类型
09_注解分类
- 注解分类
- 标记注解
- 注解中没有属性
- 比如:@Override、@Deprecated
- 单值注解
- 注解中只有一个属性
- 比如:@SuppressWarings
- 完整注解
- 注解中有多个属性
- 标记注解
10_自定义注解
-
格式
public @interface 注解名 {
数据类型 属性名1() default 默认值1;
数据类型 属性名2() ;
} -
基本使用
public @interface MyAnnotation01 {String username() default "root"; String password() default "root123"; int age() default 16; String value(); }
-
注意事项
- value属性单独使用时,是可以省略"value="。
11_元注解概述
- 概述
- 作用在自定义注解上,规定自定义注解的作用区域、存活策略!
- 常用的元注解
- @Target
- 规定自定义注解的作用区域
TYPE:类、接口、注解、枚举
FIELD:成员你变量
METHOD:成员方法
PARAMETER:形参
CONSTRUCTOR:构造器
LOCAL_VARIABLE:局部变量
ANNOTATION_TYPE:注解类型
PACKAGE:包
- 规定自定义注解的作用区域
- @Retention
- 规定自定义注解的存活策略
SOURCE : 仅存活在源码
CLASS : 存活在编译期
RUNTIME : 存活在运行时
- 规定自定义注解的存活策略
- @Target
01_元注解使用
- @Target
-
定义
@Target(value = {
ElementType.TYPE ,
ElementType.FIELD ,
ElementType.METHOD ,
ElementType.PARAMETER ,
ElementType.LOCAL_VARIABLE }
)
public @interface MyAnnotation01 {String value() default "hello annotation";
}
-
使用
@MyAnnotation01
public class Demo01 {@MyAnnotation01 private int num = 1; @MyAnnotation01 public static void main(@MyAnnotation01 String[] args) { @MyAnnotation01 int num = 1; }
}
-
- @Retention
-
定义
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation01 {String value() default "hello annotation";
}
-
一般情况***解存活策略是RUNTIME,什么原因?
- 注解要起作用,必须结合反射使用,而反射是在程序运行时(RUNTIME)执行!
-
02_自定义注解@MyTest
- 需求
- 测试类中,方法上如果使用了@MyTest注解,该方法就会执行!
- 开发步骤
- 自定义注解@MyTest
- 在测试类Test01上使用@MyTest
- 让@MyTest注解生效
- 获取到Test01类对应的Class对象
- 获取Test01类中的所有方法
- 判断方法上是否有@MyTest注解
- 如果有,就将该方法执行
- 如果没有,就不处理
- 代码实现
-
@MyTest注解
- @MyTest的存活策略必须是RUNTIME
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest {
String value() default “”;
}
- @MyTest的存活策略必须是RUNTIME
-
Test01测试类
public class Test01 {@Test public void test01(){ System.out.println("test01"); } @MyTest public void test02(){ System.out.println("test02"); } @MyTest public void test03(){ System.out.println("test03"); } public void test04(){ System.out.println("test04"); }
}
-
demo类
public class Demo02 {public static void main(String[] args) { //使用反射,扫描Test01类里面有哪些方法有@MyTest注解 //如果有@MyTest注解,就将起执行 //如果没有@MyTest注解,不做任何处理 //1,获取Test01类对应的Class对象 Class<Test01> clazz = Test01.class; //2,获取Test01类下所有的方法对象 Method[] methods = clazz.getMethods(); //stream流 lambda表达式 Arrays.stream(methods).forEach(method -> { //method就是单个方法对象 //3,判断方法上是否有@MyTest注解 boolean present = method.isAnnotationPresent(MyTest.class); if (present) { //方法上有@MyTest注解,执行方法 try { method.invoke(clazz.newInstance()); } catch (Exception e) { e.printStackTrace(); } } else { //方法上没有@MyTest注解 } }); }
}
-
03_自定义注解@JDBCInfo
- 需求
- 使用注解@JDBCInfo替代jdbc.properties配置文件
- 开发步骤
- 自定义注解@JDBCInfo
- 在JDBCUtils工具类上使用@JDBCInfo
- 在JDBCUtils工具类中静态代码块中,获取@JDBCInfo注解上的属性,并给JDBCUtils工具类中成员变量赋值
- 代码实现
-
自定义注解@JDBCInfo
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface JDBCInfo {String driverClass() default "com.mysql.jdbc.Driver"; String jdbcUrl() default "jdbc:mysql://localhost:3306/day53"; String user() default "root"; String password() default "root123";
}
-
在JDBCUtils工具类上使用@JDBCInfo
-
反射读取注解@JDBCInfo中的内容
@JDBCInfo(password = “root”)
public class JDBCUtils {private static final String DRIVERCLASS ; private static final String JDBCURL; private static final String USER; private static final String PASSWORD; static { Class<JDBCUtils> clazz = JDBCUtils.class; boolean present = clazz.isAnnotationPresent(JDBCInfo.class); if (present) { //JDBCUtils类上有@JDBCInfo注解,获取该注解 JDBCInfo jdbcInfo = clazz.getAnnotation(JDBCInfo.class); //从@JDBCInfo注解获取driverClass、jdbcUrl、user、password属性值 DRIVERCLASS = jdbcInfo.driverClass(); JDBCURL = jdbcInfo.jdbcUrl(); USER = jdbcInfo.user(); PASSWORD = jdbcInfo.password(); } else { //JDBCUtils类上没有@JDBCInfo注解,从properties文件 DRIVERCLASS = ""; JDBCURL = ""; USER = ""; PASSWORD = ""; } }
}
-
-
04_反射、注解、设计模式初级版
- 开发步骤
- 自定义注解@SystemLog
- className
- methodName
- 定义一个UserDao接口,且在该接口使用注解@SystemLog
- 编写装饰者设计模式
- 获取UserDao实现子类的Class对象
- 获取UserDao接口的Class对象
- 获取UserDao接口中的方法对象
- 自定义注解@SystemLog
- 代码实现
-
自定义注解@SystemLog
-
注解存活策略:RUNTIME
@Retention(RetentionPolicy.RUNTIME)
public @interface SystemLog {String className();//记录类名 String methodName();//记录方法名
}
-
-
定义一个UserDao接口,且在该接口使用注解@SystemLog
-
设置@SystemLog中className属性、methodName属性
public interface UserDao {@SystemLog(className = "com.qfedu.dao.UserDao" , methodName = "addUser") void addUser() throws Exception; void deleteUser() throws Exception; @SystemLog(className = "com.qfedu.dao.UserDao" , methodName = "updateUser") void updateUser() throws Exception;
}
-
-
编写装饰者设计模式
public class UserDaoWrapper implements UserDao{private UserDao userDao; public UserDaoWrapper(UserDao userDao) { this.userDao = userDao; } @Override public void addUser() throws Exception { userDao.addUser(); printLog("addUser"); } @Override public void deleteUser() throws Exception { userDao.deleteUser(); printLog("deleteUser"); } @Override public void updateUser() throws Exception { userDao.updateUser(); printLog("updateUser"); } /** * 日志记录 * @param runMethodName */ private void printLog(String runMethodName) throws Exception { //判断接口上对应的方法中是否有@SystemLog注解 //获取UserDao接口实现子类的Class对象 Class<? extends UserDao> sonClazz = userDao.getClass(); //获取UserDao接口的Class对象 Class<?>[] interfaces = sonClazz.getInterfaces(); Class<?> fatherClazz = interfaces[0]; //获取接口中对应的方法对象(addUser方法) Method method = fatherClazz.getMethod(runMethodName); if (null != method) { //判断方法上是否有@SystemLog注解 boolean present = method.isAnnotationPresent(SystemLog.class); if (present) { //方法有@SystemLog注解,打印日志 SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss"); Date currentDate = new Date(); String currentTimeStr = format.format(currentDate); SystemLog systemLog = method.getAnnotation(SystemLog.class); String className = systemLog.className(); String methodName = systemLog.methodName(); System.out.println(currentTimeStr + " --- " + className + "类中 ---" + methodName + "()方法 --- 运行了"); } } } }
-
- 存在的问题
- 由装饰者设计模式的弊端导致每个方法中都需要调用printLog方法
05_反射、注解、设计模式优化版
- 开发步骤
- 自定义注解@SystemLog
- className
- methodName
- 定义一个UserDao接口,且在该接口使用注解@SystemLog
- 动态代理
- 获取UserDao接口中的方法对象
- 自定义注解@SystemLog
- 代码实现
-
自定义注解@SystemLog
@Retention(RetentionPolicy.RUNTIME)
public @interface SystemLog {String className();//记录类名 String methodName();//记录方法名 }
-
定义一个UserDao接口,且在该接口使用注解@SystemLog
-
设置@SystemLog中className属性、methodName属性
public interface UserDao {@SystemLog(className = "com.qfedu.dao.UserDao" , methodName = "addUser") void addUser() throws Exception; void deleteUser() throws Exception; @SystemLog(className = "com.qfedu.dao.UserDao" , methodName = "updateUser") void updateUser() throws Exception;
}
-
-
动态代理
UserDao userDao = new UserDaoImpl();
UserDao userDaoProxy = (UserDao) Proxy.newProxyInstance(
userDao.getClass().getClassLoader(),
userDao.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//获取到接口中方法对象 , 比如 : UserDao接口中addUser方法
//之前
//先获取实现子类的Class对象
//再获取到接口的Class对象
//再获取到接口中的方法对象
//现在
//再获取到接口的Class对象 – userDao.getClass().getInterfaces(),
//再获取到接口中的方法对象 – Method method
//method : 就是接口中的方法对象
Object returnValue = null;
if (null != method) {
boolean present = method.isAnnotationPresent(SystemLog.class);
if (present) {
//如果有@SystemLog注解 , 执行原有功能 , 打印日志
returnValue = method.invoke(userDao, args);
String currentTimeStr = new SimpleDateFormat(“yyyy年MM月dd日 hh:mm:ss”)
.format(new Date());
SystemLog systemLog = method.getAnnotation(SystemLog.class);
String className = systemLog.className();
String methodName = systemLog.methodName();
System.out.println(currentTimeStr + " — " + className + “类中 —” + methodName + “()方法 — 运行了”);
} else {
//如果没有@SystemLog注解, 执行原有功能, 不打印日志
returnValue = method.invoke(userDao, args);
}
}return returnValue; } }); userDaoProxy.addUser();
-
06_request与response对象介绍
- 概念
- 当浏览器发起请求后,服务器会创建一个请求对象、一个响应对象,通过service方法传入给Serlvet
- 作用
- request对象处理请求信息(请求行、请求头、请求正文)
- response对象处理响应信息(响应行、响应头、响应正文)
- ServletRequest和HttpServletRequest的关系、ServletResponse和HttpServletResponse的关系?
- ServletRequest是HttpServletRequest的父接口,
- ServletResponse是HttpServletResponse的父接口,
- HttpServletRequest针对Http协议、HttpServletResponse针对Http协议
07_response对象操作响应行、响应头
- 操作响应行
- setStatus:操作正常响应状态码,比如:200、302
- sendError:操作错误响应状态码,比如: 404
- 操作响应头
- setHeader:直接覆盖响应头原有值
- addHeader:在响应头原有值的后面追加
08_response操作重定向
- 重定向的流程
- 当浏览器访问一个资源Demo03Servlet,访问名称为“/demo03”
- Demo03Servlet进行重定向
- 操作响应状态码302
- 操作响应头Location,服务器告诉浏览器,重定向的资源的访问路径
- 浏览器进行一个新的请求,完成重定向
- 实现代码
- 方式一
response.setStatus(302);
response.setHeader(“Location”,“demo04”); - 方式二
response.sendRedirect(“demo04”);
相当于方式一
- 方式一
09_response操作定时跳转
- 概念
- 一个资源定时一段时间之后,跳转到另外一个资源
- 操作响应头refresh
- 代码实现
resp.setHeader(“refresh”,“5;url=demo07”);
01_response操作响应正文(重点)
- 响应正文
- 就是浏览器显示的主体内容
- 常用方法
- response.getWriter().write():操作响应正文
- 注意事项
- 响应正文中文乱码
- 本质原因,服务器的编码和浏览器的解码,格式不同!
- setCharacterEncoding(“utf-8”):
- 告诉服务器,应该以utf-8格式编码响应正文
- setHeader(“Content-Type”,“text/html;charset=utf-8”):
- 告诉浏览器 , 响应正文是文本+html标签,应该以utf-8格式解码响应正文
- setContentType(“text/html;charset=utf-8”)
- 告诉服务器,应该以utf-8格式编码响应正文
- 告诉浏览器 , 响应正文是文本+html标签,应该以utf-8格式解码响应正文
- 响应正文中文乱码
02_request操作请求行
- 常用方法
- getRequestURI
- 获取请求路径
- getMethod
- 获取请求方式
- getRemoteAddr
- 获取请求ip
- getLocalPort
- 获取请求端口
- getQueryString
- 请求网址"?"后面的路径
- getRequestURI
03_request操作请求头
- getHeader()
- 获取指定请求头的值
04_request操作请求参数(重点)
- 请求正文
- post+请求参数
- 请求参数
- 不管是get请求 还是 post请求 都有!!!
- 常用方法
- getParameter
- 获取指定请求参数值
- getParameterNames
- 获取所有请求参数名称
- getParameterValues(String parameterName)
- 获取指定请求参数所有值
- getParameterMap
- 键,获取所有请求参数名称 , 相当于getParameterNames方法
- 值,获取指定请求参数所有值,相当于getParameterValues方法
- getParameter
- 代码实现
//获取指定参数值
String username = request.getParameter(“username”);
String password = request.getParameter(“password”);
System.out.println(“username : " + username + " , password : " + password);
System.out.println(”---------------");
//获取所有请求参数名称
Enumeration parameterNames = request.getParameterNames();
while (parameterNames.hasMoreElements()) {
String parameterName = parameterNames.nextElement();
String parameterValue = request.getParameter(parameterName);
System.out.println(“name : " + parameterName + " , value : " + parameterValue);
}
System.out.println(”---------------");
//获取指定请求参数所有值
String[] usernames = request.getParameterValues(“username”);
System.out.println(usernames[0]);
String[] hobbys = request.getParameterValues(“hobbys”);
for (String hobby : hobbys) {
System.out.println(hobby);
}
System.out.println("---------------");
//获取请求参数对应的map :Map(String,String[])
//键:请求参数名称 相当于 getParameterNames
//值:一组请求参数值 相当于 getParameterValues
Map<String, String[]> parameterMap = request.getParameterMap();
Set<Map.Entry<String, String[]>> entrySet = parameterMap.entrySet();
for (Map.Entry<String, String[]> entry : entrySet) {
//键 - 请求参数名称
String parameterName = entry.getKey();
//值 - 一组请求参数值
String[] values = entry.getValue();
StringBuffer valueStr = new StringBuffer();
for (String value : values) {
valueStr.append(value + " ");
}
System.out.println("参数名称 : "+ parameterName + " , 参数值 : " + valueStr);
}
05_请求参数中文乱码之post
- 本质
- 请求正文中的中文参数乱码
- 解决方案
- request.setCharacterEncoding(“utf-8”)
- 告诉服务器应该以utf-8解码请求正文
- 逆向,先编码在解码
- 先将乱码字符串以iso8859-1编码成字节
- 将字节以utf-8解码成字符串
username = new String(username.getBytes(“iso8859-1”),“utf-8”);
- request.setCharacterEncoding(“utf-8”)
- 注意事项
- 将tomcat容器的URIEncoding=“utf-8”,对Query String中的请求参数有效,对请求正文中无效,对post请求下的中文乱码无效!
06_请求参数中文乱码之get
- 本质
- Query String中的中文参数乱码
- 解决方案
- 逆向,先编码在解码
- 先将乱码字符串以iso8859-1编码成字节
- 将字节以utf-8解码成字符串
username = new String(username.getBytes(“iso8859-1”),“utf-8”);
- 修改tomcat容器的URIEncoding=“utf-8”
- 逆向,先编码在解码
- 注意事项
- request.setCharacterEncoding(“utf-8”)对get请求无效,告诉服务器应该以utf-8来解码请求正文,跟Query String 没有关系!
07_请求参数中文乱码终极解决方案
- 方案
- tomcat8.5
- 相当于是tomcat7.0修改了URIEncoding=“utf-8”
- 就解决了get请求参数中文乱码问题
- request.setCharacterEncoding(“utf-8”)
- 就解决了post请求参数中文乱码问题
- tomcat8.5
08_request操作请求转发
- 请求转发
- 服务器中,从一个资源跳转到另外一个资源
- 流程
- 浏览器发起请求,请求Demo01Servlet
- Demo01Servlet,请求转发到Demo02Servlet
- 在服务器内部,直接从Demo01Servlet跳转到了Demo02Servlet
- 注意事项
- 转发只有一次请求
09_请求转发和重定向的区别
- 请求次数
- 重定向有2次请求
- 转发有1次请求
- 跳转区别
- 重定向既可以站内资源进行跳转,站外资源也可以进行跳转
- 转发只能站内资源进行跳转
- 路径区别
- 要转发的资源的相对路径无区别
- 要转发的资源的绝对路径有区别
- 重定向,是从先从项目开始找,再找资源
- 转发,是直接从项目中找资源
10_request作为域对象
- 域对象
- 可以存储数据的对象
- 回顾ServletContext域对象的作用范
- 不同设备、当前项目中的任意一个资源都可以访问ServletContext域中的数据!
- request域对象的作用范围
- request对象的生命周期
- 发起请求时,request初始化
- 响应时,request销毁
- request域对象的作用范围在一次请求中!
- request对象的生命周期
- request在重定向和转发中使用!
- 重定向中不能使用request域对象
- 转发中可以使用request域对象
下一篇: 选择企业ERP和数据库应用服务器