AOP与代理模式

为什么将一个对象加入到IOC容器后,取出该对象后,发现它的方法多了一些功能?

原因是:如果该类被AOP切中,从IOC容器取出的对象,不是该类的对象,而是该类代理类的对象。

文章目录:

1.代理模式

  1. 简介
  2. 作用
  3. 分类

2.静态代理实现

3.动态代理实现

  1. JDK动态代理
  2. CGLB动态代理

AOP的实现原理

  1. 当调用容器的getBean方法后,Spring查找对象后会判断该对象的方法是否被某个切面切中
  2. 如果没有切中,就创建原有类的对象
  3. 如果被切中了,再判断该类是否实现过任何接口
  4. 如果实现过接口,则通过JDK动态代理生成代理类,并创建对象
  5. 如果没有实现过接口,则通过CGLib动态代理生成代理类,并创建对象

代理模式简介

代理模式为其他对象提供一种代理以控制对这个对象的访问。

在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

代理模式的作用

  1. 中介的作用,当调用者不能或不方便调用某个对象时,代理起到中介的作用,帮助调用者间接的调用对象。
  2. 符合开闭原则,在不修改原有类代码的前提下,对类的功能进行增强。

代理模式分为两种

  1. 静态代理,在运行前,通过编写代码的方式生成代理类
  2. 动态代理,在运行后,通过反射机制生成代理类
静态代理实现步骤
  1. 代理者和被代理者都实现相同的接口
  2. 代理者包含被代理者的对象
  3. 创建代理对象时传入被代理对象
  4. 代理者执行方法时,会调用被代理者的方法,同时扩展新的功能

例子:

UserService接口
public interface UserService {
    void updateMoney(String name);
}
UserService实现类
public class UserServiceImpl implements UserService {
    @Override
    public void updateMoney(String name) {
        System.out.println("账号向"+name+"转账");
    }
}
StaticProxy类
复制代码
//代理类与客户类实现同一个接口
public class StaticProxy implements UserService {
    private UserService userService=null;

    //2.在构造方法中将参数传入
    public StaticProxy(UserService userService){
        this.userService=userService;
    }
    @Override
    public void updateMoney(String name) {
        System.out.println("打印日志");
        userService.updateMoney(name);
    }
}
复制代码

测试:

public class testStaticProxy {
    public static void main(String[] args) {
         StaticProxy staticProxy = new StaticProxy(new 
         UserServiceImpl());
         staticProxy.updateMoney("李四");
    }
}

结果:

添加了打印日志的功能,但是一个代理类只能代理一种业务,如果有多种业务,就必须创建大量的代理类

动态代理的分类
  • JDK动态代理
  • CGLib动态代理

JDK动态代理

JDK自带的,前提是:被代理类必须实现过接口。

实现步骤

  1. 实现InvocationHandler接口
  2. 实现invoke方法
  3. 通过Proxy.newProxyInstance方法返回代理对象

例子:

GameService接口:

public interface GameService {
    void shot(String name);
}

GameService实现类:

public class GameServiceImpl implements GameService {
    @Override
    public void shot(String name) {
        System.out.println("我打了"+name);
    }
}

JDKProxy.java

复制代码
public class JDKProxy implements InvocationHandler{
    //被代理对象
    private Object object=null;
    //创建代理对象
    public Object createProxy(Object object){
        this.object=object;
        return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //扩展功能
        System.out.println("打印打游戏时间!");
        Object invoke = method.invoke(object, args);
        return invoke;
    }
}
复制代码

测试:

public class testJDKProxy {
    public static void main(String[] args) {
        JDKProxy().createProxy(new GameServiceImpl());
        proxy.shot("张三");
    }
}

截图:

扩展打印打游戏时间功能

CGLib动态代理

需要引入CGLib依赖,它的原理是:通过反射+继承机制动态生成被代理类的子类,所以被代理类不能是final的。

实现步骤

  1. 引入cglib
  2. 实现MethodInterceptor接口
  3. 实现intercept方法
  4. 通过Ehancer返回代理对象

例子:

Bank类

public class Bank {
    public void transferAccount(String formName, String toName, double money){
        System.out.println(formName+"向"+toName+"转账"+money);
    }
}

CGLBProxy类

复制代码
public class CGLibProxy{

    public static Object createProxy(Object obj){
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(obj.getClass());
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                //增强功能
                System.out.println("日志");
                return methodProxy.invokeSuper(o, objects);
            }
        });
        return enhancer.create();
    }
}
复制代码

测试:

public class testCGLBProxy {
    public static void main(String[] args) {
        Bank proxy = (Bank) CGLibProxy.createProxy(new Bank());
        proxy.transferAccount("张三","李四",34);
    }
}

扩展了增加日志功能

 我的例子的码云地址:https://gitee.com/fan-gege/proxy.git

posted @   时光里的少年  阅读(111)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示