注解 & 动态代理
1、设置Eclipse,关联Tomcat
开发过程中,环境发生变化(重新关联tomcat)。
- 删除servers窗口下的tomcat
- Project Explorer 窗口下的server
- 重新关联window —> preferences —> servers —> runtime —> tomcat
2、注解
2.1 什么是注解?
注解和接口、类一样,都是属于数据类型。
2.2 注解作用
(1)编译检查
(2)配置 (后期使用最多)
(3)生成帮助文档
2.3 注解的特点
(1)注解可以在变量,方法,类之上加载。
(2)注解可以有属性也可以没有属性
@Override @Test(timeout=1000)
(3)注解有作用范围(源码,编译期间,运行期间)
- 源码期间有效: 例如String类之上的@Author,@Since,@See
作用:使用命令javadoc命令将当前的源码生成帮助文件,可以识别String类上的相关的注解。 - 编译期间有效:@Override,@Deprecated,@Suppresswarning
作用:告诉编译器部分信息。 - 运行期间有效::@Test
作用:当我们再当前代码上以Junit方式运行时,Junit会运行方法上包含@Test注解的方法。
2.4 回顾JDK中出现的3种注解
@Override,@Deprecated,@Suppresswarning
2.5 自定义注解
(1)格式:
public @interface 注解名称{
public 属性类型 属性名称1();
public 属性类型 属性名称2() default 默认值;
}
(2)自定义注解属性支持的类型:
支持基本数据类型(8种)。
String,Class,Annotation(非自定义注解),枚举类型。
以及以上类型的一维数组类型。
(3)注解作用:标注作用,配置信息等。
(4)配置:开发的时候部分信息不希望写死在程序中。
例如:数据库的用户名和密码,可以将用户名和密码存放在.txt,.properties,.xml文件中,利用程序来读取文件中的内容。
(5)框架:一大堆工具类组合,目的是加速项目开发。
后期的学习中,框架部分hibernate,spring,struts2很多信息需要配置,提供了2种形式配置:(xml,注解)
(6)什么时候用注解来做配置?
如果配置信息不会发生的修改,例如servlet路径,建议使用注解的形式。
如果配置信息需要发生频繁的修改,例如数据库的用户名和密码信息,建议采用传统方法 (.txt, .properties,.xml)。
测试使用自定义注解
@MyAnno01(timeout=100,c=java.util.Date.class,strs={ "aaa","bbb"})
public void test01(){
}
(7)通过反射读取字节码上的注解信息
2.6案例:模拟Junit
(1)自定义注解@MyTest
通过元注解@Rentention,@Target声明当前注解作用域,以及目标对象,如果没有声明,在运行期间是无法获取到注解的信息。
(2)定义UserDao
创建4个方法addUser,delUser,uptUser,getUser,在前三个方法上加载注解。
(3)定义类MyJunit,模拟JUnit
将UserDao.class文件加载到内存,获取到字节码文件上所有的方法。遍历方法,判断每个方法上是否加载了@MyTest注解,如果当前方法上设置@MyTest,执行当前的方法。
注解要求:
开发中地位:类似dom4j解析XML文件,XML文件的解析程序员不会去解析,配置XML文件。
后期的开发中一般不会自定义注解,反射读取注解信息。
3、使用动态代理解决网站的字符集编码问题
3.1 什么是设计模式?
软件开发过程中,遇到相似问题,将问题的解决方式抽取模型(套路)。
例如:单例,工厂,适配器,装饰者,动态代理等。
3.2 谷歌汽车场景
(1)假设java设计了汽车开发约定
public interface ICar { start,run,stop三个方法}
(2)Google实现了这个接口
public class GoogleCar implements ICar { }
如果希望在将谷歌Car接入到生态圈平台时,增强汽车启动功能。
使用装饰者模式。
场景:二次开发的时候,无法获取到源码,无法使用继承前提下,要对已经存在对象上的功能进行增强。
前提条件:可以获取到被装饰的对象GoogleCar实现的所有接口。
实现思路:自定定义装饰类实现ICar接口,为自定义装饰类传递被装饰的对象。
弊端:如果被实现的接口中的方法过多,装饰类中的方法过多冗余。
3.3 动态代理模式
原理:通过JVM在内存中创建类似MyCar.class文件。
要创建MyCar.class文件告诉虚拟机:
- 被创建的字节码文件上应该有多少方法。
- 被创建的字节码上的方法如何来实现。
测试代码:
// 假设这是Sun公司定义的一套规范,无人驾驶汽车接口
public interface ICar {
void start();
void run();
void stop();
}
// 这是Google公司对ICar接口的实现类
public class GoogleCar implements ICar {
@Override
public void start() {
System.out.println("GoogleCar开始启动!");
}
@Override
public void run() {
System.out.println("GoogleCar正在行驶!");
}
@Override
public void stop() {
System.out.println("GoogleCar最后停车!");
}
}
public class Test01 {
public static void main(String[] args) {
// 第一个参数:固定值,指定用哪个字节码加载器,去加载内存中创建出的字节码文件
// 第二个参数:正在被创建的字节码文件中应该有哪些方法
// 第三个参数:被创建的字节码上的各个方法应该怎么处理
ICar car = (ICar) Proxy.newProxyInstance(Test01.class.getClassLoader(), GoogleCar.class.getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 如果当前方法是start()方法
if (method.getName().equalsIgnoreCase("start")) {
// 在这里对start()方法进行功能加强
System.out.println("启动之前,查询天气!");
System.out.println("启动之前,查询路况!");
}
// 在这里执行原有的方法
method.invoke(new GoogleCar(), args);
return null;
}
});
car.start();
car.run();
car.stop();
}
}
3.4 什么是字节码加载器?
jdk有一些程序,专门将各种字节码文件加载到内存。
这类程序简称为字节码加载器。
如何将字节码文件class文件加载到内存?
底层实现过程,利用IO流技术,获取到文件中的数据加载到内存。
字节码加载器:3种
- 系统引导类加载器
这类字节码加载器主要用于平时使用比较频繁的类,而且要求加载速度一定要快,比如说String.class、int.class等。
这种加载器不是java类,底层使用c或者c++写的。 - 扩展类加载器
jre文件下的ext包内的都是通过扩展类加载器加载到内存的,其底层也是一个java类。 - 应用类加载器
程序员自己写的所有类都属于应用类,获取应用类的加载器就是应用类加载器。底层通过Java实现。
3.5 案例:动态代理解决全站乱码问题
步骤:
1、new DynamicWeb Project —> Index.html
<h3>post方式提交中文</h3>
<form action="/day18_v3/ServletDemo" method="post">
User:<input type="" name="username"/><br/>
<input type="submit"/>
</form>
<h3>get方式提交中文</h3>
<form action="/day18_v3/ServletDemo" method="get">
User:<input type="" name="username"/><br/>
<input type="submit"/>
</form>
2、ServletDemo
要求实现:无论是在post/get方法,执行以下语句不存在中文乱码问题。
String username = request.getParameter(“username”);
System.out.println(username);
3、在过滤器中,为request上的getParameter()功能进行增强
思路:
判断当前的请求是get/post,用request.getMethod();方法。
如果是post,设置一句话:request.setCharacterEncoding(“utf-8”);
放行。
如果是get,获取value:String value = request.getParameter(name);
将value进行转码:String newValue = new String(value.getBytes(“iso-8859-1”), “utf-8”);
放行。
测试代码:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException {
// 先将request对象转换为HTTPServletRequest类型,并设置为final修饰
final HttpServletRequest req = (HttpServletRequest)request;
// 生成代理对象,增强req对象的getParameter()方法
HttpServletRequest myRequest = (HttpServletRequest)Proxy.newProxyInstance(this.class.getClassLoader(), req.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object obj = null;
// 如果当前方法是getParameter()方法
if (method.getName().equalsIgnoreCase("getParameter")) {
// 在这里对getParameter()方法进行功能加强
String md = req.getMethod();
if ("get".equalsIgnoreCase(md)) { // 如果是get请求
// 将请求数据转码
String value = (String)method.invoke(req, args);
return new String(value.getBytes("iso-8859-1"), "utf-8");
} else if ("post".equalsIgnoreCase(md)) { // 如果是post请求
// 设置编码格式
req.setCharacterEncoding("utf-8");
}
}
// 在这里执行原有的方法
obj = method.invoke(req, args);
return obj;
}
});
// 放行代理对象
chain.doFilter(myRequest, response);
}
还没有评论,来说两句吧...