`
java-mans
  • 浏览: 11427820 次
文章分类
社区版块
存档分类
最新评论

AOP之动态代理

 
阅读更多
代理分为静态代理和动态代理,采用代理是为了通过不修改源代码的情况下给程序动态统一添加功能,利用代理技术可以将业务逻辑中一些非业务逻辑的代码分离出来,把他们独立到业务逻辑类外,比如日志记录,性能统计,安全控制,事务处理,异常处理等。这样做,不仅降低了业务逻辑和非业务逻辑的耦合性,提高程序的可重用性,同时提高了开发的效率。

下面以添加日志记录为例,分析静态代理的使用。创建一个用户管理类UserManagerImpl,并创建添加用户方法addUser,为其良好扩展性,创建一个通用接口UserManager,代码分别如下:

接口代码

  1. packagecom.snail.pattern;
  2. publicinterfaceUserManager{
  3. publicvoidaddUser(StringuserId,StringuserName);
  4. }

实现类代码

  1. packagecom.snail.pattern;
  2. publicclassUserManagerImplimplementsUserManager{
  3. publicvoidaddUser(StringuserId,StringuserName){
  4. try{
  5. //System.out.println("开始执行");
  6. System.out.println("HelloWorld!");
  7. //System.out.println("执行成功!");
  8. }catch(Exceptione){
  9. e.printStackTrace();
  10. //System.out.println("执行失败!");
  11. thrownewRuntimeException();
  12. }
  13. }
  14. }

从代码可以看出,注释里面的日志内容和业务逻辑毫无关系,无形中使耦合性增加,如果很多类中需要添加这些日志代码,工作量不言而喻,修改起来也非常麻烦。如果采用静态代理把打印日志的代码抽取到代理类中,通过代理类和业务逻辑类继承自同一个父类,客户端直接调用代理类完成需求,这样就解决了客户端与业务逻辑类的耦合。示例代码如下:

  1. packagecom.snail.pattern;
  2. publicclassUserManagerImplProxyimplementsUserManager{
  3. privateUserManageruserManager;
  4. publicUserManagerImplProxy(UserManageruserManager){
  5. this.userManager=userManager;
  6. }
  7. @Override
  8. publicvoidaddUser(StringuserId,StringuserName){
  9. try{
  10. System.out.println("开始执行");
  11. userManager.addUser(userId,userName);
  12. System.out.println("执行成功!");
  13. }catch(Exceptione){
  14. e.printStackTrace();
  15. System.out.println("执行失败!");
  16. }
  17. }
  18. }

客户端调用代码如下:

  1. packagecom.snail.pattern;
  2. publicclassClient{
  3. /**
  4. *@paramargs
  5. */
  6. publicstaticvoidmain(String[]args){
  7. UserManageruserManager=newUserManagerImplProxy(newUserManagerImpl());
  8. userManager.addUser("0111","张三");
  9. }
  10. }

静态代理虽隔离了与业务逻辑无关的代码,降低了耦合,让业务逻辑类更专注于业务逻辑,但无法减少代码量,系统重复代码过多,加大了程序员工作量。因此,JDK动态代理完美解决了此问题,动态代理支持在系统运行期给类动态添加代理,然后通过操控代理类完成对目标类的调用。

继续演化上面举的例子,将静态代理改为动态代理,抽象类UserManager和目标类UserManagerImpl中的代码不变,将静态代理类UserManagerImplProxy删除,添加LoadHandler类,并让它实现InvocationHandler接口中的invoke方法,代码如下:

  1. packagecom.snail.pattern;
  2. importjava.lang.reflect.InvocationHandler;
  3. importjava.lang.reflect.Method;
  4. importjava.lang.reflect.Proxy;
  5. publicclassLogHandlerimplementsInvocationHandler{
  6. //保留一份targetObject目标类对象
  7. privateObjecttargetObject;
  8. //Proxy类动态创建一份目标代理类
  9. publicObjectnewProxyInstance(ObjecttargetObject){
  10. this.targetObject=targetObject;
  11. returnProxy.newProxyInstance(targetObject.getClass().getClassLoader(),targetObject.getClass().getInterfaces(),this);
  12. }
  13. @Override
  14. publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)
  15. throwsThrowable{
  16. System.out.println("开始执行!");
  17. for(inti=0;i<args.length;i++){
  18. System.out.println(args[i]);
  19. }
  20. Objectret=null;
  21. try{
  22. //调用目标方法
  23. ret=method.invoke(targetObject,args);
  24. System.out.println("执行成功!");
  25. }catch(Exceptione){
  26. e.printStackTrace();
  27. System.out.println("执行失败!");
  28. throwe;
  29. }
  30. returnret;
  31. }
  32. }

Proxy类所创建的目标类必须实现至少一个接口,在调用newProxyInstance方法时必须与目标类的类加载器和接口一致;invoke方法非常类似Filter中的doFilter方法,它将调用目标类的所有方法在未到达UserManagerImpl之前截获,根据我们自己的需求进行预处理后,继续调用UserManagerImpl。


为了保持invoke方法的通用性,目标方法中的参数以数组args形式传递,如果方法中有返回值,则返回,没有返回值,则返回null。如此一来,程序员不必为每个目标类设计一个代理类,所有需要打印日志的类都可以共用这个LogHandler,如果不想使用日志功能就可以直接删除LogHandler类,对原功能没有丝毫影响,如同揭去显示器上的保护膜,不会影响显示器的使用一般。

客户端调用代码如下:

  1. packagecom.snail.pattern;
  2. publicclassClient{
  3. /**
  4. *@paramargs
  5. */
  6. publicstaticvoidmain(String[]args){
  7. LogHandlerlogHandler=newLogHandler();
  8. UserManageruserManager=(UserManager)logHandler.newProxyInstance(newUserManagerImpl());
  9. userManager.addUser("id","name");
  10. }
  11. }

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics