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

作者:elviskang 时间:2023-02-20 19:32:38 

介绍

众所周知,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....................
标签:Java,AOP
0
投稿

猜你喜欢

  • 使用itextpdf操作pdf的实例讲解

    2022-11-16 00:22:43
  • 简单总结C++中指针常量与常量指针的区别

    2022-06-28 17:33:12
  • list集合去除重复对象的实现

    2022-10-16 23:02:42
  • SpringBoot项目中遇到的BUG问题及解决方法

    2022-01-19 14:44:38
  • WinForm开发中屏蔽WebBrowser脚本错误提示的方法

    2021-06-23 08:49:40
  • C#执行存储过程并将结果填充到GridView的方法

    2022-08-08 06:25:38
  • Java 8 lambda表达式引入详解及实例

    2022-10-25 22:41:37
  • 在C#中使用Channels的完整教程

    2021-11-03 15:41:28
  • C#6 null 条件运算符

    2022-03-04 19:42:14
  • Java设计模式之访问者模式

    2023-04-17 20:00:15
  • 详解基于Android App 安全登录认证解决方案

    2022-12-28 04:45:18
  • Android动画之雷达扫描效果

    2021-06-10 08:06:52
  • 十种JAVA排序算法实例

    2022-11-11 00:19:54
  • Android使用TextView,设置onClick属性无效的解决方法

    2022-06-27 11:32:39
  • SpringBoot AOP使用笔记

    2023-12-09 13:25:50
  • Mybatis-Plus自动填充更新操作相关字段的实现

    2022-01-14 20:43:01
  • java 模仿拼多多红包递减算法的实现

    2022-05-11 11:03:30
  • Android中Retrofit的简要介绍

    2022-07-19 08:42:49
  • .NET/C#实现识别用户访问设备的方法

    2021-12-20 06:30:58
  • MyBatis-Plus多表联查(动态查询)的项目实践

    2023-11-19 21:43:17
  • asp之家 软件编程 m.aspxhome.com