Spring AOP:面向切面编程(三)

在Spring AOP中,用到了代理模式,本文将讲解代理模式在Spring AOP中的应用

文章有参考其他博主文章:

  • https://www.jianshu.com/p/a10d7917e0e6
  • https://www.jianshu.com/p/9cdcf4e5c27d
  • https://www.jianshu.com/p/269afd0a52e6
一、代理模式的定义
  • 代理模式(Proxy Pattern)也称委托模式(Delegate Pattern),是一种结构型设计模式,也是一项基础设计技巧

  • 代理模式用于解决两种问题:控制对原有对象的访问、增加额外的功能。可以说保护了原有对象

  • 按照代理创建的时期来进行分类的话, 可以分为两种:静态代理、动态代理

二、静态代理

​ 静态代理是由程序开发者或者工具生成的源代码 ,然后对其进行编译。在编译期代理类.class文件就已被创建。

静态代理示例:

  • 首先定义一个接口和实现类,实现类有若干个方法

    // 操作接口
    public interface Operate {
        void doSomething();
    }
    
    // 操作者
    public class Operator implements Operate {
        @Override
        public void doSomething() {
            System.out.println("I'm doing something");
        }
    }
    
  • 然后定义一个代理类,也实现了Operate接口

    public class OperationProxy implements Operate {
        private Operator operator = null;
    
        @Override
        public void doSomething() {
            beforeDoSomething();
            if(operator == null){
                operator =  new Operator();
            }
            operator.doSomething();
            afterDoSomething();
        }
    
        private void beforeDoSomething() {
            System.out.println("before doing something");
        }
    
        private void afterDoSomething() {
            System.out.println("after doing something");
        }
    }
    
  • 最后测试一下

    public class StaticProxyTest {
        public static void main(String[] args) {
            Operate operate = new OperationProxy();//使用OperationProxy代替Operator
            operate.doSomething();  //代理者代替真实者做事情
        }
    }
    

    以上就是静态代理的实现,静态代理让调用者不用再直接持有操作者的引用,而是将一切操作交由代理者去完成。

静态代理有它的局限性:

  • 如果我们新增加了需要代理的方法,代理类的代码也必须进行新的改动
  • 如果有多个接口和实现类,我们又需要代理类,同样需要对代理者进行扩展并且更加麻烦
三、动态代理
  • 从静态代理发现,每个代理类只能为一个接口服务,这样开发中会产生很多代理类。假如通过一个代理类就能完成全部的代理功能,那么我们就需要动态代理

  • 常见的动态代理有:Java的JDK动态代理、Spring的CGLIB代理等

1.JDK动态代理
  • JDK动态代理是基于Java的反射机制实现的,在Java中要想实现动态代理机制,需要java.lang.reflect.Invoca-tionHandler接口和 java.lang.reflect.Proxy类的支持

(1)InvocationHandler接口

package java.lang.reflect;

/**
 * @author      Peter Jones
 * @see         Proxy
 * @since       1.3
 */
public interface InvocationHandler {
    
	// Object proxy -> 被代理的对象
    // Method method -> 要调用的方法
    // Object[] args -> 方法调用时所需要的参数
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
    
}

(2)Proxy类源码(部分)

​ 这里只贴出创建代理newProxyInstance()的方法,以后会进行详细的解读

package java.lang.reflect;

import ... ;

public class Proxy implements java.io.Serializable { 
    
    // ClassLoader loader -> 使用的类加载器
    // Class<?>[] interfaces —> 得到的全部接口  
    // InvocationHandler h -> 得到InvocationHandler接口的子类的实例
    @CallerSensitive
    public static Object newProxyInstance(
        ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
        throws IllegalArgumentException{
        .....
    }

}

JDK动态代理代码示例:

  • 创建接口和实现类,模拟业务实现

    public interface UserService{
        void add(String name);
    }
    
    public class UserServiceImpl implements IUserService{
    
        @Override
        public void add(String name) {
            System.out.println("向数据库中插入名为:  "+name+" 的用户");
        }
    
    }
    
  • 创建InvocationHandler接口实现类

    public class MyInvocationHandler implements InvocationHandler {
        //被代理对象,Object类型
        private Object target;
        
        public MyInvocationHandler(Object target) {
            this.target = target;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 	  {
    
            System.out.println("开始执行方法...");
            Object returnvalue = method.invoke(target, args);
            System.out.println("方法执行完毕...");
            return returnvalue;
        }
    
    }
    
  • 测试使用,通过newProxyInstance创建 代理对象

    public class DynamicProxyTest {
    
        public static void main(String[] args) {
    
            IUserService target = new UserServiceImpl();
            MyInvocationHandler handler = new MyInvocationHandler(target);
            // 第一个参数是指定代理类的类加载器(我们传入当前测试类的类加载器)
            // 第二个参数是代理类需要实现的接口(我们传入被代理类实现的接口,这样生成的代理类和被代理类就实 
            // 现了相同的接口)
            // 第三个参数是invocation handler,用来处理方法的调用。这里传入我们自己实现的handler
            IUserService proxyObject = (IUserService) 
                Proxy.newProxyInstance(DynamicProxyTest.class.getClassLoader(),
                        				target.getClass().getInterfaces(), handler);
            proxyObject.add("蓝蓝路");
        }
    
    }
    

    以上就是动态代理的简单例子,我们并没有像静态代理那样去自己定义一个代理类,并实例化代理对象。实际上,动态代理的代理对象是在内存中的,是JDK根据我们传入的参数生成好的。

2.Cglib动态代理
  • 和JDK代理不同的是,JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口,如果想代理没有实现接口的类,就可以使用Cglib实现。
  • 不能对final类以及final方法进行代理
  • cglib动态代理是采用ASM字节码框架来修改字节码达到代理效果
  • 关于cglib源码分析可以参考:https://blog.csdn.net/yu_kang/article/details/88530016

Cglib动态代理代码示例:

  • 被代理类,这里目标类没有实现任何接口

    public class HelloServiceImpl {
        public void sayHello(){
            System.out.println("Hello Zhanghao");
        }
    
        public void sayBey(){
            System.out.println("Bye Zhanghao");
        }
    }
    
  • 类似与jdk动态代理,这里需要实现一个叫做MethodInterceptor的接口,生成一个方法拦截器

    public class HelloMethodInterceptor  implements MethodInterceptor{
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy 
                                methodProxy) throws Throwable {
            System.out.println("Before: "  + method.getName());
            Object object = methodProxy.invokeSuper(o, objects);
            System.out.println("After: " + method.getName());
            return object;
        }
    }
    
  • 测试类测试调用结果

    public class TestClinet {
          Enhancer enhancer = new Enhancer();
            //继承被代理类
            enhancer.setSuperclass(HelloServiceImpl.class);
            //设置回调
            enhancer.setCallback(new HelloMethodInterceptor());
            //设置代理类对象
            HelloServiceImpl helloService = (HelloServiceImpl) enhancer.create();
            //在调用代理类中方法时会被我们实现的方法拦截器进行拦截
            helloService.sayBey();
    }
    

    我们可以从上面的代码示例中看到,代理类是由enhancer.create()创建的。Enhancer是CGLIB的字节码增强器,可以很方便的对类进行拓展

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值