概念:代理模式是一种设计模式,是为了在不变动目标对象(类)源码的情况下,进行目标对象现有功能的拓展。保证不修改原有类的同时(即满足对扩展开放,对修改关闭的原则),对原有类增加一些功能实现。
1、静态代理(代理类需要自己写)
-
定义:静态代理就是在编译时就确定了代理类与被代理类的关系。因为在编译阶段已经确认了代理关系。
-
实现思路:定义一个接口 ,定义一个目标类 和一个代理类 同时实现 这个接口,在代理类中持有目标类对象,并对目标类对象的方法进行一些拓展功能的实现。代码如下:
- 代码
public interface Eat
{
public void eat();
}
//定义一个目标对象,功能是提示开始吃晚饭了
public class EatDinner implements Eat{
public void eat(){
System.out.println("It's time to eat dinner .");
}
}
//但是现在想拓展这个吃晚饭的功能了,
//拓展为,先饭前洗手,再吃晚饭
//不想改变EatDinner类源码的情况下怎么实现呢?写一个代理类,这个类包含EatDinner类,
//并进行目标类功能拓展。也就是说,将EatDinner类对象注入到代理类中,然后调用拓展后的功能方法
public class EatProcxy implements Eat{
private EatDinner eatDinner;
public EatProcxy(EatDinner eatDinner){
this.eatDinner = eatDinner;
}
public void eat(){
System.out.println("饭前洗手");
System.out.println("It's time for dinner");
}
public static void main (String[] args) {
EatDinner eatDinner = new EatDinner();
EatProcxy eatProcxy = new EatProcxy(eatDinner);
System.out.println(eatDinner.eat());
System.out.println(eatProcxy.eat());
}
}
这种模式只是将目标对象中的方法覆盖了感觉,像是重新写了一个方法。。。假装是拓展了类方法的功能,实际上并没有包含目标对象的方法,也就不算代理模式了。一旦接口改动了,所有实现了该接口的代理类也需要改动。维护成本太高。
2、动态代理
- 定义 :代理类在程序运行时创建的代理方式被称为动态代理。动态代理的代理类并不需要在Java代码中定义,而是在运行时动态生成的。能解决静态代理中维护代理类的成本。
- Java的动态代理主要有两种,即JDK代理和Cglib代理
2.1 JDK代理
JDK动态代理,写法很固定了,主要需要一个代理类,一个接口(InvocationHandler ),所有的代理类都需要实现InvocationHandler 接口。通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态的将横切逻辑和业务逻辑编织在一起。
/**
* 动态代理之JDK代理
*/
public class JDKProxy implements InvocationHandler {
// 目标对象
private Object object;
public JDKProxy(Object object) {
this.object = object;
}
//InvocationHandler 接口唯一的实现方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("-----------JDKProxy before------------");
Object invoke = method.invoke(object, args);
System.out.println("-----------JDKProxy after------------");
return invoke;
}
//测试方法
public static void main(String[] args) {
// 被代理的类,目标类
EatDinner realSubject = new EatDinner();
// 代理类
JDKProxy jdkProxy = new JDKProxy(realSubject);
// 生成代理对象
Subject subject = (Subject)
Proxy.newProxyInstance(RealSubject.class.getClassLoader(), new Class[]{Subject.class}, jdkProxy);
subject.doSomething();
}
}
实现JDK动态代理,调用Proxy类的静态方法newProxyInstance即可,该方法会返回代理类对象
|
接收的三个参数依次为:
loader:类加载器,一般使用被代理类的类加载器,当然使用接口的类加载器也是可以的
InvocationHandler:表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
h:就是我们上面所说的InvocationHandler接口具体实现对象
invoke方法:
Object invoke(Object proxy, Method method, Object[] args) throws Throwable 三个参数的具体意思: proxy:生成的代理类对象 method:被代理类的某个具体方法 arg0:实现方法的具体参数
可以看出静态代理和JDK代理有一个共同的缺点,就是目标对象必须实现一个或多个接口,如果没有,则可以使用Cglib代理
2.2 Cglib动态代理
使用cglib实现动态代理,并不要求委托类必须实现接口,底层采用asm字节码生成框架生成代理类的字节码,我们需要导入 cglib-nodep-3.2.6.jar 包。由于Spring的核心包中已经包括了Cglib功能,所以也可以直接引入spring-core-3.2.5.jar。目标类不能为final、目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法
//定义一个目标类,不用实现接口
public class EatDinner{
public void eat(){
System.out.println("It's time to eat dinner .");
}
}
//代理类CglibProxy实现MethodInterceptor接口并重写intercept方法
public class EatProcxy implements MethodInterceptor{
private EatDinner eatDinner;
public EatProcxy(EatDinner eatDinner){
this.eatDinner = eatDinner;
}
//给目标对象创建一个代理对象
public Object getProxyInstance(){
//1.工具类
Enhancer en = new Enhancer();
//2.设置父类
en.setSuperclass(eatDinner.getClass());
//3.设置回调函数
en.setCallback(this);
//4.创建子类(代理对象)
return en.create();
}
@Override
public Object intercept(Object obj, Method method, Object[]args, MethodProxy proxy) throws Throwable {
//扩展功能
。。。。。
//执行目标对象的方法
Object returnValue = method.invoke(eatDinner, args);
return returnValue;
}
public static void main (String[] args) {
//目标对象
EatDinner target = new EatDinner();
//代理对象
EatDinner proxy = (EatDinner)new EatProcxy(target).getProxyInstance();
//执行代理对象的方法
proxy.eat();
}
}
总结:三种代理模式各有优缺点和相应的适用范围,主要看目标对象是否实现了接口。以Spring框架所选择的代理模式举例
在Spring的AOP编程中:
如果加入容器的目标对象有实现接口,用JDK代理
如果目标对象没有实现接口,用Cglib代理
参考链接:https://www.cnblogs.com/boboxing/p/8126046.html
https://www.jianshu.com/p/4539e6d9f337
补充:程序设计的单一性原则,讲究类的功能单一,这样在该类改动的可能性才会更小。