day23 框架之基础加强
day23 框架之基础加强
今日任务
aptana(javascript的eclipse插件):http://www.cnblogs.com/terrylin/archive/2012/06/20/2555962.html
jquery技术,查询文档。
-
今天的总思路:读取配置文件,反射获取类信息,创建对象,监控方法的执行
SSH : struts2 spring hibernate
SSM :springMVC spring Mybaties
spring 核心:IOC——控制反转,创建对象,不再由着程序员,创建对象由spring管理
AOP——面向切面编程,面向整个功能(功能的实现,不止一个类)
-
反射技术
-
动态代理
-
注解技术
注解技术:老程序员看不上,新程序员特别喜欢。
使用配置文件:特别多,特别复杂(新程序员讨厌原因)
注解:简单,方便,内容少。(新程序员特别喜欢) 注解的配置:都在类上或者方法上,代码维护代价高,注解性能问题(老程序员看不上)
课堂笔记
读取配置文件的三种方式
第一种:String realPath = this.getServletContext().getRealPath("/upload");
只能在web工程中使用。
第二种:InputStream in = ClassLoader.getSystemResourceAsStream("peizhi.properties");
使用类加载器读取配置文件的方法(类加载器:加载类的对象),任何工程都可以使用
任何java工程都需要类加载器来,帮助我们加载类,运行,所以任何工程都可以使用类加载器加载配置文件。
ClassLoader是什么:加载类的对象。
第三种:ResourceBundle bundle = ResourceBundle.getBundle("peizhi");
注意:专门用来读取properties文件
演示读取配置文件的方式(第二种和第三种):
准备接口和实现类:
配置文件准备:
思路:
-
定义接口
-
书写实现类
-
定义配置文件
模拟框架开发:接口定义好的,如何知道程序员定义的实现类?
配置文件——定义好了接口的实现类。
通过读取配置文件,可以获取到实现类的全路径,通过反射获取实现类的对象
第二种代码演示:
/**
* 测试使用类加载器,读取配置文件
* @throws Exception
*/
@Test
public void test1() throws Exception{
//获取输入流,读取配置文件
InputStream in = ClassLoader.getSystemResourceAsStream("peizhi.properties");
//InputStreamReader: 他的构造函数需要InputStream对象,将字节流转换成字符流
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
String line = null;
while((line = reader.readLine()) != null){
System.out.println(line);
}
}
效果:
第三种代码演示:
/**
* 测试使用ResourceBundle,读取配置文件
* @throws Exception
*/
@Test
public void test2() throws Exception{
//创建对象,读取配置文件
//ResourceBundle 专门读取properties文件
ResourceBundle bundle = ResourceBundle.getBundle("peizhi");
String value = bundle.getString("servlet");
System.out.println(value);
}
效果:
注意:任何配置文件,修改过后,都需要重新启动java虚拟机。
原因:读取配置文件的动作只有一次。
如果想要不重新启动java虚拟机,将数据保存在数据库,然后,从数据库获取数据,再进行操作——这样的数据,一般叫做系统变量。
1、反射技术回顾
1.1、Class回顾
Class:相当于工厂在制作产品之前,先设计好的产品图纸。
反射的作用:可以动态的创建某个类的对象,然后动态调用这个类中的成员(方法和属性)。
-
对象的getClass方法
-
类名.class
-
Class类forName方法
代码演示:
/**
* 演示获取Class对象的三种方式
* @throws ClassNotFoundException
*/
@Test
public void test3() throws ClassNotFoundException {
MyServlet myServlet = new MyServlet();
System.out.println(myServlet.getClass());
System.out.println(MyServlet.class);
//clazz:这是一个源远流长的习惯
//框架经常使用第三种方式
Class clazz = Class.forName("cn.itcast.web.impl.MyServlet");
System.out.println(clazz);
}
效果:
1.2、反射类中的成员回顾
第一步:反射类中的构造方法:
通过构造方法,就可以创建类的对象
Class:
Constructor:
/**
* 演示获取对象的构造方法
* @throws Exception
*/
@Test
public void test4() throws Exception{
//读取配置文件
ResourceBundle bundle = ResourceBundle.getBundle("peizhi");
//获取类全名
String className = bundle.getString("servlet");
//获取类的构造方法
Class<?> clazz = Class.forName(className);
/*Constructor[] constructors = clazz.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}*/
//获取指定的构造方法
//parameterTypes:给的是构造函数的参数
Constructor<?> constructor = clazz.getConstructor(null);
System.out.println(constructor);
Constructor<?> constructor2 = clazz.getConstructor(String.class,String.class);
System.out.println(constructor2);
//根据构造函数创建对象
//initargs:构造函数使用需要的参数
Object newInstance = constructor.newInstance(null);
System.out.println(newInstance);
Object newInstance2 = constructor2.newInstance("req","res");
System.out.println(newInstance2);
}
效果:
反射类中的成员变量:
Class:
/**
* 演示获取成员变量(属性)
*
* @throws Exception
*/
@Test
public void test5() throws Exception {
// 读取配置文件
ResourceBundle bundle = ResourceBundle.getBundle("peizhi");
// 获取类全名
String className = bundle.getString("servlet");
// 获取Class对象
Class<?> clazz = Class.forName(className);
Field[] declaredFields = clazz.getDeclaredFields();
/*for (Field field : declaredFields) {
System.out.println(field);
}*/
Field declaredField = clazz.getDeclaredField("request");
System.out.println(declaredField);
}
效果:
反射类中的成员方法:
获取所有方法:
/**
* 演示获取类中的成员方法
*
* @throws Exception
*/
@Test
public void test6() throws Exception {
// 读取配置文件
ResourceBundle bundle = ResourceBundle.getBundle("peizhi");
// 获取类全名
String className = bundle.getString("servlet");
// 获取Class对象
Class<?> clazz = Class.forName(className);
Method[] methods = clazz.getMethods();
for (Method method : methods) {
System.out.println(method);
}
System.out.println("================");
Method method = clazz.getMethod("doGet", java.lang.String.class,java.lang.String.class);
System.out.println(method);
System.out.println("==============");
//演示调用方法obj:调用方法的对象,args:方法的参数
Object invoke = method.invoke(clazz.newInstance(), "haha","hehe");
System.out.println(invoke);
}
效果:
案例:模拟BeanUtils封装数据演示:
思路:
-
需要一个封装数据的对象(Person)
-
需要一个封装数据的map集合(key值和Person的字段名称一致)
-
获取Person的Class对象,只有获取Class对象,才能获取到所有的字段(属性)
-
获取所有字段
-
循环所有字段和map集合,当key值和字段名称一致,就封装数据
要使用的API:
Class:
Field:
代码演示:
测试类:
/**
* 演示模拟beanUtils封装数据
*
* @throws Exception
*/
@Test
public void test7() throws Exception {
//第一步:创建要被封装数据的对象
User u = new User();
System.out.println(u);
//String:用来保存属性的名称(对应User中的属性)
//Object:要被封装的数据,任意类型
Map<String, Object> map = new HashMap<String, Object>();
map.put("name", "张三");
map.put("age", 11);
//封装数据,使用BeanUtils.populate(u, map);
BeanUtils.populate(u, map);
System.out.println(u);
}
工具类:
package cn.itcast.utils;
import java.lang.reflect.Field;
import java.util.Map;
public class BeanUtils {
public static void populate(Object obj, Map<String, Object> map) {
//第一步:通过Class对象来获取obj对象中的成员变量
Class<?> clazz = obj.getClass();
Field[] declaredFields = clazz.getDeclaredFields();
//第二步:遍历循环所有的字段,将数据封装进去
for (Field field : declaredFields) {
for (String key : map.keySet()) {
//判断,当前的字段名称是否和map集合中key值,一致,一致,才能封装数据
if(key.equals(field.getName())){
//封装数据
//取消访问的检查
field.setAccessible(true);
try {
field.set(obj, map.get(key));
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
}
效果:
2、动态代理
2.1、动态代理介绍
struts2 、 hibernate 底层使用的动态代理。
Spring 在AOP当中,使用了大量的动态代理,JDK自带的接口代理(有接口才能代理),子类代理(cglib代理)
动态代理:访问被代理的类,进行拦截,处理,处理完了,放行
代理设计模式:
注意:以上还只是代理设计模式,怎么变成动态代理?上面的故事,如果改成,看到姐姐之后,直接上帝创建了妹妹,就是动态代理。
2.2、动态代理演示
1、先获取创建代理对象的类,调用它的方法,创建出一个代理对象
2、代理对象具体要做什么事情(代理策略),需要设置
创建代理类的对象:
创建代理对象的方法:
代理策略对象:
代理策略方法:
需求和步骤(模拟框架读取配置文件,创建代理对象,并调用接口方法):
-
读取配置文件
-
获取类全名
-
反射获取Class对象
-
获取当前类接口(根据接口做代理)
-
创建代理类(Proxy)
-
代理策略:监控方法执行
-
获取代理类的接口方法,调用执行
代码演示:
/**
* 演示动态代理
*/
@Test
public void test8()throws Exception{
//1) 读取配置文件
ResourceBundle bundle = ResourceBundle.getBundle("peizhi");
//2) 获取类全名
String className = bundle.getString("servlet");
//3) 反射获取Class对象
final Class<?> clazz = Class.forName(className);
//4) 获取当前类接口(根据接口做代理)
Class<?>[] interfaces = clazz.getInterfaces();
//以上都是反射操作
//5) 创建代理类(Proxy),设置了代理的策略
Object obj = Proxy.newProxyInstance(clazz.getClassLoader(), interfaces, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//6) 代理策略:监控方法执行
//method 当前被调用的方法
//args 是调用方法时候的参数
System.out.println("======方法启动=====");
System.out.println("method:"+method);
for (Object dd : args) {
System.out.println(dd);
}
//method是一个方法对象,可以调用它的invoke方法,让他去执行
if(method.getName().equals("doGet")){
Object invoke = method.invoke(clazz.newInstance(), args);
}
System.out.println("======执行完成=====");
return null;
}
});
//7) 获取代理类的接口方法,调用执行
//通过接口数组的第一个接口,获取第一个接口中的所有方法
Method[] methods = interfaces[0].getMethods();
//通过操作methods方法数组,获取第一个方法对象,通过他的invoke方法,调用方法执行
methods[0].invoke(obj, "req","res");
}
//为什么要用动态代理?
//1)可以通过代理对象,对指定的类的方法进行修改(拦截或者增强),使用动态代理修改类,不需要修改原来的代码,因为我们是创建一个新的类
//2)一次动态代理,可以对当前指定的类,的所有方法,进行拦截或者增强的操作
/**
* 继承
* 当你要写的这个类,与原来要被增强的那个类是同一类型事物(animal cat dog)
*
* 装饰设计模式
* 当你要写的这个类,与原来要被增强的那个类不是同一类型事物(animal dianziGou / 对request对象进行增强,也是使用装饰设计模式)
*
* 动态代理
* 需要,在java虚拟机启动之后,再去修改指定的类,那么就使用动态代理
*
* 怎么就突然出现了一个类呢?
* Person.java -->javac命令 Person.class
* 创建代理类,底层机制类似javac命令,所以不属于java范畴,后期,入职sun公司,请回来告诉我。
*
* */
总结:
使用动态代理中,可以一个类的方法执行前后做增强和过滤的动作
2.3、动态代理完成全站乱码处理
过滤器:全站乱码过滤器
需求:使用动态代理方式,处理乱码
-
过滤器:对请求进行增强
-
过滤器:过滤任务写在doFilter方法
-
过滤任务:通过创建代理对象来增强原来的request
-
根据不同请求方式处理:
-
Post:setCharacterEncoding("utf-8")'
-
Get:根据不同的方法,不同处理
-
getParameter方法:new String();
-
getParameterValues方法:遍历循环,new String();
-
getParameterMap:循环遍历,new String();
-
request对象还要其他方法,其他方法执行不做任何处理
package cn.itcast.filter;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.Set;
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.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class EncodingFilter2 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;
HttpServletResponse res = (HttpServletResponse) response;
res.setContentType("text/html;charset=utf-8");
// 使用动态代理创建代理对象,对request进行增强
HttpServletRequest myrequest = (HttpServletRequest) Proxy
.newProxyInstance(req.getClass().getClassLoader(), req
.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
// 设置代理策略,对request的获取数据的方法进行增强
//获取请求方式
String method2 = req.getMethod();
if(method2.equalsIgnoreCase("post")){
req.setCharacterEncoding("utf-8");
return method.invoke(req, args);
}else if(method2.equalsIgnoreCase("get")){
//获取请求参数的方法
//根据不同的方法,进行不同增强操作
if("getParameter".equals(method.getName())){
String parameter = req.getParameter((String)args[0]);
if(parameter != null){
parameter = new String(parameter.getBytes("iso-8859-1"),"utf-8");
}
return parameter;
}else if("getParameterValues".equals(method.getName())){
String[] values = req.getParameterValues((String)args[0]);
if(values != null){
for (int i = 0; i < values.length; i++) {
values[i] = new String(values[i].getBytes("iso-8859-1"),"utf-8");
}
}
return values;
}else if("getParameterMap".equals(method.getName())){
Map<String, String[]> map = req.getParameterMap();
if(map != null){
Set<String> keySet = map.keySet();
for (String key : keySet) {
String[] values = map.get(key);
for (int i = 0; i < values.length; i++) {
values[i] = new String(values[i].getBytes("iso-8859-1"),"utf-8");
}
}
}
return map;
}else{
return method.invoke(req, args);
}
}else{
return method.invoke(req, args);
}
}
});
chain.doFilter(myrequest, res);
}
@Override
public void destroy() {
}
}
3、注解技术
3.1、注解和注释区别
注释:它是对程序中的代码进行解释说明。注释是给人看。
注解:它是给程序使用的。在程序运行的时候(编译的时候),会根据相应的注解,然后完成对应的操作。注解经常需要和反射一起使用。
早期的程序:
基本是程序配合配置文件一起工作,在程序运行的时候,会读取配置文件中的数据。
现在程序:
在程序运行的时候,不再使用配置文件(少量使用配置文件),而是在程序加入注解来代替配置文件。
3.2、注解格式和JDK注解简单演示
注解的格式:@注解的名称( key=value , key=value, key=value。。。。。。 )
JDK中自带的3个注解:
1、方法复写的时候注解:
使用Override注解声明当前方法是复写父类的方法,或者是实现了接口中的方法
jdk1.5@override不支持接口的方法复写,1.6以上可以
2、过时的方法注解:
@Deprecated它声明某个方法已经过时。
3、消除程序中的警告
@SuppressWarnings 消除警告。
3.3、自己定义注解的实现
自己定义注解:
格式:public @interface 注解名字{
定义注解的成员变量;
}
第一步:
效果:
自定义注解:
自定义注解使用:
3.4、注解的细节
@Test注解不能出现在类上:
获取Junit源代码:
第一步:找到junit在eclipse中的位置
第二步:使用反编译神器,将class文件还原成.java文件
发现:在@Test注解的底层类上有两个注解
@Retention(RetentionPolicy.RUNTIME)
指定注解什么时候存在
@Target({java.lang.annotation.ElementType.METHOD})
指定注解可以写在什么位置
1、在定义注解的时候,可以指定注解什么时候存在——@Retention
这个注解是存在于源代码上,还是编译之后的class文件上,还是运行时期也要存在。
@Retention 使用jdk中的Retention 来声明自己的注解将来在程序中存活的具体时间
RetentionPolicy:声明注解存活的时间:
代码演示:
注意:声明注解的存活时间时,使用runtime是给反射程序使用的。
2、在定义注解的时候,指定注解可以写在什么位置——@Target
注解是使用在类上,方法上,构造方法上,成员变量,或者是其他注解上。
在Java中提供的@Target注解,声明自己 定义的注解在其他的类中的存在位置。
代码演示:
效果:
演示代码(反射获取注解):
@Test
@MyAnnotation
public void test9() throws Exception{
Method method = this.getClass().getMethod("test9", null);
MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
System.out.println(annotation);
}
效果:
-
在注解中定义成员变量
格式:数据类型 变量名() default 默认值
测试代码:
效果:
3.5、使用注解完成数据库连接的获取
定义注解:
package cn.itcast.zhujie;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
@Target({java.lang.annotation.ElementType.METHOD})
public @interface MyAnnotation {
String driverClass();
String jdbcUrl();
String user();
String password();
}
使用注解:
package cn.itcast.test;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.DriverManager;
import org.junit.Test;
import cn.itcast.zhujie.MyAnnotation;
public class TestUtils2 {
@MyAnnotation(driverClass="com.mysql.jdbc.Driver",jdbcUrl="jdbc:mysql:///day14",user="root",password="root")
@Test
public void test() throws Exception{
//获取注解上的值,获取数据库连接
Method method = this.getClass().getMethod("test", null);
MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
String driverClass = annotation.driverClass();
String jdbcUrl = annotation.jdbcUrl();
String user = annotation.user();
String password = annotation.password();
Class.forName(driverClass);
Connection connection = DriverManager.getConnection(jdbcUrl, user, password);
System.out.println(connection);
}
}