----------------------ASP.Net+Android+IOS开发、.Net培训、期待与您交流! ----------------------
内省
IntroSpector
主要是对JavaBean进行操作
什么是JavaBeanJavaBean
是一种特殊的java类,主要是用于传递数据信息,这种java类中的方法主要用于访问私有字段,且方法名符合某种规则
在多个模块中传递信息的时候,可以将这些信息封装到一个JavaBean中,这种JavaBean的实例对象通常称之为值对象(Value Object ,VO)
JavaBean类的书写规则
1.JavaBean类必须是具体的和公共的,不能是抽象的
2.类中必须有一个无参的构造方法,为了实现序列化Serializable
3.类中的属性应该声明为私有权限,定义getter和setter方法对属性进行操作
package cn.itcast.day1;
public class PersonBean {
private String name;
private int age;
public PersonBean(){}
public PersonBean(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
下面用PropertyDescriptor来获取和设置PersonBean中的name值
package cn.itcast.day1;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class IntroSepctorTest {
/**
* @param args
*/
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
PersonBean personBean = new PersonBean("lisi", 18);
String propertyName = "name";
PropertyDescriptor pd = new PropertyDescriptor(propertyName,
personBean.getClass());
Object retVal = getProperty(personBean, pd);
System.out.println(retVal);
Object value = "zxx";
setProperty(personBean, pd, value);
System.out.println(getProperty(personBean, pd));
}
private static void setProperty(Object bean, PropertyDescriptor pd,
Object value) throws IllegalAccessException,
InvocationTargetException {
Method methodSetName = pd.getWriteMethod();
methodSetName.invoke(bean, value);
}
private static Object getProperty(Object bean, PropertyDescriptor pd)
throws IllegalAccessException, InvocationTargetException {
Method methodGetName = pd.getReadMethod();
Object retVal = methodGetName.invoke(bean);
return retVal;
}
}
这是内省的一种简单的应用方式
还有一种复杂的应用方式
package cn.itcast.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 IntroSepctorTest {
/**
* @param args
*/
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
PersonBean personBean = new PersonBean("lisi", 18);
String propertyName = "name";
Object retVal = getProperty(personBean, propertyName);
System.out.println(retVal);
Object value = "zxx";
setProperty(personBean, propertyName, value);
System.out.println(getProperty(personBean, propertyName));
}
private static void setProperty(Object bean, String propertyName,
Object value) throws IllegalAccessException,
InvocationTargetException, IntrospectionException {
/*
* PropertyDescriptor pd = new PropertyDescriptor(propertyName,
* bean.getClass()); Method methodSetName = pd.getWriteMethod();
* methodSetName.invoke(bean, value);
*/
BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());
PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor pd : pds) {
if (pd.getName().equals(propertyName)) {
Method methodSetName = pd.getWriteMethod();
methodSetName.invoke(bean, value);
break;
}
}
}
private static Object getProperty(Object bean, String propertyName)
throws IllegalAccessException, InvocationTargetException,
IntrospectionException {
/*
* PropertyDescriptor pd = new PropertyDescriptor(propertyName,
* bean.getClass()); Method methodGetName = pd.getReadMethod(); Object
* retVal = methodGetName.invoke(bean);
*/
BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());// BeanInfo是把一个java类当作javabean看的结果
PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
Object retVal = null;
for (PropertyDescriptor pd : pds) {
if (pd.getName().equals(propertyName)) {//当找到这个属性就对它进行操作
Method methodGetName = pd.getReadMethod();
retVal = methodGetName.invoke(bean);
break;//操作完成直接跳出循环
}
}
return retVal;
}
}
在开发中最常用的方式是用apache开发的开源的BeanUtils工具包
System.out.println(BeanUtils.getProperty(personBean, "name"));
BeanUtils.setProperty(personBean, "age", "38");// 参数类型为object
BeanUtils.setProperty(personBean, "name", "lhm");
System.out.println(BeanUtils.getProperty(personBean, "name").getClass()
.getName());
System.out.println(BeanUtils.getProperty(personBean, "age").getClass()
.getName());// 返回值类型为String
BeanUtils.setProperty(personBean, "birthday.time",
System.currentTimeMillis());
System.out.println(BeanUtils.getProperty(personBean, "birthday"));
/*java7的新特性
Map map={name:"zxx",age:18};
BeanUtils.setProperty(map, "name", "lhm");
BeanUtils.getProperty(map, "name");*/
PropertyUtils.setProperty(personBean, "age", 38);// 参数类型是javabean类中属性的类型
System.out.println(PropertyUtils.getProperty(personBean, "age")
.getClass().getName());// 返回值类型为javabean类中属性的类型
类加载器
什么是类加载器
就是加载类的工具
类加载器的作用
在源程序中用到的类,首先JVM要在硬盘上找到这个类的class文件,加载到内存中再进行一些处理
java虚拟机中有多个类加载器,系统默认提供了三个主要的类加载器
每个类加载器负责加载特定位置的类
BootStrap,ExtClassLoader,AppClassLoader
类加载器本身也是java类,因为其他java类的类加载器也要被类加载器加载,所以要有第一个不是java类的类加载器,来加载这些类加载器,就是BootStrap
BootStrap不是java类,它不需要被类加载器加载,是嵌套在java虚拟机内核里面的,是用C++写的
java虚拟机中所有类加载器采用具有父子关系的树形结构进行组织,在实例化每一个类加载器对象时,需要为其指定一个父级类加载器对象或者默认系统类加载器为其父级类加载器
类加载器的委托机制
当java虚拟机需要加载一个类时,到底要用哪个类加载器去加载呢
首先用当前线程的类加载器去加载线程中的第一个类
如果类A中引用了类B,java虚拟机将用加载类A的类加载器去加载类B
还可以用ClassLoader.loadClass()方法去指定某个类加载器去加载某个类
第个类加载器加载时,都先委托给其上级类加载器
当所有上级类加载器没有加载到类,回到发起者类加载器,如果还加载不了,则抛ClassNotFoundException,不是再去找发起者类加载器的下级,因为没有getChild方法,即使有,那有多个下级,找哪一个呢
有一首面试题,能不能自己写一个类叫java.lang.System
能写,但写出之后,java虚拟机永远不会调用这个System类
因为当我用到System类时,java虚拟机会AppClassLoader ,而这时就会委托给它的上级ExtClassLoader,又委托给BootStrap类加载器去加载java类库中的System,这样就不会用到自己写的System类
如果自己写一个类加载器,而不指定它的上级类加载器,就可以用到了
编写自己的类加载器
package cn.itcast.day2;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class MyClassLoader extends ClassLoader {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
String srcPath=args[0];
String destDir=args[1];
String destFileName=srcPath.substring(srcPath.lastIndexOf("\\")+1);
String destPath=destDir+"\\"+destFileName;
InputStream in=null;
try {
in=new FileInputStream(srcPath);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
OutputStream out=null;
try {
out=new FileOutputStream(destPath);
cipher(in,out);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
finally{
if(in!=null)
try {
in.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(out!=null)
try {
out.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
private static void cipher(InputStream ips,OutputStream ops){
int b=-1;
try {
while((b=ips.read())!=-1){
ops.write(b ^ 0xff);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private String classDir;
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
// TODO Auto-generated method stub
String classFileName=classDir+"\\"+name+".class";
try {
FileInputStream fis = new FileInputStream(classFileName);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
cipher(fis, bos);
fis.close();
byte[] buf = bos.toByteArray();
return defineClass(buf, 0, buf.length);
} catch (Exception e) {
// TODO: handle exception
}
return super.findClass(name);
}
public MyClassLoader(){}
public MyClassLoader(String classDir){
this.classDir=classDir;
}
}
代理
当我们想在原有的类上增加一些功能,比如测试程序运行时间,我们不用修改原有的代码
而是创建一个代理类,在这个类中定义一个相同的方法,在方法中调用目标类的方法
这样不但保持了代码的封装性,也控制了对目标类的对象的访问
这个被代理的类一般称为目标类或委托类
一般情况下,代理类和目标类有同样的接口
代理类为目标类提供了额外的处理和操作:日志记录,性能统计,安全控制,事务处理,异常处理等等。代理类的对象并不实现真正的服务,而是通过调用目标类的对象的相关方法提供服务
采用代理是为了通过不修改源代码的情况下给程序动态统一添加功能,利用代理技术可以将业务逻辑中一些非业务逻辑的代码分离出来,把他们独立到业务逻辑类外,比如日志记录,性能统计,安全控制,事务处理,异常处理等。这样做,不仅降低了业务逻辑和非业务逻辑的耦合性,提高程序的可重用性,同时提高了开发的效率
如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,只要在配置文件中明确要用目标类还是代理类。这样以后很容易切换,如果想要日志功能时,就配置代理类,否则配置目标类。这样,增加系统功能很容易,以后运行一段时间后,又想换掉系统功能也很容易
系统中存在着交叉业务,安全、事务、日志等功能要贯穿于好多个模块中,所以他们就是交叉业务一个交叉业务就是要切入到系统中的一个方面
交叉业务的问题即为面向方面编程(Aspect Oriented Programming 简称AOP)AOP的目标,就是让交叉业务模块化,可以使用将切面移动到原始方法的周围,这与直接在方法中编写切面代码的运行效果是一样的。
使用代理技术正好可以解决这种问题,代理是实现AOP功能的核心和关键技术
动态代理技术
静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的文件就已经存在了
动态代理:在程序运行时,运用反射机制动态创建而成
要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,全部采用静态代理方式,将是一件非常麻烦的事情!写成百上千个代理类,太繁琐了
JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理类
JVM生成的动态类必须实现一个或多个接口,所以,生成的动态类只能用作具有相同接口的目标类的代理
CGLIB(Code Generation Library)库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库
代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外,还可以在代理方法中的如下四个位置加上系统功能代码:
1,在调用的目标方法之前
2,在调用目标方法之后
3,在调用目标方法前后
4,在处理目标方法异常的catch块中
创建动态代理类对象
package cn.itcast.day3;
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 {
/**
* @param args
*/
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
Class<?> clazzProxy1 = Proxy.getProxyClass(
Collection.class.getClassLoader(), Collection.class);
// Proxy.getProxyClass(loader, interfaces)这里的loader通常用后面interface的类加载器
System.out.println(clazzProxy1.getName());
System.out
.println("-------------------begin constructor list----------------------");
Constructor<?>[] costructors = clazzProxy1.getConstructors();
for (Constructor<?> constructor : costructors) {
String name = constructor.getName();
StringBuilder sBuilder = new StringBuilder();
sBuilder.append(name + "(");
Class<?>[] clazzParams = constructor.getParameterTypes();
if (clazzParams.length != 0) {
for (Class<?> clazzParam : clazzParams) {
sBuilder.append(clazzParam.getName() + ", ");
}
sBuilder.delete(sBuilder.length() - 2, sBuilder.length());
}
sBuilder.append(")");
System.out.println(sBuilder);
}
System.out
.println("-------------------begin method list----------------------");
Method[] methods = clazzProxy1.getMethods();
for (Method method : methods) {
String name = method.getName();
StringBuilder sBuilder = new StringBuilder();
sBuilder.append(name + "(");
Class<?>[] clazzParams = method.getParameterTypes();
if (clazzParams.length != 0) {
for (Class<?> clazzParam : clazzParams) {
sBuilder.append(clazzParam.getName() + ", ");
}
sBuilder.delete(sBuilder.length() - 2, sBuilder.length());
}
sBuilder.append(")");
System.out.println(sBuilder);
}
System.out
.println("-------------------begin create instance object list----------------------");
// 创建动态代理类对象方法1
Constructor<?> constructor = clazzProxy1
.getConstructor(InvocationHandler.class);
class MyInvocationHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
return null;
}
}
Collection<?> proxy1 = (Collection<?>) constructor
.newInstance(new MyInvocationHandler());
System.out.println(proxy1);
// 返回了null,说明它的toString方法返回的是null,如果对象为null那么此时应该报空指针异常,也可以调用代理类的方法来测试
proxy1.clear();// 这个方法没有返回值,可以调用
//proxy1.size();// 这个方法有返回值,报告了NullPointerException
// 创建动态代理类的方法2
Collection<?> proxy2 = (Collection<?>) constructor
.newInstance(new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
// TODO Auto-generated method stub
return null;
}
});
System.out.println(proxy2);
/*
* 刚才让JVM创建了动态类及其实例对象 都做了三件事
* 1.为Proxy的getProxyClass传递了目标类的接口,即Collection,告诉代理类生成的类中有哪些方法
* 2.指定了这个接口的ClassLoader,产生类的字节码必须有一个关联的类加载器对象
* 3.生成的为中的方法的代码是怎样的,也要由我们提供,
* 把我们的代码写在一个约定好了的接口对象的方法中,把对象传递给它,它调用了我的方法,就相当于插入了我的代码,
* 提供执行代码的对象就是那个InvocationHandler对象
* ,它是在创建动态类的实例对象的构造方法时传递进去的。在上面的InvocationHandler对象的invoke
* 方法中加一点代码,就可以看到这些代码被调用并运行了
*/
@SuppressWarnings("unchecked")
Collection<String> proxy3=(Collection<String>)Proxy.newProxyInstance(
Collection.class.getClassLoader(),
new Class[]{Collection.class},
new InvocationHandler(){
ArrayList<String> target=new ArrayList<String>();
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
//ArrayList<String> target=new ArrayList<String>();
//每调用一个方法都会去调用InvocationHandler的invoke方法,第调用一次就会有一个目标,ArrayList对象,所以size打印为0
long beginTime=System.currentTimeMillis();
Object retVal=method.invoke(target, args);
long endTime=System.currentTimeMillis();
System.out.println("running time is "+(endTime-beginTime)+" of "+method.getName());
return retVal;
}});
proxy3.add("zxx");
proxy3.add("zxx");
proxy3.add("zxx");
System.out.println(proxy3.size());
}
}
分析InvocationHandler内部原理
编写可生成代理和插入通告的通用方法
package cn.itcast.day3;
import java.lang.reflect.Method;
public interface Advice {
void beforeMethod(Method method);
void afterMethod(Method method);
}
package cn.itcast.day3;
import java.lang.reflect.Method;
public class MyAdvice implements Advice {
private long endTime;
private long beginTime;
@Override
public void beforeMethod(Method method) {
// TODO Auto-generated method stub
beginTime = System.currentTimeMillis();
System.out.println("到传志播客来学习了");
}
@Override
public void afterMethod(Method method) {
// TODO Auto-generated method stub
System.out.println("从传志播客毕业上班了");
endTime = System.currentTimeMillis();
System.out.println(method.getName()+" running time is " + (endTime - beginTime));
}
}
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;
}
final ArrayList<String> target = new ArrayList<String>();
@SuppressWarnings("unchecked")
Collection<String> proxy3 = (Collection<String>) getProxy(target,new MyAdvice());
proxy3.add("zxx");
proxy3.add("zxx");
proxy3.add("zxx");
System.out.println(proxy3.size());
----------------------ASP.Net+Android+IOS开发、.Net培训、期待与您交流! ----------------------