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

 更新时间:2016年04月27日 18:36:22   作者:elviskang  
这篇文章主要介绍了Java实现AOP面向切面编程的实例教程,通常Java中的AOP都是利用Spring框架中造好的轮子来开发,而本文则关注于Java本身AOP的设计模式实现,需要的朋友可以参考下

介绍

众所周知,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.................... 

相关文章

  • 关于SpringBoot中的跨域问题

    关于SpringBoot中的跨域问题

    这篇文章主要介绍了关于SpringBoot中的跨域问题,同源策略是由Netscape提出的一个著名的安全策略,它是浏览器最核心也最基本的安全功能,现在所有支持JavaScript的浏览器都会使用这个策略,需要的朋友可以参考下
    2023-08-08
  • java使用任务架构执行任务调度示例

    java使用任务架构执行任务调度示例

    在Java 5.0之前启动一个任务是通过调用Thread类的start()方法来实现的,5.0里提供了一个新的任务执行架构使你可以轻松地调度和控制任务的执行,并且可以建立一个类似数据库连接池的线程池来执行任务,下面看一个示例
    2014-01-01
  • jvm垃圾回收GC调优基础原理分析

    jvm垃圾回收GC调优基础原理分析

    谈到调优,这一定是针对特定场景、特定目的的事情, 对于 GC 调优来说,首先就需要清楚调优的目标是什么?从性能的角度看,通常关注三个方面,内存占用(footprint)、延时(latency)和吞吐量(throughput)
    2022-01-01
  • 详解JVM类加载机制及类缓存问题的处理方法

    详解JVM类加载机制及类缓存问题的处理方法

    这篇文章主要给大家介绍了关于JVM类加载机制及类缓存问题的处理方法的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。
    2018-01-01
  • SpringDataJpa like查询无效的解决

    SpringDataJpa like查询无效的解决

    这篇文章主要介绍了SpringDataJpa like查询无效的解决,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • Java线程并发中常见的锁机制详细介绍

    Java线程并发中常见的锁机制详细介绍

    越来越多的互联网企业面临着用户量膨胀而带来的并发安全问题。接下来通过本文给大家介绍Java线程并发中常见的锁机制,感兴趣的朋友一起看看吧
    2016-05-05
  • Java中的CGLIB动态代理的使用及原理详解

    Java中的CGLIB动态代理的使用及原理详解

    这篇文章主要介绍了Java中的CGLIB动态代理的使用及原理详解,CGLIB是一个功能强大,高性能的代码生成包,它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充,需要的朋友可以参考下
    2023-09-09
  • IDEA配置Tomcat创建web项目的详细步骤

    IDEA配置Tomcat创建web项目的详细步骤

    Tomcat是一个Java Web应用服务器,实现了多个Java EE规范(JSP、Java Servlet等),这篇文章主要给大家介绍了关于IDEA配置Tomcat创建web项目的详细步骤,需要的朋友可以参考下
    2023-12-12
  • spring如何通过FactoryBean配置Bean

    spring如何通过FactoryBean配置Bean

    这篇文章主要介绍了spring如何通过FactoryBean配置Bean,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-01-01
  • Java设计模式之迭代器模式

    Java设计模式之迭代器模式

    这篇文章介绍了Java设计模式之迭代器模式,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-10-10

最新评论