代理模式
什么叫做代理:
举个例子你叫你朋友(委托类又名代理类)给你(被代理)带饭,然后你朋友出去吃饭的时候顺带给你带了,这时你在宿舍bb说要他给你带个饮料。这时你朋友就还忍了给你带了饮料,但是你不满足你还要让他给你带个手抓饼,这时你朋友就不能忍了,说老子不干了,给爷爬。
现在我们分别用静态代理和动态代理来实现这个需求
一、静态代理
代理接口:
/**
* 带东西
*/
public interface TakeSomething {
void take();
}
代理类:
/**
* 代理类
*/
public class ProxyClass implements TakeSomething {
private TakeSomething targe;
public ProxyClass () {
}
public ProxyClass (TakeSomething targe) {
this.targe = targe;
}
@Override
public void take() {
targe.take();
System.out.println("带一个米饭");
//然后再给我带一个饮料,再给我带一个手抓饼
System.out.println("带一个饮料");
System.out.println("带一个手抓饼");
}
}
被代理类:
/**
* 被代理类
*/
public class Me implements TakeSomething {
@Override
public void take() {
System.out.println("叫室友");
}
}
main:
public class ProxyMain {
public static void main(String[] args) {
TakeSomething me = new Me();
ProxyClass proxyClass = new ProxyClass(me);
proxyClass.take();
}
}
结果:
这时候出现一个问题如果是我其他室友叫他带东西,这里就需要再添加一个代理类,假如室友有一百个,当然了我们都是正常性取向的哈,这里你就需要写一百个代理类,你想想看烦不烦。同时如果TakeSomething 接口类需要添加一个方法,ProxyClass (代理类)也需要添加一个方法,当然这个可以避免,你只需要ProxyClass 不实现TakeSomething接口类就可以,但是明显不太规范 ,所以就更需要动态代理了你只需要写被代理类和代理类就可以了。
二、动态代理
动态代理利用了JDK API,动态地在内存中构建代理对象,从而实现对目标对象的代理功能。动态代理又被称为JDK代理或接口代理。
静态代理与动态代理的区别主要在:
静态代理在编译时就已经实现,编译完成后代理类是一个实际的class文件。
动态代理是在运行时动态生成的,即编译完成后没有实际的class文件,而是在运行时动态生成类字节码,并加载到JVM中
1.JDK代理
特点:
动态代理对象不需要实现接口,但是要求目标对象必须实现接口,否则不能使用动态代理。
JDK中生成代理对象主要涉及的类有
java.lang.reflect Proxy,主要方法为
static Object newProxyInstance(
ClassLoader loader, //指定当前目标对象(被代理类)使用类加载器
Class<?>[] interfaces, //目标对象(被代理类)实现的接口的类型
InvocationHandler h //事件处理器,用于jdk动态代理生成的类(代理类)指向实现InvocationHandler接口的类
)
//返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。
java.lang.reflect InvocationHandler,主要方法为
Object invoke(Object proxy, Method method, Object[] args)
// 在代理实例上处理方法调用并返回结果。
动态代理类:
public class ProxyFactory {
private Object target;// 维护一个目标对象
public ProxyFactory(Object target) {
this.target = target;
}
// 为目标对象生成代理对象
public Object getProxyInstance() {
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 执行目标对象方法
Object returnValue = method.invoke(target, args);
System.out.println("带一个米饭");
//然后再给我带一个饮料,再给我带一个手抓饼
System.out.println("带一个饮料");
System.out.println("带一个手抓饼");
return null;
}
});
}
}
main:
public class ProxyMain {
public static void main(String[] args) {
// TakeSomething me = new Me();
// ProxyClass proxyClass = new ProxyClass(me);
// proxyClass.take();
System.out.println("动态代理");
TakeSomething me = new Me();
System.out.println("目标对象信息:"+me.getClass()); //输出目标对象信息
TakeSomething proxy = (TakeSomething) new ProxyFactory(me).getProxyInstance();
System.out.println("代理对象信息:"+proxy.getClass()); //输出代理对象信息
proxy.take(); //执行代理方法
}
}
结果:
2.cglib代理
cglib (Code Generation Library )是一个第三方代码生成类库,运行时在内存中动态生成一个子类对象从而实现对目标对象功能的扩展。
cglib特点
JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口。
如果想代理没有实现接口的类,就可以使用CGLIB实现。CGLIB是一个强大的高性能的代码生成包,它可以在运行期扩展Java类与实现Java接口。
它广泛的被许多AOP的框架使用,例如Spring AOP和dynaop,为他们提供方法的interception(拦截)。
CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。
不鼓励直接使用ASM,因为它需要你对JVM内部结构包括class文件的格式和指令集都很熟悉。
cglib与动态代理最大的区别就是
使用JDK动态代理的对象必须实现一个或多个接口。
使用cglib代理的对象则无需实现接口,达到代理类无侵入。
/**
* 其他室友
*/
public class OtherRoomie implements TakeSomething {
@Override
public void take() {
System.out.println("xxx室友叫带东西");
}
}
/**
* CGLIB代理类
*/
public class CGLIBProxyFactory implements MethodInterceptor {
private Object target;//维护一个目标对象
public CGLIBProxyFactory(Object target) {
this.target = target;
}
//为目标对象生成代理对象
public Object getProxyInstance() {
//工具类
Enhancer en = new Enhancer();
//设置父类
en.setSuperclass(target.getClass());
//设置回调函数
en.setCallback(this);
//创建子类对象代理
return en.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// 执行目标对象的方法
Object returnValue = method.invoke(target, args);
System.out.println("带一个米饭");
//然后再给我带一个饮料,再给我带一个手抓饼
System.out.println("带一个饮料");
System.out.println("带一个手抓饼");
return null;
}
}
main:
public class ProxyMain {
public static void main(String[] args) {
System.out.println("CGLIB动态代理");
//目标对象
TakeSomething target = new OtherRoomie();
System.out.println("目标对象信息:"+target.getClass());
//代理对象
TakeSomething cglibProxy = (TakeSomething) new CGLIBProxyFactory(target).getProxyInstance();
System.out.println("代理对象信息:"+cglibProxy.getClass());
//执行代理对象方法
cglibProxy.take();
}
}
结果: