第29节 内省_JavaBean讲解
JavaBean是一个特殊的Java类
方法打头一定要是get,或者setclass Person { private int x; public int getAge()//返回这个对象的Age属性,注意!!!get不能有参数, { return x; } public void setAge(int age)//设置Age属性.注意!!没有返回值 { this.x = age; } }
用上面的小例子来说明一下JavaBean
如果看做是Java类的话,那么看的就是x变量
如果看做是JavaBean的话,那么是根据getAge来判断是age
特点:也就是说,JavaBean的属性是根据方法名来的.(去掉set或者get)
要注意:Age-->如果第二个字母是小写,则把第一个字母变成小写-->age
CPU-->如果第二个字母是大写,则把第一个字母也继续大写-->CPU
比如:
gettime->time
setTime->time
getCPU->CPU
第30节 对JavaBean的简单内省操作
内省访问JavaBean属性的两种方式:
通过PropertyDescriptor类操作Bean的属性
通过Instospector类获得Bean对象的BeanInfo,然后通过BeanInfo来获取属性的描述器(PropertyDescriptor),
通过这个属性描述其就可以获取某个属性对应的getter/setter方法,然后通过反射机制调用这些方法.
内省-beanutils工具包
Sun公司的内省API过于繁琐,所以Apache组织结合很多实际开发中的应用场景开发了一套简单易用的API操作Bean的属性-->BeanUtils
工具包内常用的类
BeanUtils
PropertyUtils
ConvertUtils.regsiter(Converter convert,Class clazz)
------补充结束
需求:用内省的方式来读取JavaBean的x属性.
PropertyDescriptor 描述 Java Bean 通过一对存储器方法导出的一个属性。
PropertyDescriptor(String propertyName, Class<?> beanClass)
通过调用 getFoo 和 setFoo 存取方法,为符合标准 Java 约定的属性构造一个 PropertyDescriptor。
//需求:用内省的方式来读取JavaBean的x属性. import java.beans.IntrospectionException; import java.beans.PropertyDescriptor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class IntroSpectorTest { public static void main(String[] args)throws Exception { ReflectPoint pt1 = new ReflectPoint(3,4); String propertyName = "x"; //普通方法:"x"-->"X"-->"getX"-->"Method getX"-->得到值 //内省的方法:PropertyDescriptor(属性名,JavaBean类),属性描述符 Object retVal = getProperty(pt1, propertyName); System.out.println(retVal); //接下来,set一个值 Object value = 1; setProperty(pt1, propertyName, value); System.out.println(pt1.getX()); } private static Object getProperty(Object pt1, String propertyName) throws IntrospectionException, IllegalAccessException, InvocationTargetException { //PropertyDescriptor(属性名,JavaBean类),属性描述符 PropertyDescriptor pd1 = new PropertyDescriptor(propertyName,pt1.getClass()); Method methodGetX=pd1.getReadMethod(); Object retVal = methodGetX.invoke(pt1); return retVal; } private static void setProperty(Object pt1, String propertyName, Object value) throws IntrospectionException, IllegalAccessException, InvocationTargetException { PropertyDescriptor pd2 = new PropertyDescriptor(propertyName,pt1.getClass()); Method methodSetX = pd2.getWriteMethod(); methodSetX.invoke(pt1,value); } }
第31节 对JavaBean的复杂内省操作
采用遍历BeanInfo的所有属性方法来查找和设置某个ReflectPoint对象的x属性.
在程序中把一个类当做JavaBean来看,就是调用了IntroSpector.getBeanInfo方法,
得到的BeanInfo对象封装了把这个类当做JavaBean的结果信息
//获取值 private static Object getProperty(Object pt1, String propertyName) throws IntrospectionException, IllegalAccessException, InvocationTargetException { /* PropertyDescriptor pd1 = new PropertyDescriptor(propertyName,pt1.getClass()); Method methodGetX=pd1.getReadMethod(); Object retVal = methodGetX.invoke(pt1); return retVal; */ //比较复杂的方式 BeanInfo beanInfor = Introspector.getBeanInfo(pt1.getClass()); PropertyDescriptor[] pds = beanInfor.getPropertyDescriptors(); Object retVal = null; for(PropertyDescriptor pd : pds) { if(pd.getName().equals(propertyName)) { Method methodGetX = pd.getReadMethod(); retVal = methodGetX.invoke(pt1); break; } } return retVal; }
第32节 使用BeanUtils工具包操作JavaBean
1,首先,去下载BeanUtils,http://commons.apache.org/proper/commons-beanutils/
Download,点击commons-beanutils-1.8.3-bin.zip,解压,
2,在工程下创建一个目录lib,然后将jar包复制到lib文件夹下,点击jar包,右键Build path-->Add
ReflectPoint pt1 = new ReflectPoint(3,4);
//工具包
//获取值
System.out.println(BeanUtils.getProperty(pt1, "x"));
//设置值
BeanUtils.setProperty(pt1, "x", 10);
System.out.println(pt1.getX());
再介绍几个常用的东东
(把一个JavaBean的属性转换成Map集合)
static java.util.Map describe(java.lang.Object bean)
(把Map集合放到bean中)
static void populate(java.lang.Object bean,java.util.Map properties)
//java7 的新特性
//Map[] map = {name:"xxx",age:12};
//BeanUtils.setProperty(map, "name", "xxe");
PropertyUtils.setProperty(pt1,"x",9);
System.out.println(PropertyUtils.getProperty(pt1,"x").getClass().getName();
小总结一下:BeanUtils是以String类型进行操作.而PropertyUtils是以属性本身的类型进行操作
PropertyUtils不用类型转换.而想要用BeanUtils则是需要进行自动类型转换的.
第33节 了解和入门注解(annotation)的应用
JPA全称Java Persistence API.JPA通过JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。
(注解在java.lang包下)
@SuppressWarnings("deprecation")//忽略已经过时的方法
@Deprecated//标注:此方法过时了
@Override//判断是否覆盖父类方法
class AnnotationTest { @SuppressWarnings("deprecation")//忽略已经过时的方法 public static void main(String[] args) { System.runFinalizersOnExit(true);//此方法已经过时 } @Deprecated//标注:此方法过时了 public static void sayHello(){ System.out.println("hai,itheima"); } }
总结:注解就相当于一个标记,加了注解就相当于给程序打了某种标记.
标记可以加在包上,类上,成员变量,方法的参数上,可以加在成员变量等等
第34节 注解的定义与反射调用
注解就相当于一个源程序中要调用的一个雷,要在源程序中应用某个注解,就要先准备好这个猪街垒,
就想要调用某个类,要先开发好某个类一样.
注解应用结构图:
注解类----------------应用了注解类的类---------对"应用了注解类的类"进行反射操作的类
@interface A()-------- @A ClassB{}------------Class C{B.class if AnnontionPersent{A class};A a = B.classgetAnnontion{A.Class};}
注解在程序中的三个阶段:java源文件-->Java.class文件-->Java运行时字节码
Retention(RetentionPolicy....)表示注解运行到哪一个阶段
RetentionPolicy.SOURCE//编译阶段
RetentionPolicy.CLASS//class文件阶段(默认值)
RetentionPolicy.RUNTIME//运行时
@Override//编译阶段; @SuppressWarnings//编译阶段; @Deprecated //是字节码阶段,class
@Target({ElementType.METHOD,ElementType.TYPE})//Target,表示注解只能放置的位置,可以接受数组类型,其中METHOD方法TYPE,类
//注解第一步分类
@Retention(RetentionPolicy.RUNTIME)//元注解
@Target({ElementType.METHOD,ElementType.TYPE})//表示这个注解可以放在方法上,类上
public @interface ItCastAnnotation {
}
//补充:元注解,元数据,元信息.
//信息的信息为元信息,数据的数据为元数据,注解的注解为元注解
//注解第二部分类
@ItCastAnnotation
class AnnotationTest { /** * @param args */ @SuppressWarnings("deprecation")//忽略已经过时的方法 public static void main(String[] args) throws Exception{ System.runFinalizersOnExit(true);//此方法已经过时 //注解,第三部分检查这个类上是否有某些东西 if(AnnotationTest.class.isAnnotationPresent(ItCastAnnotation.class)) {//如果在,就返回true,进行下一步造作 ItCastAnnotation annotation = (ItCastAnnotation)AnnotationTest.class.getAnnotation(ItCastAnnotation.class); System.out.println(annotation); } } }
第35节 为注解增加各种属性
数组类型的属性
int[] arrayAttr() default {1,2,3};
@MyAnnotation(arrayAttr={2,3,4};
如果数组属性只有一个元素.这时候属性值部分可以省略大括号
枚举类型的属性
EnumTest.TrafficLamp lamp();
@MyAnnotation(lame = EnumTest.TrafficLamp.GREEN)
注解类型的属性:
MetaAnnotation annotationAttr()
default@MetaAnnotation("xxxx);
@MyAnnotation(annotationAttr=@MetaAnnotation("yyy"))
可以认为上面这个@MeAnnotation是MetaAnnotation类的一个实例对象.
用代码如下:
MetaAnnotation ma = myAnnotation.annotationAttr();
package cn.itcast.day2; //在注解中添加注解的那个类 public @interface MetaAnnotation { String value(); } //注解类 import cn.itcast.day1.EnumDemo; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; //注解第一步分类 @Retention(RetentionPolicy.RUNTIME)//元注解 @Target({ElementType.METHOD,ElementType.TYPE})//表示这个注解可以放在方法上,类上 public @interface ItcastAnnotation { String color() default "blue"; String value(); int[] arrayAttr() default {4,5,5}; EnumDemo.TrafficLamp lamp() default EnumDemo.TrafficLamp.RED; MetaAnnotation annotationAttr() default @MetaAnnotation("lhm"); } //调用注解类与输出注解类的内容 package cn.itcast.day2; //注解第二部分类 @ItcastAnnotation(annotationAttr=@MetaAnnotation("flx"),color="red",value = "abc",arrayAttr = {4,68}) class AnnotationTest { @SuppressWarnings("deprecation")//忽略已经过时的方法 @ItcastAnnotation("sst")//这里如果是就一个元素,那么此处可以省略=前面的值.另一种方法就是在注释类中其它属性定义default(缺省)属性. public static void main(String[] args) throws Exception{ System.runFinalizersOnExit(true);//此方法已经过时 //注解,第三部分检查这个类上是否有某些东西 if(AnnotationTest.class.isAnnotationPresent(ItcastAnnotation.class)) {//如果在,就返回true,进行下一步造作 ItcastAnnotation annotation = (ItcastAnnotation)AnnotationTest.class.getAnnotation(ItcastAnnotation.class); System.out.println(annotation.color()); System.out.println(annotation.value()); System.out.println(annotation.arrayAttr().length); System.out.println(annotation.lamp().nextLamp().name()); System.out.println(annotation.annotationAttr().value()); } } }
第36节 入门泛型的基本应用
//体验泛型 import java.util.ArrayList; class GenericTest { public static void main(String[] args) { //未利用泛型,编译时会出错,因为其中有不是Integer类型的元素 ArrayList collection1 = new ArrayList(); collection1.add(1); collection1.add(1L); collection1.add("abc"); int i = (Integer)collection1.get(1); //利用泛型的,只可以存入一种元素 ArrayList<String> collection2 = new ArrayList<String>(); //collection1.add(1); //collection1.add(1L); collection2.add("abc"); String element = collection2.get(1); } }
总结:没有定义泛型,任何类都可以存入到一个集合当中,如果定义泛型,那么只限定一种类型的元素存入到集合当中.
第37节 泛型的内部原理及更深应用
泛型,内部原理:是提供给javac编译器使用的,可以限定集合中的输入类型,让编译器挡住源程序中的非法输入
编译器编译带类型说明的集合时会除去掉"类型"信息,使原程序运行效率不受影响.
对于参数变化的反省类型,getClass()方法的返回值与原始类型完全一样.由于编译生成的字节码会去除掉泛型
的类型信息.只要能跳过编译器,就可以往某个反省集合中加入其它的数据,入股:用反射得到集合,再调用其add方法即可.
了解泛型:
ArrayList<E>类定义和ArrayList<Integer>类引用中设计如下术语:
整个称为ArrayList<E>泛型类型
ArrayList<E>中E称为类型变量或类型参数
整个ArrayList<Integer>称为参数化的类型
ArrayList<Integer>中的Integer称为类型参数的实例或实际类型参数
ArrayList<Integer>中的<>年做typeof
ArrayList称为原始类型
参数化类型与原始类型的兼容性:
参数化类型可以引用一个原始类型的对象,编译报告警告,例如:Collection<String> c = new Vectro();
原始类型可以引用一个参数化类型的对象,编译报告警告,例如:Collection c = new Vector<String();
参数化类型不考虑类型参数的继承关系:
Vector<String> v = new Vector<Object>();//错误
Vector<Object> v = new Vector<String>();//错误
在创建数组实例时,数组的元素不能用参数化的类型,例如,下面语句有错误;
Vector<Integer> vectorList[] = new Vector<Integer>[10];//这里会报错,不能用参数化的类型
注意 ,下面的代码不会报错.原因:按照编译器的思想一行一行来推理
Vertor v1 = new Vector<String>;
Vector<Object> v= v1;
第38节 泛型的通配符<?>扩展应用
使用?通配符可以引用其它各种参数实例化的类型,?通配符的定义的变量作用主要用作引用,
可以调用与参数化无关的方法,不能调用与参数化有关的方法.
限定通配符上边界:(此处,必须为Number或者Number的子类)
Vector<? extends Number> x = new Vector<Integer>();
限定通配符下边界:(此处,必须为Integer或者Integer的父类)
Vector<? super Integer> x = new Vector<Number>();
第39节 泛型的综合案例
HashMap<String,Integer> map = new HashMap<String,Integer>(); map.put("wanghao", 21); map.put("sunjie", 22); map.put("chenzhognyan", 23); Set<Map.Entry<String, Integer>> entrySet = map.entrySet(); for(Map.Entry<String, Integer> entry : entrySet) { System.out.println(entry.getKey() + ":" + entry.getValue()); }
对在jsp页面中也要经常对Set或Map集合进行迭代:
<c:forEach items="${map}" var="entry">
${entry.key}:${entry.value}
</c:forEach>
第40节 自定义泛型方法及其应用
定义方法:
private static <T> T add(T x,T y)
{
return null;
}
//利用泛型,进行位置互换
main:
add(3,5);//这里进行自动装箱了Integer
Number x1 = add(3.5,3);
Object x2 = add(3,"abc");
swap(new String[]{"abc","xyz","itcast"},1,2);
//swap(new int[]{1,3,5,4,5},3,4);//这里不能进行自动装箱
method:
//位置互换
private static <T> void swap(T[] a,int i,int j){
T tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
小注意!!
1,定义的泛型必须在修饰符后面,返回值类型前面,泛型通常为大写
2,注意!泛型<T>只能是引用类型,不能是基本类型.
3,除了在应用泛型使用extends限定符,在定义泛型时也可以使用extends限定符
例如:Class.getAnnotation()方法的定义,并且可以使用&来制定多个边界
如<V extends Serializable&coloneable>void mehtod(){}
4,普通,构造,静态方法中都可以使用泛型.
5,可以使用类型变量表示异常,可以用于方法的throws列表中,但是不能用于catch子句中.
如:private static<T extends Exception> sayHello throws T
{
try{
}
catch(Exception e)//这里不可以是catch(Exception e)
{
throw(T)e;
}
}
6,在反省中可以有多个类型参数,在定义它们的尖括号有逗号分隔
如:putlic static <K,V> V getValue(K key){retrun map.get(key);}
第41节 自定义泛型类型方法的练习与类型推断总结
练习1:编写一个泛型方法,自动将Object类型的对象转换成其它类型.
main:
//泛型自动类型转换
Object obj = "abc";
String x3 = autoConvert(obj);
method:
//自动将一个Object类型转换成任意类型
private static <T> T autoConvert(Object obj){
return (T)obj;
}
练习2:定义一个方法,可以将任意类型的数组中的所有元素填充为相应类型的某个对象.
method
private static <T> void fillArray(T[] t,T obj)
{
for(int i = 0;i<t.length;i++)
{
t[i] = obj;
}
}
练习3:打印任意类型的集合.
method:
public static <T> void printCollection2(Collection<T> collection){
//collection.add(1);
System.out.println(collection.size());
for(Object obj : collection){
System.out.println(obj);
}
}
练习4,5:定义一个方法,把任意参数类型的集合中的数据安全滴复制到相应类型的数组中(集合中)
//将集合拷贝到数组中
public static <T> void copy1(Collection<T> dest,T[] src){
}
//将集合拷贝到集合中
public static <T> void copy2(T[] dest,T[] src){
}
//复制,数组,集合
copy1(new Vector<String>(),new String[10]);
//复制,数组,集合
copy2(new Date[10],new String[10]);
//copy1(new Vector<Date>(),new String[10]);
类型总结:
1,某个类型变量只在整个参数列表中的所有返回值中的一处被调用,那么类型就为调用类型
2,如果多出相同类型调用,那么为调用类型
3,如果多出不同类型调用,那么为多出方法类型的最大交际.
4,如果多处类型调用对应了不同的类型,并使用返回值,这时候优先考虑返回值类型
5,参数类型有传递性,
第一种为Object,编译没问题;第二种:根据参数化的Vector类实例将类型变量直接确定为String类型,编译将会出现问题
copy(new Integer[5],new String[5])-->static <T> void copy(T[] a,T[] b);//编译会报错
第42节 自定义泛型类的应用
//dao的全称data access object()//数据访问接口,也就是说与数据库打交道的 -->crud.creat(创建一个)read(读)update(更新)delete(删除) package cn.itcast.day2; import java.util.Set; //Dao -->crud,creat,read,update,delete public class GenericDao<T> { //添加 public void add(T x) { } //读取 public T findId(int id) { return null; } //删除 public void delete(T obj) { } public void delete(int id) { } //更新 public void update(T obj) { } //登陆;按照姓名查询 public T findByUserName(String name) { return null; } //按照条件查询 public Set<T> findByConditions(String where) { return null; } } //GenericTest main: GenericDao<ReflectPoint> dao = new GenericDao<ReflectPoint>(); dao.add(new ReflectPoint(3,3)); //String s = dao.findById(1);//定义了泛型类型,所以返回的也必须是泛型类型.
静态的不能用泛型的类型,如果用的话,这么用
public static <E> void updata2(E obj){}
第43节 通过反射获得泛型的哈斯基类型参数
main: //字节码,方法,("applyVector",参数类型) Method applyMethod = GenericTest.class.getMethod("applyVector", Vector.class); //getGenericParameterTypes参数的泛型类型 Type[] types = applyMethod.getGenericParameterTypes(); //参数化的类型 ParameterizedType pType = (ParameterizedType)types[0]; //获取返回的实际类型 System.out.println(pType.getRawType()); System.out.println(pType.getActualTypeArguments()[0]); method: public static void applyVector(Vector<Date> v1){ }
第44节 类加载器及其委托机制的深入分析
类加载器:加载类的工具.
系统默认的三个主要类加载器.每个类负责加载特定位置的类.
BootStrap,ExtClassLoader,AppClassLoader
JRE/lib/rt.jar||JRE/lib/ext/*.jar||CLASSPATH制定目录的所有jar或目录
类加载器也是Java类,因为其他是java类的加载器本身也要被加载器加载,而第一个加载的不是Java类.是BootSrap[
当虚拟机要加载一个类时,到底排除哪个类加载器去加载呢?
1,首先当前线程的类加载器去加载线程中的第一个雷.
2,如果类A引用了类B,Java虚拟机将使用加载器A的类装载器来加载类B
3,还可以直接调用ClassLoader.loadClass()方法来制定某个加载器去加载某个类
委托机制:
每个类加载器加载类,又先委托给上级加载器.上级给上级
当所有祖宗类加载器都没有加载到类,会到发起者加载器,还加载不到则跑ClassNotFoundException.异常
而并不是去找儿子,因为没有getChild方法.即使有,多个也不知道找哪一个
自定义加载器的时候必须继承一个类:ClassLoader
public class ClassLoaderTest { public static void main(String[] args) { //输出ClassLoaderTest的类加载器 System.out.println(ClassLoaderTest.class.getClassLoader().getClass().getName()); //输出System的类加载器,返回空,并不代表没有,而是说加载器为Bootstrap(加载器的祖宗呀,^_-!,) System.out.println(System.class.getClassLoader()); } }
第45节 编写自己的类加载器
分析:
1,自定义加载器必须继承ClassLoader
2,loadClass方法为父类方法(去找父类加载器)findClass方法(自己的加载器的方法,只需要覆盖这货就哦啦)
3,defineClass(将得到的class文件转换成字节码)
package cn.itcast.day2; import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; public class MyClassLoader extends ClassLoader{ /** * @param args */ //加密部分 public static void main(String[] args) throws Exception { // TODO Auto-generated method stub String srcPath = args[0];//目标目录 String destDir = args[1];//源目录 FileInputStream fis = new FileInputStream(srcPath); String destFileName = srcPath.substring(srcPath.lastIndexOf('\\')+1); String destPath = destDir + "\\" + destFileName; FileOutputStream fos = new FileOutputStream(destPath); cypher(fis,fos); fis.close(); fos.close(); } private static void cypher(InputStream ips ,OutputStream ops) throws Exception{ int b = -1; while((b=ips.read())!=-1){ ops.write(b ^ 0xff); } } private String classDir; //解密部分 @Override protected Class<?> findClass(String name) throws ClassNotFoundException { // TODO Auto-generated method stub String classFileName = classDir + "\\" + name.substring(name.lastIndexOf('.')+1) + ".class"; try { FileInputStream fis = new FileInputStream(classFileName); ByteArrayOutputStream bos = new ByteArrayOutputStream(); cypher(fis,bos); fis.close(); System.out.println("aaa"); byte[] bytes = bos.toByteArray(); return defineClass(bytes, 0, bytes.length); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } public MyClassLoader(){ } public MyClassLoader(String classDir){ this.classDir = classDir; } } package cn.itcast.day2; //做加密用的,对此进行加密处理 //加密处理后放的class入到itcastlib文件夹下 import java.util.Date; public class ClassLoaderAttachment extends Date { public String toString() { return "hello,itcast"; } }
第48节 代理类与AOP概念
概念:
生活中的代理:就是常说的代理商,从厂商将商品卖给消费者,消费者不用很麻烦的到厂商在购买了。
程序中的代理:要为已经存在的多个具有相同接口的目标类的各个方法增加一些系统功能,如异常处理、日志、计算方法的运行时间、事物管理等等。
简单示例:编写一个与目标类具有相同接口的代理类,代理类的每个方法调用目标类的相同方法,并在调用方法时加上系统功能的代码,如:
目标类: 代理类:
class X{ Xproxy{
void sayHello(){ void sayHello(){
syso:Hello; startTime
} X. sayHello();
} endTime;}}
一般用接口来引用其子类,如:Collectioncoll = new ArrayList();
代理类的优点:
如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,在配置文件中配置是使用目标类还是代理类。这样以后很容易切换,如果想要日志功能时,就配置代理类,否则配置目标类,这样,增加系统功能很容易,以后运行一段时间后,更换系统也容易
import java.lang.reflect.*; import java.util.*; public class ProxyTest { public static void main(String[] args) throws Exception{ Class clazzProxy1 = //获取代理类Proxy的Class对象,传入的是类加载器和相应的字节码对象 Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class); System.out.println(clazzProxy1.getName());//$Proxy0 System.out.println("---begin constructor list------"); Constructor[] constructors = //获取代理类的构造方法,可能含有多个,得到数组 clazzProxy1.getConstructors(); for(Constructor constructor : constructors){ //遍历数组,获取每个构造方法 String name = constructor.getName(); //先得到构造方法的名字,并装入字符串容器中 StringBuilder sBul = new StringBuilder(name); sBul.append('('); Class[] clazzParams = //获取构造方法中的参数类型,并遍历 constructor.getParameterTypes(); for(Class clazzParam : clazzParams){ sBul.append(clazzParam.getName()).append(','); //将最后一个逗号去除 } if(clazzParams != null && clazzParams.length!=0) sBul.deleteCharAt(sBul.length()-1); sBul.append(')'); System.out.println(sBul.toString()); } System.out.println("---begin method list------"); Method[] methods = clazzProxy1.getMethods(); for(Method method : methods){ String name = method.getName(); StringBuilder sBul = new StringBuilder(name); sBul.append('('); Class[] clazzParams = method.getParameterTypes(); for(Class clazzParam : clazzParams){ sBul.append(clazzParam.getName()).append(','); } if(clazzParams!=null && clazzParams.length!=0) sBul.deleteCharAt(sBul.length()-1); sBul.append(')'); System.out.println(sBul.toString()); } }
AOP:
简述:AOP(Aspect Oriented Program)即面向方面的编程。
系统中存在着交叉业务,一个交叉业务就是要切入到系统中的一个方面
安全 事务 日志
StudentService ------|----------|------------|-------------
CourseService ------|----------|------------|-------------
MiscService ------|----------|------------|-------------
安全、事务、日志等功能要贯穿于好多个模块中,所以他们就是交叉业务。
交叉业务的编程问题即面向方面的编程(AOP),AOP的目标就是使交叉业务模块化,可以采用将切面代理移动到原始方法的周围,这与直接在方法中编写切面代理的过程效果是一样的,如图:
------------------------------------------------------切面
func1 func2 func3
{ { {
.... .... ......
} } }
------------------------------------------------------切面
因此使用代理技术正好可以解决这种问题,代理是实现AOP功能的核心和关键技术。
第49节 动态代理
概述:
1、要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,这时就不能采用静态代理方式,需用动态代理技术。
2、动态代理类:JVM可在运行时,动态生成类的字节码,这种动态(不是代理,只是拿出来作为代理类)生成的类往往被用作代理类,即动态代理类。
注:JVM生成的动态类必须实现一或多个接口,所以JVM生成的动态代理类只能用作具有相同接口的目标类代理。
3、CGLIB库可以动态生成一个类的子类,一个类的子类也可以作为该类的代理,所以,如果要为一个没有实现接口的类生成动态代理,那么可以使用CGLIB库。
4、代理类各个方法通常除了调用目标相应方法和对外返回目标返回的结果外,还可以再代理方法中的如下位置上加上系统功能代码:
1)在调用目标方法之前
2)在调用目标方法之后
3)在调用目标方法前后
4)在处理目标方法异常的catch块中。
第50节 完成InvocationHandler对象的内部功能
小补充:---
Proxy 提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。
创建某一接口 Foo 的代理
InvocationHandler handler = new MyInvocationHandler(...); Class proxyClass = Proxy.getProxyClass( Foo.class.getClassLoader(), new Class[] { Foo.class }); Foo f = (Foo) proxyClass. getConstructor(new Class[] { InvocationHandler.class }). newInstance(new Object[] { handler }); 或使用以下更简单的方法: Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(), new Class[] { Foo.class }, handler);
补充完毕----
完成InvocationHandler对象的内部功能.
总结:让jvm创建动态类及实例对象,需要给它提供那些信息?
三个方面:
1,生成的类中有哪些方法,通过让其实现哪些接口的方式进行告知
2,产生的类字节码必须有一个关联的加载器对象
3,生成的类中的方法的代码是怎么样的,也由得我们提供.把我们的代码卸载一个约号了的接口对象的方法中,
把对象传给他,它调用的我的方法,即相当于插入了我的代码.提供执行代码的对象,就是那个InvocationHandler对象.
它是在创建动态类的实例对象的构造方法时传递进去的.在上面的InvocationHandler对象的invoke方法中
加一点代码,就可以看到这些代码被调用运行了.
main: Collection proxy3 = (Collection)Proxy.newProxyInstance( Collection.class.getClassLoader(), new Class[]{Collection.class}, new InvocationHandler() { //指定一个目标 ArrayList target = new ArrayList(); public Object invoke(Object proxy, Method method, Object[] args)throws Throwable { //测试时间 long beginTime = System.currentTimeMillis(); Object retVal = method.invoke(target, args); long endTime = System.currentTimeMillis(); System.out.println(method.getName()+"running time of"+(endTime-beginTime)); return retVal; } }); proxy3.add("zxx"); proxy3.add("lhm"); proxy3.add("bxd"); System.out.println(proxy3.size()); }
第51节 分析InvocationHandler对象的运行原理
动态生成的类实现了Collection接口(可以实现若干接口),生成的类有Collection借口中的所有方法和一个如下接收InvocationHandler参数的构造方法。
构造方法接受一个InvocationHandler对象,接受对象了要干什么用呢?该方法内部的代码会是怎样的呢?
public $Proxy0(InvocationHandler handler) {
this.handler = handler;
}
实现的Collection接口中的各个方法的代码又是怎样的呢?
如:会调用代理的size方法
int size() {
return handler.invoke(this, this.getClass().getMethod(“size”), null)
}
我们生成的那些方法内部都是在调用invoke方法
InvocationHandler接口中定义的invoke方法接受的三个参数又是什么意思?
分析先前打印动态的实例对象时,结果为什么会是null呢?调用有基本类型返回值的方法时为什么会出现NullPointerException异常?
NullPointerException异常? class MyInvocationHandler1 implements InvocationHandler{ @Override public Object invoke(Object arg0, Method arg1, Object[] arg2)throws Throwable { // TODO Auto-generated method stub return null; } }
Collection proxy1 = (Collection)constructor.newInstance(new MyInvocationHandler1());
System.out.println(proxy1);
proxy1.clear();
proxy1.size();
这里,proxy1.size是要有返回类型int的,但是,调用InvocationHandler里面的invoke方法时返回为null因此会出错。对于无返回值的方法,如proxy1.clear(),invoke有一个返回值null,相当于无返回值void,这里才不会出错。
分析为什么动态类的实例对象的getClass()方法返回了正确结果呢?
调用代理对象的从Object类继承的hashCode, equals, 或toString这几个方法时,代理对象将调用请求转发给InvocationHandler对象,对于其他方法,则不转发调用请求。
第52节 总结分析动态代理类的设计原理与结构
1、怎样将目标传进去:
1)直接在InvocationHandler实现类中创建目标类的实例对象,可看运行效果和加入日志代码,但是毫无意义。
2)为InvocationHandler实现类注入目标的实例对象,不能采用匿名内部类的形式了。
3)让匿名内部类的InvocationHandler实现类访问外面的方法中的目标类实例对象的final类型的引用变量。
2、动态代理的工作原理:
1)Client(客户端)调用代理,代理的构造方法接受一个InvocationHandler,client调用代理的各个方法,代理的各个方法请求转发给刚才通过构造方法传入的handler对象,又把各请求分发给目标的相应的方法。就是将handler封装起来,其中this引用了当前的放(发来什么请求就接受哪个方法)。
示意图:
2)将创建代理的过程改为一种更优雅的方式,eclipse重构出一个getProxy方法绑定接受目标,同时返回代理对象,让调用者更懒惰,更方便,调用者甚至不用接触任何代理的API。
3、把系统功能代理模块化,即切面代码也改为通过参数形式提供,怎么把要执行的系统功能代码以参数的形式提供:
1)把要执行的代码装到一个对象的某个方法中,然后把此对象作为参数传递,接收者只要调用这个对象的方法,即等于执行了外接提供的代码。
2)为bind方法增加一个Advice参数。
示例:
//封装getProxy方法 package cn.itcast.test3; import java.lang.reflect.*; import java.util.*; public class MyProxy { public static void main(String[] args)throws Exception { final ArrayList target = new ArrayList(); //创建目标对象,并进行操作测试 Collection proxy = (Collection)getProxy(target,new MyAdvice()); proxy.add("sdf"); proxy.add("wgcd"); proxy.add("hgwe"); System.out.println(proxy.size()); } private static Object getProxy //作为一个通用的方法,就使用Object (final Object target,final Advice advice){ //传入一个目标,并传入一个接口, //此接口作为通信的契约,才能调用额外的方法 Object proxy = Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), //这里的接口要和target实现相同的接口 new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { advice.beforeMethod(method); //通过契约,使用其方法--before和after方法 Object value = method.invoke(target, args); advice.afterMethod(method); return value; } } ); return proxy; } } package cn.itcast.test3; //创建实现Advice接口的子类 import java.lang.reflect.Method; public class MyAdvice implements Advice { //实现Advice接口中方法的具体内容 long beginTime = 0; public void beforeMethod(Method method) { System.out.println("从这里开始"); beginTime = System.currentTimeMillis(); } public void afterMethod(Method method) { long endTime = System.currentTimeMillis(); System.out.println("从这里结束"); System.out.println(method.getName() + " run time of " + (endTime-beginTime)); } } import java.lang.reflect.Method; /*接口中需要实现四个方法 调用目标方法之前 调用目标方法之后 调用目标方法前后 在处理目标方法异常的catch块中 */ //这里只列出两个作为示例 public interface Advice { //创建接口Advice void beforeMethod(Method method); void afterMethod(Method method); }
第53节 编写而生成代理和插入通告的通用方法
ProxyTest.java main final ArrayList target = new ArrayList(); Collection proxy3 = (Collection)getProxy(target,new MyAdvice()); proxy3.add("zxx"); proxy3.add("lhm"); proxy3.add("bxd"); System.out.println(proxy3.size()); System.out.println(proxy3.getClass().getName()); } //做成通用的方法 private static Object getProxy(final Object target,final Advice advice) { Object proxy3 = Proxy.newProxyInstance( target.getClass().getClassLoader(), /*new Class[]{Collection.class},*/ target.getClass().getInterfaces(), new InvocationHandler(){ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { /*long beginTime = System.currentTimeMillis(); Object retVal = method.invoke(target, args); long endTime = System.currentTimeMillis(); System.out.println(method.getName() + " running time of " + (endTime - beginTime)); return retVal;*/ advice.beforeMethod(method); Object retVal = method.invoke(target, args); advice.afterMethod(method); return retVal; } } ); return proxy3; } } Advice.java package cn.itcast.day3; import java.lang.reflect.Method; public interface Advice { void beforeMethod(Method method); void afterMethod(Method method); }
第54节 实现类似spring的可配置的AOP框架
工厂类BeanFactor负责创建目标类或代理类的实例对象,并通过配置文件实现切换.
其getBean方法根据参数字符串返回一个相应的实例对象,如果参数字符串在配置文件中对应
的不是ProxyFactoryBean,则直接返回该类的实例对象,否则返回该类实例对象的getProxy方法返回的对象
BeanFactory的构造方法接受代表配置文件的输入对象,配置文件格式如下:
#xxx=java.util.ArrayList
xxx=cn.itcast.day3.aopframework.ProxyFactoryBean
xxx.advice=cn.itcast.day3.MyAdvice
xxx.target=java.util.ArrayList
ProxyFacotryBean充当封装生成动态代理的工厂,需要为工厂类提供的配置参数信息:1,目标;2,通知
第一个类BeanFactory:
package cn.itcast.day3.aopframework; import java.io.IOException; import java.io.InputStream; import java.util.Properties; import cn.itcast.day3.Advice; public class BeanFactory { Properties props = new Properties(); public BeanFactory(InputStream ips)//构造方法要接受一个配置文件. { try { props.load(ips); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public Object getBean(String name) { String className = props.getProperty(name); Object bean = null; try { //拿到class Class clazz = Class.forName(className); //创建一个bean对象,调用不带参数测构造方法,对于javabean必须要调用一个无参数的构造方法 bean = clazz.newInstance(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } //检查这个对象 if(bean instanceof ProxyFactoryBean)//如果是特殊的类,就创建一个代理 { Object proxy = null; ProxyFactoryBean proxyFactoryBean = (ProxyFactoryBean)bean; try { Advice advice = (Advice)Class.forName(props.getProperty(name + ".advice")).newInstance(); Object target = Class.forName(props.getProperty(name + ".target")).newInstance(); proxyFactoryBean.setAdvice(advice); proxyFactoryBean.setTarget(target); proxy = proxyFactoryBean.getProxy(); } catch (Exception e) { e.printStackTrace(); } return proxy; } //如果不是,就返回bean return bean; } }
第二个ProxyFactoryBean:
package cn.itcast.day3.aopframework; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import cn.itcast.day3.Advice; public class ProxyFactoryBean { //定义两个私有变量 private Advice advice; private Object target; public Advice getAdvice() { return advice; } public void setAdvice(Advice advice) { this.advice = advice; } public Object getTarget() { return target; } public void setTarget(Object target) { this.target = target; } public Object getProxy() { // TODO Auto-generated method stub Object proxy3 = Proxy.newProxyInstance( target.getClass().getClassLoader(), /*new Class[]{Collection.class},*/ target.getClass().getInterfaces(), new InvocationHandler(){ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { /*long beginTime = System.currentTimeMillis(); Object retVal = method.invoke(target, args); long endTime = System.currentTimeMillis(); System.out.println(method.getName() + " running time of " + (endTime - beginTime)); return retVal;*/ advice.beforeMethod(method); Object retVal = method.invoke(target, args); advice.afterMethod(method); return retVal; } } ); return proxy3; } }
第三个AopFrameworkTest测试的类:
//测试框架类 package cn.itcast.day3.aopframework; import java.io.InputStream; import java.util.Collection; public class AopFrameworkTest { /** * @param args */ public static void main(String[] args) throws Exception { //首先加载文件 InputStream ips = AopFrameworkTest.class.getResourceAsStream("config.properties"); //新建一个BeanFactory(配置文件) Object bean = new BeanFactory(ips).getBean("xxx"); System.out.println(bean.getClass().getName()); ((Collection)bean).clear(); } }
第四个config.properties配置文件:
#xxx=java.util.ArrayList
xxx=cn.itcast.day3.aopframework.ProxyFactoryBean
xxx.advice=cn.itcast.day3.MyAdvice
xxx.target=java.util.ArrayList