动态代理了解一下?

279 阅读5分钟

介绍动态代理之前,先介绍一下什么是代理模式。当我们不想访问或者不能直接访问一个对象的时候,我们就需要用到代理模式。代理模式一般涉及到委托类与代理类两个概念,代理类用于为委托类处理一些事务,代理类对象常常与委托类对象相关联。代理模式可以分为静态代理和动态代理,静态代理需要为每一个委托类实现一个代理类,程序运行之前代理类的.class文件就已经存在了,而动态代理则是在运行时利用反射机制动态生成的。

静态代理

为了帮助大家看懂静态代理,我们举个简单的例子,小丽要买一只口红,但是这只口红在国内买不到,只有美国有得卖,这时就需要代购(代理)帮助小丽去美国买口红。我们尝试实现一下这个过程。

首先我们定义一个Subject接口如下,主要用于声明真实对象要让代理对象做的事情。

public interface Subject {
 void buy();
}

然后创建委托类。

public class RealSubject implements Subject {

 @Override
 public void buy() {
  System.out.println("我要买口红");
 }
}

然后创建代理类,也就是我们说的代购。

public class StaticProxy implements Subject {
 
 private Subject subject;

 public StaticProxy(Subject subject) {
  this.subject = subject;
 }

 @Override
 public void buy() {
  subject.buy();
 }

}

我们创建一个测试类测试一下代码。

public class TestProxy {

 public static void main(String[] args) {
  Subject subject = new StaticProxy(new RealSubject());
  subject.buy();
 }
}

上面的代码演示了静态代理的过程,可以看到我们用代理类StaticProxy实现了Subject接口,接下来我们看看动态代理的实现方式。

动态代理

我们重新创建一个动态代理的类,该类实现了InvocationHandler接口。

public class DynamicProxy implements InvocationHandler {
 
 private Object object;
 
 public DynamicProxy(Object object) {
  this.object = object;
 }

 @Override
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  //调用委托类对象的方法
  Object result = method.invoke(object, args);
  return result;
 }
}

上面代码展示了动态代理的基本实现思路,定义object对象为委托类的对象,而实际执行的方法是在invoke()方法中完成的。此时我们不用再关心代理的对象是谁,只需要实现一套逻辑即可。下面我们修改一下测试类。

public class TestProxy {

 public static void main(String[] args) {
  Subject subject = new RealSubject();
  Subject dynamicProxy = (Subject) Proxy.newProxyInstance(
    subject.getClass().getClassLoader(), 
    subject.getClass().getInterfaces(), 
    new DynamicProxy(subject));
  dynamicProxy.buy();
 }
}

上面动态代理和静态代理的运行效果是一样的,而动态代理帮我们省去了重复编写代理类的麻烦。

另外还需要提到的是,上面这种使用动态代理的方式是JDK层面的动态代理,除此之外CGLIB动态代理也经常被提到,Spring AOP可以使用JDK动态代理和CGLIB两种方式动态代理,默认使用JDK动态代理,如果对象没有实现接口,则使用CGLIB代理。CGLIB相对JDK动态代理一个最主要的优点在于JDK动态代理被代理的类不能是一个普通的类,一定得实现了一个接口。CGLIB则不同,它是动态生成代理类的子类,在子类中采用方法拦截的技术拦截父类方法的调用,这就要求被代理的类不能被final修饰,因为final修饰的类无法被继承。

AOP

学过Spring的同学肯定都听过AOP,但是AOP并不是Spring提出来的,只是Spring是AOP的一个典型应用。AOP全称是Aspect oriented programming,面向切面编程。在传统的面向对象编程中,业务逻辑会存在一些横切性问题,AOP的思想就是将核心代码和这些横切性的问题解耦合,提高代码的复用率。

关于AOP,老外给取了很多名词,类似于阿里“抓手、赋能、沉淀”那套恶心的互联网话术,大家了解一下就好。

名词解释

  • Join point(连接点)。程序执行过程中的一点,例如方法执行或异常处理。在Spring AOP中,连接点始终代表方法的执行。

  • Pointcut(切点)。与所有方法都是连接点不同,切点可以是具体的某一个方法。默认情况下,Spring使用AspectJ切入点表达语言。

  • Advice(增强/通知)。在特定的连接点处采取的操作,不同类型的Advice包括如before()/after()等。

  • Aspect(切面)。由Pointcut和Advice组成。

  • Weaving(织入)。把Advice应用到目标类而创建出代理对象的这一过程。

  • Introduction(引入)。允许向现有的类声明新的方法与字段。

常见应用场景

  • Spring声明式事务管理配置。

  • Controller层的参数校验。

  • 登录权限检查。

  • 日志打印。