欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Java实现AOP面向切面编程的实例教程

程序员文章站 2024-03-11 18:08:25
介绍 众所周知,aop(面向切面编程)是spring框架的特色功能之一。通过设置横切关注点(cross cutting concerns),aop提供了极高的扩展性。那a...

介绍

众所周知,aop(面向切面编程)是spring框架的特色功能之一。通过设置横切关注点(cross cutting concerns),aop提供了极高的扩展性。那aop在spring中是怎样运作的呢?当你只能使用core java,却需要aop技术时,这个问题的解答变得极为关键。不仅如此,在高级技术岗位的面试中,此类问题也常作为考题出现。这不,我的朋友最近参加了一个面试,就被问到了这样一个棘手的问题——如何在不使用spring及相关库,只用core java的条件下实现aop。因此,我将在本文中提供一份大纲,帮助大家了解如何只用core java实现一个aop(当然啦,这种aop在功能上有一定的局限性)。注意,本文不是一篇有关spring aop与java aop的对比研究,而是有关在core java中借助固有的设计模式实现aop的教程。

想必读者已经知道aop是什么,也知道在spring框架中如何使用它,因此本文只着眼于如何在不用spring的前提下实现aop。首先,我们得知道,spring是借助了jdk proxy和cglib两种技术实现aop的。jdk dynamic proxy提供了一种灵活的方式来hook一个方法并执行指定的操作,但执行操作时得有一个限制条件:必须先提供一个相关的接口以及该接口的实现类。实践出真知,让我们透过一个案例来理解这句吧!现在有一个计算器程序,用于完成一些数学运算。让我们来考虑下除法功能,此时的问题是:如果core framework 已经具备了一份实现除法的代码,我们能否在代码执行时劫持(highjack)它并执行额外的校验呢?答案是肯定的,我将用下面提供的代码片段来证明这点。首先来看基础接口的代码:

public interface calculator {
  public int calculate( int a , int b);
}

该接口实现类的代码如下:

public class calculatorimpl implements calculator {
  @override
  public int calculate(int a, int b) {
    return a/b;
  }
}

假设我们既不能修该上面的代码,也不能对核心库进行任何改动,怎样才能完美地实现校验功能呢?不如试下jdk dynamic proxy的功能吧。

public class somehandler implements invocationhandler {
 
// code omitted for simplicity…..
 
  @override
  public object invoke(object proxy, method method, object[] params) throws throwable {
// your complex business validation and logic
    object result = method.invoke(targetobject ,params);
    return result;
  }
 
}

让我们通过测试类来看看由jdk dynamic proxy实现的校验功能的效果如何。

public static void main(string[] args) {
    calculatorimpl calcimpl = new calculatorimpl();
    calculator proxied = (calculator)proxyfactory.getproxy (calculator.class, calcimpl, 
        new somehandler(calcimpl));
    int result = proxied.calculate(20, 10);
    system.out.println("final result :::" + result);
  }

从结果可以看出,简单地实现功能强大的invocationhandler接口,我们便能得到一个hooking implementation。按照jdk文档的描述,invocationhandler接口是借助一个代理实例(proxy instance)来处理一个方法调用的。

现在我们已经知道,invocationhandler的invoke()方法能够帮助我们解决问题。那么再来解决一个新问题——怎样才能在方法执行的前后执行操作呢?说的更具体一些,我们能通过添加多个aop(before、after、around)来hook一个方法吗(译注:原文为add multiple aops,但我认为handler是充当aspect的角色)?答案同样是肯定的。按照以下的步骤建立一个精简的代码模板便能满足这样的需求:

  • 创建一个抽象类,用于将aop应用于目标对象上。
  • 创建名为beforehandler 和 afterhandler的两个aop。前者在方法执行之前工作,而后者则在方法执行结束后工作。
  • 创建一个代理类,使所有的aop handler和目标对象只需作为参数传入,就能创建一个hook。
  • 加入你自己的业务逻辑或者横切关注点。
  • 最后,通过传入相关的参数创建代理对象(proxy object)。

两种实现aop的方式: 

1,jdk提供的动态代理实现  
接口 

public interface userbean 
{ 
  void getuser(); 
  void adduser(); 
  void updateuser(); 
  void deleteuser(); 
} 

原始实现类 

public class userbeanimpl implements userbean 
{ 
  private string user = null; 
  public userbeanimpl() 
  {     
  }   
  public userbeanimpl(string user) 
  { 
    this.user = user; 
  }   
  public string getusername() 
  { 
    return user; 
  }   
  public void getuser() 
  { 
    system.out.println("this is getuser() method!"); 
  } 
 
  public void setuser(string user) 
  { 
    this.user = user; 
    system.out.println("this is setuser() method!"); 
  } 
  public void adduser() 
  { 
    system.out.println("this is adduser() method!"); 
  } 
   
  public void updateuser() 
  { 
    system.out.println("this is updateuser() method!"); 
  }   
  public void deleteuser() 
  { 
    system.out.println("this is deleteuser() method!");  
  }     
} 

