欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

周总结

程序员文章站 2024-01-29 14:21:52
...

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域中指定名称的参数移除!
    • 获取全局初始化参数
      • 会随着服务器的启动而初始化
  <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”

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">账号:&nbsp;&nbsp;<input class="input_va" type="text" name="user" /></p>
                        <p class="tx_01">密码:&nbsp;&nbsp;<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的初始化参数

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

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

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();
        }
        
  • 优点
    • 不需要重写接口所有的方法!

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

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 : 存活在运行时

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 “”;
        }
    • 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

      • 注解存活策略: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
      @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
      • 请求网址"?"后面的路径

03_request操作请求头

  • getHeader()
    • 获取指定请求头的值

04_request操作请求参数(重点)

  • 请求正文
    • post+请求参数
  • 请求参数
    • 不管是get请求 还是 post请求 都有!!!
  • 常用方法
    • getParameter
      • 获取指定请求参数值
    • getParameterNames
      • 获取所有请求参数名称
    • getParameterValues(String parameterName)
      • 获取指定请求参数所有值
    • getParameterMap
      • 键,获取所有请求参数名称 , 相当于getParameterNames方法
      • 值,获取指定请求参数所有值,相当于getParameterValues方法
  • 代码实现
    //获取指定参数值
    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”);
  • 注意事项
    • 将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请求参数中文乱码问题

08_request操作请求转发

  • 请求转发
    • 服务器中,从一个资源跳转到另外一个资源
  • 流程
    • 浏览器发起请求,请求Demo01Servlet
    • Demo01Servlet,请求转发到Demo02Servlet
    • 在服务器内部,直接从Demo01Servlet跳转到了Demo02Servlet
  • 注意事项
    • 转发只有一次请求

09_请求转发和重定向的区别

  • 请求次数
    • 重定向有2次请求
    • 转发有1次请求
  • 跳转区别
    • 重定向既可以站内资源进行跳转,站外资源也可以进行跳转
    • 转发只能站内资源进行跳转
  • 路径区别
    • 要转发的资源的相对路径无区别
    • 要转发的资源的绝对路径有区别
      • 重定向,是从先从项目开始找,再找资源
      • 转发,是直接从项目中找资源

10_request作为域对象

  • 域对象
    • 可以存储数据的对象
  • 回顾ServletContext域对象的作用范
    • 不同设备、当前项目中的任意一个资源都可以访问ServletContext域中的数据!
  • request域对象的作用范围
    • request对象的生命周期
      • 发起请求时,request初始化
      • 响应时,request销毁
    • request域对象的作用范围在一次请求中!
  • request在重定向和转发中使用!
    • 重定向中不能使用request域对象
    • 转发中可以使用request域对象