注解:
注解相当于一种标记,在程序中加了注解就等于为程序打上了某种标记,
没加,则等于没有某种标记,以后,javac编译器,开发工具和其他程序
可以用反射来了解你的类及各种元素上有无何种标记,看你有什么标记,
就去干相应的事。
注意:标记可以加在包,类,字段,方法,方法的参数以及局部变量上,还可以加在注解上,这种注解为元注解。
@SuppressWarnings("deprecation") //过时不提醒注解。
//指示应该在注释元素(以及包含在该注释元素中的所有程序元素)中取消显示指定的编译器警告。
@Deprecated
//注释的程序元素,不鼓励程序员使用这样的元素,通常是因为它很危险或存在更好的选择。
//在使用不被赞成的程序元素或在不被赞成的代码中执行重写时,编译器会发出警告。
@Override
//表示一个方法声明打算重写超类中的另一个方法声明。如果方法利用此注释类型进行
//注解但没有重写超类方法,则编译器会生成一条错误消息。
注解的应用结构:
注解类:
@interface A
{
}
应用了“注解类”的类
@A
Class B
{
}
对“应用了注解类的类”进行反射操作的类
Class C
{//检测注解在不在。返回true,即存在
B.class.isAnnotionPresent(A.class);
//得到该注解。如果存在该元素的指定类型的注释,则返回这些注释,否则返回 null。
A a = (A)B.class.getAnnotion(A.class);//得到指定注解
}
注解的生命周期:
RetetionPolicy.SOURCE、RetetionPolicy.CLASS、RetetionPolicy.RUNTIME;
分别对应:java源文件-->class文件-->内存中的
自定义注解:
//导包
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
@Retention(RetentionPolicy.RUNTIME)//定义在注解上的注解:元注解,这个是生命周期注解。
@Target({ElementType.METHOD,ElementType.TYPE})//方法,和类上可以定义此注解,如果只有一个元素,就可以省略{}。
//指示注释类型所适用的程序元素的种类。
//如果注释类型声明中不存在 Target 元注释,则声明的类型可以用在任一程序元素上。
//如果存在这样的元注释,则编译器强制实施指定的使用限制。
@interface ItcastAnnotation//自定义注解
{
//为注解增加各种属性。
String color() default "blue";
String value();//如果只有value值设定值,可以直接设置
int[] arrayAttr() default {3,4,4};//数组属性
TrafficLamp lamp() default TrafficLamp.RED;//枚举属性
MetaAnnotation annotationAttr() default @MetaAnnotation("lhm");//注解属性
}
//枚举
enum TrafficLamp{
RED(30){
public TrafficLamp nextLamp(){
return GREEN;
}
},
GREEN(45){
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;}
}
//注解
@interface MetaAnnotation {
String value();
}
//下面是设置注解里的属性值
@ItcastAnnotation(annotationAttr=@MetaAnnotation("flx"),color="red",value="abc",arrayAttr=1)
class AnnotationTest
{
@SuppressWarnings("deprecation")
@ItcastAnnotation("xyz")
public static void main(String[] args) throws Exception
{
System.runFinalizersOnExit(true);
if(AnnotationTest.class.isAnnotationPresent(ItcastAnnotation.class))
{
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());
}
}
}
//如果想使几个类放在一个文本框里,类钱必须不加public
泛型:
JDK1.5版本以后出现新特性。用于解决安全问题,是一个类型安全机制。
ArrayList<E>类定义和ArrayList<Integer>类引用中涉及如下术语:
整个称为ArrayList<E>泛型类型
ArrayList<E>中的E称为类型变量或类型参数
整个ArrayList<Integer>称为参数化的类型
ArrayList<Integer>中的Integer称为类型参数的实例或实际类型参数
ArrayList<Integer>中的<>念着typeof
ArrayList称为原始类型
参数化类型与原始类型的兼容性:
参数化类型可以引用一个原始类型的对象,编译报告警告,
例如, Collection<String> c = new Vector();//可不可以,不就是编译器一句话的事吗?
原始类型可以引用一个参数化类型的对象,编译报告警告,
例如, Collection c = new Vector<String>();//原来的方法接受一个集合参数,新的类型也要能传进去
参数化类型不考虑类型参数的继承关系:
Vector<String> v = new Vector<Object>(); //错误!///不写<Object>没错,写了就是明知故犯
Vector<Object> v = new Vector<String>(); //也错误!
编译器不允许创建泛型变量的数组。即在创建数组实例时,数组的元素不能使用参数化的类型,例如,下面语句有错误:
Vector<Integer> vectorList[] = new Vector<Integer>[10];
好处
1.将运行时期出现问题ClassCastException,转移到了编译时期。,
方便于程序员解决问题。让运行时问题减少,安全。,
2,避免了强制转换麻烦。
泛型格式:通过<>来定义要操作的引用数据类型。
ArrayList<String> al = new ArrayList<String>();
在使用java提供的对象时,什么时候写泛型呢?
通常在集合框架中很常见,
只要见到<>就要定义泛型。
其实<> 就是用来接收类型的。
当使用集合时,将集合中要存储的数据类型作为参数传递到<>中即可。
什么时候定义泛型类?
当类中要操作的引用数据类型不确定的时候,
早期定义Object来完成扩展。
现在定义泛型来完成扩展
泛型前:
class Tool
{
private Object obj;
public void setObject(Object obj)
{
this.obj = obj;
}
public Object getObject()
{
return obj;
}
}
泛型后:
class Utils<T>
{
private T q;
public void setObject(T q)
{
this.q = q;
}
public T getObject()
{
return q;
}
}
泛型类注意:
class Demo<T>//泛型类定义的泛型,在整个类中有效。如果被方法使用,
//那么泛型类的对象明确要操作的具体类型后,所有要操作的类型就已经固定了。
{
public void show(T t)
{
System.out.println("show:"+t);
}
public <Q> void print(Q q)//为了让不同方法可以操作不同类型,而且类型还不确定。那么可以将泛型定义在方法上。
{//用于放置泛型的类型参数的尖括号应出现在方法的
//其他所有修饰符之后和在方法的返回类型之前,也就是紧邻返回值之前。
//按照惯例,类型参数通常用单个大写字母表示。
System.out.println("print:"+q);
}
//错误public static void method(T t){}//静态方法不可以访问类上定义的泛型。
public static <W> void method(W t)
//如果静态方法操作的应用数据类型不确定,可以将泛型定义在方法上。
{
System.out.println("method:"+t);
}
}
泛型方法:
public <Q> void print(Q q)//为了让不同方法可以操作不同类型,而且类型还不确定。那么可以将泛型定义在方法上。
{//用于放置泛型的类型参数的尖括号应出现在方法的
//其他所有修饰符之后和在方法的返回类型之前,也就是紧邻返回值之前。
//按照惯例,类型参数通常用单个大写字母表示。
System.out.println("print:"+q);
}
交换数组中的两个元素的位置的泛型方法语法定义如下:
static <E> void swap(E[] a, int i, int j) {
E t = a[i];
a[i] = a[j];
a[j] = t;
编译器判断范型方法的实际类型参数的过程称为类型推断,类型推断是相对于知觉推断的,
其实现方法是一种非常复杂的过程。
根据调用泛型方法时实际传递的参数类型或返回值的类型来推断,具体规则如下:
当某个类型变量只在整个参数列表中的所有参数和返回值中的一处被应用了,
那么根据调用方法时该处的实际应用类型来确定,这很容易凭着感觉推断出来,
即直接根据调用方法时传递的参数类型或返回值来决定泛型参数的类型,例如:
swap(new String[3],3,4) -- static <E> void swap(E[] a, int i, int j)
当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,
如果调用方法时这多处的实际应用类型都对应同一种类型来确定,
这很容易凭着感觉推断出来,例如:
add(3,5) -- static <T> T add(T a, T b)
当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,
如果调用方法时这多处的实际应用类型对应到了不同的类型,且没有使用返回值,
这时候取多个参数中的最大交集类型,例如,下面语句实际对应的类型就是Number了,
编译没问题,只是运行时出问题:
fill(new Integer[3],3.5f) -- static <T> void fill(T[] a, T v)
当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,
如果调用方法时这多处的实际应用类型对应到了不同的类型, 并且使用返回值,
这时候优先考虑返回值的类型,例如,下面语句实际对应的类型就是Integer了,
编译将报告错误,将变量x的类型改为float,对比eclipse报告的错误提示,
接着再将变量x类型改为Number,则没有了错误:
int x =(3,3.5f) -- static <T> T add(T a, T b)
参数类型的类型推断具有传递性,下面第一种情况推断实际参数类型为Object,
编译没有问题,而第二种情况则根据参数化的Vector类实例将类型变量直接确定
为String类型,编译将出现问题:
copy(new Integer[5],new String[5]) -- static <T> void copy(T[] a,T[] b);
copy(new Vector<String>(), new Integer[5] -- static <T> void copy(Collection<T> a , T[] b);
泛型定义在接口上。
interface Inter<T>
{
void show(T t);
}
? 通配符。也可以理解为占位符。
泛型的限定;
? extends E: 可以接收E类型或者E的子类型。上限。
? super E: 可以接收E类型或者E的父类型。下限
限定通配符的上边界:
正确:Vector<? extends Number> x = new Vector<Integer>();
错误:Vector<? extends Number> x = new Vector<String>();
限定通配符的下边界:
正确:Vector<? super Integer> x = new Vector<Number>();
错误:Vector<? super Integer> x = new Vector<Byte>();
提示:
限定通配符总是包括自己。
?只能用作引用,不能用它去给其他变量赋值
Vector<? extends Number> y = new Vector<Integer>();
Vector<Number> x = y;
上面的代码错误,原理与Vector<Object > x11 = new Vector<String>();相似,
只能通过强制类型转换方式来赋值。
反射在泛型中的应用:
反射方式可以后期往里面加入别的元素。因为编译完后是去类型化的。
public class GenericTest
{
public static void main(String[] args) throws Exception
{
ArrayList<Integer> collection3 = new ArrayList<Integer>();
//通过反射想Integer里传String
collection3.getClass().getMethod("add", Object.class).invoke(collection3, "abc");
System.out.println(collection3.get(0));
}
}
通过反射获得泛型的参数化类型:
Class GenericalReflection
{
private Vector<Date> dates = new Vector<Date>();
//通过对象无法获取类型,因为编译的时候去类型化
public void setDates(Vector<Date> dates)
//而获取方法后,可以通过Method里的方法里的形参类型
{
this.dates = dates;
}
public static void main(String[] args)
{
//Method getDeclaredMethod(String name, Class<?>... parameterTypes)
//返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法。
Method methodApply = GenericalReflection.class.getDeclaredMethod("applyGeneric", Vector.class);
// Type getGenericReturnType() 返回表示由此 Method 对象所表示方法的正式返回类型的 Type 对象。
//Type是java编程语言中所有类型的公共高级接口。它们包括原始类型、参数化类型、数组类型、类型变量和基本类型。
//Type接口的子类:ParameterizedType 表示参数化类型
ParameterizedType pType = (ParameterizedType)(methodApply .getGenericParameterTypes())[0];
System.out.println("setDates("+ ((Class) pType.getRawType()).getName() + "<"
+ ((Class) (pType.getActualTypeArguments()[0])).getName()+ ">)" );
}
}
//向Map<K,V>集合有两个,可以通过下面方法:
Type[] getGenericParameterTypes()
按照声明顺序返回 Type 对象的数组,
这些对象描述了此 Method 对象所表示的方法的形参类型的。
类加载器:
简要介绍什么是类加载器和类加载器的作用
Java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器,
每个类负责加载特定位置的类:BootStrap,ExtClassLoader,AppClassLoader
类加载器也是Java类,因为其他是java类的类加载器本身也要被类加载器加载,
显然必须有第一个类加载器不是java类,这正是BootStrap(C++语言写的二进制代码,在虚拟机内核里面)。
Java虚拟机中的所有类装载器采用具有父子关系的树形结构进行组织,
在实例化每个类装载器对象时,需要为其指定一个父级类装载器对象或者默认采用系统类装载器为其父级类加载。
类加载器之间的父子关系和管辖范围图:
类加载器的委托机制:
当Java虚拟机要加载一个类时,到底派出哪个类加载器去加载呢?
首先当前线程的类加载器去加载线程中的第一个类。
如果类A中引用了类B,Java虚拟机将使用加载类A的类装载器来加载类B。
还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类。
每个类加载器加载类时,优先委托给其上级类加载器。
当所有祖宗类加载器没有加载到类,回到发起者类加载器,
还加载不了,则抛ClassNotFoundException,不是再去找发起者类加载器的儿子
,因为没有getChild方法,即使有,那有多个儿子,找哪一个呢?
对着类加载器的层次结构图和委托加载原理,
解释先前将ClassLoaderTest输出成jre/lib/ext目录下的itcast.jar包中后,
运行结果为ExtClassLoader的原因。
获取类加载器:
/*
Class里的方法
ClassLoader getClassLoader() 返回该类的类加载器。
ClassLoader里的方法:
Class loadClass()方法来指定某个类加载器去加载某个类
Object里的方法:
Class<?> getClass()
返回此 Object 的运行时类。
*/
public class ClassLoaderTest
{
public static void main(String[] args) throws Exception
{
System.out.println(
ClassLoaderTest.class.getClassLoader().getClass().getName());
System.out.println(System.class.getClassLoader());//父类加载器(CC++语言写的二进制代码)
}
}
//自定义类加载器:(加密,解密)
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//运行时,传入目标类加密 ,加密后class文件存入目录
{
public static void main(String[] args) throws Exception
{
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
//覆写ClassLoader里的findClass方法。编写自己的类加载器,里面定义解码方法,这个里面加密解密是一样的
//protected Class<?> findClass(String name) 使用指定的二进制名称查找类
protected Class<?> findClass(String name) throws ClassNotFoundException
{
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)
{
e.printStackTrace();
}
return null;
}
public MyClassLoader(){}
public MyClassLoader(String classDir)
{
this.classDir = classDir;
}
}
//需要加载的类
import java.util.Date;
public class ClassLoaderAttachment extends Date
{
public String toString()
{
return "hello,黑马";
}
}
//使用自己的类加载器加载指定位置的加密文件:
import java.util.Date;
public class ClassLoaderTest
{
public static void main(String[] args) throws Exception
{//itcastlib为存储位置,里面的ClassLoaderAttachment
Class clazz = new MyClassLoader("itcastlib").loadClass("ClassLoaderAttachment");
//加载到的字节码,来创建Date对象
Date d1 = (Date)clazz.newInstance();
System.out.println(d1);
}
}
代理及AOP:
代理类:
AOP:面向方面的编程
系统中存在交叉业务,一个交叉业务就是要切入到系统中的一个方面,如下所示:
安全 事务 日志
StudentService ------|----------|------------|-------------
CourseService ------|----------|------------|-------------
MiscService ------|----------|------------|-------------
用具体的程序代码描述交叉业务:
method1 method2 method3
{ { {
------------------------------------------------------切面
.... .... ......
------------------------------------------------------切面
} } }
AOP的目标就是要使交叉业务模块化。可以采用将切面代码移动到原始方法的周围,
这与直接在方法中编写切面代码的运行效果是一样的,如下所示:
------------------------------------------------------切面
func1 func2 func3
{ { {
.... .... ......
} } }
------------------------------------------------------切面
动态代理技术:
要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,全部采用静态代理方式,
将是一件非常麻烦的事情!写成百上千个代理类,是不是太累!
JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理类。
JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理。
CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,
所以,如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库。
代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外,
还可以在代理方法中的如下四个位置加上系统功能代码:
1.在调用目标方法之前
2.在调用目标方法之后
3.在调用目标方法前后
4.在处理目标方法异常的catch块中。如下:
Class proxy{
void sayHello(){
……….
try{
target.sayHello();
}catch(Exception e){
………..
}
………….
}
}
动态代理类:
public class ProxyTest
{/*
让jvm创建动态类及其实例对象,需要给它提供哪些信息?
三个方面:
1.生成的类中有哪些方法,通过让其实现哪些接口的方式进行告知;
2.产生的类字节码必须有个一个关联的类加载器对象;
3.生成的类中的方法的代码是怎样的,也得由我们提供。
把我们的代码写在一个约定好了接口对象的方法中,把对象传给它,
它调用我的方法,即相当于插入了我的代码。
提供执行代码的对象就是那个InvocationHandler对象,
它是在创建动态类的实例对象的构造方法时传递进去的。
在上面的InvocationHandler对象的invoke方法中加一点代码,
就可以看到这些代码被调用运行了。*/
public static void main(String[] args) throws Exception
{
//static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)
// 返回代理类的 java.lang.Class 对象,并向其提供类加载器和接口数组。
Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
//获取Proxy的构造方法
Constructor constructor = clazzProxy1.getConstructor(InvocationHandler.class);
//构造方法:protected Proxy(InvocationHandler h) //InvocationHandler是接口
//使用其调用处理程序的指定值从子类(通常为动态代理类)构建新的 Proxy 实例。
//调用构造方法创建对象,构造方法里的指定参数,即InvocationHandler的子类。
class MyInvocationHander1 implements InvocationHandler
{
public Object invoke(Object proxy, Method method, Object[] args)throws Throwable
{
return null;
}
}//因为是一次性使用,其实可以一次性定义匿名内部类。
Collection proxy1 = (Collection)constructor.newInstance(new MyInvocationHander1());
//打印Sop(proxy1)结果为null,有可能是对象创建失败,或者toString()方法,返回为null
//这个里面是后者toString()方法,返回为null
proxy1.clear();//可以清除,即对象创建成功。因为没有返回值。即不报错
//调用调用代理对象的从Object类继承的hashCode, equals, 或toString这几个方法时,
//代理对象将调用请求转发给InvocationHandler对象,对于其他方法,则不转发调用请求。
//proxy1.size();//此方法有返回值。但调用失败,为什么?
//因为size();方法调用了invoke();invoke();返回了个unll,但size()方法需要int,所以报错。
//下面是一次性创建对象。替换上面,用到方法如下:
//static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
// 返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。
Collection proxy2 = (Collection)Proxy.newProxyInstance(Collection.class.getClassLoader(),
new Class[](Collection.class),
new InvocationHandler()//匿名内部类
{
/*调用动态代理对象的时候其实都是调用的invoke方法
Object invoke(Object proxy, Method method, Object[] args)
在代理实例上处理方法调用并返回结果。*/
/*里面没有定义东西
public Object invoke(Object proxy, Method method, Object[] args)throws Throwable
{
return null;
}
*/
//ArrayList target =new ArrayList();
public Object invoke(Object proxy, Method method, Object[] args)throws Throwable
{
ArrayList target =new ArrayList();
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;
}
}
);
//外部调用动态代理对象方法:
proxy2.add("zxx");
proxy2.add("lhm");
proxy2.add("bxd");
proxy2.size();//当ArrayList在InvocationHandler子类对象的成员上的时候打印为3,在invoke方法里是答应为0
}
}
动态代理运行原理图:
优化代码(将其提取成方法,理解就先看上面代码):
import java.lang.reflect.Constructor;
public class ProxyTest1
{
public static void main(String[] args) throws Exception
{
final ArrayList target = new ArrayList();//创建需要传递的对象
Collection proxy3 = (Collection)getProxy(target,new MyAdvice());//创建需要传递的系统功能对象
proxy3.add("zxx");
proxy3.add("lhm");
proxy3.add("bxd");
//传递两个对象,做成框架,加final是为了方法里面能访问到,一个是正常的类对象,一个是需要添加的功能
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;
}
}
}
import java.lang.reflect.Method;
public interface Advice //定义一个功能接口。
{
void beforeMethod(Method method);
void afterMethod(Method method);
}
import java.lang.reflect.Method;
public class MyAdvice implements Advice//实现此功能的类。需要传递的参数
{
long beginTime = 0;
public void afterMethod(Method method)
{
System.out.println("从黑马毕业上班啦!");
long endTime = System.currentTimeMillis();
System.out.println(method.getName() + " running time of " + (endTime - beginTime));
}
public void beforeMethod(Method method)
{
System.out.println("到黑马来学习啦!");
beginTime = System.currentTimeMillis();
}
}
分析动态生成的类的内部代码:
//注意:Collection程序调用objProxy.add(“abc”)方法时,涉及三要素:objProxy对象、add方法、“abc”参数,
//即对应invoke(Object proxy, Method method, Object[] args)
Class Proxy$
{
add(Object object)
{
return handler.invoke(Object proxy, Method method, Object[] args);
}
}
实现类似于Spring的可配置的AOP框架:(和配置文件挂钩)
工厂类BeanFactory负责创建目标类或代理类的实例对象,并通过配置文件实现切换。
其getBean方法根据参数字符串返回一个相应的实例对象,如果参数字符串在配置文件中对应的类名不是ProxyFactoryBean,
则直接返回该类的实例对象,否则,返回该类实例对象的getProxy方法返回的对象。
BeanFactory的构造方法接收代表配置文件的输入流对象,配置文件格式如下:
#xxx=java.util.ArrayList
xxx=cn.itcast.ProxyFactoryBean
xxx.target=java.util.ArrayList
xxx.advice=cn.itcast.MyAdvice
ProxyFacotryBean充当封装生成动态代理的工厂,需要为工厂类提供哪些配置参数信息?
目标
通知
编写客户端应用:
编写实现Advice接口的类和在配置文件中进行配置
调用BeanFactory获取对象
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
public class BeanFactory //判断是否生成代理类
{
Properties props = new Properties();
public BeanFactory(InputStream ips)
{
try {
props.load(ips);
}
catch (IOException e)
{
e.printStackTrace();
}
}
public Object getBean(String name)
{
String className = props.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(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;
}
return bean;
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyFactoryBean //代理类生成器,里面定义了生成代理的方法。
{
private Advice advice;//定义需要传递的两个对象,Bean属性
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()
{
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
{
advice.beforeMethod(method);
Object retVal = method.invoke(target, args);
advice.afterMethod(method);
return retVal;
}
}
);
return proxy3;
}
}
//下面是需要用到的实例。
import java.lang.reflect.Method
public interface Advice //定义一个功能接口。
{
void beforeMethod(Method method);
void afterMethod(Method method);
}
import java.lang.reflect.Method;
public class MyAdvice implements Advice//实现此功能的类。需要传递的参数
{
long beginTime = 0;
public void afterMethod(Method method)
{
System.out.println("从黑马毕业上班啦!");
long endTime = System.currentTimeMillis();
System.out.println(method.getName() + " running time of " + (endTime - beginTime));
}
public void beforeMethod(Method method)
{
System.out.println("到黑马来学习啦!");
beginTime = System.currentTimeMillis();
}
}
//(String即都是在配置文件里配置)还要定义配置文件config.properties:传入需要转换的类
#xxx=java.util.ArrayList//#表示屏蔽此对象,就是不传入这个
xxx=路径.ProxyFactoryBean
xxx.target=java.util.ArrayList
xxx.advice=路径.MyAdvice
//下面是测试、
import java.io.InputStream;
import java.util.Collection;
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());
((Collection)bean).clear();
}
}