1、基本概念
为其他对象提供一种代理,来控制这个对象的访问,属于结构型的模式,实现代码增强的功能。
生活场景:如婚恋介绍所,黄牛,租房
应用场景:如spring中的aop
2、代理类型
静态代理和动态代理
动态代理又有cglib和jdk的动态代理。
2.1、静态代理
案例:父亲给儿子相亲:
类图:
接口:相亲人员
public interface IblindDatePerson {
void blindDate();
}
被代理对象:儿子
public class Wangwu implements IblindDatePerson { @Override public void blindDate() { System.out.println("我的要求是:女的,活的"); } }
代理对象:父亲
public class LaoWang implements IblindDatePerson { private IblindDatePerson wangwu; public LaoWang(IblindDatePerson wangwu){ this.wangwu = wangwu; } @Override public void blindDate() { before(); wangwu.blindDate(); after(); } private void before() { System.out.println("开始替儿子王五在相亲公园寻找"); } private void after() { System.out.println("儿子王五的相亲对象找到了"); } }
测试类:
public class Test { public static void main(String[] args) { IblindDatePerson iperson = new LaoWang(new Wangwu()); iperson.blindDate(); } }
输出:
开始替儿子王五在相亲公园寻找
我的要求是:女的,活的
儿子王五的相亲对象找到了
从上面案例可以看到,我们代理的是同一类型的对象,即相亲对象,如果是别的人员,就无法代理,这就是静态代理。那么动态
代理,就可以代理任何的对象。
2.2、动态代理
动态代理采用在运行时动态生成代码的方式,取消了对被代理类的扩展限制,遵循开闭原则
1、基于JDK动态代理
被代理的类需要继承统一接口
案例:类图
接口:
public interface IblindDatePerson { void blindDate(); }
被代理类:
public class Zhangsan implements IblindDatePerson { @Override public void blindDate() { System.out.println("我的要求是:女的,活的"); } }
代理类:实现InvocationHandler 接口
public class LaoWangJdkproxy implements InvocationHandler { private Object target; public Object getProxyInstance(Object target){ this.target = target; Class<?> clazz = target.getClass(); return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { before(); Object result = method.invoke(this.target, args); after(); return result; } private void before() { System.out.println("开始替儿子王五在相亲公园寻找"); } private void after() { System.out.println("儿子王五的相亲对象找到了"); } }
测试类:
public class Test { public static void main(String[] args) { IblindDatePerson iperson = (IblindDatePerson)new LaoWangJdkproxy().getProxyInstance(new Zhangsan()); iperson.blindDate(); } }
输出:
开始替儿子王五在相亲公园寻找
我的要求是:女的,活的
儿子王五的相亲对象找到了
这里的重点就是这个代理类了,它需要实现InvocationHandler 接口
private Object target; //对外提供一个获取代理对象的方法,target是传入被代理的类 public Object getProxyInstance(Object target){ this.target = target; Class<?> clazz = target.getClass(); return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this); }
生成代理类的关键方法:需要传入三个参数:target的类加载器,target的所有接口,当前对象
Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
利用反射进行方法的调用,可以在执行目标方法前后进行增强
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { before(); Object result = method.invoke(this.target, args); after(); return result; }
这个是基于Jdk的动态代理。
2、基于cglib的动态代理
被代理的类不需要继承统一接口
它需要导入一个cglib包
<dependency> <groupId>cglib</groupId> <artifactId>cglib-nodep</artifactId> <version>2.2</version> </dependency>
类图:
代理类:实现MethodInterceptor 接口
public class LaoWangCglibProxy implements MethodInterceptor { public Object getProxyInstance(Class<?> clazz) throws Exception{ Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(clazz); enhancer.setCallback(this); return enhancer.create(); } @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { before(); Object ob = method.invoke(obj,args); after(); return ob; } private void before() { System.out.println("开始替儿子王五在相亲公园寻找"); } private void after() { System.out.println("儿子王五的相亲对象找到了"); } }
以上就是两种动态代理的实现
动态代理相比静态代理的优点:
1、静态代理只能手动代理:如果我们新增一个一种情况,我们就需要新增一个代理对象,例如:
我们新增一个狗类,有个实现类二哈
public interface IDog { void play(); }
public class ErhaDog implements IDog { @Override public void play() { System.out.println("二哈在拆家。。。"); } }
如果静态代理就需要新增一个代理类,去处理狗相关的代理
如果是动态代理就不需要了,我们测试方法里,直接把ErhaDog对象传过去,即可生成代理对象
结合策略模式,即可很好的进行扩展。
2、如果被代理对象新增一个方法,那么代理类就需要新增一个代理类去处理,而动态代理不需要。
3、动态代理采用在运行时动态生成代码的方式,取消了对被代理类的扩展限制,遵循开闭原则