1. 程式人生 > >java-Web對於反射的支援+ClassLoader(三)-非重點

java-Web對於反射的支援+ClassLoader(三)-非重點

Web對於反射的支援

非重點:
在我們現在的開發之中,假設迴歸到模式一的時代,那麼這個過程裡面重點就是JSP+JavaBean的時代,那麼在這種模式下,使用者需要接受請求的引數,然後將其變成簡單java類,而這一過程JSP本身就已經做了優化。l在JSP裡面提供有三類標籤指令:
1.定義Bean操作(自動利用反射例項化物件):將自動呼叫無參構造例項化物件

<jsp:useBean id="物件名稱" scope="page|request|session|application" class="包.類"></jsp:useBean>

2.設定屬性:(反射呼叫setter)
自動根據引數名稱匹配屬性:

<jsp:setProperty  name=""使用的Id名稱 property="*" />

設定指定的屬性內容:

<jsp:setProperty  name=""使用的Id名稱 property="屬性"  />

將指定的引數設定給指定屬性:

<jsp:setProperty  name=""使用的Id名稱 property="屬性"  param="引數名稱"/>

將指定的內容設定給指定屬性:

<jsp:setProperty  name=""使用的Id名稱 property="屬性"  value="內容"/>

3.取得屬性:(反射呼叫getter)
取得指定屬性內容並輸出:

<jsp:getProperty  name=""使用的Id名稱 property="屬性名稱" />
  • Bean的例項化與屬性設定
    為了方便操作需要定義一個簡單java類
    範例:
    定義一個Emp.java類
@SuppressWarnings("serial")
public class Emp implements Serializable{

    private Integer empno;
    private String empname;
    private double
sal; private Date hiredate; public Emp(){ } }

生成構造方法以觀察例項化輸出,同時準備出setter、getter方法

定義一個input_do.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<jsp:useBean id="emp" scope="page" class="com.csii.wanghaoxin.demo.Emp"></jsp:useBean>
<!-- 表示例項化物件 執行無參構造方法 -->
<html>
<head>
<title>javaBean操作</title>
</head>
<body>

</body>
</html>

可以看到伺服器控制檯會執行構造方法
反射的特點非常明顯:它必須明確的寫上包.類名。例項化操作的目的主要是為了接受請求引數

定義input.jsp頁面:

<form action="input_do.jsp" method="post">
編號:<input type="text" name= "empno" id="empno"value="7369"><br>
姓名:<input type="text" name= "empname" id="empname" value="wanghaoxin"><br>
工資:<input type="text" name= "sal" id="sal" value="800.0"><br>
日期:<input type="text" name= "hiredate" id="hiredate" value="1911-11-11"><br>
<input type="submit" value="提交">
<input type="submit" value="重置">

引數的名字和類中的屬性名字是一樣的,但是這不重要,因為可以根據引數名稱找到setter方法的名稱

對於接受頁面。使用自動處理
<jsp:useBean id="emp" scope="page" class="com.csii.wanghaoxin.demo.Emp"></jsp:useBean>
<!-- 表示例項化物件 執行無參構造方法 -->
<jsp:setProperty property="*" name="emp"/>

表示將引數的內容匹配到所有屬性
訪問input.jsp提交,則顯示
Emp [empno=7369, empname=wanghaoxin, sal=800.0, hiredate=null]

控制檯顯示
setEmpno 呼叫,引數為7369
setEmpname 呼叫,引數為wanghaoxin

這個時候的確可以自動賦值,但是其最終的問題也很明顯
1.不支援Date資料的輸入轉換操作
2.缺少中間的驗證環節
可以只給固定引數傳值 比如empname

<jsp:setProperty property="empname" name="emp"/>

把hiredate的值傳給empname

<jsp:setProperty property="empname" name="emp" param = "hiredate" />

設定固定值

<jsp:setProperty property="empname" name="emp" value = "wanghaoxin" />

這種方式已經不適合於現在的開發結構

  • 取得屬性內容
    如果要取得屬性內容使用的是getProperty();使用的是反射呼叫getter方法
    input_do.jsp內容:
<body>
${emp.empno}<br>
<jsp:getProperty property="empname" name="emp"/>

</body>

如果把物件儲存在了屬性範圍之中,直接利用表示式語言就可以方便的進行操作了,這樣就沒必要使用了

  • 總結
    簡單java類從最初就已經存在於專案開發裡面。

  • *ClassLoader
    如果要說反射機制,肯定就是Class類,但是Class類有一個最大的侷限,就是它只能夠載入classPath中的類檔案。
    如果現在你想編寫更加靈活的內容,例如:有些核心的類是在伺服器上,不在本地專案的classPath裡邊

