Java 动态代理与AOP

最近被问起AOP和动态代理,这里做一个小总结

代理模式是设计模式中的结构型模式,在不改变原始类的情况下,通过引入代理类来给原始类附加功能.通常用于在侵入代码的情况下,给业务代码附加一些与业务不相关的功能,比如日志,统计信息,鉴权等.

public interface UserDao {
    void add();
    void update();
}
public class UserDaoImpl implements UserDao{
    @Override
    public void add() {
        System.out.println("add");
    }

    @Override
    public void update() {
        System.out.println("update");
    }
}

静态代理

public class UserDaoProxy implements UserDao {
    private UserDao userDao;
    @Override
    public void add() {
        System.out.println("pre");
        userDao.add();
        System.out.println("after");
    }

    @Override
    public void update() {
        System.out.println("pre");
        userDao.update();
        System.out.println("after");
    }

    public UserDaoProxy(UserDao userDao) {
        this.userDao = userDao;
    }
}

上面我们通过实现接口的方式,实现了UserDao的代理类,当目标类没有实现接口时,我们可以通过继承的方式进行代理.

使用时,我们直接将实现替换即可,如下:

public static void main(String[] args) {
    UserDao userDao = new UserDaoProxy(new UserDaoImpl());

    userDao.add();
}

运行结果:

image-20210527102026829

这样我们就在不侵入代码的情况下,完成了业务代码的功能拓展.

但是静态代理有一个缺点,就是我们要针对每一个实现类,都去实现一个它的每一个方法,并且为每个方法添加很多相同的逻辑.并且当我们有多个类需要增强的情况下,还需要实现多个代理类.因此,我们可以使用动态代理解决这个问题.

动态代理

动态代理,就是在代码运行期间,动态的创建代理类,并将原始类进行替换.

Java中我们可以使用反射机制进行实现.

我们实现代理类,实现InvocationHandler接口即可.如下:

public class MyProxy implements InvocationHandler {

    private UserDao userDao;

    public Object createProxy(UserDao userDao) {
        this.userDao = userDao;
        ClassLoader classLoader = MyProxy.class.getClassLoader();
        Class<?>[] interfaces = userDao.getClass().getInterfaces();
        return Proxy.newProxyInstance(classLoader, interfaces, this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("pre");
        Object invoke = method.invoke(userDao, args);
        System.out.println("after");
        return invoke;
    }
}

首先我们需要自定义一个createProxy方法,用于生成我们的代理类对象,然后实现invoke方法,里面是我们的业务逻辑,通过放射机制method.invoke来执行我们的目标方法.

public static void main(String[] args) {
    MyProxy myProxy = new MyProxy();
    UserDao userDao  = (UserDao) myProxy.createProxy(new UserDaoImpl());

    userDao.add();
    userDao.update();
}

结果如下:

image-20210527103103319

这样就动态的实现了方法的拓展,只写了一个代理类,一次代理逻辑,以后如果有别的类需要相同的拓展,并且它实现了相同的接口,那么我们就可以使用代理类去创建代理对象,即可实现复用.

如果没有实现接口,那么我们就需要通过继承的方式来实现,JDK默认不支持,我们需要使用CGLIB来支持继承方式的动态代理.

我们现在不实现接口

public class UserDaoImpl{
    public void add() {
        System.out.println("add");
    }

    public void update() {
        System.out.println("update");
    }
}

代理类:

public class MyProxy implements MethodInterceptor {
    UserDaoImpl userDao;

    public MyProxy(UserDaoImpl userDao) {
        this.userDao = userDao;
    }


    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("pre");
        Object invoke = method.invoke(userDao, objects);
        System.out.println("after");
        return invoke;
    }
    
}

运行:

public static void main(String[] args) {
    Enhancer enhancer = new Enhancer();

    UserDaoImpl userDao = new UserDaoImpl();

    enhancer.setCallback(new MyProxy(userDao));
    enhancer.setSuperclass(UserDaoImpl.class);
    UserDaoImpl o = (UserDaoImpl) enhancer.create();
    o.add();
    o.update();
}

结果:

image-20210527104611834

也可以得到一样的结果.

AOP

Spring的AOP就是通过动态代理来实现的,在目标类实现了接口的情况下,会使用JDK自带的动态代理,如果怕目标类没有实现接口,则会使用CGLIB,如果目标类是final类则会报错,或者方法为static或者final,无法进行动态代理.具体源码的话下次再做总结....

最后修改:2021 年 05 月 30 日
如果觉得我的文章对你有用,请随意赞赏