Spring 之 AOP 动态代理实现原理

什么是代理?

指为一个目标对象提供一个代理对象, 并由代理对象控制对目标对象的引用. 使用代理对象, 是为了在不修改目标对象的基础上, 增强目标对象的业务逻辑。

file

动态代理

静态代理会为每一个业务增强都提供一个代理类, 由代理类来创建代理对象, 而动态代理并不存在代理类, 代理对象直接由代理生成工具动态生成.

JDK动态代理是使用 java.lang.reflect 包下的代理类来实现。JDK动态代理动态代理必须要有接口

添加用户的接口

package cn.et.dao;

public interface UserDao {
    public void addUser();
}

实现添加用户接口:

package cn.et.dao.impl;

import cn.et.dao.UserDao;

public class UserDaoImpl implements UserDao {
    public void addUser(){
        System.out.println("添加用户");
    }
}

未集成或实现的接口(cglib代理,用来和jdk实现做对比)

package cn.et.dao.impl;

public class StudentDao {
    public void addStudent(){
        System.out.println("添加学生");
    }
}

JDK实现代理InvocationHandler

package cn.et.utils;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class MyInvocationHandler implements InvocationHandler{
    private Object targetObj;
    public MyInvocationHandler(Object targetObj){
        this.targetObj = targetObj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("权限验证");
        Object resultObj = method.invoke(targetObj, args);
        System.out.println("日志记录");
        return resultObj;
    }
}
package cn.et.utils;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class CglibProxy implements MethodInterceptor {
    private Enhancer enhancer = new Enhancer();  
    public Object getProxy(Class clazz){  
        enhancer.setSuperclass(clazz);  
        enhancer.setCallback(this);  
        //通过字节码技术动态创建子类实例  
        return enhancer.create();  
    }  
    //实现MethodInterceptor接口方法  
    public Object intercept(Object obj, Method method, Object[] args,  
        MethodProxy proxy) throws Throwable {  
        System.out.println("权限验证");  
        Object result = proxy.invokeSuper(obj, args);  
        System.out.println("日志记录");  
        return result;  
    }  
}
package cn.et.utils;
import java.lang.reflect.Proxy;

import org.junit.Test;

import cn.et.dao.UserDao;
import cn.et.dao.impl.StudentDao;
import cn.et.dao.impl.UserDaoImpl;

public class Checkout {
    @Test
    public void Testing(){
        UserDao userDao = new UserDaoImpl();
        /*
        Spring AOP(面向切面编程),扩展功能不修改源代码实现,采用横向抽取机制,取代了传统纵向继承体系重复性代码
        横向抽取机制,底层使用动态代理方式实现
            针对有接口的情况时使用JDK中的java.lang.reflect类库提供的动态代理
            针对没有接口的情况时,使用Cglib框架提供的动态代理
        */
        MyInvocationHandler handler = new MyInvocationHandler(userDao);
        UserDao proxyUserDao = (UserDao) Proxy.newProxyInstance(userDao.getClass().getClassLoader(),userDao.getClass().getInterfaces(),handler);
        proxyUserDao.addUser();

        System.out.println("。。。。。。。。。。。。。");

        CglibProxy proxy = new CglibProxy();  
        StudentDao proxyImp = (StudentDao)proxy.getProxy(StudentDao.class);  
        proxyImp.addStudent();  

    }
}

file

模拟Spring AOP场景

了解了动态代理后, 我们就可以自己来实现Spring AOP功能了, 所以下面我们来模拟下Spring AOP场景。

(1) 转账业务

public interface IAccountService {
    //主业务逻辑: 转账
    void transfer();
}
public class AccountServiceImpl implements IAccountService {
    @Override
    public void transfer() {
        System.out.println("调用dao层,完成转账主业务.");
    }
}

(2) 切面抽象类

定义一个切面抽象类, 该类使用了模板方法的设计模式, 为开始, 结束, 异常, 前置增强, 后置增强提供了默认实现, 当我们定义切面类时只需要按需重写它们就行. isIntercept() 方法用来判断切入点是否正确, 切面类需要重写这个方法

public abstract class BaseAspect implements MethodInterceptor {
    private static final Logger logger = LoggerFactory.getLogger(BaseAspect.class);

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        Object result = null;

        begin();
        try {
            if (isIntercept(method, args)) {
                before();
                result = methodProxy.invokeSuper(obj, args);
                after();
            } else {
                result = methodProxy.invokeSuper(obj,args);
            }
        } catch (Exception e) {
            logger.error("proxy failure", e);
            error(e);
            throw e;
        } finally {
            end();
        }
        return result;
    }

    /**
     * 开始增强
     */
    public void begin() {
    }

    /**
     * 切入点判断
     */
    public boolean isIntercept(Method method, Object[] args) throws Throwable {
        return true;
    }

    /**
     * 前置增强
     */
    public void before() throws Throwable {
    }

    /**
     * 后置增强
     */
    public void after() throws Throwable {
    }

    /**
     * 异常增强
     */
    public void error(Throwable e) {
    }

    /**
     * 最终增强
     */
    public void end() {
    }
}

AOP 思想: 基于代理思想,对原来目标对象,创建代理对象,在不修改原对象代码情况下,通过代理对象,调用增强功能的代码,从而对原有业务方法进行增强 !

AOP应用场景

场景一: 记录日志
场景二: 监控方法运行时间 (监控性能)
场景三: 权限控制
场景四: 缓存优化 (第一次调用查询数据库,将查询结果放入内存对象, 第二次调用, 直接从内存对象返回,不需要查询数据库 )
场景五: 事务管理 (调用方法前开启事务, 调用方法后提交关闭事务 )


相关文章:
Spring 之 AOP 动态代理实现原理
Spring AOP原理之动态代理
Spring AOP的实现原理及应用场景(通过动态代理)
spring源码解析之AOP原理

为者常成,行者常至