代理模式
注:代理模式是SpringAOP的基础,而本篇文章主要是为学习SpringAOP做铺垫,完整地学习代理模式请参考其他文章
[1]代理模式分类
A、静态代理
B、动态代理:[JDK动态代理]和[CGLIB动态代理]
[2]代理模式的三个要是
A、抽象的类或接口 ------完成一件怎样的事情
B、被代理的对象 -----事情操作的具体内容
C、代理对象-----帮助我们完成事情的同时增加其他功能
具体的例子:我们找中介租房子
A、抽象的类或接口 ------租房子
B、被代理的对象 -----房东
C、代理对象 -----中介
[3]代理模式的好处
A、房东可以安心做自己的事情-----(被代理对象可以安心做自己的事情)
B、我们有了问题直接找中介-----(被代理对象变得比较安全)
C、提升了代码的拓展性
[4]三种代理模式的代码实现
1.静态代理代码实现(以租房为例)
A、租房接口类
package cn.qt.proxy1;
//租房
public interface LetRoon {
public void zf();
}
B、被代理的对象
房东1
package cn.qt.proxy1;
//房东把房子租出去
public class FD implements LetRoon{
@Override
public void zf() {
System.out.println("------出租桂碧园12楼4层3户------");
}
}
房东2
package cn.qt.proxy1;
public class FD2 implements LetRoon{
@Override
public void zf() {
System.out.println("出租桂碧园12楼5层6户");
}
}
C、代理对象
中介类
package cn.qt.proxy1;
//中介代理租房
public class ZJ implements LetRoon{
private int money;
public ZJ(int money) {
this.money = money;
}
@Override
public void zf() {
//中介拓展的方法
System.out.println("-----收取50元介绍费----");
//中介处理租房的信息
if (money<=800){
//代理房东1的租房
FD fd=new FD();
fd.zf();
}else if (money>800&&money<2000){
//代理房东2的租房
FD2 fd2=new FD2();
fd2.zf();
}
//中介拓展的方法
System.out.println("-----收取500元管理费----");
}
}
D、客户类(租房者)
package cn.qt.proxy1;
//租房者
public class MyRent {
public static void main(String[] args) {
ZJ zj=new ZJ(900);//租一个每月价格为900块的房子
zj.zf();
}
}
静态代理模式的缺点:随着被代理对象的增加,代理对象(中介)的压力会越来越大,而且里面书写的代码也比较臃肿。
2.JDK动态代理
A、接口类租房
package cn.qt.proxy2;
//租房
public interface LetRoon {
public void zf();
}
B、被代理对象(房东对象)
房东1
package cn.qt.proxy2;
//房东把房子租出去
public class FD implements LetRoon{
@Override
public void zf() {
System.out.println("------出租桂碧园12楼4层3户------");
}
}
房东2
package cn.qt.proxy2;
public class FD2 implements LetRoon{
@Override
public void zf() {
System.out.println("出租桂碧园12楼5层6户");
}
}
C.实现InvocationHandler接口的中介对象
package cn.qt.proxy2;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//书写动态产生代理对象的方法
public class MyInv implements InvocationHandler {
private LetRoon lr;
public void setLr(LetRoon lr) {
this.lr = lr;
}
//执行该方法会动态产生代理对象
public Object getProxy(){
/*
* 参数一:保证类型是ClassLoade
* 参数二:接口的class类型数组
* 参数三:参数类型是InvocationHandler即可
* */
return Proxy.newProxyInstance(MyInv.class.getClassLoader(),new Class[]{LetRoon.class},this);
}
//这个方法就类似于书写中介的zf(),即书写代理对象的扩展
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/**
* proxy:代理对象----中介
* method:代理对象中的方法
* args:参数----null
*/
System.out.println("收取推荐费50元");
//运用反射执行指定方法的zf()。//应为所有的房东对象都是LetRoom的子类
Object invoke = method.invoke(lr, args);
System.out.println("收取管理费500元");
return invoke;
}
}
D.测试类(租房者类)
package cn.qt.proxy2;
public class Test {
public static void main(String[] args) {
MyInv my=new MyInv();
//设置具体的房东
my.setLr(new FD2());
//运行时,虚拟机会自动产生代理的对象
LetRoon proxy =(LetRoon) my.getProxy();
proxy.zf();
}
}
下面是用于理解的图
JDK代理模式的缺点:
我们发现JDK代理模式必须要有接口的操作,若没有这一接口,JDK动态代理则无法使用
3.CGLIB动态代理代码实现
A、导包
B、被代理对象(房东类)
package cn.qt.proxy3;
public class FD {
public void zf(){
System.out.println("----出租9号楼4层2户的房子-----");
}
}
C、代理对象(中介类)
package cn.qt.proxy3;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class MethodInv implements MethodInterceptor {
//产生代理类对象
public Object getProxy(){
Enhancer en = new Enhancer();
//类似于之前的接口
en.setSuperclass(FD.class);
en.setCallback(this);
//使整个设置的内容生效
return en.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
/*
* o:被代理的对象
* method:被代理对象的方法
* objects:参数
* methodProxy:代理对象中的方法
* */
System.out.println("--收取推荐费100元--");
//调用真正房东的zf()方法
Object o1 = methodProxy.invokeSuper(o, objects);
System.out.println("--收取管理费600元--");
return o1;
}
}
D、测试类(租房者类)
package cn.qt.proxy3;
public class Test {
public static void main(String[] args) {
MethodInv inv=new MethodInv();
FD proxy =(FD) inv.getProxy();
proxy.zf();
}
}