1.什么是代理模式?
指由于某些原因,访问对象不适合或不能直接访问某个对象,此时需要通过一个代理对象帮组我们访问该对象,代理对象是访问对象和被访问对象中间的一个中介。
2.java中什么是静态代理?
例如我们定义一个业务-卖手机。通常我们买手机是在市场中买的,但我们都知道市场价和商家在手机制造工厂的进货价是有区别的。假设我们不能直接访问手机工厂买手机,只能通过商家买手机。商家就是我们和工厂之间的代理。
卖手机这个业务我们抽象为一个java接口Phone中的sell方法。工厂类,商家类实现Phone接口,实现接口中的方法。工厂sell给商家一台手机5000块。商家sell我们6000块。商家赚我们1000块,这1000块类似“功能增强”。我们在main方法中调用商家类对象的sell方法买一部手机。这就是静态代理。
静态代理能实现功能代理的功能,功能增强,控制访问。但静态代理的缺点非常明显。
第一:当我们需要对很多个工厂类进行代理时,我们必须也要编写很多个商家类。这样的话代码就会很臃肿,庞大。一个代理类只能代理一个目标类。
第二:静态代理模式中目标类和代理类均实现接口,当接口增加方法时,不仅仅目标类要实现新的方法,代理类也要实现新的方法。这样就很麻烦。
为了解决这些问题,我们引入动态代理。
3.什么是动态代理?
在程序运行的时候,动态代理可以在我们没有编写代理类的情况下,为我们创建代理类的对象。而且代理的目标类对象是活动的。
4. 动态代理的分类
JDK动态代理:使用jdk反射机制在程序运行时,为我们创建代理类的对象,而我们不需要关心代理类的创建。代理的目标类必须实现接口。
cgLib动态代理Code Generation Library:cglib利用继承目标类,为我们创建代理类的对象。cglib通过继承目标类,重写与父类同名的方法,实现对功能的修改。因此cglib实现动态代理不需要目标类实现接口,并且目标类不能被final修饰,目标类中的方法也不能被final修饰。cglib动态代理在mybatis,spring等框架中均有使用。cglib在代理效率优于jdk动态代理。
5.jdk动态代理的实现步骤
1.创建接口,定义目标类需要完成的功能
2.创建目标类,实现接口。
3.创建InvocationHandler接口的实现类。在invoke方法中完成代理类的功能。
目标方法的调用
功能增强
4.使用Proxy类中静态方法Proxy.newProxyInstance完成代理类对象的创建,返回代理对象,并把返回值转为接口类型。
6.jdk动态代理的例子
//1.创建接口,定义目标类需要完成的功能
public interface People {
void sayHello();
void sayGoodBye();
}
//2.创建接口的实现类,及目标类,实现接口中的方法
public class PeopleImpl implements People {
@Override
public void sayHello() {
System.out.println("hello!!!!!!");
}
@Override
public void sayGoodBye() {
System.out.println("saygood!!!!!!");
}
//这个方法没用
public void sayNihao(){
System.out.println("nihao");
}
}
//3.实现InvocationHandler接口,在invoke方法中完成代理类的功能。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class InvocationHandlerImp implements InvocationHandler {
private People people;
InvocationHandlerImp(People people){
this.people=people;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理开始");
method.invoke(people, args);
System.out.println("增强功能");
return null;
}
}
import java.lang.reflect.Proxy;
public class main {
public static void main(String[] args) {
People o1=(People)Proxy.newProxyInstance(PeopleImpl.class.getClassLoader(),
PeopleImpl.class.getInterfaces(), new InvocationHandlerImp(new PeopleImpl()));
o1.sayHello();
o1.sayGoodBye();
}
}
输出结果:
代理开始
hello!!!!!!
增强功能
代理开始
saygood!!!!!!
增强功能
从运行结果可以看到我们不但完成了目标方法的调用还能进行功能增强。
7.如果我们把main函数中创建代理类对象的语句改变一下
//People o1=(People)Proxy.newProxyInstance(PeopleImpl.class.getClassLoader(), PeopleImpl.class.getInterfaces(), new InvocationHandlerImp(new PeopleImpl()));
People o1=(PeopleImp)Proxy.newProxyInstance(PeopleImpl.class.getClassLoader(), PeopleImpl.class.getInterfaces(), new InvocationHandlerImp(new PeopleImpl()));
我们将得到的Object类的代理对象强转成目标类,此时会发生什么?
程序运行是抛出异常。java.lang.ClassCastException: class com.sun.proxy.$Proxy0 cannot be cast to class PeopleImpl。不能转为PeopleImple对象。为什么不能转为PeopleImple类型,而能转成People类型呢????
为什么不能转为PeopleImple类型,而能转成People类型呢????
这里我拿一个网上的例子说明,tagService是我们定义的接口。目标类实现这个接口。printSomeThing(String paramString)是我们定义给目标类的功能。
我们将jdk动态代理为我们创建的代理对类反编译得出:
package com.sun.proxy;
import com.Activeproxy.jdk.tagService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;public final class $Proxy0 extends Proxy implements tagService{
private static Method m1;
private static Method m3;
private static Method m0;
private static Method m2;
public $Proxy0(InvocationHandler paramInvocationHandler) {
super(paramInvocationHandler);
}
public final boolean equals(Object paramObject) {
try {
return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
}
catch (Error|RuntimeException localError) {
throw localError;
}
catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void printSomeThing(String paramString) {
try {
this.h.invoke(this, m3, new Object[] { paramString });
return;
}
catch (Error|RuntimeException localError) {
throw localError;
}
catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
}
public final int hashCode() {
try {
return ((Integer)this.h.invoke(this, m0, null)).intValue();
}
catch (Error|RuntimeException localError) {
throw localError;
}
catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
}
public final String toString() {
try {
return (String)this.h.invoke(this, m2, null);
}
catch (Error|RuntimeException localError) {
throw localError;
}
catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m3 = Class.forName("com.Activeproxy.jdk.tagService").getMethod("printSomeThing", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException) {
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException) {
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
}
总结:从反编译的代理类结合我们自己写的jdk动态代理例子可以看出以下信息:
1.jdk动态代理的代理类的com.sun.proxy.$Proxy0类型的。
2.jdk动态代理为我们创建的代理类继承了Proxy类,实现了我们定义的接口People。为什么要继承Proxy类,因为h引用指向我们的InvocationHandler实现类,h引用在Proxy类中。所以继承Proxy类就能继承到这个h引用。h.invoke()就能调用我们在InvocationHandler实现类中的实现的invoke方法。实现目标类的方法且进行功能增强。
3.这里就能解释为什么不能强转为PeopleIml类型了。因为代理类的本质类型是People类型。
4.****目标类的部分作用就是告诉jdk动态代理机制,我们要让代理类实现哪个接口,所以在创建代理类的对象时我们需要传入目标类的接口类型数组。
5.真正实现代理类功能的地方是我们在InvocationHandler实现类中的invoke方法中编写的代码。在invoke方法代码中,目标类对象不是固定的,可以是接口的任意实现类。
8.演示第五条结论
//创建目标类2,实现功能
public class PeopleImpl2 implements People {
@Override
public void sayHello() {
System.out.println("你好");
}
@Override
public void sayGoodBye() {
System.out.println("再见");
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class main {
public static void main(String[] args) {
PeopleImpl2 people2 = new PeopleImpl2();
People o1=(People)Proxy.newProxyInstance(PeopleImpl.class.getClassLoader(),
PeopleImpl.class.getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理 开始");
//people是PeopleImpl2类的对象
method.invoke(people2,args);
System.out.println("功能 增强");
return null;
}
});
o1.sayHello();
o1.sayGoodBye();
}
}
运行结果:
代理 开始
你好
功能 增强
代理 开始
再见
功能 增强
可以看到,我们用PeopleImp目标类创建代理对象,但invoke方法中我们调用的是PeopleImpl2类对象的方法method.invoke(people2,args);。
由此可看出jdk动态代理真正实现功能增强的是我们自己定义的InvocationHandler实现类中的invoke方法上。 而创建代理类$Proxy0可以根据接口的任意一个实现类完成,因为接口的实现类均实现了接口中定义的所有方法。 jdk动态代理的最终目的是创建一个代理类,实现我们定义的接口中全部方法。 在代理类全部方法中调用我们在InvocationHandler实现类中实现的invoke方法。实现目标类的方法且进行功能增强。 而这个InvocationHandler实现类引用h却在Proxy类中,所以要继承Proxy类,获得有关方法和将InvocationHandler实现类对象用构造方法传给父类Proxy。 继承父类就能继承到这个h引用。h.invoke()就能调用我们在InvocationHandler实现类中的实现的invoke方法。实现目标类的方法且进行功能增强。