Java 高新技术(3)
---------- android培训、java培训、期待与您交流!----------
1、内省
JavaBean:是一种特殊的Java类,其满足特定的规则:(1) JavaBean类必须是具体的和公共的,不能是抽象的类或者其它访问权限。
(2) 必须提供一个无参数的构造方法,可以提供若干有参构造方法,但是此时必须手写一个无参构造方法。
(3) JavaBean类中属性最好声明为私有权限,定义setter和getter方法类对属性进行操作。
满足以上规定的类可以认为一个普通的类,也可以认为是一个JavaBean类,在开发框架时,JavaBean类通常用于存储信息、利用其属性封装程序的数据等。
内省:如果每次通过反射技术来完成对JavaBean属性的操作过于麻烦,内省是用于专门操作JavaBean对象的属性。
内省(IntroSpector)是Java语言对Bean类属性、事件的一种缺省处理方法。这些API存放在java.beans包下,我们可以利用内省的方法操作JavaBean对象。
操作JavaBean有多种方法:
(1) 通过PropertyDescriptor来操作Bean对象(java.beans.PropertyDescriptor)
(2) 通过IntroSpector类中的IntroSpector.getBeanInfo方法获得JavaBean的BeanInfo对象
下面代码演示了使用了这两种方法操作JavaBean:
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 JavaBeanDemo {
public static void main(String[] args) throws Exception {
Person p1 = new Person("hao", 123);
String proname = "name";
String proage = "age";
setProperty(p1, proname, "dwd");//通过使用PropertyDescriptor的getWriteMethod方法设置属性值
System.out.println(getProperty(p1, proname));//通过使用PropertyDescriptor的getReadMethod方法获取属性值
setProp(p1, proname,"dsasfsf");//通过使用Introspector的getBeanInfo方法设置属性值
System.out.println(getProp(p1, proname));//通过使用Introspector的getBeanInfo方法获取属性值
}
private static void setProperty(Object p, String proname, Object value)
throws IntrospectionException, IllegalAccessException,
InvocationTargetException {
PropertyDescriptor pset = new PropertyDescriptor(proname, p.getClass());
Method wmethod = pset.getWriteMethod();
wmethod.invoke(p, value);
}
private static Object getProperty(Object p, String proname)
throws IntrospectionException, IllegalAccessException,
InvocationTargetException {
PropertyDescriptor pget = new PropertyDescriptor(proname, p.getClass());
Method rmethod = pget.getReadMethod();
Object robj = rmethod.invoke(p);
return robj;
}
public static Object getProp(Object p, String proname) throws Exception{
BeanInfo info = Introspector.getBeanInfo(p.getClass());//通过内省的getBeanInfo方法获取BeanInfo
PropertyDescriptor[] pd = info.getPropertyDescriptors();
Object value = null;
for(PropertyDescriptor pp : pd){
if(pp.getName().equals(proname)){
Method m = pp.getReadMethod();
value = m.invoke(p);
break;
}
}
return value;
}
public static void setProp(Object p, String proname, Object value) throws Exception{
BeanInfo info = Introspector.getBeanInfo(p.getClass());//通过内省的getBeanInfo方法获取BeanInfo
PropertyDescriptor[] pd = info.getPropertyDescriptors();
for(PropertyDescriptor pp : pd){
if(pp.getName().equals(proname)){
Method m = pp.getWriteMethod();
value = m.invoke(p, value);
break;
}
}
}
}
(3) 通过使用BeanUtils工具包操作JavaBean
由上述可看出,内省操作非常的繁琐,所以Apache开发了一套简单、易用的API来操作Bean的属性——BeanUtils工具包。以下是常用的获取与设置属性的静态方法:
static String getProperty(Object bean, String name):获取属性值,参数是bean对象和属性名
static void setProperty(Object bean, String name, Object value):设置属性,参数是字符串或基本类型自动包装
下面代码演示了使用了BeanUtils工具包操作JavaBean:
import org.apache.commons.beanutils.BeanUtils;
public class BeanUtilsDemo {
public static void main(String[] args) throws Exception {
Person p1 = new Person("hao", 22);
String proname = "name";
BeanUtils.setProperty(p1, proname, "hello");
System.out.println(BeanUtils.getProperty(p1, proname));
}
}
BeanUtils的特点:
(1) 对基本数据类型的属性的操作:在WEB开发、使用中,录入和显示时,值会被转换成字符串,但底层运算用的是基本类型,这些类型转到动作由BeanUtils自动完成
(2) 对引用数据类型的属性的操作:首先在类中必须有对象,不能是null,例如private Date birthday=new Date(),并且要记住BeanUtils操作的是对象的属性而不是整个对象。
2、类加载器
Java虚拟机中可以安装多个类加载器,系统默认的主要有三个类加载器,每个负责加载特定位置的类。类加载器也是java类,是java类的类加载器本身也要被类加载器加载,那么就必须有一个类加载器不是java类,就是BootStram。类加载器的关系如下图所示:
类加载器的几种类型:
(1) Bootstrap ClassLoader
负责加载$JAVA_HOME中jre/lib/rt.jar里所有的class,由C++实现,不是ClassLoader子类
(2) Extension ClassLoader
负责加载java平台中扩展功能的一些jar包,包括<JAVA_HOME>/jre/lib/ext或者java.ext.dirs指定目录下的jar包
(3) App ClassLoader
负责加载classpath中指定的jar包及目录中class
(4) Custom ClassLoader
属于应用程序根据自身需要自定义的ClassLoader,如tomcat、jboss都会根据j2ee规范自行实现ClassLoader
类加载的机制:
如果我们编写一个类放到<JAVA_HOME>/jre/lib/ext后,运行时系统会从该目录加载类,而不是用AppClassLoader加载,这中间就有一种类加载的机制,这就是类加载的委托机制。类加载过程中会先检查类是否被已加载,检查顺序是自底向上,从Custom ClassLoader到BootStrap ClassLoader逐层检查,只要某个classloader已加载就视为已加载此类,保证此类只所有ClassLoader加载一次。而加载的顺序是自顶向下,也就是由上层来逐层尝试加载此类。
类加载的过程:
类加载器加载Class大致要经过如下8个步骤:
(1) 检测此Class是否载入过(即在缓存中是否有此Class),如果有则直接进入第(8)步,否则接着执行第(2)步。
(2) 如果父加载器不存在(如果没有父加载器,则要么parent一定是根加载器,要么本身就是跟加载器),则跳到第(4)步执行。如果父加载器存在,则接着执行第(3)步。
(3) 请求父加载器载入目标类,如果成功载入则跳到第(8)步,不成功接着执行第(5)步。
(4) 请求使用根加载器来载入目标类,如果成功到(8)。如果不成功跳到第(7)步。
(5) 寻找Class文件(从与此ClassLoader相关的类路径中寻找)。如果找到则执行第(6)步,如果找不到则跳到第(7)步。
(6) 从文件中载入Class,成功载入后跳到第(8)步。
(7) 抛出ClassNotFoundException。
(8) 返回Class。
3、动态代理
代理设计模式是Java开发中使用较多的一种模式,所谓代理就是指由一个代理类来操作真实类,代理类对真实类进行功能的扩充。
(1) 代理类的可以有两种方式获得:
(a) 静态代理:由程序员创建生成源代码,编写一个与目标类具有相同接口的代理类,代理类的每个方法调用目标类的相同方法,并在调用方法 时加上系统功能的代码,在程序运行前代理类就已经存在了。
(b) 动态代理:在程序运行时,JVM生成出字节码,运用反射机制动态创建而成。JVM生成一个动态代理类必须让其实现一个或者多个接口,所以动态代理类只能用作具有相同接口的目标类代理。
(2) 动态代理的作用:
采用代理是为了通过不修改源代码的情况下给程序动态统一添加功能,利用代理技术可以将业务逻辑中一些非业务逻辑的代码分离出来,把他们独立到业务逻辑类外,比如日志记录,性能统计,安全控制,事务处理,异常处理等。这样做,不仅降低了业务逻辑和非业务逻辑的耦合性,提高程序的可重用性,同时提高了开发的效率。
(3) 动态代理类的创建
Proxy提供了用于创建动态代理类和代理对象的静态方法,它也是所有动态代理类的父类,如果我们在程序中为一个或多个接口动态地生成实现类,就可以使用Proxy来创建动态代理类;如果需要为一个或多个接口动态地创建实例,也可以使用Proxy来创建动态代理实例。
下面的代码演示了如何动态的创建一个代理类:
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Collection;
public class ProxyDemo2 {
public static void main(String[] args) throws Exception {
//方式1:使用代理类的构造方法方法创建代理类对象
Class proxy = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
Constructor[] cons = proxy.getConstructors();
for(Constructor c : cons){
System.out.println(c); //由输出结果可知,代理类中只有一个有参构造方法
}
Constructor c = proxy.getConstructor(InvocationHandler.class);//获取其构造方法
Collection pro1 = (Collection)c.newInstance(new InvocationHandler() {//用构造方法创建代理对象
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
return null;
}
});
//方式2:直接使用Proxy.newProxyInstance方法创建代理类对象
Object pro2 = Proxy.newProxyInstance(
Collection.class.getClassLoader(),
new Class[]{Collection.class},
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
return null;
}
});
}
}
(4) 使用动态代理类代理目标类原理
客户端调用代理的方法,代理的构造方法接收InvocationHandler,代理的方法将调用请求转发给InvocationHandler,InvocationHandler转发给target中的方法,其中InvocationHandler中可以在target的方法周围添加所需要的其他增强系统功能的方法。
(5) 利用动态代理实现功能扩充
在上面的分析原理可以看到,我们在InvocationHandler实现类中创建目标类的实例对象,并且可以加入方法,但是这是手动编写代码来加入功能的,并不具备实际意义,所以我们可以通过使用某些手段将强功能的方法封装在一个类中,将该类的对象传到代理类中,增强target类的功能,即:让匿名的InvocationHandler实现访问外面方法中的目标类实例对象的final类型的引用变量。
具体的代码实现如下所示:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class ProxyDemo1 {
public static void main(String[] args) throws Exception {
ArrayList target = new ArrayList();
Collection c = (Collection)getProxy(target, new myDevice());
c.add("a");
c.add("b");
}
public static Object getProxy(final Object target, final Device device){
Object proxy = Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
device.beforeMethod(method);
Object obj = method.invoke(target, args);
device.afterMethod(method);
return obj;
}
});
return proxy;
}
}
interface Device{
void beforeMethod(Method m);
void afterMethod(Method m);
}
class myDevice implements Device{
public void beforeMethod(Method m) {
System.out.println("before");
}
public void afterMethod(Method m) {
System.out.println("after");
}
}
(6) 实现AOP功能的封装与配置
工厂类BeanFactory负责创建目标类或代理类的实例对象,并通过配置文件实现切换,其getBean方法根据参数字符串返回一个相应的实例对象,如果参数String在配置文件中对应的文件名不是ProxyFactoryBean,则直接返回该类的实例对象,否则返回该类实例对象的getProxy方法返回的对象。
实现的代码如下所示:
import java.io.InputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Properties;
import org.apache.commons.beanutils.BeanUtils;
public class ProxyDemo1 {
public static void main(String[] args) throws Exception {
Object bean = new BeanFactory(
ProxyDemo1.class.getResourceAsStream(
"config.properties")).getBean("hao");
System.out.println(bean.getClass());
System.out.println(bean.toString());
}
}
class BeanFactory{
Properties prop = new Properties();
public BeanFactory(InputStream in) throws Exception{
prop.load(in);
}
public Object getBean(String name) throws Exception{
String classname = prop.getProperty(name);
Class clazz = Class.forName(classname);
Object obj = clazz.newInstance();
if(obj instanceof ProxyFactoryBean){
ProxyFactoryBean pfb = (ProxyFactoryBean)obj;
MyAdvise advise = (MyAdvise)Class.forName(prop.getProperty(name+".advise")).newInstance();
Object target = Class.forName(prop.getProperty(name+".target")).newInstance();
BeanUtils.setProperty(pfb, "Advise", advise);//利用内省设置属性值
BeanUtils.setProperty(pfb, "Target", target);//利用内省设置属性值
return ((ProxyFactoryBean)obj).getProxy();
}else
return obj;
}
}
class ProxyFactoryBean{
private MyAdvise advise = new MyAdvise();
private Object target = new Object();
public MyAdvise getAdvise() {
return advise;
}
public void setAdvise(MyAdvise advise) {
this.advise = advise;
}
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
public Object getProxy(){
Object c = Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
public Object invoke(Object arg0, Method arg1, Object[] arg2)
throws Throwable {
advise.beforeMethod();
advise.afterMethod();
return null;
}
});
return c;
}
public String toString(){
System.out.println("hello");
return null;
}
}
class MyAdvise implements Advise{
public void beforeMethod() {
System.out.println("before");
}
public void afterMethod() {
System.out.println("after");
}
}
interface Advise{
public void beforeMethod();
public void afterMethod();
}
config.properties文件为:
#hao=java.util.ArrayList
hao=com.day6.ProxyFactoryBean
hao.advise=com.day6.MyAdvise
hao.target=java.util.ArrayList