代理模式
14.7.什么是代理模式?
为其他对象提供一种代理以控制对这个对象的访问。
买火车票不一定在火车站买,也可以去代售点。
package com.lx.test1;
public interface SellPaio {
void maipiao();
}
package com.lx.test1;
public class HuoCheZhan implements SellPaio{
@Override
public void maipiao() {
System.out.println("买票");
}
}
package com.lx.test1;
public class DaiShouDian implements SellPaio{
private HuoCheZhan hcz=null;
public DaiShouDian(HuoCheZhan hcz) {
this.hcz=hcz;
}
@Override
public void maipiao() {
hcz.maipiao();
}
}
package com.lx.test1;
public class TestMain {
public static void main(String[] args) {
HuoCheZhan hcz=new HuoCheZhan();
hcz.maipiao();
DaiShouDian dsd=new DaiShouDian(hcz);
dsd.maipiao();
}
}
上面这个实例中体现了静态代理,【兄弟模式的静态代理】
package com.lx.test2;
public class HuoCheZhan {
public void maipiao() {
System.out.println("买票");
}
}
package com.lx.test2;
public class DaiShouDian extends HuoCheZhan{
@Override
public void maipiao() {
super.maipiao();
}
}
package com.lx.test2;
public class TestMain {
public static void main(String[] args) {
HuoCheZhan hcz=new HuoCheZhan();
hcz.maipiao();
DaiShouDian dsd=new DaiShouDian();
dsd.maipiao();
}
}
上面这个实例中体现了静态代理,【父子模式的静态代理】
静态代理的缺点需要手动创建子类,当需要被代理语言较多的时候,我们的工作量变大了。
由于静态代理的上述缺点,所以我们需要一个专门生产代理对象的类
动态代理–专门由一个类来生产需要被代理的类的代理对象。
有2中方式
JDK动态代理----通过java.lang.reflect Class Proxy类来创建代理类对象。
【只能为实现过某个接口的java类提供代理类对象】
CGlib代理-------CGlib是一个第三发的开发包,用的时候需要自己事先下载导入到项目中
【所有的java类提供代理类对象】
14.9.JDK动态代理与CGlib代理的区别
JDK动态代理:
动态代理相比于静态代理,动态代理的代理类是在动态生成的,也就是jvm通过反射获取代码生成代理类,所以用户并不能决定代理角色和真实角色之间的联系,而是由程序运行时候决定的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类的函数。
动态代理实现的三步走:
1.实现InvocationHandler接口,创建自己的调用处理器 。
2.给Proxy类提供ClassLoader和代理接口类型数组创建动态代理类 。
3.执行真实角色具体任务。
要想创建处理器必须实现InvocationHandler接口,之后通过类装载器创建代理类。每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。
Object invoke(Object proxy, Method method, Object[] args) throws Throwable
proxy:指代我们所代理的那个真实对象
method:指代的是我们所要调用真实对象的某个方法的Method对象
args:指代的是调用真实对象某个方法时接受的参数
我们依旧要创建抽象角色和真实角色类
package com.lx.test3;
public interface MaiPiaoInterface {
void maipiao();
}
package com.lx.test3;
public class HuoCheZhan implements MaiPiaoInterface {
@Override
public void maipiao() {
System.out.println("火车站卖票");
}
}
代理角色:和静态代理相比,静态代理运用对象调用完成代理,而动态代理通过实现InvocationHandler接口,创建了一个调用处理器,其实是通过Java的反射机制完成.
package com.lx.test3;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 创建代理类对象的java类
* 1、实现
*
* @author DELL
*
*/
public class CreateDaiShouDianObject implements InvocationHandler{
//定义目标对象
private Object targetObject;
public CreateDaiShouDianObject(Object targetObject) {
this.targetObject=targetObject;
}
//得到代理对象
public Object getProxy() {
//参数1--ClassLoader loader
//参数2--Class<?>[] interfaces
//参数3--InvocationHandler h
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
targetObject.getClass().getInterfaces(), this);
}
/**
* proxy:指代我们所代理的那个真实对象
method:指代的是我们所要调用真实对象的某个方法的Method对象
args:指代的是调用真实对象某个方法时接受的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(targetObject, args);
}
}
package com.lx.test3;
public class TestMain {
public static void main(String[] args) {
HuoCheZhan hcz = new HuoCheZhan();
hcz.maipiao();
CreateDaiShouDianObject cdlo = new CreateDaiShouDianObject(hcz);
MaiPiaoInterface daishoudian = (MaiPiaoInterface)cdlo.getProxy();
daishoudian.maipiao();
}
}
看上去实现很简单,我们只需要实现接口,调用其中的方法就可以实现动态代理。
通过newProxyInstance方法获取代理类实例,而后我们便可以通过这个代理类实例调用代理类的方法,对代理类的方法的调用实际上都会调用中介类(调用处理器)的invoke方法。
注意:JDK动态代理只能处理有接口实现关系的java类
Cglib代理
cglib是一个强大的高性能的代码生成包,广泛的被许多AOP的框架使用,它的开源地址在https://github.com/cglib/cglib
通过它实现动态代理,主要用到import net.sf.cglib.proxy包下的MethodInterceptor、MethodProxy和Enhancer类
(1)、MethodInterceptor
MethodInterceptor类是方法拦截器,代理类实例的方法被调用时会回调MethodInterceptor的intercept()方法拦截,用来实现自己的代理逻辑,类似于jdk动态代理的InvocationHandler接口
(2)、MethodProxy
MethodProxy是intercept()方法中的第四个参数的类型,它是实际类方法的代理引用,使用methodProxy比使用jdk自身的method在效率上会有提升
(3)、Enhancer
Enhancer用来动态创建实际类子类的代理类实例,setSuperclass()方法设置实际类为父类,setCallback()方法建立方法回调,create()方法创建一个代理类实例
package com.lx.test4;
public class HuoCheZhan implements MaiPiaoInterface {
@Override
public void maipiao() {
System.out.println("火车站卖票");
}
}
package com.lx.test4;
public interface MaiPiaoInterface {
void maipiao();
}
package com.lx.test4;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
/**
* 创建代理类对象的java类
*
*/
public class CreateDaiShouDianObject implements MethodInterceptor{
//定义目标对象
private Object targetObject;
public CreateDaiShouDianObject(Object targetObject) {
this.targetObject=targetObject;
}
//创建代理对象
public Object getProxy() {
Enhancer enhancer=new Enhancer();
enhancer.setSuperclass(targetObject.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
/**
* 用来实现自己的代理逻辑,就是在这个方法中访问被代理类的实际方法
*/
@Override
public Object intercept(Object proxy, Method method, Object[] params, MethodProxy methodProxy) throws Throwable {
return methodProxy.invokeSuper(proxy, params);
}
}
package com.lx.test4;
public class TestMain {
public static void main(String[] args) {
HuoCheZhan hcz=new HuoCheZhan();
hcz.maipiao();
CreateDaiShouDianObject cdlo=new CreateDaiShouDianObject(hcz);
HuoCheZhan daishoudian=(HuoCheZhan)cdlo.getProxy();
daishoudian.maipiao();
}
}
比较
1.静态代理是通过在代码中显式定义一个业务实现类一个代理,在代理类中对同名的业务方法进行包装,用户通过代理类调用被包装过的业务方法;
手动创建一个与目标类相同接口的子类,包装目标类。
2.JDK动态代理是通过接口中的方法名,在动态生成的代理类中调用业务实现类的同名方法;【兄弟模式】
通过jdk提供的反射包中Proxy这个类,动态的创建一个与目标类实现相同接口的子类对象,包装目标。
3.CGlib动态代理是通过继承业务类,生成的动态代理类是业务类的子类,通过重写业务方法进行代理。【父子模式】
通过CGlib提供的Enhancer这个类,动态的创建一个目标类的子类对象,包装目标类。