枚举
一、概述二、代码 1.用一个普通的类来描述枚举
package cn.enhance.day1;2.枚举类(由于定义在其他类中,所以此枚举类为内部类,所以该枚举类有4个访问权限可以设置)
public abstract class WeekDay1 {
private WeekDay1(){}
public final static WeekDay1 SUN=new WeekDay1(){
public WeekDay1 nextDay()
{
return MON;
}
};
public final static WeekDay1 MON=new WeekDay1(){
public WeekDay1 nextDay()
{
return SUN;
}
};
/*public WeekDay nextDay()
{
if(this==SUN)
{
return MON;
}
else
{
return SUN;
}
}*/
public abstract WeekDay1 nextDay();
public String toString()
{
return this==SUN?"SUN":"MON";
}
}
package cn.enhance.day1;3.带有抽象方法的枚举
public class EnumTest {
public static void main(String[] args) {
WeekDay1 weekDay=WeekDay1.MON;
System.out.println(weekDay.nextDay());
WeekDay weekDay2=WeekDay.FRI;
System.out.println(weekDay2);//自动调用toString()方法,FRI
System.out.println(weekDay2.name());//打印这个枚举对象的名字FRI
System.out.println(weekDay2.ordinal());//返回这个枚举对象在枚举类中的位置:5
System.out.println(WeekDay.valueOf("SUN"));//返回一个指定字符串的所表示的对象。
System.out.println(WeekDay.values().length);//返回一个WeekDay类型的数组。
}
public enum WeekDay{
SUN,MON,TUE,WED,TRI,FRI,SAT;//元素列表必须要放在最上面。并且如果对象列表后面还有代码,需要加上";"
}
}
public enum TrafficLamp{
RED(40)
{
public TrafficLamp nextLamp()
{
return GREEN;
}
},
GREEN(30)
{
public TrafficLamp nextLamp()
{
return YELLOW;
}
},
YELLOW(5)
{
public TrafficLamp nextLamp()
{
return RED;
}
};
public abstract TrafficLamp nextLamp();
private int time;
private TrafficLamp(int time){this.time=time}
}
4.带有构造方法的枚举类
public enum WeekDay
{
SUN(8),MON,TUE,WED,TRI,FRI,SAT;
private WeekDay(){System.out.println(1);}
private WeekDay(int x){System.out.println(x);}
}
Class类
一、概念1.Java程序中的各个Java类属于同一类事物,描述这类事物的Java类名就是Class。2.对比提问:众多的人用一个什么类表示?众多的Java类用一个什么类表示?人——>PersonJava类——>Class3.对比提问:Person类代表人,它的实例对象就是张三,李四这样一个个具体的人,Class类代表Java类,他的各个实例对象又分别对应什么呢?
4.如何得到各个字节码对应的实例对象(Class类型)Class cls1=字节码。字节码:当一个类需要被使用的时候,需要先将该类的二进制数据加载到内存中,那么,加载到内存中的数据就为字节码。①类名.class,例如,String.class②对象.getClass(),例如new Date().getClass()③Class.forName("类名"),例如,Class.forName("java.util.Date")//这个写程序比较常用,因为在写程序之前并不知道要使用该类,所以要使用该方法将该类的字节码加载到内存
5.九个预定义Class实例对象:八个基本数据类型和void类型的Class对象
二、代码
public class ReflectTest {
public static void main(String[] args) throws ClassNotFoundException {
String str1="abc";
Class cls1=str1.getClass();
Class cls2=String.class;
Class cls3=Class.forName("java.lang.String");
System.out.println(cls1==cls2);//返回true
System.out.println(cls1==cls3);//返回true
System.out.println(cls1.isPrimitive());//isPrimitive()方法是用于检测该类型是否是基本数据类型。由于cls1是String类型,所以返回false
System.out.println(int.class.isPrimitive());//int是基本数据类型,所以返回true
System.out.println(int.class==Integer.TYPE);//Integer类型提供了一个字段TYPE,用来表示基本数据类型int的Class实例,所以返回true
System.out.println(int[].class.isPrimitive());//数组也是一个类型,但是并不是基本数据类型,所以返回false
System.out.println(int[].class.isArray());//查看该Class实例是否是数组,返回true。
}
}
反射
一、概念反射就是把Java类中的各种成分映射成相应的java类二、构造方法的反射应用
import java.lang.reflect.Constructor;三、成员变量的反射应用1.定义一个类
import java.lang.reflect.InvocationTargetException;
public class ReflectConstructor {
public static void main(String[] args) throws Exception{
Constructor constructor1=String.class.getConstructor(StringBuffer.class);//根据String类中的方法的"参数"来传递给getConstructor方法,由于jdk1.5以后添加了可变参数的功能,所以可以任意添加参数个数,而jdk1.5以前,用的是数组来传递参数。
/*1*/String str1=new String(new StringBuffer("abc"));
/*2*/String str2=(String)constructor1.newInstance(new StringBuffer("abc"));
System.out.println(str1);
System.out.println(str2);//两个的输出结果一样,即1和2的功能相同
}
}
public class ReflectPoint {2.主函数
private int x;
public int y;
public ReflectPoint(int x, int y) {
super();
this.x = x;
this.y = y;
}
}
package cn.enhance.day1;3、练习题将任意一个对象中的所有String类型的成员变量所对应的字符串内容中的"b"改成"a"
import java.lang.reflect.Field;
public class ReflectVariable {
public static void main(String[] args) throws Exception{
ReflectPoint pt1=new ReflectPoint(3,5);
ReflectPoint pt2=new ReflectPoint(3,4);
Field fieldY=pt1.getClass().getField("y");
int y=(int)fieldY.get(pt1);
int y1=(int)fieldY.get(pt2);
System.out.println(y);//返回5
System.out.println(y1);//返回4
Field fieldX=pt1.getClass().getField("x");//会报错。因为找不到x变量,因为x是私有的
int x=(int)fieldX.get(pt1);
Field fieldX1=pt1.getClass().getDeclaredField("x");
int x1=(int)fieldX1.get(pt1);//会报错。虽然可以发现x变量,但是因为私有,还是无法读取数据
Field fieldX2=pt1.getClass().getDeclaredField("x");
fieldX2.setAccessible(true);//设置该Field类型的对象中私有变量可以访问
int x2=(int)fieldX1.get(pt1);//可以读出数据(暴力反射)
}
}
private static void changeStringValue(Object obj) throws Exception{
Field[] fieldS=ReflectPoint.class.getFields();
for(Field field:fieldS)
{
if(field.getType()==String.class)
{
String oldValue=(String)field.get(obj);
String newValue=oldValue.replace('b', 'a');
field.set(obj, newValue);
}
}
}
四、成员方法的反射1.代码
import java.lang.reflect.Method;2.对接收数组参数的成员方法进行调用
public class ReflectMethod {
public static void main(String[] args) throws Exception{
String str1="wqz";
Method methodCharAt=String.class.getMethod("charAt",int.class);
char c=(char)methodCharAt.invoke(str1, 1);
//char c1=(char)methodCharAt.invoke(null, 1);//代表静态方法。
char c2=(char)methodCharAt.invoke(str1,new Object[]{2});//为了兼容jdk1.4,采用数组来传递参数
System.out.println(c);
//System.out.println(c1);
System.out.println(c2);
}
}
①目标写一个程序,这个程序能够根据用户提供的类名,去执行所给出类名中的main方法。需要明白:为什么要用反射的方式去调用main方法。②代码
ReflectMethodArrays.main(new String[]{"111","222","333"});//传统方法五、数组的反射
Method mainMethod=Class.forName(args[0]).getMethod("main", String[].class);
mainMethod.invoke(null,new Object[]{new String[]{"111","222","333"}});//正确的反射方法1
mainMethod.invoke(null,(Object)new String[]{"111","222","333"});//正确的反射方法2
//mainMethod.invoke(null,new String[]{"111","222","333"});//错误的反射方法。因为为了兼容jdk1.4,String[]数组被虚拟机当做一个Obeject[]数组,所以讲字符串数组打开后当做了3个参数传递给所调用的方法,所以会报参数个数异常。
import java.util.Arrays;应用:如果是一个数组,则打印所有元素,如果是单个对象,则打印该对象。
public class ReflectArray {
public static void main(String[] args) {
int[] a1=new int[]{1,2,3};
int[] a2=new int[4];
int[][] a3=new int[2][3];
String[] a4=new String[]{"a","b","c"};
System.out.println(a1.getClass()==a2.getClass());//返回true
//System.out.println(a1.getClass()==a4.getClass());//返回false
//System.out.println(a1.getClass() == a3.getClass());//返回false
System.out.println(a1.getClass().getName());//返回 [I
System.out.println(a1.getClass().getSuperclass().getName());//返回 java.lang.Object
System.out.println(a4.getClass().getSuperclass().getName());//返回 java.lang.Object
Object obj1=a1;
Object obj2=a4;
//Object[] obj3=a1;//会编译失败,因为int类型(基本数据类型)不是Object的子类。
Object[] obj4=a3;
Object[] obj5=a4;
System.out.println(a1);//返回 [I@15bdc50
System.out.println(a4);//返回 [Ljava.lang.String;@1dd3812
System.out.println(Arrays.asList(a1));//返回 [[I@15bdc50]。
System.out.println(Arrays.asList(a4));//返回 [a, b, c].因为兼容了jdk1.4(该版本没有可变参数),当向asList()方法中传递一个Object对象的数组时,会调用jdk1.4的方法,则会返回整个数组的内容
}
}
private static void printObject(Object obj) {
Class clazz=obj.getClass();
if(clazz.isArray())
{
int len=Array.getLength(obj);
for(int i=0;i<len;i++)
{
System.out.println(Array.get(obj,i));
}
}
else
{
System.out.println(obj);
}
}
反射的作用——框架
一、概述我们使用别人的类有两种方式:1.我们调用别人的类,这叫做工具。2.别人来调用我们的类,这叫做框架。二、一个简单的框架例子1.首先在选定的工程下创建一个名为config.properties的文件,并在该文件内输入以下键值对className=java.util.ArrayList(或者java.util.HashSet)2.在某个类中获取该键值对public class ReflectTest2 {3.ReflectPoint类
public static void main(String[] args) throws Exception{
InputStream is=new FileInputStream("config.properties");
Properties props=new Properties();
props.load(is);
is.close();
String className=props.getProperty("className");
Collection collections=(Collection)Class.forName(className).newInstance();
//Collection collections=new HashSet();
ReflectPoint pt1=new ReflectPoint(3,3);
ReflectPoint pt2=new ReflectPoint(5,5);
ReflectPoint pt3=new ReflectPoint(3,3);
collections.add(pt1);
collections.add(pt2);
collections.add(pt3);
collections.add(pt1);
System.out.println(collections.size());
}
}
package itcast.enhance.day1;
public class ReflectPoint {
private int x;
public int y;
public ReflectPoint(int x, int y) {
super();
this.x = x;
this.y = y;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 6;
result = prime * result + x;
result = prime * result + y;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ReflectPoint other = (ReflectPoint) obj;
if (x != other.x)
return false;
if (y != other.y)
return false;
return true;
}
}
用类加载器的方式管理资源和配置文件
一、概述一个程序的配置文件(例如框架)一般都放在指定的classpath目录下如果放在classpath目下,那么上一节中的读取配置文件的方式可以改为类加载器的方式://InputStream is=new FileInputStream("config.properties");
/*①*/InputStream is=ReflectTest2.class.getClassLoader().getResourceAsStream("itcast/enhance/day1/config.properties");
/*②*/InputStream is1=ReflectTest2.class.getResourceAsStream("config.properties");
由内省引出JavaBean
一、概述内省的英文:IntroSpector内省的作用就是对JavaBean进行操作。JavaBean就是一个特殊的Java类,主要用于传递数据信息,这种java类中的方法主要用于访问私有的字段,且里面的方法要按照某种规则来命名,例如:
class Person将javabean中方法的get和set去掉,那么剩下的就是变量名字。1.如果第二个字母是小的,则把第一个字母编程小的,比如:Age——>age2.如果第二个字母是大的,则首字母保持原样,比如:CPU——>CPU
{
private int age;
public int getAge()
{
return age;
}
public void setAge(int age)
{
this.age=age;
}
}
对JavaBean内省操作
一、概述如果要在两个模块之间传递多个信息,可以将这些信息封装到一个JavaBean中,这种JavaBean的实例对象通常称之为值对象(ValueObject,简称VO)。二、代码package itcast.enhance.day1;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class IntroSpector
{
public static void main(String[] args)throws Exception
{
ReflectPoint1 pt1=new ReflectPoint1(3,5);
String propertyName="x";
Object retVal = getProperty(pt1, propertyName);
System.out.println(retVal);
Object value=7;
setProperty(pt1, propertyName,value);
System.out.println(pt1.getX());
}
private static void setProperty(Object pt1, String propertyName,Object value)throws IntrospectionException, IllegalAccessException,InvocationTargetException
{
PropertyDescriptor pd=new PropertyDescriptor(propertyName,pt1.getClass());
Method methodSet=pd.getWriteMethod();
methodSet.invoke(pt1,value);
}
private static Object getProperty(Object pt1, String propertyName)throws IntrospectionException, IllegalAccessException,InvocationTargetException
{
/*PropertyDescriptor pd=new PropertyDescriptor(propertyName,pt1.getClass());
Method methodGet=pd.getReadMethod();
Object retVal=methodGet.invoke(pt1);
//注释部分为简单的内省操作,注释外为复杂的内省操作。
*/
BeanInfo bi=Introspector.getBeanInfo(pt1.getClass());
PropertyDescriptor[] pd=bi.getPropertyDescriptors();
Object retVal=null;
for(PropertyDescriptor pd1:pd)
{
if(pd1.getName().equals(propertyName))
{
Method methodGet=pd1.getReadMethod();
retVal=methodGet.invoke(pt1);
break;
}
}
return retVal;
}
}
使用BeanUtils工具包操作JavaBean
一、步骤1.在工程下创建一个文件夹“lib",并将BeanUtils和logging的jar包复制到该文件夹下。2.将两个jar包选中,鼠标右键,选择”build path“——>add to build path二、代码package itcast.enhance.day1;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import org.apache.commons.beanutils.BeanUtils;
public class IntroSpectorBU {
public static void main(String[] args) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException
{
ReflectPoint1 pt1=new ReflectPoint1(10,20);
//①使用BeanUtils工具包设置pt1对象的x成员变量为18
BeanUtils.setProperty(pt1, "x", "18");
//②使用BeanUtils工具包获取pt1对象的x成员变量的值,返回的值的类型为String
String str1=BeanUtils.getProperty(pt1, "x");
System.out.println(str1);
//③在ReflectPoint1类中定义一个Date对象,并使用BeanUtils工具包设置birthday对象内的time成员变量为111(因为Date类中有一个setTime方法)
BeanUtils.setProperty(pt1,"birthday.time","111");
//④获取birthday.time的值。说明BeanUtils还支持级联操作。
String str2=BeanUtils.getProperty(pt1, "birthday.time");
System.out.println(str2);
//BeanUtils工具包还有其他功能,比如可以使javabean和Map集合进行转换,详情参加帮助文档
//PropertyUtils是对属性的类型进行操作,而不是转换成String类型,如果不希望对类型进行转换,可以使用PropertyUtils类。
}
}
了解注解——入门注解的应用
一、概述注解相当于一个标记,加了注解就等于为程序打上了某种标记,没加,则等于没有某种标记。以后,javac编译器,开发工具和其他程序就可以用反射来了解你的类几各种元素上有无何种标记,看你有什么标记,就去干相应的事情。标记可以加在包、类、字段、成员变量、方法、方法的参数以及局部变量上。注解annotation放在java.lang包中。注解属于JDK1.5新特性。二、代码package itcast.enhance.day2;
public class AnnotationTest {
@SuppressWarnings("deprecation")//注解是用于给编译器传达信息,一个注解就是一个类
public static void main(String[] args) {
System.runFinalizersOnExit(true);
}
@Deprecated//如果不希望别人再使用这个方法,那么在这个方法上加入这个注解,已经使用过的人可以继续使用,如果没使用的人第一次使用则会提示这个方法已经过时,不建议使用
public static void sayHello()
{
System.out.println("sai hello");
}
@Override//用于检测该方法是否重写了父类,如果不是,则会报错。
public boolean equals(AnnotationTest t)//正确写法是Object obj
{
}
}
注解的定义——反射的调用
一、代码1.被添加自定义注解的类package itcast.enhance.day2;2.自定义注解
@ItcastAnnotation
public class AnnotationTest {
public static void main(String[] args) {
if(AnnotationTest.class.isAnnotationPresent(ItcastAnnotation.class))//利用反射查看AnnotationTest这个类有没有ItcastAnnotation注解
{
ItcastAnnotation annotation=(ItcastAnnotation)AnnotationTest.class.getAnnotation(ItcastAnnotation.class);
}
}
}
package itcast.enhance.day2;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
//元注解,用于标注这个注解的存在与哪个周期,他有三个可选项,
//1.RetetionPolicy.SOURCE java源文件
//2.RetetionPolicy.ClASS class文件
//3.RetetionPolicy.RUNTIME 内存中的字节码
//@Override 源文件阶段
//@SuppressWarmings 源文件阶段
//@Deprecated 内存中的字节码
@Target({ElementType.METHOD,ElementType.TYPE})//TYPE是Class这个类的父接口,TYPE用于描述一类事物,比如接口、Class等等。
//Target这个元注解用于标明这个注解可以放在什么位置上,比如方法上,类上等等
public @interface ItcastAnnotation {
}
为注解增加各种属性
一、代码1.被添加注解的类package itcast.enhance.day2;2.注解类①
@ItcastAnnotation(color="red",value="abc",arrayAttr={1,2,3},annotationAttr=@InnerAnnotation("flx"))
public class AnnotationTest {
public static void main(String[] args) {
if(AnnotationTest.class.isAnnotationPresent(ItcastAnnotation.class))
{
ItcastAnnotation annotation=(ItcastAnnotation)AnnotationTest.class.getAnnotation(ItcastAnnotation.class);
System.out.println(annotation.arrayAttr().length);//输出3
System.out.println(annotation.value());//输出abc
System.out.println(annotation.color());//输出red
System.out.println(annotation.day());//输出MON
System.out.println(annotation.annotationAttr().value());//返回flx
}
}
}
package itcast.enhance.day2;3.注解类②
import itcast.enhance.day1.EnumTest;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
public @interface ItcastAnnotation {
String color() default "blue";
String value();//注解中的————String类型的属性
int[] arrayAttr() default {3,4,5};//注解中的————数组类型的属性
EnumTest.WeekDay day() default EnumTest.WeekDay.MON ;//注解中的————枚举类型的属性
InnerAnnotation annotationAttr() default @InnerAnnotation("haha");//注解中的————注解类型的属性
}
package itcast.enhance.day2;4.枚举类
public @interface InnerAnnotation {
String value();
}
package itcast.enhance.day1;
public class EnumTest
{
public static void main(String[] args)
{
WeekDay weekDay2=WeekDay.FRI;
System.out.println(weekDay2);
System.out.println(weekDay2.name());
System.out.println(weekDay2.ordinal());
System.out.println(WeekDay.valueOf("SUN"));
System.out.println(WeekDay.values().length);
}
public enum WeekDay
{
SUN(8),MON,TUE,WED,TRI,FRI,SAT;
private WeekDay(){System.out.println(1);}
private WeekDay(int x){System.out.println(x);}
}
}
类加载器及其委托机制的深入分析
一、类加载器概述1.Java虚拟机中可以安装多个类加载器,系统默认三个主要的类加载器,每个类加载特定位置的类:BootStrap,ExtClassLoader,AppClassLoader2.类加载器也是Java类,因为其他是java类的类加载器本身也要被类加载器加载,显然必须有一个累加器不是java类,这正是BootStrap3.Java虚拟机中的所有类加载器采用具有父子关系的树形结构进行组织,在实例化每个类装载器对象时,需要为其制定一个父级类装载器对象或者默认采用系统类装载器为其父级类加载。二、类加载器的委托机制1.当java虚拟机要加载一个类时,到底派哪个类加载器去加载呢?①首先当前线程的类加载器去加载线程中的第一个类。②如果类A引用了类B,Java虚拟机将使用加载类A的类加载器来加载类B③还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器加载某个类。2.每个类加载器加载类时,又先委托给其上级类加载器。①当所有祖宗类加载器没有加载到类时,回到发起者类加载器,还加载不了,则会抛ClassNotFoundException,不是再去找发起者类加载器的儿子,因为没有getChild方法,即使有,如果有多个儿子,也无法明确找哪个儿子②对着类加载器的层次结构图和委托加载原理,解释先前将ClassLoadTest打成jar包放到jre/lib/ext目目下会输出ExtClassLoader的原因。
三、类加载器之间的父子关系和管辖范围图
四、代码
package itcast.enhance.day2;
public class ClassLoadTest {
public static void main(String[] args)
{
System.out.println(ClassLoadTest.class.getClassLoader().getClass().getName());
//①会输出sun.misc.Launcher$AppClassLoader
//②如果将ClassLoadTest类打成jar包放到JRE/lib/ext文件下则会输出sun.misc.Launcher$ExtClassLoader
System.out.println(System.class.getClassLoader());
//会输出null,说明System类是由BootStrap加载器来加载的
ClassLoader loader=ClassLoadTest.class.getClassLoader();
while(loader!= null)
{
System.out.println(loader.getClass().getName());
loader=loader.getParent();
}
System.out.println(loader);
}
}
自定义类加载器
一、预备知识1.自定义的类加载器必须继承ClassLoader。2.不需要重写父类的loadClass方法,因为load方法已经把调用父类去查找类的方法写好了。3.所以,只需要重写findClass(),即重写子类需要完成的工作——这叫做模板设计模式4.当找到相应名字的类时,使用defineClass方法将.class文件转换成Class对象,加载进内存。二、代码1.自定义类加载器
package itcast.enhance.day2;2.任意类调用这个自定义类加载器
import java.io.*;
public class MyClassLoader extends ClassLoader {
public static void main(String[] args) throws IOException {
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 IOException//将文件进行简单加密和解密
{
int b;
while((b=ips.read())!=-1)
{
ops.write(b^0xff);
}
}
private String classDir;
@SuppressWarnings("deprecation")
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException //重写父类的findClass方法
{
String classFileName=classDir+"\\" + name + ".class";
try
{
FileInputStream fis=new FileInputStream(classFileName);
ByteArrayOutputStream bos=new ByteArrayOutputStream();
cypher(fis,bos);
fis.close();
byte[] bytes=bos.toByteArray();
return defineClass(bytes,0,bytes.length);//使用fefineClass方法将.class文件加载成字节码
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
public MyClassLoader(){}
public MyClassLoader(String classDir)//指定要加载的目录
{
this.classDir=classDir;
}
}
package itcast.enhance.day2;
import java.util.Date;
public class ClassLoadTest {
public static void main(String[] args) throws Exception
{
Class clazz =new MyClassLoader("mylib").loadClass("ClassLoaderTest");
Date d1=(Date)clazz.newInstance();
System.out.println(d1);
}
}
分析代理类的作用与原理及AOP
一、概述要为已经存在的多个具有相同接口的目标类的各个方法增加一些系统功能,例如,异常处理、日志、计算方法的运行时间、事务管理等等,你准备怎样做?
1.编写一个与目标类相同接口的代理类,代理类的每个方法调用目标类的相同方法,并在调用方法时加上系统功能的代码。2.如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,在配置文件中,不管配置是使用目标类、还是代理类,这样以后都很容易切换。譬如,想要日志功能时就配置代理类,否则配置目标类,这样,增加系统功能很容易,以后运行一段时间后,又想去掉系统功能也很容易。
二、代理架构图
三、AOP(面向方面的编程)——Aspect Oriented Program
安全、事物、日志等功能要贯穿到好多个模块中,所以,他们就是交叉业务。
四、动态代理技术
1.要为系统中的各种接口的类增加代理功能,那将需要田铎的代理类,全部采用静态代理方式,将是一件非常麻烦的事情,而用动态代理技术就可以解决这个问题。2.JVM可以再运行期间动态生成类的字节码,这种动态生成的类往往被用作代理类,即动态代理类3.JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理。如果要为一个没有实现接口的类生成动态代理类,那就需要使用CGLIB库。4.CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理。5.代理类的各个方法中除了要调用目标的相应方法和对外返回目标返回的结果外,还可以在代理方法中的如下四个位置加上系统功能代码:①在调用目标方法之前。②在调用目标方法之后。③在调用目标方法前后。④在处理目标方法异常的catch块中。
创建动态类及查看其方法列表信息
一、概述通过java虚拟的api生成动态类——java.lang.reflect包下的Proxy类通过使用Proxy类下的静态方法getProxyClass()获得指定接口的动态类static Class<?> getProxyClass(ClassLoader loader,Class<?>... interfaces)
二、代码
package itcast.enhance.day3;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Collection;
public class ProxyTest {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Class clazzProxy1=Proxy.getProxyClass(Collection.class.getClassLoader(),Collection.class);
System.out.println(clazzProxy1.getName());
System.out.println("——————————————Constructor————————————————");
Constructor[] constructors=clazzProxy1.getConstructors();
for(Constructor c:constructors)
{
String name=c.getName();
StringBuilder sb=new StringBuilder(name);
sb.append('(');
Class[] cc=c.getParameterTypes();
for(Class cs:cc)
{
sb.append(cs.getName());
sb.append(',');
}
if(cc.length>0){
sb.deleteCharAt(sb.length()-1);}
sb.append(')');
System.out.println(sb.toString());
}
System.out.println("——————————————Method————————————————");
Method[] methods=clazzProxy1.getMethods();
for(Method c:methods)
{
String name=c.getName();
StringBuilder sb=new StringBuilder(name);
sb.append('(');
Class[] cc=c.getParameterTypes();
for(Class cs:cc)
{
sb.append(cs.getName());
sb.append(',');
}
if(cc.length>0){
sb.deleteCharAt(sb.length()-1);}
sb.append(')');
System.out.println(sb.toString());
}
}
}
创建动态类的实例对象及其调用方法
一、代码package itcast.enhance.day3;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.Collection;
public class ProxyTest {
public static void main(String[] args) throws Exception
{
class MyInvocationHandler1 implements InvocationHandler
{
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
return null;
}
}
Collection proxy1=(Collection)constructor.newInstance(new MyInvocationHandler1());
System.out.println(proxy1.toString());
proxy1.clear();
//proxy1.size();会报空指针异常。具体原因见下一节
}
}
完成InvocationHandler对象的内部功能
一、代码package itcast.enhance.day3;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;
public class ProxyTest {
public static void main(String[] args) throws Exception
{
Collection proxy3=(Collection)Proxy.newProxyInstance(
Collection.class.getClassLoader(),
new Class[]{Collection.class},
new InvocationHandler()
{
ArrayList target=new ArrayList();
@Override
public Object invoke(Object proxy, Method method,Object[] args) throws Throwable {
long begintime=System.currentTimeMillis();
Object obj=method.invoke(target, args);
long endtime=System.currentTimeMillis();
System.out.println(method.getName()+"run time"+(endtime-begintime));
return obj;
}
});
proxy3.add("zxx");
proxy3.add("wqz");
proxy3.add("bxd");
System.out.println(proxy3.size());
}
}
分析InvocationHandler对象的运行原理与结构
一、概述通过JVM内部的API——Proxy生成的动态类对象,每次调用该对象的相应方法时,都会调用构造方法传递进去的InvocationHandler这个对象的invoke方法,该方法涉及到三个参数——①动态类对象,②调用的方法③所调用方法的参数,即invoke(Object proxy,Method method,Object[] args)二、动态代理的工作原理
三、代码
package itcast.enhance.day3;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;
public class ProxyTest {
public static void main(String[] args) throws Exception
{
Collection proxy3=(Collection)Proxy.newProxyInstance(
Collection.class.getClassLoader(),
new Class[]{Collection.class},
new InvocationHandler()
{
ArrayList target=new ArrayList();//目标类对象
@Override
public Object invoke(Object proxy, Method method,Object[] args) throws Throwable {
long begintime=System.currentTimeMillis();//代理类做的额外操作
Object obj=method.invoke(target, args);
long endtime=System.currentTimeMillis();//代理类做的额外操作
System.out.println(method.getName()+"run time"+(endtime-begintime));//代理类做的额外操作
return obj;
}
});
proxy3.add("zxx");
proxy3.add("wqz");
proxy3.add("bxd");
System.out.println(proxy3.size());//在上上节中调用size()方法会报错的原因是————InvocationHandler对象的nvoke方法直接返回null,而size()方法需要返回int,所以会报空指针异常。
//以上调用方法的过程为————首先调用InvocationHandler对象的invoke方法,add或size()方法被反射成了一个对象method,
//通过调用该mehtod方法来使用目标类的方法来处理,并返回一个结果,该结果又被invoke方法返回
}
}
编写可生成代理和插入通告的通用方法
一、概述将上一节中的代码做成一个框架,需要做三件事情:①.将生成代理对象的代码封装成一个方法。②.将目标类对象作为一个参数传递给①生成的方法。③将代理对象需要进行的额外操作(比如安全、事务、日志)等封装成一个接口,并将该接口生成的对象传递作为一个参数传递给①注意:1.代理对象需要进行的额外操作,需要先封装成一个接口,然后由实现该接口的类生成对象来完成,并且该接口以“Advice”为开头进行命名(例如Spring)二、代码1.主函数
package itcast.enhance.day3;2.在主函数内进行封装的——生成代理类实例对象的方法
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;
public class ProxyTest {
public static void main(String[] args) throws Exception
{
final ArrayList target=new ArrayList();//方法内的内部类,如果要访问局部变量需要加上final
Collection proxy3=(Collection)getProxyInstance(target,new MyAdvice());
proxy3.add("zxx");
proxy3.add("wqz");
proxy3.add("bxd");
System.out.println(proxy3.size());
}
}
public static Object getProxyInstance(final Object target,final Advice advice)3.Advice接口
{
Object proxy3=Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler()
{
@Override
public Object invoke(Object proxy, Method method,Object[] args) throws Throwable {
advice.beforeMethod(method);
Object obj=method.invoke(target, args);
advice.afterMethod(method);
return obj;
}
});
return proxy3;
}
package itcast.enhance.day3;4.Advice接口实现类——MyAdvice
import java.lang.reflect.Method;
public interface Advice {
void beforeMethod(Method method);//可以会调用InvocationHandler对象的invoke方法的三个参数,例如spring
void afterMethod(Method method);
}
package itcast.enhance.day3;
import java.lang.reflect.Method;
public class MyAdvice implements Advice {
long beginTime;
long endTime;
@Override
public void beforeMethod(Method method) {
System.out.println("来学习了");
beginTime=System.currentTimeMillis();
}
@Override
public void afterMethod(Method method) {
System.out.println("终于要上班了!");
endTime=System.currentTimeMillis();
System.out.println(method.getName()+"..run time.."+(endTime-beginTime));
}
}
实现类似Spring的、可配置的AOP框架
一、代码1.BeanFactory (用来判断所生成的JavaBean是否为代理类,如果是,通过ProxyFactoryBean返回代理对象;如果不是,直接返回配置文件中指定类的对象。package itcast.enhance.day3.AOP;2.ProxyFactroyBean
import itcast.enhance.day3.Advice;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
public class BeanFactory {
Properties p=new Properties();
public BeanFactory(InputStream ips)
{
try
{
p.load(ips);
} catch (IOException e)
{
e.printStackTrace();
}
}
public Object getBean(String name)
{
String className=p.getProperty(name);
Object bean=null;
try
{
Class clazz=Class.forName(className);
bean=clazz.newInstance();
}
catch (Exception e) {
e.printStackTrace();
}
if(bean instanceof ProxyFactoryBean)
{
Object proxy=null;
ProxyFactoryBean proxyFactoryBean=(ProxyFactoryBean)bean;
try {
Advice advice=(Advice)Class.forName(p.getProperty(name+".advice")).newInstance();
Object target=Class.forName(p.getProperty(name+".advice")).newInstance();
proxyFactoryBean.setAdvice(advice);
proxyFactoryBean.setTarget(target);
proxy=proxyFactoryBean.getProxyInstance();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return proxy;
}
return bean;
}
}
package itcast.enhance.day3.AOP;3.主函数
import itcast.enhance.day3.Advice;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyFactoryBean {
private Object target;
private Advice advice;
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
public Advice getAdvice() {
return advice;
}
public void setAdvice(Advice advice) {
this.advice = advice;
}
public Object getProxyInstance()
{
Object proxy3=Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler()
{
@Override
public Object invoke(Object proxy, Method method,Object[] args) throws Throwable {
advice.beforeMethod(method);
Object obj=method.invoke(target, args);
advice.afterMethod(method);
return obj;
}
});
return proxy3;
}
}
package itcast.enhance.day3.AOP;
import java.io.InputStream;
public class AOPFrameworkTest {
public static void main(String[] args) throws Exception{
InputStream ips=AOPFrameworkTest.class.getResourceAsStream("config.properties");
Object bean=new BeanFactory(ips).getBean("xxx");
System.out.println(bean.getClass().getName());
}
}