day47基础加强注解+动态代理+类加载器.docx
《day47基础加强注解+动态代理+类加载器.docx》由会员分享,可在线阅读,更多相关《day47基础加强注解+动态代理+类加载器.docx(22页珍藏版)》请在冰豆网上搜索。
day47基础加强注解+动态代理+类加载器
Day47_基础加强
今日内容介绍
案例:
1.自定义注解模拟@Test
2.使用动态代理解决网站的字符集编码
注解(取代xml文件)、动态代理(对方法进行增强)、类加载器ClassLoaders(获取资源文件)
今日内容学习目标
1.能够说出注解概述(定义、作用)
2.能够说出JDK自带注解作用(3种)
3.够定义自定义注解(了解)
4.够使用自定义注解(重点掌握)
5.够解释元注解@Retention的各使用值的作用
6.够解释元注解@Target的各使用值的作用
7.够对动态代理技术进行描述(掌握动态的使用!
)【动态代理里面涉及的一些参数】
8.能够对类加载进行描述(类加载的作用,常见的类加载的分类,全盘委托机制)
一、自定义注解模拟@Test
1.回顾@Test操作
对@Test注解的要求:
方法签名:
必须是publicvoid非static无参数
运行时:
所有被@Test修饰的方法,都会被执行!
需求:
现在想自己写一个@MyTest来执行类似于@Test的效果!
我们需要学习注解相关的知识!
2.相关知识点
2.1注解概述
2.1.1注解的概念
Annotation注解,是一种代码级别的说明。
它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。
注解就是告诉程序,我所修饰的代码是如何执行的!
⏹注释:
注释是给开发人员阅读的,程序是无法进行解析的。
⏹注解:
给程序看的
2.1.2注解的作用:
●编译检查:
通过代码里标识注解,让编译器能够实现基本的编译检查。
例如:
@Override
●代码分析:
通过代码里标识注解,对代码进行分析,从而达到取代xml目的。
代码的抽取过程:
1、把运行了两次以上的代码进行合并为一个工具(方法,类)
2、把工具中异常进行处理
3、把工具的固定值,变为变量(使用配置文件)
4、变量-->>>常量
5、常量---->>>配置文件xmlproperties
6、配置文件---->>>注解@xxx(class="",url="",xxx="")
研发中的意义:
如果项目特别大,配置文件也会极多,配置文件内容也会特别多,不好进行维护。
可以用注解缓解
●编写文档:
通过代码里标识注解,辅助生成帮助文档对应的内容
2.2JDK常用注解介绍
2.2.1@Override
标记方法重写。
标记父类的重写(JDK5.0),标记接口方法的实现重写(JDK6.0)
会在程序的源码-预编译阶段进行重写检查,如果发现方法不是重写,那么在源码-预编译时期报错
注意:
它只能标注在方法上
Servlet中,可以重写父类HttpServlet里面的方法!
!
!
被@Override注解标记的方法的要求是:
方法签名必须与父类或者父接口完全一样!
修饰的方法>=父类和父接口!
!
!
2.2.2@SuppressWarnings
压制警告
会在程序的源码-预编译阶段进行警告的压制。
一般使用:
@SuppressWarnings("all")
注意:
它可以标注方法,变量,类上
压制警告的使用:
开发时,要把所有的警告压制,防止被警告打乱开发思路
测试时,要把压制警告去掉,警告机制开启,这样方便测试
rawtypes,忽略类型安全(泛型未指定类型)
unused,忽略不使用(定义变量未使用)
unchecked,忽略安全检查
null,忽略空指针(为空变量继续调用方法)
all,忽略所有
2.2.3@Deprecated
标记方法过时
会在程序的源码-预编译阶段开始起作用。
告知程序我所写的方法是过时,如果有程序员调用这样的过时方法,会报过时的警告。
即使调用了过时方法,过时方法也正常执行
过时方法存在问题(安全问题)
2.3自定义注解
2.3.1注解本质
注解本质是一个接口(查看源代码得知)
在定义的注解中有方法(String[]value())
我们换一个名字:
属性String[]value(),如下图
value=all
2.3.2注解的属性赋值
属性的类型:
基本、String类型、Class类型、枚举类型、注解类型、数组
演示:
创建一个注解@My1
编写测试类
其中枚举类型和注解类型需要自己单独定义:
要求:
能根据指定注解给自己的类里面的方法(添加了注解)把值赋值成功!
2.3.3注解的赋值特殊情况
●如果注解中,只有一个属性,并且名字叫value,赋值可以忽略属性名
●属性如果没有默认值,使用时必须赋值
●赋予默认值,使用就就不用赋值了,如果赋值,就会覆盖默认值
●赋予默认值,使用就就不用赋值了,如果赋值,就会覆盖默认值
3.案例实现
3.1自定义注解步骤
1、定义注解
写一个注解@interface【研发】
2、使用注解
@MyTest【开发】
3、程序解析,并执行注解
写反射代码【研发】最终封装成工具类(如JUnit)
Junit它个干了两件事:
它创建了一个注解@Test
它是调用反射的代码执行了这个注解
关注的是使用注解
4、要想使用isAnnotationPresent必须规定注解是在运行阶段起作用
以下两个注解是专门修饰注解
@Target({TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
专门修饰注解的注解叫做:
元注解
@Target规定注解能够修饰在哪个地方一般都是Method非必须
@Retention规定注解在哪个阶段起作用一般都是RetentionPolicy.RUNTIME必须规定的强制元注解
3.2案例代码
第一步:
不用写属性,@Test也木有!
第二步:
第三步:
第四步:
二、使用动态代理解决网站的字符集编码
1.案例需求以及动态代理引入
学习过滤器时,我们使用“装饰者”对request的getParameter()方法进行增强,从而使get和post使用request.getParameter()获得的数据都没有乱码。
本案例我们将使用一个全新的技术—动态代理,对“统一GET和POST乱码”案例进行重写。
1.1回顾装饰者解决乱码问题
使用装饰者完成中文乱码问题
1.1.1编写JSP页面
1.1.2编写servlet
1.1.3编写过滤器
先解决post请求
然后测试,post提交已解决,get依旧存在问题
1.1.4使用装饰者完善HttpServletRequest
1.1.5再次修改过滤器
测试问题得以解决!
问题发现:
使用装饰者设计模式,有严重的不足之处!
装饰者设计模式(静态代理,增强的是类)
优点:
不用知道实现类是哪个,因为我增强的是这个接口下所有的实现类
不占用继承位,方便后期程序扩展(相对继承)extends
固定步骤:
增强类实现被增强类实现的接口
增强类获得被增强对象的引用构造方法
缺点:
如果接口中的方法过多,实现大量无关的方法。
【对类增强】
如果只能获取到对象,那么我是无法知道类有哪些接口,就无法使用装饰者【必须明确所有的接口】
如果只对某个方法进行增强,使用动态代理非常合适!
2.相关知识点
2.1代理模式介绍
2.2动态代理对象介绍
Proxy提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。
核心方法:
参数介绍:
三个参数全都是固定写法!
参数一:
通过被增强对象获取类加载器(xxxx.class.getClassLoader)
参数二:
通过被增强对象获取所有实现类的接口
参数三:
动态代理的处理类,用于对方法的增强
2.3动态代理操作步骤
固定步骤:
1、创建被增强对象【被代理对象】
2、创建增强对象【代理对象】
2.1、通过被代理对象获取类加载器
被代理对象.getClass().getClassLoader()
2.2、通过被代理对象获取其所有的接口
被代理对象.getClass().getInterfaces()
2.3、创建一个处理类【专门用来增强方法】
动态代理只对接口里面的方法进行增强,实现类的不曾强!
(我们实现动态代理压根儿不知道它的实现类)
2.4动态代理演示
2.4.1创建接口
2.4.2创建实现类
2.4.3创建处理类
测试:
增强的方法被调用。
调用之前的方法:
发现动态代理与之前的类一点关系都没有!
2.5动态代理多方法执行以及return说明
再次改进代码
2.5.1修改接口
动态代理:
接口中没有的方法,动态代理是没法增强的!
2.5.2修改被增强对象
2.5.3修改处理类
Return是增强方法的返回值
2.6动态代理处理带参的方法
2.6.1修改接口
2.6.2修改被增强对象
2.6.3修改处理类
2.7动态代理针对某个方法增强
只需要修改处理类,加个判断即可
2.8动态代理与静态代理区别
1、静态代理代理的是类创建一个增强类【代理类】
2、动态代理代理的是对象创建一个增强对象【代理对象】
1、静态代理(工作量极大)
要实现所有接口中的所有方法全部实现。
工作量极大
需要知道具体的接口名字。
通过class找所有接口。
工作量极大
2、动态代理(灵活,工作量极小)只需要被代理对象
3.案例代码实现
修改过滤器代码
4.总结(类加载器)
●类加载器:
类加载器是负责加载类的对象。
将class文件(硬盘)加载到内存生成Class对象。
作用:
类加载器负责将class文件加载到内存生成对应的Class对象!
所有的类加载器都是java.lang.ClassLoader的子类
⏹引导类加载器:
BootStrapClassLoader
它加载的是Java\jdk1.7.0_72\jre\lib\rt.jar!
JDK工具返回null
⏹扩展类加载器:
ExtClassLoader
它加载的是jdk1.7.0_72\jre\lib\ext\*.jar
⏹应用类加载器:
AppClassLoader
它加载的是程序员自己编写的文件
●使用类.class.getClassLoader()获得加载自己的类加载器
●类加载器加载机制:
全盘负责委托机制
全盘负责:
A类如果要使用B类(不存在),A类加载器C必须负责加载B类。
委托机制:
A类加载器如果要加载资源B,必须询问父类加载是否加载。
如果加载,将直接使用。
如果没有机制,自己再加载。
●采用全盘负责委托机制保证一个class文件只会被加载一次,形成一个Class对象。
packagecom.itheima.web.filter;
importjava.io.IOException;
importjava.lang.reflect.InvocationHandler;
importjava.lang.reflect.Method;
importjava.lang.reflect.Proxy;
importjavax.servlet.Filter;
importjavax.servlet.FilterChain;
importjavax.servlet.FilterConfig;
importjavax.servlet.ServletException;
importjavax.servlet.ServletRequest;
importjavax.servlet.ServletResponse;
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletResponse;
publicclassEncodingFilterimplementsFilter{
publicvoiddestroy(){
}
publicvoiddoFilter(ServletRequestreq,ServletResponseresp,FilterChainchain)
throwsIOException,ServletException{
//1.强转(创建一个被代理对象!
!
!
!
)
//【动态代理不用单独去创建其它的对象!
!
!
(它只对接口里面的里面的方法增强,使用的对象是接口的默认实现类);装饰者是需要单独创建一个类去实现默认实现类的父接口或者继承默认实现类父接口的包装类】
finalHttpServletRequestrequest=(HttpServletRequest)req;
HttpServletResponseresponse=(HttpServletResponse)resp;
//2.处理响应的中文乱码
response.setContentType("text/html;charset=utf-8");
//3.创建一个代理对象
/**
*第一个参数:
通过被代理对象获得的类加载器(负责将字节码文件加载到内存,生成对应的class对象!
)
*第二个参数:
通过被代理对象获得的接口(动态代理只能对接口里面的方法进行增强!
)
*第三个参数:
创建代理对象的方法!
!
!
(要执行代理对象了里面的代码!
必须放到方法里面!
创建一个代理对象专门执行功能的一个对象<接口>)
*/
HttpServletRequestproxyReq=(HttpServletRequest)Proxy.newProxyInstance(request.getClass().getClassLoader(),request.getClass().getInterfaces(),newInvocationHandler(){
/**
*第一个参数:
代理对象(没啥卵用!
!
!
)
*第二个参数:
被代理对象里面的方法对象!
!
!
!
(希望在代理对象中调用被代理对象里面的原有核心功能!
)
*第三个参数:
被大理对象里面的方法对象的参数对象!
!
!
!
!
*方法的返回值:
它不太确定被代理的那个方法的返回值类型!
!
!
干脆给Object(通过调用的方法的return来返回被代理对象里面方法的返回值!
!
!
)
*/
@Override
publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{
//只对getParameter方法进行增强!
if(method.getName().equals("getParameter")){
//获得请求方式(靠被代理对象)
Stringmethod1=request.getMethod();
//判断用户的请求方式
if(method1.equalsIgnoreCase("POST")){
//说明是POST请求(增强的功能!
!
!
)
request.setCharacterEncoding("utf-8");
//执行原有功能(调用的是被代理对象里面的方法!
它就request.getParameter("username"))【args它就是username】
returnmethod.invoke(request,args);
}elseif(method1.equalsIgnoreCase("GET")){
//说明是GET请求,执行原有功能!
(原有核心功能!
)
Stringpar=(String)method.invoke(request,args);
//增强!
par=newString(par.getBytes("iso8859-1"),"utf-8");
//返回
returnpar;
}else{
//说明是其它请求方式
returnmethod.invoke(request,args);
}
}else{
//其它方法,执行执行
returnmethod.invoke(request,args);
}
}
});
//放行的必须是代理对象!
!
!
!
chain.doFilter(proxyReq,response);
}
publicvoidinit(FilterConfigfConfig)throwsServletException{
}
}