动态代理

首先了解下动态代理技术出现的背景

在Java中若是需要为一个类扩充功能,大概有这几种方式

1、继承

 优点:方便、可以从父类中继承一些成员变量还有方法,若不满意某个方法的实现可以重写这个方法

 缺点:由于Java语言只能从一个类,因此不够灵活,并且会使类与类之间的关系耦合度强

2、装饰模式

 Java的io中就大量的使用了这个设计模式,把底层的InputStream和OutputStream通过一层层的装饰包装,不断的扩充功能(例如:可以读取字符流,加上缓存等等)

 优点:可以动态的给一个对象添加一些职责,可以比继承使用较少的类

    缺点:使用装饰模式会产生比使用继承关系更多的对象,出现错误的话不好查,回想一样,刚开始学习java的      io时,是不是感觉有点乱,new出来这个Stream放进那个对象的,一大堆对象,看着就晕,我是这样感觉     的,不知道你们刚学时神马情况,呵呵

3、组合方式:

  个人觉得组合方式和装饰模式在语言层面上都差不多(设计模式也是如此,有好几个感觉差不多,主要是解决的问题不同思想不同而已,),内部都有一个被扩展类的引用,在装饰模式中这个引用被传递过来,组合是直接在类中new出来,

组合方式有诸多好处,但是在实际的项目中,需要在不改变原来类的基础上扩展它的功能,需求是多种多样的,可能今天要在dao层上加上统计方法的执行时间,以便于数据库的优化,明天又想加入个日志功能等等,时间一长类的数量就会爆炸性的增长。



正是有这些需求,动态代理技术出现了,它可以动态的生成一段内部有一个被扩展类引用的代码,生成的类类似于用组合这种方式,并把它动态的编译成class文件,然后装载进jvm中执行。著名的框架Spring的核心技术之一aop就是使用的就是动态代理技术。

动态代理初级:

 Java中为动态代理提供了支持,用到了类Proxy用于产生代理对理,还有一个接口InvocationHandler,用于在被代理的类的方法前后加入处理逻辑,这个接口内部有一个invoke方法,invoke接受三个参数,

摘自Java api

-------------------------------------------------------------

      proxy -在其上调用方法的代理实例

method -对应于在代理实例上调用的接口方法的 Method 实例。Method 对象的声明类将是在其中声明方法的接口,该接口可以是代理类赖以继承方法的代理接口的超接口。

args -包含传入代理实例上方法调用的参数值的对象数组,如果接口方法不使用参数,则为 null。基本类型的参数被包装在适当基本包装器类(如 java.lang.Integer 或 java.lang.Boolean)的实例中。 

-------------------------------------------------------------

生成代理类的方法Proxy.newProxyInstance,它接受三个参数,第一个参数为被代理对象的类加载器,第二个参数为需要代理的接口,这些接口的所有方法都会被代理,第三个参数为建立的InvocationHandler的实例,现在就举个例子说明吧

 由于Jdk提供的代理只能大力接口,所以首先我们建议一个被代理的接口还有他的实现,就用项目中的dao层演示

现在有个用户访问数据库的Userdao,里面有个保存用户的方法,接受一个名字

public interface UserDao {
 void save(String username
);
}

UserDao的实现类UserDaoImpl

public class UserDaoImpl implements UserDao {
 
@Override
 public void save(String username)
{
  System.out.println(username+ "存进了数据库"); 

 }
}

现在想对UserDaoImpl记录日志,写一个处理类LogHandler实现InvocationHandler接口

public class LogHandler implements InvocationHandler {
 //被代理的对象
 private Object target;

 @Override
 public Object invoke(Object proxy,Method method, Object[] args
)
   throws Throwable{

  //对被代理对象target的对应方法进行调用,把代理类接受的参数传递进去
  Object result = method.invoke(target, args
);
  System.out.println(newSimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(newDate()) + "用户" + args[0]+ "
保存进数据库");
  return result
;
 
}
 

 //接收被代理的对象
 public LogHandler(Object target)
{
 
super();
  this.target = target
;
 
}
}

用于测试的有main的类  ProxyTest

public class ProxyTest {
 public static void main(String[] args){
  //被代理的UserDaoImpl对象
  UserDao ud = new UserDaoImpl(); 

  //生成代理对象,转成UserDao引用使用,注意这里不能转为UserDaoImpl的引用,只能使用对应接口,

  //这是由于接口可以规范方法
  UserDao proxy = (UserDao)Proxy.newProxyInstance
(
       ud.getClass().getClassLoader()//
加载生成代理类的加载器
       , ud.getClass().getInterfaces()//
需要代理的接口,这里为UserDao
       , newLogHandler(ud));//日志处理类

  //对代理对象调用save方法
  proxy.save("
黑马程序员");
 
}
}

控制台打印:

黑马程序员存进了数据库
2012-09-06
14:12:23用户黑马程序员保存进数据库

怎么样,这样就为UserDaoImpl动态的增加了功能,呵呵

 

动态代理中级:

