cols) {
for(Object obj:cols) {
System.out.println(obj);
}
cols.add("string");//没错
cols = new HashSet
();//会报告错误!
}
正确方式:
public static void printCollection(Collection
cols) {
for(Object obj:cols) {
System.out.println(obj);
}
cols.add("string");//错误,因为它不知自己未来匹配就一定是String
cols.size();//没错,此方法与类型参数没有关系
cols = new HashSet
();//没错
}
总结:使用?通配符可以引用其他各种参数化的类型,?通配符定义的变量主要用作引用,可以调用与参数化无关的方法,不能调用与参数化有关的方法。
4.泛型中?通配符的扩展
限定通配符的上边界:
正确:Vector<? extends Number> v = new Vector<Integer>();
错误:Vector<? extends Number> v = new Vector<String>();
限定通配符的下边界:
正确:Vector<? super Integer> v = new Vector<Number>();
错误:Vector<? super Integer> v = new Vector<Byte>();
注意:
限定通配符总是包括自己;
?只能做引用,不能用它去给其他变量赋值
5.泛型集合类的综合案例
HashMap
hm = new HashMap
();
hm.put("liwei", 34);
hm.put("minfangqun", 23);
hm.put("limuchen", 2);
Set
> entrySet = hm.entrySet();
for(Map.Entry
entry : entrySet)
System.out.println(entry.getKey()+":"+entry.getValue());
6.定义泛型方法
用于表示泛型的类型参数应该出现在方法的其他所有修饰符之后和返回类型之前,按照惯例,类型参数用单个大写字母表示。
交换数组中的两个元素位置的泛型方法语法定义入下:
static
void swap(E[] a, int i, int j) {
E t = a[i];
a[i] = a[j];
a[j] = t;
}
只有引用类型才能作为泛型方法的实际参数,swap(new int[],3,5) ;会报告编译错误。
除了在应用泛型时可以使用extends限定符,在定义泛型时也可以使用,并且可以用&来指定多个边界,如<V extends Serializable&Clonable> 。
普通方法、构造方法和静态方法中都可以使用泛型。
也可以使用类型变量表示异常,称为参数化的异常,可以用于方法的throws 列表中,但不能用于catch 子句中。如:
private static
sayHello() throws T
{
try{
}catch(Exception e){
throw (T)e;
}
}
在泛型中可以同时有多个类型参数,在定义他们的<>中用逗号分隔,如:
public static <K,V> V getValue(K key) { return map.get(key);}
7.几个泛型方法示例
(1)自动将Object类型的对象转换成其他类型的泛型方法
private static
T autoConvert(Object obj) {
return (T)obj;
}
(2)将任意类型的数组中的所有元素填充为相应类型的某个对象
private static
void fillArray(T[] a,T obj) {
for(int i=0;i
(3)打印出任意参数化类型的集合中的所有内容
public static
void printCollection2(Collection
collection,T t) {
System.out.println(collection.size());
for(T obj : collection)
System.out.println(obj);
collection.add(t);
}
在这种情况下,前面的通配符方案要比泛型更加有效。当一个类型变量用来表达两个参数之间活着参数和返回值之间的关系时,即同一个类型变量在方法签名的两处被使用,或者类型变量在方法体代码中也被使用而不是仅在签名的时候用,才需要使用泛型方法。
(4)把任意参数类型的集合中的数据安全的复制到相应类型的数组中
private static
void copy1(Vector
src, T[] dest) {
int size = src.size();
for(int i = 0; i < size; i ++) {
dest[i] = src.get(i);
}
}
(5)把任意参数类型的一个数组中的数组安全的复制到相应类型的另一个数组中
private static
void copy2(T[] src, T[] dest) {
for(int i=0;i
8.类型参数的类型推断
编译器判断泛型方法的实际类型参数的过程称为类型推断。
根据调用泛型方法时实际传递的参数类型或返回值的类型来推断,具体规则如下:
(1)当某个类型变量只在所有参数和返回值中的一处被应用了,那么根据调用方法时该处的实际应用类型来确定,即直接根据调用方法时传递的参数类型或返回值来决定泛型参数的类型。如:
swap(new String[3],1,2)-->static <E> void swap(E[],int i,int j) ,可以推出E为String
(2)当某个类型变量在所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型都是同一种类型,很容易推出来。如:
add(3,5)-->static <T> add(T a,T b) ,可以推出T为Integer
(3)当某个类型变量在所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型对应到了不同的类型,且没有使用返回值,这个时候取多个参数的最大交集类型,例如,下面语句实际对应的类型就是Number了,编译没有问题,只是运行时出问题
fill(new Integer[3],3.5f)-->static <T> void fill(T[],T t)
(4)当某个类型变量在所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型对应到了不同的类型,且使用返回值,这时优先考虑返回值的类型,例如,下面语句实际对应的类型就是Integer 了,编译将报告错误,将变量x的类型改为float ,对比eclipse 报告的错误提示,接着再将变量x的类型改为Number ,则没了错误
int x = add(3,3.5f)-->static <T> T add(T a,T b)
(5)参数类型的类型推断具有传递性,下面第一种情况判断实际类型参数为Object ,编译没有问题,而第二种情况则根据参数化的Vector 类实例将类型变量直接确定为String 类型,编译将出现问题:
copy(new Integer[5],new String[5])-->static <T> void copy(T[] a,T[] b);
copy(new Vector<String>(),new Integer[3])-->static <T> void copy(Collection<T> a,T[] b);
9.定义泛型类型
如果类的实例对象中的多处都要用到同一个泛型参数,即这些地方引用的泛型类型要保持一致时,就要采用泛型类型的方式进行定义,也就是类级别的泛型,语法格式如下:
package com.itheima.day2;
import java.util.Set;
//dao-->data access object 增删改查crud
public class GenericDao
{
public void add(T obj) {
}
public void delete(T obj) {}
public void deleteById(int id) {}
public void update(T obj) {}
public T findById(int id) {
return null;
}
public Set
findByConditions(String where) {
return null;
}
public T findByUsername(String name) {
return null;
}
public static
void staticMethod(T obj){}
}
类级别的泛型是根据引用该类名时指定的类型信息来参数化类型变量的,例如下面两种方式都可以:
GenericDao<String> dao = null;
new GenericDao<String>();
注意:
在对泛型类型进行参数化时,类型参数的实例必须是引用类型,不能是基本类型;
当一个变量被声明为泛型时,只能被实例变量、方法和内部类调用,而不能被静态变量和静态方法调用。因为静态成员是被所有参数化的类所共享的,所以静态成员不应该有类级别的类型参数。
10.通过反射获得泛型的参数化类型
Method applyMethod = GenericTest.class.getMethod("applyVector", Vector.class);
Type[] types = applyMethod.getGenericParameterTypes();
ParameterizedType pType = (ParameterizedType)types[0];
System.out.println(pType.getActualTypeArguments()[0]);
System.out.println(pType.getRawType());
二、类加载器
1.了解类加载器
类加载器是负责加载类的对象。
Java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器,BootStrap 、ExtClassLoad 、AppClassLoader ,每个类负责加载特定位置的类。
类加载器也是Java类,因为其他是Java类的类加载器本身也要被类加载器加载,显然必须有一个不是Java类的类加载器,这就是BootStrap 。
Java虚拟机中的所有类加载器采用具有父子关系的树形结构进行组织,在实例化每个类加载器对象时,需要为其之指定一个父级类加载器对象或默认采用系统类加载器为其父级类加载器。
package com.itheima.day2;
public class ClassloaderTest {
/**
* @param args
* @throws ClassNotFoundException
* @throws IllegalAccessException
* @throws InstantiationException
*/
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
ClassLoader cl = ClassloaderTest.class.getClassLoader();
while(cl!=null) {
System.out.println(cl.getClass().getName());
cl = cl.getParent();
}
System.out.println(cl);
}
}
上面代码的输出结果为
sun.misc.Launcher$AppClassLoader,sun.misc.Launcher$ExtClassLoader,null 。最后的加载器其实是
BootStrap ,但因为它不是Java类,所以为空。
如果用eclipse 的打包工具将ClassLoaderTest 输出到jre/lib/ext 目录下的itheima.jar 包,再运行这个类时,输出结果为sun.misc.Launcher$ExtClassLoader,null 。这是为什么呢?这时classpath 目录下有ClassLoaderTest.class ,jre/lib/ext/itheima.jar 中也有ClassLoaderTest.class,我们需要了解类加载的具体过程和原理。
2.类加载器的委托机制
当Java虚拟机要加载一个类时,到底派哪个类加载器去加载呢?首先用当前线程的类加载器去加载线程中的第一个类;如果类A中引用了类B,Java虚拟机将使用加载类A的类加载器去加载类B;也可以直接调用ClassLoader.loadClass() 方法来指定某个类加载器去加载某个类。
每个ClassLoader 本身只能分别加载特定位置和目录中的类,但它们可以委托其他的类加载器去加载类,这就是类加载器的委托机制。类加载器一级级委托到BootStrap 类加载器,如果BootStrap 在它对应的目录下找不到要加载的类,就一级级回退到子孙类加载器去找要加载的类。当回退到最初的类加载器还无法找到时,就抛出ClassNotFoundException 。这就说明了上面的问题。
3.编写自己的类加载器
自定义的类加载器必须继承ClassLoader ,并覆写findClass方法。该类加载器还实现简单的加密类功能。参考文档中的示例代码。
先创建一个被操作的类ClassLoaderAttachment
package com.itheima.day2;
import java.util.Date;
public class ClassLoaderAttachment extends Date {
@Override
public String toString() {
// TODO Auto-generated method stub
return "hello,beijing";
}
}
下面是自定义的类加载器,
MyClassLoader
package com.itheima.day2;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class MyClassLoader extends ClassLoader {
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
String srcPath = args[0];//要加密的文件路径
String destDir = args[1];//加密后的文件存储目录
String destFileName = srcPath.substring(srcPath.lastIndexOf("\\")+1);?//获取原文件的文件名
String destPath = destDir+"\\"+destFileName;//加密后文件的完整路径
FileInputStream fis = new FileInputStream(srcPath);//读取流
FileOutputStream fos = new FileOutputStream(destPath);//写入流
cypher(fis, fos);//加密
fis.close();
fos.close();
}
//简单的加密方法
//将原文件中的数据取出,加密后输出到目的文件中
private static void cypher(InputStream ips, OutputStream ops) throws IOException {
int b = -1;
while((b=ips.read())!=-1)
ops.write(b ^ 0xff);
}
private String classDir;
public MyClassLoader(String classDir) {
super();
this.classDir = classDir;
}
public MyClassLoader() {
super();
// TODO Auto-generated constructor stub
}
@Override
protected Class
findClass(String name) throws ClassNotFoundException {
// TODO Auto-generated method stub
System.out.println(name);
byte[] b = null;
try {
b = loadClassData(name);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return defineClass(null, b, 0, b.length);
}
private byte[] loadClassData(String name) throws IOException {
// load the class data from the connection
String classFilePath = this.classDir+"\\"+name.substring(name.lastIndexOf('.')+1)+".class";
FileInputStream fis = new FileInputStream(classFilePath);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
cypher(fis,bos);
fis.close();
return bos.toByteArray();
}
}
原文件路径即arg[0]为
ClassLoaderAttachment.class 的绝对路径,目标目录即arg[1]为工程根目录下的
itheimalibs 目录,运行
MyClassLoader 之后在该目录下会有一个加密后的
ClassLoaderAttachment.class 文件。用加密后的文件去覆盖原文件,在
ClassLoaderTest 中加载
ClassLoaderAttachment
Class clazz = new MyClassLoader("itheimalibs").loadClass("com.itheima.day2.ClassLoaderAttachment");
Date d1 = (Date)clazz.newInstance();
System.out.println(d1);
在
ClassLoaderTest 中运行以上代码,会出现
“java.lang.ClassFormatError: Incompatible magic value xxx” 的错误,这是因为加载类的时候会先去父加载器中寻找,在classpath下有
CalssLoaderAttachment.class ,但是加过密的,父加载器无法解析,就出现了上面错误。把
classpath 下的
CalssLoaderAttachment.class 删掉,最后就会通过
MyClassLoader 加载
itheimalibs 目录下的
class 文件,并输出结果
“hello,beijing” 。
4.一个类加载器的高级问题分析
编写一个能打印自己的类加载器名称和当前类加载器的父子结构关系链的
MyServlet ,正常发布后,看到打印结果为
WebAppClassloader 。
package com.itheima.itheimaweb.web.servlets;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyServlet extends HttpServlet {
/**
* The doGet method of the servlet.
*
* This method is called when a form has its tag value method equals to get.
*
* @param request the request send by the client to the server
* @param response the response send by the server to the client
* @throws ServletException if an error occurred
* @throws IOException if an error occurred
*/
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
ClassLoader loader = this.getClass().getClassLoader();
while(loader != null) {
out.println(loader.getClass().getName()+" ");
loader = loader.getParent();
}
out.close();
}
}
把M
yServlet.class 打包成jar包,放到jre/lib/ext目录下,重启tomcat,发现有找不到
HttpServlet 的错误。把servlet-api.jar也放到ext目录下,问题解决了,打印结果为
ExtClassLoader 。这是因为父级类加载器加载的类无法引用只能被子类加载器加载的类。原理图如下:
三、代理
1.代理的概念和作用
生活中的代理:从代理商手里买电脑和直接从厂家买电脑,主体义务目标是相同的。
程序中的代理:
为已存在的多个具有相同接口的目标类的各个方法增加一些系统功能,如异常处理、日志、计算方法的运行时间、事物管理等等,编写一个与目标类具有相同接口的代理类,代理类的每个方法调用目标类的相同方法,并在调用方法时加上系统功能的代码。
2.AOP和动态代理技术
AOP就是面向方面的编程,AOP的目标就是要使交叉业务模块化。使用代理技术正好可以解决这种问题,代理是实现AOP功能的核心和关键技术。
要为系统中的各种接口的类增加代理功能,那将需要太多的代理类。JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理类。JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理。
CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库。
代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外,还可以在理方法中的如下四个位置加上系统功能代码:
(1)调用目标方法之前
(2)调用目标方法之后
(3)调用目标方法的前后
(4)在处理目标方法异常的catch块中
3.分析JVM动态生成的类
package com.itheima.day3;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;
public class ProxyTest {
/**
* @param args
* @throws NoSuchMethodException
* @throws SecurityException
* @throws InvocationTargetException
* @throws IllegalAccessException
* @throws InstantiationException
* @throws IllegalArgumentException
*/
public static void main(String[] args) throws SecurityException, NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException {
// 动态生成实现了Collection接口(可以实现若干接口)的类
Class clazzProxy = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
System.out.println(clazzProxy.getName());//打印动态类的名字,结果为$Proxy0
// 打印动态类的所有构造方法,输出打印格式为 构造方法(参数1, 参数2, ...,参数n)
// 从结果可以看到动态类只有一个接收InvocationHandler参数的构造方法,$Proxy0(java.lang.reflect.InvocationHandler)
System.out.println("-----------------begin constructor list------------------");
//获取动态类的所有构造方法
Constructor[] constructors = clazzProxy.getConstructors();
for(Constructor constructor : constructors) {
//使用StringBuilder来构造打印格式
StringBuilder sb = new StringBuilder();
//获得构造方法名
String name = constructor.getName();
sb.append(name);
sb.append('(');
//获得所有参数类型
Class[] paramTypes = constructor.getParameterTypes();
for(Class paramType : paramTypes) {
sb.append(paramType.getName()).append(',');
}
//如果参数个数不为0,则将最后的逗号删除
if (paramTypes.length>0)
sb.deleteCharAt(sb.length()-1);
sb.append(')');
System.out.println(sb.toString());
}
// 打印动态类的所有构造方法,格式同上
// 可以看到动态类中有Collection接口中的所有方法
System.out.println("------------------------begin method list-----------------------");
Method[] methods = clazzProxy.getMethods();
for(Method method : methods) {
StringBuilder sb = new StringBuilder();
sb.append(method.getName());
sb.append('(');
Class[] paramTypes = method.getParameterTypes();
for(Class paramType : paramTypes) {
sb.append(paramType.getName()).append(',');
}
if (paramTypes.length>0)
sb.deleteCharAt(sb.length()-1);
sb.append(')');
System.out.println(sb.toString());
}
// 创建动态类的实例对象
System.out.println("------------------------begin create new instance-----------------------");
// 通过反射获得动态类的构造函数
Constructor cons = clazzProxy.getConstructor(InvocationHandler.class);
// 用构造方法的newInstance方法创建动态类的实例对象
// 参数为InvocationHandler的匿名内部类
Collection proxy1 = (Collection)cons.newInstance(new InvocationHandler() {
//覆写父类的invoke方法
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
return null;
}
});
//调用下面的方法,最终就是调用匿名内部类的invoke方法
// 打印创建的对象,结果为null
System.out.println(proxy1.toString());
proxy1.clear();
// 调用size()方法,会报告空指针异常,因为返回的是null,而需要的是int
// proxy1.size();
// 直接调用Proxy.newInstance方法创建动态代理对象
Collection proxy2 = (Collection)Proxy.newProxyInstance(
Collection.class.getClassLoader(),//类加载器
new Class[]{Collection.class},//要实现的接口数组
new InvocationHandler() {//InvocationHandler对象,执行业务代码
// 目标类,执行业务的主体
ArrayList target = new ArrayList();
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
long beginTime = System.currentTimeMillis();//增加的系统功能代码
Object retVal = method.invoke(target, args);//调用目标类的方法
long endTime = System.currentTimeMillis();//增加的系统功能代码
System.out.println(method.getName()+" running time:"+(endTime-beginTime));//增加的系统功能代码
return retVal;
}
});
proxy2.add("zxx");
proxy2.add("lhm");
proxy2.add("bxd");
System.out.println(proxy2.size());// 打印结果为3
System.out.println(proxy2.getClass().getName());// 打印结果为$Proxy0
// 封装获得动态代理类的代码到getProxy方法中,接收两个参数,一个是目标类
// 另一个是实现了Advice接口的类
// Advice接口中可以封装所有要实现的系统功能的方法
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());// 打印结果为3
System.out.println(proxy3.getClass().getName());// 打印结果为$Proxy1
}
private static Object getProxy(final Object target, final Advice advice) {
Object proxy = Proxy.newProxyInstance(
target.getClass().getClassLoader(),//传入目标类的类加载器
target.getClass().getInterfaces(),//传入目标类的接口数组
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
advice.beforeMethod(method);// 执行系统功能代码
Object retVal = method.invoke(target, args);// 执行业务主体
advice.afterMethod(method);// 执行系统功能代码
return retVal;
}
});
return proxy;
}
}
动态代理的工作原理图
4.实现AOP功能的封装与配置
(1)工厂类BeanFactory 负责创建目标类或代理类的实例对象,并通过配置文件进行切换。其getBean 方法根据参数字符串返回一个相应的实例对象,如果参数字符串在配置文件中对应的类名不是ProxyFactoryBean ,则直接返回该类的实例对象,否则返回该实例对象的getProxy 方法返回的对象。
package com.itheima.day3.aopframework;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import com.itheima.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 clazz = Class.forName(className);
bean = clazz.newInstance();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(bean instanceof ProxyFactoryBean) {
Object proxy = null;
ProxyFactoryBean proxyFacotryBean = (ProxyFactoryBean)bean;
try {
Object target = Class.forName(props.getProperty(name+".target")).newInstance();
Advice advice = (Advice)Class.forName(props.getProperty(name+".advice")).newInstance();
proxyFacotryBean.setTarget(target);
proxyFacotryBean.setAdvice(advice);
proxy = proxyFacotryBean.getProxy();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return proxy;
}
return bean;
}
}
(2)BeanFacotry 的构造方法接收代表配置文件的输入流对象,配置文件格式如下:
#xxx=java.util.ArrayList
xxx=com.itheima.day3.aopframework.ProxyFactoryBean
xxx.target=java.util.ArrayList
xxx.advice=com.itheima.day3.MyAdvice
(3)ProxyFacotryBean 充当封装生成动态代理的工厂,需要为工厂类提供目标和通知(Advice )两个参数。
package com.itheima.day3.aopframework;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import com.itheima.day3.Advice;
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 getProxy() {
// TODO Auto-generated method stub
Object proxy = Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
advice.beforeMethod(method);
Object retVal = method.invoke(target, args);
advice.afterMethod(method);
return retVal;
}
});
return proxy;
}
}
(4)编写客户端应用,编写实现Advice 接口的类和在配置文件中进行配置,调用BeanFactory 获取对象。
package com.itheima.day3.aopframework;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.Collection;
public class AopFrameworkTest {
/**
* @param args
* @throws FileNotFoundException
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
InputStream ips = AopFrameworkTest.class.getResourceAsStream("config.properties");
Object bean = new BeanFactory(ips).getBean("xxx");
System.out.println(bean.getClass().getName());
((Collection)bean).add("nimei");
System.out.println(((Collection) bean).size());
}
}
----------------------
ASP.Net+Unity开发 、.Net培训 、期待与您交流! ----------------------
详细请查看:http://edu.youkuaiyun.com