目录
一、案例1-模拟junit测试
1-1 注解
1-1 java中3个注解(理解)
1-2 自定义注解(理解)
1-2-1 注解属性
1-2-2 注解属性类型
1-2-3 赋值的格式
1-2-4 元注解:(理解)
1-3 步骤分析:(了解)
1-3-1 定义一个注解 @MyTest
1-3-2 在一个测试类 MyTestTest 上的添加几个方法
1-3-3 在另一个有主方法的类上添加main方法
1-4 案例扩展- 通过注解获取连接的工具类
1-5 步骤分析
1-5-1 自定义一个注解JDBCInfo
1-5-2 在jdbcutils工具类中提供一个getConnection,在方法上面添加一个注解 @JDBCInfo(...)
1-5-3 运行的时候可以通过getConnection获取一个连接
二、案例2-完成文件上传
2-1 servlet3.0
2-2 文件上传
2-2-1 浏览器端的要求
2-2-2 服务器端的要求
2-2-3 案例简单实现
2-3 上传注意的问题
2-3-1 名字重复 随机名称
2-3-2 文件安全
2-3-3 文件存放目录
2-3-4 文件上传工具类
2-3-5 文件上传完善
2-4 类加载器:(了解)
2-4-1 类加载
2-4-2 类加载器层次结构
2-4-5 全盘负责委托机制
三、案例3-统一编码
3-1 静态代理书写步骤
3-2 动态代理
3-2-1 方式1-Proxy类
3-2-2 方式2-cglib
3-2-3 动态的在内存中创建一个代理对象
3-3 步骤分析
总结
注解:了解注解,可以使用注解
servlet3.0:体验下注解,完成文件上传
类加载器(了解)
动态代理(★):加强方法
一、案例1-模拟junit测试
需求:
在一个类的方法中添加一个@MyTest,在另一个类的main方法中,就可以执行带有@MyTest的方法
技术分析:
注解
反射
1-1 注解
jdk5之后提供了一个特性,和类 接口同级
格式:
@interface 注解名{}
作用:
编译检查
替代配置文件
定义注解(元注解:注解上的注解)
分析代码(用到反射)
1-1 java中3个注解(理解)
- @Override:声明该方法是从父类上继承过来的,执行编译期的检查
- @SuppressWarnings:抑制警告 值有好多,只需要知道一个 all 抑制所有的警告
@SuppressWarnings("all")
- @Deprecated:声明 该方法不赞成使用(已过时,有bug)
1-2 自定义注解(理解)
1-2-1 注解属性
注解本质就是一个接口,接口中可以有常量和抽象方法
抽象方法在注解中就称之为注解属性
1-2-2 注解属性类型
- 基本类型
- String
- Class
- Annotation
- Enum:枚举(严格的类,不能new, 只能使用已经定义好的属性 private Color RED; )
Color RED();
- 以上类型对应的一维数组
public @interface MyAnnotation2 {//注解属性int i();String s();//Date d();String[] ss();Class cl();MyAnnotation1 m1();Color RED();}
注意:
一旦注解有属性了,使用注解的时候必须赋值,(除非这个注解属性有默认值)
1-2-3 赋值的格式
@注解名(属性名=属性值)
若注解类型为数组,且只有一个值的时候,可以有两种写法
方式1:
属性名 = { 值 } ss = {"qew", "123"}
方式2:
属性名=属性值 ss = "123"
若属性名为value的时候,且只需要为这个value属性赋值的时候,value可以省略//@MyAnnotaion3(i = 0) @MyAnnotaion3("") public class Demo3 {}public @interface MyAnnotaion3 {//int i();//String s();/*** int i();String s();String[] ss();*/String value(); }
1-2-4 元注解:(理解)
定义在注解上的注解
@Retention 规定注解保留到什么阶段 值为RetentionPolicy的三个枚举值
SOURCE:只在代码中保留,在字节码文件中就删除了(默认)
CLASS:在代码和字节码文件中保留
RUNTIME:所有阶段都保留 (配置文件)
@Target 规定注解作用在什么上面 值为ElementType的枚举值
TYPE:作用在类 接口 等上面
METHOD:作用方法上面
FIELD:作用字段上面
1-3 步骤分析:(了解)
1-3-1 定义一个注解 @MyTest
//注解在运行时保留 @Retention(RetentionPolicy.RUNTIME)//注解只能作用在方法上 @Target(ElementType.METHOD)public @interface MyTest {}
1-3-2 在一个测试类 MyTestTest 上的添加几个方法
在方法上添加@MyTest
public class MyTestTest {@MyTestpublic void f1(){System.out.println("f1方法执行了~~~~");}public void f2(){System.out.println("f2方法执行了~~~~");}@MyTestpublic void f3(){System.out.println("f3方法执行了~~~~");} }
1-3-3 在另一个有主方法的类上添加main方法
运行main方法的时候,需要将带有 @MyTest注解的方法执行
//判断方法是否有指定的注解
boolean flag=m.isAnnotationPresent(MyTest.class);
package com.itheima.a_annotation;import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method;public class MainTest {/*public static void main(String[] args) {//运行这个类的时候 需要将 测试类中带有@mytest所有方法执行//获取字节码对象Class clazz=MyTestTest.class;//获取所有的方法Method[] arr = clazz.getMethods();//让方法执行for (Method m : arr) {//打印所有方法的名称System.out.println(m.getName());}}*/public static void main(String[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException {//运行这个类的时候 需要将 测试类中带有@mytest所有方法执行//1 获取字节码对象Class clazz=MyTestTest.class;//2 获取所有的方法Method[] arr = clazz.getMethods();//3 让带有注解的方法执行for (Method m : arr) {//获取有注解的方法//判断方法是否有指定的注解 boolean flag=m.isAnnotationPresent(MyTest.class);if(flag){//System.out.println(m.getName());m.invoke(clazz.newInstance());}}} }
1-4 案例扩展- 通过注解获取连接的工具类
获取连接的工具类,通过注解,配置四个参数
1-5 步骤分析
1-5-1 自定义一个注解JDBCInfo
添加元注解:
在程序运行的时候使用 @Retention
只能作用在方法上 @Target
添加注解属性
String driverClass() default "com.mysql.jdbc.Driver"; //添加默认值
String url();
String username() default "root";
String password();@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface JdbcInfo {String driverClass() default "com.mysql.jdbc.Driver";String url();String user() default "root";String password() default "root";}
1-5-2 在jdbcutils工具类中提供一个getConnection,在方法上面添加一个注解 @JDBCInfo(...)
getConnection方法需要进行的操作:获取注解上的四个属性值
- 获取字节码文件
- 获取该方法上的注解
//jdbcinfo注解 获取 JdbcInfo info = m.getAnnotation(JdbcInfo.class);
- 获取注解的值
//4.获取注解四个属性 String driverClass = info.driverClass(); String url = info.url(); String user = info.user(); String password = info.password();
public class JdbcUtils {@JdbcInfo(url = "jdbc:mysql://localhost:3306/day16",password="1234")public static Connection getConnection() throws Exception{//1.获取字节码文件Class clazz=JdbcUtils.class;//2.获取getConnenctionMethod m = clazz.getMethod("getConnection");//3.判断该方法上是否有 jdbcinfo注解 若有的话获取if(m.isAnnotationPresent(JdbcInfo.class)){//jdbcinfo注解 获取JdbcInfo info = m.getAnnotation(JdbcInfo.class);//4.获取注解四个属性String driverClass = info.driverClass();String url = info.url();String user = info.user();String password = info.password();//5.注册驱动Class.forName(driverClass);//6.获取连接return DriverManager.getConnection(url, user, password);}return null;}public static void main(String[] args) throws Exception {System.out.println(getConnection());//打印地址} }
1-5-3 运行的时候可以通过getConnection获取一个连接
public static void main(String[] args) throws Exception {System.out.println(getConnection());//打印地址}
二、案例2-完成文件上传
技术分析:
文件上传
- servlet3.0
- commons-fileupload
- 框架
2-1 servlet3.0
3.0支持注解开发,没有web.xml这个文件了
内嵌了文件上传功能(2.5 无)
例如:
- 创建servlet
在类上面添加 @WebServlet(urlPatterns={ "/demo2", "/demo21" },loadOnStartup=2)
urlPatterns 可以省略 @WebServlet({ "/demo2", "/demo21" })(一个servlet可以有多个路径)
@WebServlet(urlPatterns={ "/demo2", "/demo21" },loadOnStartup=2) public class Demo2Servlet extends HttpServlet {@Overridepublic void init() throws ServletException {System.out.println("demo2 servlet初始化了");}private static final long serialVersionUID = 1L;protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {System.out.println("demo2 servlet 执行了~~");}}
- 创建listener
在类上添加 @WebListener
@WebListener public class MyServletContextLis implements ServletContextListener {@Overridepublic void contextInitialized(ServletContextEvent sce) {System.out.println("servletcontext 创建了.~");}@Overridepublic void contextDestroyed(ServletContextEvent sce) {// TODO Auto-generated method stub}}
- 创建filter
在类上添加 @WebFilter(urlPatterns="/*")
@WebFilter(urlPatterns="/demo2") public class MyFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {System.out.println("filter 初始化了");}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {System.out.println("filter 接受到了请求");chain.doFilter(request, response);}@Overridepublic void destroy() {// TODO Auto-generated method stub}}
2-2 文件上传
2-2-1 浏览器端的要求
- 表单的提交方法必须是post
- 必须有一个文件上传组件 <input type="file" name=""/>
- 必须设置表单的enctype=multipart/form-data
2-2-2 服务器端的要求
servlet3.0中
1 需要在servlet中添加注解
@MultipartConfig
2 接受普通上传组件 (除了文件上传组件):request.getParameter(name属性的值)
3 接受文件上传组件 request.getPart(name属性的值);获取的name的属性值
part.getName()
获取文件名:
part.getHeader("Content-Disposition"):获取头信息 然后截取
2-2-3 案例简单实现
<body><form action="/day1701/upload1" method="post" enctype="multipart/form-data">用户名:<input name="username"><br/> 图片:<input type="file" name="f"><br/><input type="submit"></form>
</body>
@WebServlet("/upload1")
@MultipartConfig
public class Upload1Servlet extends HttpServlet {private static final long serialVersionUID = 1L;protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//获取usernameString username = request.getParameter("username");System.out.println(username);//获取f 获取的内容为null/*String f = request.getParameter("f");System.out.println(f);*/Part part = request.getPart("f");//System.out.println(part);String name = part.getName();//获取name的值System.out.println(name);String dis = part.getHeader("Content-Disposition");//String dis = request.getHeader("Content-Disposition");System.out.println(dis);String s = dis.substring(dis.indexOf("filename=")+10,dis.length()-1);System.out.println(s);InputStream is = part.getInputStream();FileOutputStream os = new FileOutputStream("g:/"+s);IOUtils.copy(is, os);os.close();is.close();}protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {doGet(request, response);}}
2-3 上传注意的问题
2-3-1 名字重复 随机名称
在数据库中提供两个字段,
一个字段用来存放文件的真实名称 1.jpg
另一个字段用来存放文件存放路径 g:/sdfasdf.jpg(随机名称)
随机名称:
uuid
时间戳
2-3-2 文件安全
重要的文件存放在 web-inf 或者 meta-inf 或者 服务器创建一个路径
不是很重 要的文件 项目下
2-3-3 文件存放目录
方式1:日期
方式2:用户
方式3:文件个数
方式4:随机目录
mkdirs重新部署项目时,文件会消失----备份
2-3-4 文件上传工具类
import java.util.UUID;public class UploadUtils {/*** 获取随机名称* @param realName 真实名称* @return uuid*/public static String getUUIDName(String realName){//realname 可能是 1.jpg 也可能是 1//获取后缀名int index = realName.lastIndexOf(".");if(index==-1){return UUID.randomUUID().toString().replace("-", "").toUpperCase();}else{return UUID.randomUUID().toString().replace("-", "").toUpperCase()+realName.substring(index);}//return null;}/*** 获取文件真实名称* @param name* @return*/public static String getRealName(String name){// c:/upload/1.jpg 1.jpg//获取最后一个"/"int index = name.lastIndexOf("\\");return name.substring(index+1);}/*** 获取文件目录* @param name 文件名称* @return 目录*/public static String getDir(String name){int i = name.hashCode();String hex = Integer.toHexString(i);int j=hex.length();for(int k=0;k<8-j;k++){hex="0"+hex;}// System.out.println(hex);return "/"+hex.charAt(0)+"/"+hex.charAt(1);}public static void main(String[] args) {//String s="G:\\day17-基础加强\\resource\\1.jpg";String s="1.jgp";String realName = getRealName(s);//System.out.println(realName);String uuidName = getUUIDName(realName);//System.out.println(uuidName);String dir = getDir(realName);System.out.println(dir);}
}
2-3-5 文件上传完善
<body><form action="/day1701/upload2" method="post" enctype="multipart/form-data">用户名:<input name="username"><br/> 文件:<input type="file" name="f"><br/><input type="submit"></form>
</body>
@WebServlet("/upload2")
@MultipartConfig
public class Upload2Servlet extends HttpServlet {private static final long serialVersionUID = 1L;protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//0.设置编码request.setCharacterEncoding("utf-8");//1.获取普通的上传组件 usernameString username = request.getParameter("username");System.out.println(username);//2.获取文件上传组件Part part = request.getPart("f");//2.1获取文件的名称String sss = part.getHeader("content-disposition");String realName=sss.substring(sss.indexOf("filename=")+10, sss.length()-1);System.out.println("文件的名称:"+realName);//2.2 获取随机名称String uuidName = UploadUtils.getUUIDName(realName);System.out.println("文件随机名称:"+uuidName);//2.3 获取文件存放的目录String dir = UploadUtils.getDir(uuidName);String realPath = this.getServletContext().getRealPath("/upload"+dir);File file = new File(realPath);if(!file.exists()){file.mkdirs();}System.out.println("文件的目录:"+realPath);//3.对拷流InputStream is = part.getInputStream();FileOutputStream os = new FileOutputStream(new File(file,uuidName));IOUtils.copy(is, os);os.close();is.close();//4.删除临时文件part.delete();}protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {doGet(request, response);}}
2-4 类加载器:(了解)
2-4-1 类加载
我们编写的.java文件,jvm会将变成.class文件.该文件要想运行,必须加载内存中,然后会生成一个对象.Class对象
2-4-2 类加载器层次结构
引导类加载器 rt.jar——核心 jdk/lib下 无法打印类加载器,结果为null(jvm启动时已经加载进去,不是class对象)
扩展类加载器 ext/*.jar jdk/lib/ext下
应用类加载器 我们自己编写类
2-4-5 全盘负责委托机制
当一个类运行的时候,有可能有其他类,应用类加载器询问扩展类加载器:你加载过这些类吗?
扩展类加载器在向上问(引导类加载器):你加载过这些类吗?
引导类加载器:我查查,有一个是我负责,我加载.
扩展类加载器:接下来我来查,有几个是我负责,我加载,还有几个类我已经加载完成了,你可以直接使用
应用类加载器:收到了 剩下的我来
三、案例3-统一编码
技术分析:
动态代理
3-1 静态代理书写步骤
1.要求被装饰者和装饰者实现同一个接口或者继承同一个类
2.在装饰者中要有被装饰者的引用
3.对需要加强的方法进行加强
4.对不需要加强的方法调用原来的方法
3-2 动态代理
在项目运行的时候才创建一个代理对象,对方法进行增强(控制)
3-2-1 方式1-Proxy类
jdk中Proxy类,前提:实现接口
3-2-2 方式2-cglib
spring中cglib,前提:继承类
3-2-3 动态的在内存中创建一个代理对象
Object Proxy.newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)
参数说明:
ClassLoader:代理对象类加载器 一般我们使用的是被代理对象的类加载器
Class[]:代理对象需要实现接口 一般我们使用的是被代理对象所实现的所有接口
InvocationHandler:执行处理类(匿名内部类).在这里面对方法进行加强(要么是个接口,要么是个抽象类)
invocationHandler中只有一个方法
Object invoke(Object proxy, Method method, Object[] args)
参数说明:
proxy:代理对象——newProxyInstance()的 返回值,自动传入
method:当前执行的方法
args:当前方法执行的时候所需要的参数
返回值:就是当前method对象执行的返回值
3-3 步骤分析
过滤器
doFilter(Request request,Response response)
将代理request传递过去
doFilter(Request requestPrxoy,Response response)<body><form action="/day17/login" method="post">用户名:<input name="username"><br>备注:<input name="memo"><br><input type="submit"></form> </body>
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"><filter><filter-name>EncodingFilter</filter-name><filter-class>com.itheima.c_proxy.encoding.EncodingFilter</filter-class></filter><filter-mapping><filter-name>EncodingFilter</filter-name><url-pattern>/login</url-pattern></filter-mapping><servlet><description></description><display-name>LoginServlet</display-name><servlet-name>LoginServlet</servlet-name><servlet-class>com.itheima.c_proxy.encoding.LoginServlet</servlet-class></servlet><servlet-mapping><servlet-name>LoginServlet</servlet-name><url-pattern>/login</url-pattern></servlet-mapping> </web-app>
public class EncodingFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)throws IOException, ServletException {//1.强转final HttpServletRequest request=(HttpServletRequest) req;HttpServletResponse response=(HttpServletResponse) resp;//创建代理对象HttpServletRequest requestProxy=(HttpServletRequest) Proxy.newProxyInstance(HttpServletRequest.class.getClassLoader(), request.getClass().getInterfaces(), new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {if("getParameter".equals(method.getName())){//获取请求方式String m = request.getMethod();if("get".equalsIgnoreCase(m)){String s = (String) method.invoke(request, args);//相当于 request.getParameter(args);return new String(s.getBytes("iso8859-1"),"utf-8");}else if("post".equalsIgnoreCase(m)){request.setCharacterEncoding("utf-8");return method.invoke(request, args);}}//不需要加强的方法return method.invoke(request, args);}});//2.放行chain.doFilter(requestProxy, response);}@Overridepublic void destroy() {}}
public class LoginServlet extends HttpServlet {private static final long serialVersionUID = 1L;protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {String username = request.getParameter("username");String memo = request.getParameter("memo");System.out.println(username);System.out.println(memo);}protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// TODO Auto-generated method stubdoGet(request, response);}}
总结
- 1 注解
注释:给程序员看的
注解:给jvm看的
java中三个注解:
@Override:声明该方法是从父类上继承过来的
@SuppressWarnings:抑制警告
@Deprecated:声明该方法不赞成使用
自定义注解:
注解属性
类型:
基本类型
string
class
annotation
enum
以上类型的一维数组
若有属性,使用的时候必须有值
元注解
@Retention:声明注解保留到那个阶段
SOURCE:
CLASS:
RUNTIME:
@Target:声明注解作用在那个部分
TYPE:
METHOD:
FILED:
注解的格式:
@interface 注解名{
}
- 2 servlet3.0
注解开发
@WebServlet(urlPatterns="/demo1")
@WebListener
@WebFilter(urlPatterns="/demo2")
文件上传
浏览器端的要求:
表单的提交方式必须是post
表单必须有文件上传组件
表单的enctype属性值为 multipart/form-data
服务器获取的时候
servlet3.0要求
添加一个@MultipartConfig
获取普通的组件
request.getParameter(name属性的值)
获取文件上传组件
Part part=request.getPart("name属性的值")
//获取文件的名称
Stirng sss=part.getHeader("content-disposition")
//然后截取才能获取文件名称
sss.substring(sss.indexof("filename=")+10,sss.length-1);
//获取文件流
part.getInputStream();
//删除临时文件
part.delete()
上传时候需要注意的问题:
文件重名 随机名称
文件安全 重要的文件放在 web-inf meta-inf 服务器磁盘
目录分离
时间
用户
文件个数
随机目录
- 3 动态代理(★)
jdk中proxy
前提:实现接口
Proxy.newProxyInstance(被代理对象.getClass().getClassLoader(),被代理对象.getClass().getInterfaces(),new InvocationHandler(){
invoke(代理对象,当前执行的方法,需要的参数){
}
});