码迷,mamicode.com
首页 > 编程语言 > 详细

java之动态代理

时间:2015-04-03 09:35:48      阅读:178      评论:0      收藏:0      [点我收藏+]

标签:博客   程序员   java   程序开发   技术   

一、代理的基本概念
1、生活中的代理:例如买一台电脑我们往往从代理商那里买,而不是直接跑去总部直接买,不管从哪里买电脑都是一样的,从代理那里买我们可以不用跑来跑去的,省去了很多烦。
2、程序中的代理:要为已经存在的多个具有相同接口的目标类(有称为委托类)添加系统的功能,如异常处理、日记、方法运行的时间、事务管理等等,那么,该怎么做呢?如果我们直接修改接口实现显然违背了开闭原则,程序容易出错。有一种做法,就是编写一个与目标类有相同接口的代理类,代理类调用目标类的方法并且添加系统的功能。
3、客户端调用问题:我们在客户端调用时,有的时候需要切换目标类或者代理类,如日记功能、运行时间等,在我们系统稳定后就要使用目标类,那么怎么实现方便的切换呢?通过工厂模式和配置文件的形式进行管理,那么即使不修改客户端的源程序也可以很方便的进行目标类与代理类的切换了。
4、静态代理:静态代理是手动形式为目标类添加代理类和目标接口方法,目标类增多时代理类也需要增加,这样随着类的增多导致类爆炸问题。第二点,目标接口增多时,目标类和代理类都要去实现一遍,这样也增加了程序设计的复杂性。
5、动态代理:动态代理就是将目标接口中的所有方法和子类进行集中处理,通过JVM机制动态生成,用一个代理类就行。动态代理需要实现InvocationHandler接口。
6、动态代理的好处:(1)目标接口每添加一个方法不用再去做实现。(2)目标子类每次添加一个也不需要添加一个代理类。
7、CGLIB:如果要为一个没有实现接口的类提供动态代理的功能,可以使用CGLIB库。
8、AOP:即面向切面编程,AOP解决的是交叉业务的问题(交叉业务就是业务穿插到系统功能的某一个方面),为解决这种业务可以使用代理技术,所以代理技术是AOP的关键和核心技术。
二、代理的使用
1、静态代理
抽象类代码:
/**
 * 主题接口或者说目标类接口
 * @author zhu
 *
 */
public interface Subject {


void doSomething();

// void doOtherthing();
}


目标类代码:
public class Target implements Subject {


@Override
public void doSomething() {
System.out.println("目标类方法");
}


}


代理类代码:
public class Proxy implements Subject {
private Target target = new Target();


@Override
public void doSomething() {
System.out.println("系统功能:执行前!");
target.doSomething();
System.out.println("系统功能:执行后!");
}
}


客户端代码:
public class Client {

public static void main(String[] args) {
Subject subject = new Proxy();
subject.doSomething();
}


}


  从上面的代码可以看出,目标类接口每增加一个方法时,代理类都要去做实现。还有,没增加一个目标类,代理类都要增加一个。那么怎么解决这些问题呢?就是动态代理。
2、动态代理:通过动态代理方式获取Collection集合
(1)三步实现
                
 Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);// 第一步:获取Class字节码对象
                 Constructor constructor = clazzProxy1.getConstructor(InvocationHandler.class);// 第二步:通过Class得到构造器
                 class MyInvocationHander1 implements InvocationHandler{


        public Object invoke(Object proxy, Method method, Object[] args)
          throws Throwable {
          return null;
        }

}
Collection proxy = (Collection)constructor.newInstance(new MyInvocationHander1());// 第三步:获取Collection的代理类


(2)一步实现
final ArrayList target = new ArrayList();
Collection proxy = (Collection)Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler(){


public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
return null;
}

});


3、通过动态代理机制创建一个集合对象,并且进行封装
接口类:
public interface Advice {
void beforeMethod(Method method);
void afterMethod(Method method);
}


