基础加强
基础加强
今日任务
- 使用自定义注解完成仿@Test的作用.
- 使用Servlet3.0完成文件上传.
- 使用动态代理解决网站的字符集编码
教学导航
教学目标 |
了解JDK中提供的三个注解 会自定义注解及注解的反射. 能够简单使用Servlet3.0 掌握动态代理技术 了解类加载器 |
教学方法 |
案例驱动法 |
1.1 案例一:使用自定义注解完成@Test注解功能类似的效果:
1.1.1 需求
使用Junit是单元测试的工具.在一个类中使用 @Test 对程序中的方法进行测试.
自定义一个注解@MyTest 也将这个注解加在类的方法上. 使这个方法得到执行.
1.1.2 分析:
1.1.2.1 技术分析:
- 【注解】
程序中有 注释 和注解
* 注释:给开发人员.
* 注解:给计算机看的.
注解使用:学习框架支持注解开发.
- 【JDK提供的注解】
@Override :描述方法的重写.
@SuppressWarnings :压制警告.
@Deprecated :标记过时.
- 自定义注解:
定义一个类:class
定义一个借口:interface
定义一个枚举:enum
定义一个注解:@interface
【自定义注解案例】
@interface MyAnno1{
}
带有属性的注解:
@interface MyAnno2{
int a() default 1;
String b();
// 注解属性的类型:基本数据类型,字符串类型String,Class,注解类型,枚举类型,以及以上类型的一维数组.
// Date d();
Class clazz();
MyAnno3 m3(); // 注解
Color c(); // 枚举
String[] arrs();
}
@MyAnno4("aaa") // 如果属性名称为value 那么使用的时候 value可以省略(只出现这一个value的属性情况下).
public class AnnotationDemo3 {
}
@interface MyAnno4{
String value();
int a() default 1;
}
1.1.2.2 步骤分析:
定义一个测试类
public class AnnotationDemo3 {
@MyTest
public void demo1(){
System.out.println("demo1执行了...");
}
@MyTest
public void demo2(){
System.out.println("demo2执行了...");
}
public void demo3(){
System.out.println("demo3执行了...");
}
}
定义核心运行类:
在核心运行类中有一个主函数:
获得测试类中的所有的方法.
获得每个方法,查看方法上是否有@MyTest注解.
如果有这个注解,让这个方法执行.
1.1.3 代码实现:
通过元注解定义注解存在的阶段.
* 元注解也是一个注解:修饰注解的 注解.
自定义一个注解:
核心运行类:
public class CoreRunner {
public static void main(String[] args) {
// 反射:获得类的字节码对象.Class
Class clazz = AnnotationDemo3.class;
// 获得测试类中的所有的方法:
Method[] methods = clazz.getMethods();
// 遍历数组:
for (Method method : methods) {
// System.out.println(method.getName());
// 判断方法上是否有@MyTest注解:
boolean flag = method.isAnnotationPresent(MyTest.class);
// System.out.println(method.getName()+" "+flag);
if(flag){
// 让这个方法执行:
try {
method.invoke(clazz.newInstance(), null);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
1.1.4 总结:
使用注解完成JDBC工具类的编写:(了解)
public class JDBCUtils {
private static String driverClass;
private static String url;
private static String username;
private static String password;
@JDBCInfo
public static Connection getConnection() throws Exception{
// 反射:
Class clazz = JDBCUtils.class;
Method method = clazz.getMethod("getConnection", null);
// 获得方法上的注解:
JDBCInfo jdbcInfo = method.getAnnotation(JDBCInfo.class);
driverClass = jdbcInfo.driverClass();
url = jdbcInfo.url();
username = jdbcInfo.username();
password = jdbcInfo.password();
Class.forName(driverClass);
Connection conn = DriverManager.getConnection(url, username, password);
return conn;
}
}
1.2 案例二:使用Servlet3.0技术完成文件的上传:
1.2.1 需求:
1.2.2 分析:
1.2.2.1 技术分析:
【Servlet3.0】
Servlet3.0 与 Servlet2.5:
* Servlet3.0需要运行在tomcat7以上的服务器中.
* Servlet3.0以后web.xml就不是必须的.
1.Servlet3.0支持注解开发.
2.支持文件上传.
【Servlet3.0支持注解开发】
使用@WebServlet替换web.xml中配置的Servlet:
@WebServlet(urlPatterns="/ServletDemo1",loadOnStartup=2,initParams=@WebInitParam(name="username",value="root"))
使用@WebListener替换web.xml中监听器的配置:
@WebListener
使用@WebFilter替换web.xml中的过滤器的配置:
@WebFilter(urlPatterns="/*")
【Servlet3.0的文件上传】
- 文件上传:
文件上传:指的是将本地的文件 写到 服务器上.
- 文件上传的要素:
1.表单的提交的方式必须是POST.
2.表单中必须有一个文件上传项:<input type=”file”>,而且文件上传项必须有name属性和值.
* <input type=”file” name=”upload”/>
3.表单的enctype属性的值必须是multipart/form-data
- 文件上传的抓包分析:
未修改enctype属性的时候:
POST /WEB17_WEB/demo1/demo1.jsp HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
X-HttpWatch-RID: 22325-10011
Referer: http://localhost:8080/WEB17_WEB/demo1/demo1.jsp
Accept-Language: zh-Hans-CN,zh-Hans;q=0.5
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
Host: localhost:8080
Content-Length: 47
Connection: Keep-Alive
Cache-Control: no-cache
Cookie: JSESSIONID=99CD51DA9A47D29200168968AD983E9E
upload=C%3A%5CUsers%5Capple%5CDesktop%5Caaa.txt
已经修改了enctype属性:
POST /WEB17_WEB/demo1/demo1.jsp HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
X-HttpWatch-RID: 22325-10026
Referer: http://localhost:8080/WEB17_WEB/demo1/demo1.jsp
Accept-Language: zh-Hans-CN,zh-Hans;q=0.5
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko
Content-Type: multipart/form-data; boundary=---------------------------7e02e526160b66
Accept-Encoding: gzip, deflate
Host: localhost:8080
Content-Length: 224
Connection: Keep-Alive
Cache-Control: no-cache
Cookie: JSESSIONID=99CD51DA9A47D29200168968AD983E9E
-----------------------------7e02e526160b66
Content-Disposition: form-data; name="upload"; filename="C:\Users\apple\Desktop\aaa.txt"
Content-Type: text/plain
Hello shouyi
-----------------------------7e02e526160b66—
【文件上传的原理】
根据分割线将请求体的部分分成几块:
* 判断 每块是 普通项还是文件上传项.
* 普通项:获得名称和值.
* 文件上传项:获得文件名 和 文件内容输入流.
【文件上传的技术】
JspSmartUpload: jspSmartUpload组件是应用JSP进行B/S程序开发过程中经常使用的上传下载组件,它使用简单,方便。现在我又为其加上了下载中文名字的文件的支持,真个是如虎添翼,必将赢得更多开发者的青睐。-Model1年代的文件上传的工具.
FileUpload 是 Apache commons下面的一个子项目,用来实现Java环境下面的文件上传功能,与常见的SmartUpload齐名.应用在Model2年代了.
Servlet3.0 :
Struts2 :
1.2.2.2 步骤分析:
设计一个文件上传页面:
提交到Servlet:
l 接收普通项:request.getParameter();
l 接收上传项:Part:
通过Part对象中的方法完成文件的上传.
1.2.3 代码实现
package com.itheima.servlet;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
/**
* 文件上传的Servlet
*/
@WebServlet("/UploadServlet")
@MultipartConfig
public class UploadServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 接收普通项:
request.setCharacterEncoding("UTF-8");
String desc = request.getParameter("desc");
System.out.println("文件描述:"+desc);
Part part = request.getPart("upload");
// 获得上传的文件的大小
long size = part.getSize();
System.out.println("文件大小"+size);
String type = part.getContentType();
System.out.println("文件类型"+type); // text/plain image/jpeg
String name = part.getName();
System.out.println(name);
// 获得文件名:
String header = part.getHeader("Content-Disposition");
System.out.println(header);
int idx = header.lastIndexOf("filename=\"");
String fileName = header.substring(idx+10, header.length()-1);
System.out.println(fileName);
// 获得文件内容:
InputStream is = part.getInputStream();
// 获得文件上传路径:
String path = this.getServletContext().getRealPath("/upload");
OutputStream os = new FileOutputStream(path+"/"+fileName);
int len = 0;
byte[] b = new byte[1024];
while((len = is.read(b))!=-1){
os.write(b, 0, len);
}
is.close();
os.close();
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
1.2.4 总结:
1.2.4.1 文件名重名的问题:
UUID随机产生一个文件名.
1.2.4.2 文件上传的目录分离:
按用户分 :一个用户创建一个或多个路径.
按时间分 :按月,星期,天进行划分.
按个数分 :一个路径中存3000个文件.
按分离算法分 :按照一定的算法进行划分.
1.3 案例三:使用动态代理的方式统一网站的字符集编码
1.3.1 需求:
在一个表单中分别使用get和post提交到Servlet中,在Servlet中直接调用getParameter方法解决中文乱码的问题!!!
1.3.2 分析:
1.3.2.1 技术分析:
【动态代理】
增强一个类中的某个方法.对程序进行扩展.Spring框架中AOP.
什么是代理:
【入门案例】
class MyInvocationHandler implements InvocationHandler{
private Waiter waiter;
public MyInvocationHandler(Waiter waiter) {
this.waiter = waiter;
}
@Override
// 执行目标对象的任何一个方法 都相当于执行了invoke方法.
/**
* proxy:产生代理对象.
* method:代表正在调用的方法.
* Object[]:在调用的方法的参数.
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// System.out.println("aaaaa");
// System.out.println(method.getName());
if("server".equals(method.getName())){
// 增强server.
System.out.println("微笑...");
return method.invoke(waiter, args);
}else{
// 不增强:
return method.invoke(waiter, args);
}
// return null;
}
}
1.3.3 代码实现:
package com.itheima.encoding;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
@WebFilter(urlPatterns="/*")
public class GenericCharacterEncodingFilter implements Filter{
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
final HttpServletRequest req = (HttpServletRequest) request;
// 对req产生代理对象:
HttpServletRequest myReq = (HttpServletRequest) Proxy.newProxyInstance(req.getClass().getClassLoader(),req.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 增强getParameter:
if("getParameter".equals(method.getName())){
// 增强.
// 根据请求方式:
String type = req.getMethod();
if("get".equalsIgnoreCase(type)){
// 调用原有的getParameter:
String value = (String)method.invoke(req, args);
String s = new String(value.getBytes("ISO-8859-1"),"UTF-8");
return s;
}else if("post".equalsIgnoreCase(type)){
req.setCharacterEncoding("UTF-8");
return method.invoke(req, args);
}
}
// 不增强:
return method.invoke(req, args);
}
});
chain.doFilter(myReq, response);
}
@Override
public void destroy() {
}
}
1.3.4 总结:
1.3.4.1 【类的加载器:了解】
类加载器就是将class文件加载到内存.
JDK中提供的类加载器:
* 引导/系统类加载器 :Java\jre7\lib\rt.jar
* 扩展类加载器 :Java\jre7\lib\ext\*.jar
* 应用类加载器 :自定义的类,类路径下的所有class文件.
类的加载器的机制 :全盘委托机制.
引导类加载器
|
扩展类加载器
|
应用类加载器
class A{
String s;
}
class文件由应用类加载器获得到,没有加载,向上一层委托向扩展类加载器委托,向上一层进行委托委托给引导类加载器.引导类加载器查看class哪些它负责,将自己负责的这个class进行加载.不是其负责的就向下传递扩展类加载器.扩展类加载器查看是否是其管理的class,如果是加载,不是就再向下到应用类加载器.