一、设计模式
一、概念
代理模式是Java常见的设计模式之一。所谓代理模式是指客户端并不直接调用实际的对象,而是通过调用代理,来间接的调用实际的对象。 为什么要采用这种间接的形式来调用对象呢?一般是因为客户端不想直接访问实际的对象,或者访问实际的对象存在困难,因此通过一个代理对象来完成间接的访问。
静态代理
动态代理【JDK动态代理、CGLIB动态代理】
二、代理模式关系图
从UML图中,可以看出代理类与真正实现的类都是继承了抽象的主题类,这样的好处在于代理类可以与实际的类有相同的方法,可以保证客户端使用的透明性。
三、代理模式的角色
A、抽象的类或者接口—定义完成一件怎样的事情
B、代理对象—-完成这件事情的对象,直接面向用户的
C、被代理对象—完成事件背后的隐藏内容,用户看不到
二、JDk动态代理
动态代理有别于静态代理,是根据代理的对象,动态创建代理类。这样,就可以避免静态代理中代理类接口过多的问题。动态代理是实现方式,是通过反射来实现的,借助Java自带的java.lang.reflect.Proxy,通过固定的规则生成
示例:
接口
public interface MyCalculator {
//加法
int jia(int num1, int num2);
//减法
int jian(int num1, int num2);
//乘法
int cheng(int num1, int num2);
}
实现类
package com.bjsxt.calculator.impl;
import com.bjsxt.calculator.MyCalculator;
import com.bjsxt.util.Log4JSxt;
public class MyCalculatorImpl implements MyCalculator {
@Override
public int jia(int num1, int num2) {
int z=num1+num2;
return z;
}
@Override
public int jian(int num1, int num2) {
int z=num1-num2;
return z;
}
@Override
public int cheng(int num1, int num2) {
int z=num1*num2;
return z;
}
}
jdk代理类
package com.bjsxt.proxy;
import com.bjsxt.calculator.MyCalculator;
import com.bjsxt.util.Log4JSxt;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//JDK动态代理
public class JDKProxy {
//在虚拟机中动态的产生代理对象--中介
public Object getProxy(Object o){
//[1]调用getProxy接收new MyCalculatorImpl()这个对象,虚拟机创建了$proxy.class这个代理类对象(实现了MyCalculatorImpl的接口)
return Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), new Class[]{MyCalculator.class}, new InvocationHandler() {
/*[2]用户调用代理类proxy的方法时执行,InvocationHandler().invoke
* 将proxy对象、其中包含的方法和参数传入invoke方法
* 暴露给用户 方便用户进行功能上的扩展
* proxy对象利用反射进行方法的调用*/
//proxy:代理对象 method:需要执行方法对象 args:实参列表
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//进行功能的扩展 例如:添加日志
Log4JSxt.log(args[0],args[1],method.getName());
//利用反射 调用目标函数中方法(房东对象)
Object invoke = method.invoke(o, args);
return invoke;
}
});
}
}
扩展的日志
package com.bjsxt.util;
public class Log4JSxt {
public static void log(Object num1,Object num2,String method){
System.out.println("操作数A:"+num1+",操作数B:"+num2+",做了:"+method+"操作");
}
}
测试
package com.bjsxt.test;
import com.bjsxt.calculator.MyCalculator;
import com.bjsxt.calculator.impl.MyCalculatorImpl;
import com.bjsxt.proxy.JDKProxy;
public class TestB {
public static void main(String[] args) {
//返回的是代理对象
MyCalculator proxy = (MyCalculator) new JDKProxy().getProxy(new MyCalculatorImpl());
//进行方法的调用 ----代理对象的乘法
int rs = proxy.cheng(10, 10);
System.out.println("结果为:"+rs);
}
}
三、CGLIB动态代理
1.JDK动态代理存在的问题
JDK代理的产生必须要实现对应的接口的,如果没有对应的接口,这个时候代理对象就没有办法产生。
2.解决方案
3.CGLIB动态代理
cglib的原理是这样,它生成一个继承B的类型C(代理类),这个代理类持有一个MethodInterceptor,我们setCallback时传入的。 C重写所有B中的方法(方法名一致),然后在C中,构建名叫“CGLIB”+“$父类方法名$”的方法(下面叫cglib方法,所有非private的方法都会被构建),方法体里只有一句话super.方法名(),可以简单的认为保持了对父类方法的一个引用,方便调用。
这样的话,C中就有了重写方法、cglib方法、父类方法(不可见),还有一个统一的拦截方法(增强方法intercept)。其中重写方法和cglib方法肯定是有映射关系的。
实现类
package com.bjsxt.calculator.impl;
import com.bjsxt.calculator.MyCalculator;
public class MyCalculatorImpl2 {
public int jia(int num1, int num2) {
int z=num1+num2;
return z;
}
public int jian(int num1, int num2) {
int z=num1-num2;
return z;
}
public int cheng(int num1, int num2) {
int z=num1*num2;
return z;
}
}
CGLIB代理类
package com.bjsxt.proxy;
import com.bjsxt.util.Log4JSxt;
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 CglibProxy {
public static Object getPoexy(Object target){
Enhancer enhancer = new Enhancer();
//设置父类
enhancer.setSuperclass(target.getClass());
/**
* 参数一:代理对象
* 参数二:目标方法反射对象
* 参数三:目标方法参数
* 参数四:代理方法反射对象
*/
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//添加扩展功能
Log4JSxt.log(objects[0],objects[1],method.getName());
//调用到目标函数
Object invoke = method.invoke(target, objects);
return invoke;
}
});
//使上述操作生效 返回代理对象
Object o = enhancer.create();
return o;
}
}
测试
package com.bjsxt.test;
import com.bjsxt.calculator.impl.MyCalculatorImpl2;
import com.bjsxt.proxy.CglibProxy;
public class TestC {
public static void main(String[] args) {
//返回的是代理对象
MyCalculatorImpl2 poexy = (MyCalculatorImpl2) CglibProxy.getPoexy(new MyCalculatorImpl2());
//进行方法的调用 ----代理对象的乘法
int cheng = poexy.cheng(20, 30);
System.out.println("结果为:"+cheng);
}
}