前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >AOP原理与应用

AOP原理与应用

作者头像
topgunviper
发布2022-05-12 14:18:53
2480
发布2022-05-12 14:18:53
举报
文章被收录于专栏:啥都有的专栏
主要内容
代码语言:javascript
复制
1. AOP概述
2. Test Code
3. AOP原理与实现
    3.1 原理分析
    3.2 设计实现
4. 总结

1. AOP概述

AOP(Aspect Oriented Programming),即面向切面编程。被认为是对面向对象编程OOP的一种极大补充,大量应用于处理一些具有横切逻辑的系统中。比如:事务、缓存、安全检查等等。

为什么需要AOP?有什么OOP解决不了的吗?

在OOP的世界中,一切是以对象为核心,我们所构建的系统就是若干个具有独立状态和行为的对象组成。但随着软件日益复杂,OOP渐渐无法很好的解决我们所面对的问题。比如上面提到的事务处理,不用AOP的思想当然也可以解决:

方案1: 对于一个事务的处理过程进行分析,我们知道每个事务都包含事务的开启,提交、回滚等操作。那么最简单粗暴的方法就是将事务开启、提交、回滚的代码进行Ctrl+C、Ctrl+V。

方案2: 方案1思路太low,需要高级点的,OOP中的抽象、继承、封装啊!那好,构造一个公共的基类,将事务开启、提交、回滚等操作封装在基类中,然后每一个需要处理事务的类都继承这个基类,进行相应的方法调用即可。这不就实现代码复用了嘛。。。

这样就解决问题了?继续。。。

随着系统的发展,需求新增、变更必然是相当频繁的。新需求来了:需要某些处于事务中的方法执行前加上请求者的身份验证的操作。

哎。。。继续改!于是乎在基类中新增身份验证的处理逻辑。同时修改子类中对应方法的处理逻辑。

然后。。。新需求又来了。。。

这就是痛点,有痛点就会有解决方案:AOP应运而生。

本文主要探究AOP的原理、实现AOP所用到的一些方法。

2. Test Code

代码语言:javascript
复制
public class AdvisorChainTest extends BaseTest{

    static class ProxyBuilder{
        public static Object buildProxy(Class<?> interfaces, Object target, List<MethodInterceptor> list){
            return Proxy.newProxyInstance(ProxyBuilder.class.getClassLoader(), 
                    new Class<?>[]{interfaces},  new DefaultInvocationHandler(target, list));
        }
    }

    @Test
    public void testAOP() {
        
        Set<String> exclusionMethodNames = new HashSet<String>();
        exclusionMethodNames.add("delete");//拦截delete方法
        
        //过滤方法的拦截器
        MethodFilterInterceptor filter = new MethodFilterInterceptor(exclusionMethodNames);
        
        //记录方法执行时间拦截器
        TimeLogInterceptor time = new TimeLogInterceptor();
        
        List<MethodInterceptor> list = Arrays.asList(filter,time);
        
        UserService user = new UserServiceImpl();
        UserService proxy = (UserService)ProxyBuilder.buildProxy(UserService.class,user,list);

        proxy.update(null);
        assertTrue(time.msg.contains("update execute time:"));
        
        try{
            proxy.delete(null);
            fail("method delete is not allowed!");
        }catch(Exception e){
            assertEquals(MethodNotAllowedException.class, e.getClass());
        }
    }
}

在测试用例中,实现了两个拦截器:TimeLogInterceptor,MethodFilterInterceptor;一个用于记录方法执行时间,另一个用于方法过滤。

3. AOP原理实现

3.1 原理分析

在上一个小节AOP概述中,已经分析出AOP要解决的问题都有一个共性:即所谓的横切逻辑。比如测试用例中的TimeLogInterceptor,用于统计方法执行的时间。对于这一需求,首先想到的使用代理的方法。当然,这里指的就是动态代理。实现思路如下:

通过代理对象拦截对目标方法的调用操作,在回调方法中添加需要的横切逻辑。

Proxy

还有一个问题:我是实现的横切逻辑不止一个的情况怎么办?难道在Proxy对象的回调方法中进行方法堆积吗?这不又回到了第一节中的方案1了。针对这种问题,一个比较好的处理思路是:责任链。结构图如下:

软件设计准则:在重要的过程上设置拦截接口,这是体现软件可扩展性的一种基本实现方式。

Proxy + Chain of Responsibility

3.2 设计实现

  1. 对每次方法调用进行抽象:MethodInvocation接口,拦截器接口:MethodInterceptor
代码语言:javascript
复制
/**
 * MethodInvocation代表方法的执行
 * 
 * @author wqx
 *
 */