 为例使生成代理的过程简单,对jdk的动态代理功能可以做下封装,下面这个类ProxyFactory,可以代理任何的接口,并能在方法的前后都加上逻辑,在出现异常时还能进行处理,此类模仿了Spring对动态代理的支持

处理逻辑的接口Advice

public interface Advice {

 //在被代理的方法执行前执行
 void before(Object proxy,Method method, Object[] args);

 //在被代理的方法执行后执行
 void after(Object proxy,Method method, Object[] args);

 //出现异常时执行
 void exception(Object proxy,Method method, Object[] args);

 //在被代理的方法执行前后都执行
 void beforeAndAfter(Object proxy,Method method, Object[] args);

}

代理生成工厂ProxyFactory

public class ProxyFactory {

 //被代理的对象
 private Object target;

 //在被代理的对象方法上加入的建议
 private Advice advice;

 //产生代理的方法
 public Object createProxy(){

  //和上面那个例子一样,使用jdk的动态代理api
  Object proxyObj =Proxy.newProxyInstance
(
    target.getClass().getClassLoader
(),
    target.getClass().getInterfaces(),

   //最主要就是这个地方了,请仔细看里面的逻辑
    new InvocationHandler(){

     @Override
     public Object invoke(Object proxy,Method method, Object[] args
)
       throws Throwable{      

      Object result= null;
      try {

       //在被代理方法前执行
       advice.beforeAndAfter(proxy, method, args
);
       advice.before(proxy, method, args);

       //调用目标方法
       result = method.invoke(target, args);

       //在被代理方法执行后执行
       advice.after(proxy, method, args);

       //呵呵,看到没,beforeAndAfter在前后都执行一次,达到了目的
       advice.beforeAndAfter(proxy, method, args
);
      } catch(Exception e) {

       //当出现异常时,就会调用建议中的对异常进行处理的exception方法
        advice.exception(proxy, method, args
);
     
}
      return result
;
     

     }
    });  


  return proxyObj;
}
 public ProxyFactory(Object target,Advice advice){

  this.target = target; 

  this.advice = advice;
 }

}

测试类AdviceImpl,ProxyFactoryTest

public class AdviceImpl implements Advice {
 

 @Override
 public void before(Object proxy,Method method, Object[] args)
{
  System.out.println
("before");
 
}

 
@Override
 public void after(Object proxy,Method method, Object[] args)
{
  System.out.println
("after");
  //
为了测试处理异常的方法是否执行,自己搞出来一个异常
  Integer.parseInt("
黑马程序员");
 
}

 
@Override
 public void exception(Object proxy,Method method, Object[] args)
{
  System.out.println
("exception");
 
}

 
@Override
 public void beforeAndAfter(Object proxy,Method method, Object[] args)
{
  System.out.println
("beforeAndAfter");
 
}
}

public class ProxyFactoryTest {
 public staticvoid main(String[] args){ 

  ProxyFactory proxyFactory= new ProxyFactory(new UserDaoImpl(),new AdviceImpl());
  UserDao ud =(UserDao)proxyFactory.createProxy
();
  ud.save("
黑马");
 
}
}

运行结果:

beforeAndAfter
before
黑马存进了数据库
after

exception

怎么样,经过了这层封装,客户端中获得一个代理对象,只需要两行代码而已,并且功能强大,如果愿意的话,还可以继续把代理对象和一个新的Advice扔进去,在获得一个新的代理对象,这样就可以不断的扩展功能了,spring的面向切面编程和声明实的异常处理都是这个原理。

 

动态代理高级:

动态代理在高级点,就要自己写代理程序了,jdk提供的只能代理接口,并且必须代理接口里的所有方法,自己写的话就试试写个即可以代理接口还可以代理类,还可以代理到具体以一个方法上面,暂时还没研究出来啊。

 

关于动态代理的知识可以去看下张孝祥老师的Java高新技术,张老师讲的特别好,特别详细,视频下载地址:

http://www.itcast.cn/channel/video.shtml#java

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值