代理类  

import java.lang.reflect.invocationhandler; 
import java.lang.reflect.method; 
import java.lang.reflect.proxy; 
import com.cignacmc.finance.bean.userbeanimpl; 
 
public class userbeanproxy implements invocationhandler 
{ 
  private object targetobject; 
   
  public userbeanproxy(object targetobject) 
  { 
    this.targetobject = targetobject;     
  } 
   
  public object invoke(object proxy, method method, object[] args) throws throwable 
  { 
    userbeanimpl userbean = (userbeanimpl) targetobject; 
    string username = userbean.getusername(); 
    object result = null; 
     
    //权限判断 
    if(username != null && !"".equals(username)) 
    { 
      result = method.invoke(targetobject, args); 
    } 
     
    return result; 
  } 
} 

 
测试类  

import java.lang.reflect.proxy; 
 
import com.cignacmc.finance.bean.userbean; 
import com.cignacmc.finance.bean.userbeanimpl; 
import com.cignacmc.finance.proxy.userbeanproxy; 
 
public class proxyexe 
{ 
  public static void main(string[] args) 
  { 
    system.out.println("proved............."); 
    userbeanimpl targetobject = new userbeanimpl("bob liang");    
    userbeanproxy proxy = new userbeanproxy(targetobject); 
    //生成代理对象     
    userbean object = (userbean)proxy.newproxyinstance(targetobject.getclass().getclassloader(),  
        targetobject.getclass().getinterfaces(), proxy); 
    object.adduser(); 
     
    system.out.println("no proved............."); 
    targetobject = new userbeanimpl();    
    proxy = new userbeanproxy(targetobject); 
    //生成代理对象     
    object = (userbean)proxy.newproxyinstance(targetobject.getclass().getclassloader(),  
        targetobject.getclass().getinterfaces(), proxy); 
    object.adduser(); 
         
  } 
} 

 
输出:  

proved............. 
this is adduser() method! 
no proved............. 

 
从上面这个例子可以成功拦截了调用的方法adduser()并对其做了相应的处理  
 

2, 通过cglib创建代理类

好处是不要求我们的目标对象实现接口 
原始类 

public class clientbean 
{ 
  private string name = null; 
 
  public clientbean() 
  { 
 
  } 
 
  public clientbean(string name) 
  { 
    this.name = name; 
  } 
 
  public void addclient() 
  { 
    system.out.println("this is addclient() method!"); 
  } 
 
  public void deleteclient() 
  { 
    system.out.println("this is deleteclient() method!"); 
  } 
 
  public void getclient() 
  { 
    system.out.println("this is getclient() method!"); 
  } 
 
  public void updateclient() 
  { 
    system.out.println("this is updateclient() method!"); 
  } 
 
  public string getclientname() 
  { 
    return name; 
  } 
 
  public void setclientname(string name) 
  { 
    this.name = name; 
  } 
} 

代理类 

import java.lang.reflect.method; 
 
import com.cignacmc.finance.bean.clientbean; 
 
import net.sf.cglib.proxy.enhancer; 
import net.sf.cglib.proxy.methodinterceptor; 
import net.sf.cglib.proxy.methodproxy; 
 
public class cglibproxy implements methodinterceptor 
{ 
  private object targetobject; 
   
  public object createproxyobject(object targetobject) 
  { 
    this.targetobject = targetobject; 
    enhancer enhancer = new enhancer(); 
    enhancer.setsuperclass(this.targetobject.getclass()); 
    enhancer.setcallback(this); 
    return enhancer.create(); 
  } 
   
  public object intercept(object proxy, method method, object[] args, methodproxy methodproxy) throws throwable 
  { 
    clientbean clientbean = (clientbean)targetobject; 
    string username = clientbean.getclientname(); 
    object result = null; 
     
    if(username != null && !"".equals(username)) 
    { 
      result = method.invoke(targetobject, args); 
    } 
    return result; 
  } 
} 

测试类  

import java.lang.reflect.proxy; 
 
import com.cignacmc.finance.bean.clientbean; 
import com.cignacmc.finance.bean.userbean; 
import com.cignacmc.finance.bean.userbeanimpl; 
import com.cignacmc.finance.proxy.cglibproxy; 
import com.cignacmc.finance.proxy.userbeanproxy; 
 
public class proxyexe 
{ 
  public static void main(string[] args) 
  {   
    system.out.println(".............cglib proxy...................."); 
    system.out.println("proved...................."); 
    cglibproxy cproxy = new cglibproxy(); 
    clientbean clientbean = (clientbean)cproxy.createproxyobject(new clientbean("bob liang")); 
    clientbean.addclient(); 
     
    system.out.println("no proved...................."); 
    cproxy = new cglibproxy(); 
    clientbean = (clientbean)cproxy.createproxyobject(new clientbean()); 
    clientbean.addclient(); 
         
  } 
} 

 
输出: 

.............cglib proxy.................... 
proved.................... 
this is addclient() method! 
no proved....................