public interface MethodInvocation {
    
    /**
     * 获取方法对象
     * 
     * @return
     */
    public Method getMethod();
    
    /**
     *  获取参数
     * 
     * @return
     */
    public Object[] getParameters();
    
    /**
     * 执行下一个方法
     * 
     * @return
     * @throws Exception
     */
    Object executeNext() throws Exception;
    
}

public interface MethodInterceptor {
    
    Object invoke(MethodInvocation invocation) throws Exception;
    
}
  1. MethodInvocation的默认实现
代码语言:javascript
复制
/**
 * MethodInvocation的默认实现
 * 
 * @author wqx
 *
 */
public class DefaultMethodInvocation implements MethodInvocation {

    //目标对象
    private Object target;
    //代理
    private Object proxy;
    //目标方法
    private Method method;
    //参数
    private Object[] parameters;
    
    //拦截器链
    private List<?> interceptors;
    //当前执行的Interceptor的索引(范围:0-interceptors.size()-1),初始为-1
    private int currentIndex = -1;
    
    public DefaultMethodInvocation(Object target,Object proxy,Method method,Object[] parameters, List<?> interceptors){
        this.target = target;
        this.proxy = proxy;
        this.method = method;
        this.parameters = parameters;
        this.interceptors = interceptors;
    }
    
    @Override
    public Object executeNext() throws Exception {
        //判断拦截器链是否执行完
        if(this.currentIndex == this.interceptors.size() - 1){
            //如果执行完,直接执行目标方法
            method.setAccessible(true);
            return method.invoke(target, parameters);
        }
        Object interceptor = this.interceptors.get(++this.currentIndex);
        MethodInterceptor methodInterceptor = (MethodInterceptor)interceptor;
        return methodInterceptor.invoke(this);
    }
    
    //getter and seter
}
  1. 要实现动态代理,当然需要实现InvocationHandler接口啦
代码语言:javascript
复制
public class DefaultInvocationHandler implements InvocationHandler {

    private Object target;
    
    private List<MethodInterceptor> interceptorsChain;
    
    public DefaultInvocationHandler(Object target, List<MethodInterceptor> interceptorsChain){
        this.target = target;
        this.interceptorsChain = interceptorsChain;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        MethodInvocation methodInvocation;
        
        List<MethodInterceptor> chain = interceptorsChain;
        
        Object relVal;
        if(chain != null && !chain.isEmpty()){
            methodInvocation = new DefaultMethodInvocation(target,proxy,method,args,chain);
            relVal = methodInvocation.executeNext();
        }else{//直接调用目标方法
            relVal = method.invoke(target, args);
        }
        return relVal;
    }
}

在回调方法invoke中,我们首先判断拦截器链是否需要执行,如果需要执行拦截器链,那么就将这次方法调用信息封装成MethodInvocation,然后调用methodInvocation.executeNext()。在DefaultMethodInvocation的实现中可以看到,executeNext() 方法要做的就是查看拦截器链interceptors是否执行完毕,如果执行完了,那么调用目标方法method.invoke(target, parameters),如果拦截器链没有执行完,那么就获取下一个拦截器并执行。

代码语言:javascript
复制
//获取下一个拦截器
Object interceptor = this.interceptors.get(++this.currentIndex);
MethodInterceptor methodInterceptor = (MethodInterceptor)interceptor;
//执行拦截器的invoke方法
return methodInterceptor.invoke(this);

4. 总结

在如今的系统中,AOP的应用程度已经非常广泛。本文对AOP概念进行简单的阐述,并对AOP的原理进行了简单的实现。主要用到的方法是将横切逻辑在代理类的回调方法中实现,并通过责任链模式实现了软件设计中的一个重要基本特性:在重要过程中设置拦截接口。比如本文中提到的通过代理类进行方法调用。那么在方法调用这个过程中我们可能需要实现很多逻辑:统计调用次数;统计调用时间;添加黑白名单进行过滤等等。没有哪个框架可以Cover所有的需求,允许外置行为,这是框架基本的扩展方法。

完整源码

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-05-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. AOP概述
  • 2. Test Code
  • 3. AOP原理实现
    • 3.1 原理分析
      • 3.2 设计实现
      • 4. 总结
      相关产品与服务
      多因子身份认证
      多因子身份认证(Multi-factor Authentication Service,MFAS)的目的是建立一个多层次的防御体系,通过结合两种或三种认证因子(基于记忆的/基于持有物的/基于生物特征的认证因子)验证访问者的身份,使系统或资源更加安全。攻击者即使破解单一因子(如口令、人脸),应用的安全依然可以得到保障。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档