接口实现类:
public class MyAdvice implements Advice {
long beginTime = 0;
public void afterMethod(Method method) {
// TODO Auto-generated method stub
System.out.println("从传智播客毕业上班啦!");
long endTime = System.currentTimeMillis();
System.out.println(method.getName() + " running time of " + (endTime - beginTime));

}


public void beforeMethod(Method method) {
// TODO Auto-generated method stub
System.out.println("到传智播客来学习啦!");
beginTime = System.currentTimeMillis();
}


}


获取代理的方法:
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;
}


客户端调用:
              
  final ArrayList target = new ArrayList();
  Collection proxy = (Collection)getProxy(target,new MyAdvice());
  proxy.add("zxx");

总结:jvm创建动态代理类需要哪些信息呢?
1)生成的类中有哪些方法,通过让其实现哪些接口的方式告知。
2)生成的类的字节码必须有一个关联的类加载器对象。
3)生产类的方法由客户端代码提供。
四、动态代理的内部原理
1、动态生成的类实现了目标接口Collection(可以实现若干接口),生产的类有Collection接口的所有方法和一个接收InvocationHandler参赛的构造方法。
2、构造方法接收InvocationHandler参数,用来方便我们实现系统功能,这里通过反射机制动态处理的。
3、InvocationHandler接口中的三个参数
invoke(Object proxy, Method method, Object[] args):proxy是代理的实例对象如Collection接口,method指接口的方法,args指方法中的参数。
4、为什么动态类的实例对象的getClass()方法返回了正确结果呢?
调用代理对象时,从Object继承的hashCode、toString、equals几个方法时,代理对象请求转发给InvocationHandler对象,对于Object的其它方法则不转发代理。
三、代理与应用
1、实现AOP功能的封装与配置:在我们的AOP框架中,用户可以通过配置文件实现与代理类的相互切换,从而方便灵活的切换系统功能,如日志等。在这样的框架中有一个Bean工厂,通过工厂我们可以得到类的实例对象,在工程里面我们封装了代理工厂,从而实现了代理功能。
(1)Bean工厂:工厂类BeanFactory负责创建目标类或代理类的实例对象,并通过配置文件实现切换。其getBean方法根据参数字符串返回一个相应的实例对象,如果参数字符串在配置文件中对应的类名不是ProxyFactoryBean,则直接返回该类的实例对象,否则,返回该类实例对象的getProxy方法返回的对象。BeanFactory的构造方法接收代表配置文件的输入流对象,配置文件格式
如下:
#xxx=java.util.ArrayList
xxx=cn.itcast.ProxyFactoryBean
xxx.target=java.util.ArrayList

xxx.advice=cn.itcast.MyAdvice

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 (Exception e) {
// TODO Auto-generated catch block
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) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return proxy;
}
return bean;
}
}

(2)ProxyFacotryBean充当封装生成动态代理的工厂,需要为工厂类提供哪些配置参数信息?目标和通知
public class ProxyFactoryBean {


private Advice advice;
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() {
// TODO Auto-generated method stub
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;
}

}


(3)客户端程序
public class AopFrameworkTest {// 通过配置文件获取目标对象和通告对象


/**
* 1、拿到配置文件
* 2、通过类名称得到类的实例对象
*/
public static void main(String[] args) throws Exception {
// 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).clear();

}
}

2、代理与jdbc

  动态代理一个典型应用就是实现数据库的连接池。由于使用JDBC的程序员习惯在使用完数据库的Connection对象后调用该对象的close方法关闭连接,这样连接池就失去作用了。这个问题我们可以使用动态代理技术来解决,当程序员调用Connection的close方法时,我们将其拦截下来,将其归还到连接池中,而不是直接关闭物理连接。


java之动态代理

标签:博客   程序员   java   程序开发   技术   

原文地址:http://blog.csdn.net/u010213127/article/details/44835915

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!