這裡寫圖片描述

實際上現在我們所使用的程式裡面也存在有ClassLoader,在咱們的Class類裡面,提供有ClassLoader的取得

取得ClassLoader類:public ClassLoader getClassLoader()

在ClassLoader裡面也可以得到父的類載入器:
public final ClassLoader getParent()

範例:觀察類載入器

package com.csii.wanghaoxin.demo;

class Student{

}
public class TestClassLoader {
    public static void main(String[] args) throws Exception {
        Class<?> cls = Class.forName("com.csii.wanghaoxin.demo.Student");
        System.out.println(cls.getClassLoader());//取得ClassLoader
        System.out.println(cls.getClassLoader().getParent());//取得Class 父類載入器
        System.out.println(cls.getClassLoader().getParent().getParent());//null

/*      輸出結果:
        [email protected]
        [email protected]
        null*/
    }
}

通過此時的輸出可以發現,在任何類中都存在類載入器。
在java裡面類載入器一共有三種載入器:
Boot、系統類載入器(一般指的就是ClassPath)、擴充套件類載入器。
實際上擴充套件類載入器儲存的目錄(jdk安裝目下):D:\jdk-7-7u80\jre-1.7\lib\ext,如果現在將*.jar檔案儲存在此目錄中那麼也表示一個ClassPath,這個目錄下也可以載入箇中類檔案。

實際上ClassLoader給了使用者一個由任意位置的載入類權利。

在ClassLoader類裡面存在有一個方法,
public Class<?> loadClass(String name)
throws ClassNotFoundException

這個方法表示ClassPath載入類

public class MyClassLoader extends ClassLoader {
    public Class<?> getClass(String className) throws Exception{
        return super.loadClass(className);
    }
}

測試類:

public class TestMyClassLoader {
    public static void main(String[] args) throws Exception {
        Class<?> cls = new MyClassLoader().getClass("java.util.Date");
        System.out.println(cls.newInstance());
    }

}

此時的載入方式使用的是系統的CLASSPATH完成的,但是現在的開發絕對不是我們所需要的。因為系統類的載入直接使用Class即可。
因為在我們ClassLoader裡面存在有一個方法:
載入類檔案:
protected final Class

public class TestMyClassLoader {
    public static void main(String[] args) throws Exception {
        Class<?> cls = new MyClassLoader().getClass("com.csii.wanghaoxin.demo.Student1");
        System.out.println(cls.newInstance());
    }

}

載入外部class類

package com.csii.wanghaoxin.demo;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;

public class MyClassLoader extends ClassLoader {
    public Class<?> getClass(String className) throws Exception{
        /*return super.loadClass(className);*/
        byte data[] = this.loadData();
        return super.defineClass(className, data, 0, data.length);

    }
    public byte[] loadData() throws Exception{//描述的是載入指定的類檔案 用二進位制流的形式
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        String file = "D:"+File.separator+"Student1.class";
        File fe = new File(file);
        InputStream input = new FileInputStream(fe);
        int len = 0;
        byte[] data = new byte[1024];
        while((len= input.read(data))!= -1){
            bos.write(data,0,len);
        }
        byte[] returnData = bos.toByteArray();
        input.close();
        bos.close();
        return returnData;
    }
}

用本地檔案載入意義不大,最有意思的是可以通過網路載入。
比如將Student1.class放置在Tomcat D:\Tomcat\webapps\ROOT目錄下
新寫URL呼叫並進行呼叫

    public byte[] loadURLData() throws Exception{//描述的是載入指定的類檔案 用二進位制流的形式 --網路
        URL url = new URL("http://localhost:8080/Student1.class");
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        InputStream input = url.openStream();
        int len = 0;
        byte[] data = new byte[1024];
        while((len= input.read(data))!= -1){
            bos.write(data,0,len);
        }
        byte[] returnData = bos.toByteArray();
        input.close();
        bos.close();
        return returnData;

    }

也就是說此時你可以任意編寫類,通過網路載入執行
網路不正常可能造成程式崩潰。

面試題:如果現在定義了一個java.lang.String會如何?會載入嗎?
如果自己定義了一個java.lang.String類,那麼這個類一定不會載入,因為在java中使用了雙親載入機制,如果是系統類一定都由系統類載入器完成,它只能夠載入java本身定義的類,還有一種自定義的類靠擴充套件類載入器進行載入。
為了安全性,所以不會載入。

  • ClassLoader總結
    ClassLoader給開發者最大的感受就是它的靈活性,不再受限於ClassPath了。這一操作對於實際開發意義並不大。