章节目录:
一、代理模式介绍
代理模式(Proxy Pattern),属于结构型模式。它为一个对象提供一个替身,以控制对这个对象的访问。即通过代理对象访问目标对象,这样做的好处是可以在目标对象实现的基础上,增强额外的功能操作(即拓展目标对象的功能)。
被代理的对象可以是远程对象、创建开销大的对象或需要安全控制的对象。
代理模式的三种形式:
- 静态代理
- 动态代理(JDK代理、接口代理)
- Cglib代理(可以在内存动态的创建对象,而不需要实现接口,它是动态代理的范畴。)
此处我们有一个诉求:
- 不改变目标对象的功能实现,通过代理对象的方式去访问它,完成功能的拓展并调用方法。
- 解决方案:使用三种方式的代理模式进行实现。
二、静态代理方式
2.1 示例关系:
2.2 代码实现:
/* *
* 定义一个ITeacherDao接口。
*/
interface ITeacherDao {
void teach();
}
/* *
* TeacherDao实现ITeacherDao接口。
*/
class TeacherDao implements ITeacherDao {
@Override
public void teach() {
System.out.println(" 教师授课... ");
}
}
/* *
* TeacherDaoProxy同样也实现ITeacherDao接口。
*/
class TeacherDaoProxy implements ITeacherDao {
/* *
* 持有ITeacherDao目标对象。
*/
private ITeacherDao target;
public TeacherDaoProxy(ITeacherDao target) {
this.target = target;
}
/* *
* 调用目标对象teach()方法之外,再进行功能拓展。
*/
@Override
public void teach() {
System.out.println(" 代理开始:代课教师,课前准备。 ");
target.teach();
System.out.println(" 代理结束:代课教师,离开教室。 ");
}
}
/* *
* 客户端调用。
*/
public class Client {
public static void main(String[] args) {
// 将被代理对象(目标对象)传递给代理对象。
TeacherDaoProxy teacherDaoProxy = new TeacherDaoProxy(new TeacherDao());
// 代理对象完成方法调用。(即:执行代理对象方法,代理对象再去调用目标对象的方法)
teacherDaoProxy.teach();
// 代理开始:代课教师,课前准备。
// 教师授课...
// 代理结束:代课教师,离开教室。
}
}
2.3 方式说明:
优点:
- 在不修改目标对象的功能前提下,能通过代理对象对目标功能拓展。
缺点:
- 因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类。
- 一旦接口增加方法,目标对象与代理对象都要进行维护。
三、动态(JDK)代理方式
3.1 示例关系:
3.2 代码实现:
/* *
* 定义一个ITeacherDao接口。
*/
interface ITeacherDao {
void teach();
}
/* *
* TeacherDao实现ITeacherDao接口。
*/
class TeacherDao implements ITeacherDao {
@Override
public void teach() {
System.out.println(" 教师授课... ");
}
}
/* *
* TeacherDaoProxy类使用JDK动态代理,无需实现接口。
*/
class TeacherDaoProxy {
/* *
* 持有一个Object类型的目标对象。
*/
private Object target;
public TeacherDaoProxy(Object target) {
this.target = target;
}
/* *
* 使用java.lang.reflect.Proxy类下的newProxyInstance()方法创建动态代理。
* 为目标对象生成一个代理对象。
*/
public Object getProxyInstance() {
// 参数1 ClassLoader loader :目标对象的类加载器。
// 参数2 Class<?>[] interfaces :目标对象实现的接口类型,
// 参数3 InvocationHandler h :执行目标方法时,会触发处理器方法(把当前执行的目标对象方法作为参数传入)。
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
(proxy, method, args) -> {
System.out.println(" JDK动态代理开始:代课教师,课前准备。 ");
// 反射调用。
Object invoke = method.invoke(target, args);
System.out.println(" 代理结束:代课教师,离开教室。 ");
return invoke;
}
);
}
}
/* *
* 客户端调用。
*/
public class Client {
public static void main(String[] args) {
// 创建目标对象。
TeacherDao target = new TeacherDao();
// 传入目标对象,获取代理实例。
ITeacherDao proxyInstance = (ITeacherDao) new TeacherDaoProxy(target).getProxyInstance();
// 通过代理对象实例进行方法调用。
proxyInstance.teach();
// JDK动态代理开始:代课教师,课前准备。
// 教师授课...
// 代理结束:代课教师,离开教室。
}
}
3.3 方式说明
特点:
- 代理对象不需要实现接口,但是目标对象要实现接口,否则不能用动态代理。
- 代理对象的生成,是利用
JDK
的API,动态在内存中构建代理对象。
四、Cglib代理方式
4.1 示例关系:
4.2 依赖引入:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.0</version>
</dependency>
4.3 代码实现:
/* *
* TeacherDao(目标对象类)。
*/
class TeacherDao {
public void teach() {
System.out.println(" 教师授课... ");
}
}
/* *
* TeacherDaoProxy实现MethodInterceptor接口。
* 重写该接口的intercept()方法。
* 使用cglib包的API创建代理对象。
*/
class TeacherDaoProxy implements MethodInterceptor {
/* *
* 持有一个Object类型的目标对象。
*/
private Object target;
public TeacherDaoProxy(Object target) {
this.target = target;
}
/* *
* 重写拦截方法。
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println(" cglib代理开始:代课教师,课前准备。 ");
Object invoke = method.invoke(target, objects);
System.out.println(" 代理结束:代课教师,离开教室。 ");
return invoke;
}
/* *
* 通过cglib获取代理对象实例。
*/
public Object getProxyInstance() {
// 创建一个增强器,用于创建动态代理对象。
Enhancer enhancer = new Enhancer();
// 设置父类。
enhancer.setSuperclass(target.getClass());
// 设置回调(需要使用intercept()方法进行拦截)。
enhancer.setCallback(this);
// 创建并返回子类对象(即代理对象)。
return enhancer.create();
}
}
/* *
* 客户端调用。
*/
public class Client {
public static void main(String[] args) {
// 目标对象。
TeacherDao target = new TeacherDao();
// 代理对象实例。
TeacherDao proxyInstance = (TeacherDao) new TeacherDaoProxy(target).getProxyInstance();
proxyInstance.teach();
// cglib代理开始:代课教师,课前准备。
// 教师授课...
// 代理结束:代课教师,离开教室。
}
}
4.4 方式说明:
静态代理和JDK代理模式都要求目标对象是实现一个接口,但是有时候目标对象就是一个单独的对象,并没有实现任何接口,这个时候可以使用目标对象子类来实现代理,这就是
Cglib
代理。它也叫子类代理,它是在内存中构建一个子类对象从而实现对目标对象的功能拓展,底层是通过使用字节码处理框架
ASM
来转换字节码并生成新的类。Cglib是一个强大的高性能的代码生成包,它可以在运行期间拓展java类与实现java接口。它广泛的被许多
AOP
的框架使用,例如Spring AOP
实现方法拦截。注意:
- 代理的类不能为
final
,否则会报java.lang.IllegalArgumentException
!- 目标对象的方法如果为
final/static
,那么就不会被拦截,即不会执行目标对象额外的业务方法。
五、代理模式总结
在AOP编程中:
- 目标对象需要实现接口,则使用
JDK
代理。- 目标对象不需要实现接口,用
Cglib
代理。常见代理模式变体:
- 防火墙代理
- 缓存代理
- 远程代理
- 同步代理
六、结束语
“-------怕什么真理无穷,进一寸有一寸的欢喜。”
微信公众号搜索:饺子泡牛奶。