介绍动态代理之前,先介绍一下什么是代理模式。当我们不想访问或者不能直接访问一个对象的时候,我们就需要用到代理模式。代理模式一般涉及到委托类与代理类两个概念,代理类用于为委托类处理一些事务,代理类对象常常与委托类对象相关联。代理模式可以分为静态代理和动态代理,静态代理需要为每一个委托类实现一个代理类,程序运行之前代理类的.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层的参数校验。
登录权限检查。
日志打印。