专注于 JetBrains IDEA 全家桶,永久激活,教程
持续更新 PyCharm,IDEA,WebStorm,PhpStorm,DataGrip,RubyMine,CLion,AppCode 永久激活教程

3种代理模式-理解Spring Aop

概述

面试的时候,java开发必问的知识点是spring,spring中核心的是IOC和AOP。问到AOP的实现原理的时候,我们都知道是代理模式,却对代理模式一知半解,今天就来记记笔记。
下面借鉴一张代理模式的导图:

65_1.png可以看出,代理模式就是客户端(Client)并不直接调用真实对象,而是调用代理对象,通过代理对象调用真实对象,从而可以在代理对象实现扩展功能。

静态代理

首先假设一种业务场景,需要实现对用户进行CRUD的操作,所以我们创建了一个UserService接口和UserServiceImpl的实现类。代码如下:

  public interface UserService {
    void addUser();
    void updateUser();
}
public class UserServiceImpl implements UserService{
    @Override
    public void addUser() {
        System.out.println("添加一个用户");
    }
    @Override
    public void updateUser() {
        System.out.println("更新一个用户");
    }
}

现在我们需要在每次对用户信息进行增加、删除和更新操作的时候记录一下日志,用静态代理的方式,我们需要创建一个静态代理类,将被代理对象(目标对象)传入,然后创建需要增强的方法,如addUser和updateUser,实现记录日志的功能

public class UserStaticProxy {

    private UserService userService;

    public UserStaticProxy(UserService userService) {
        this.userService=userService;
    }
    public void addUser() {
        userService.addUser();
        System.out.println("打印一条日志");
    }
}  

测试

public class StaticProxyTest {
    public static void main(String [] args) {
        UserService userService = new UserServiceImpl();
        UserStaticProxy staticProxy = new UserStaticProxy(userService);
        staticProxy.addUser();
    }
}

运行结果:

65_2.png

静态代理的缺点

1、接口增加方法,代理类需要同步维护。
2、接口越多,需要创建的代理类就越多。比如以后我们有TeacherService,StudentService,就需要创建TeacherStaticProxy,StudentStaticProxy,这样就增加了代码量。

JDK动态代理

其实动态代理和静态代理的本质是一样的,最终程序运行时都需要生成一个代理对象实例。只不过静态代理是采用硬编码的方式在程序运行之前就创建好代理类,而动态代理是运行时动态生成的方式。
JDk帮我们实现了动态代理,使用的是newProxyInstance方法
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
该方法中接收三个参数:
ClassLoader loader,:指定当前目标对象使用类加载器
Class<?>[] interfaces,:代理类需要实现的接口列表
InvocationHandler h:调用处理程序,将目标对象的方法分派到该调用处理程序
代码示例:

public class DynamicProxy implements InvocationHandler{

    private Object target;//目标对象

    public Object bind(Object target) {
        this.target=target;
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Exception{
        //执行目标对象的方法
        Object result = method.invoke(target,args);
        //实现扩展功能
        System.out.println("打印一下日志");
        return result;
    }
}

bind方法简单封装jdk的newProxyInstance(),并返回目标接口对象。invoke方法负责增强目标对象的方法,实现扩展功能。
测试类:

public class DynamicProxyTest {

    public static void main(String [] args) {
        DynamicProxy dynamicProxy = new DynamicProxy();
        UserService userService = (UserService) dynamicProxy.bind(new UserServiceImpl());
        userService.addUser();
        userService.updateUser();
    }
}

运行结果:

65_3.png
可见,动态代理解决了静态代理的两个缺点。

CGLib动态代理

上面两种代理方式,目标对象UserServiceimpl都实现了一个接口,如果只是一个普通的类,没有实现任何接口,该如何进行代理呢?这就引出了CGLib动态代理。CGLib动态代理也叫子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展。
要用cglib代理,需要导入相应的包,好在spring已经集成了它,引入spring即可。
代理类:

public class CGLibProxy implements MethodInterceptor {

    private Object target;

    public Object bind(Object target) {
        this.target=target;
        Enhancer enhancer = new Enhancer();
        //设置父类
        enhancer.setSuperclass(this.target.getClass());
        //设置回调函数
        enhancer.setCallback(this);
        //创建并返回子类对象
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable{
        Object object = methodProxy.invokeSuper(o,objects);
        System.out.println("打印一下日志");
        return object;
    }
}

测试类:

public class CGLibProxyTest {

    public static void main(String [] args) {
        CGLibProxy cgLibProxy = new CGLibProxy();
        UserService userService = (UserService) cgLibProxy.bind(new UserServiceImpl());
        userService.addUser();
        userService.updateUser();
    }
}

运行结果:

65_4.png
使用CGLib代理有两个注意点:
1、目标对象不能被final关键字修饰,因为被final关键字修饰的对象是不可继承的。 2、目标对象的方法如果是final/staic,那么该方法是不会被拦截(不能被增强),即不会执行目标对象额外的业务方法。

总结

今天的笔记就记到这里,感谢前来阅读的你们,我们一起学习,一起进步。

文章永久链接:https://tech.souyunku.com/45828

未经允许不得转载:搜云库技术团队 » 3种代理模式-理解Spring Aop

JetBrains 全家桶,激活、破解、教程

提供 JetBrains 全家桶激活码、注册码、破解补丁下载及详细激活教程,支持 IntelliJ IDEA、PyCharm、WebStorm 等工具的永久激活。无论是破解教程,还是最新激活码,均可免费获得,帮助开发者解决常见激活问题,确保轻松破解并快速使用 JetBrains 软件。获取免费的破解补丁和激活码,快速解决激活难题,全面覆盖 2024/2025 版本!

联系我们联系我们