代理模式
在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。
这种类型的设计模式属于结构型模式。
在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口。
这样的意图是:为其他对象提供一种代理以控制对这个对象的访问。
使用场景:当我们想在访问一个类时做一些操作和控制,同时扩展类的功能时,就可以使用代理模式。
静态代理
由程序员创建代理类或者特定的工具自动生成源代码在对其进行编译,静态代理事先知道要代理的是什么,即代理对象一开始就是已知的
被代理对象和代理对象继承相同的接口或者父类(一般是继承接口)
举个简单例子:
猪八戒去找高翠兰结果是孙悟空变的,可以这样理解:把高翠兰的外貌抽象出来,高翠兰本人和孙悟空都实现了这个接口,猪八戒访问高翠兰的时候看不出来这个是孙悟空,所以说孙悟空是高翠兰代理类。
类图关系:
代码实现:
/**
* 静态代理
* 代理类和被代理类都实现接口或抽象类,通过代理类来对被代理类进行操作
*/
public class StaticProxy {
public static void main(String[] args) {
// 猪八戒进高老庄,先背上了孙悟空,孙悟空考验猪八戒,成功
new SWK(true);
// 失败
new SWK(false);
}
}
//例子
/**
* 猪八戒去找高翠兰结果是孙悟空变的,可以这样理解:
* 把高翠兰的外貌抽象出来,高翠兰本人和孙悟空都实现了这个接口,
* 猪八戒访问高翠兰的时候看不出来这个是孙悟空,
* 所以说孙悟空是高翠兰代理类
* 孙悟空看猪八戒的表现来决定是否让猪八戒看真的高翠兰
*/
abstract class Aspect{
String name;
public abstract void doSomething();
}
class GCL extends Aspect{
String name = "高翠兰";
public GCL(){
doSomething();
}
@Override
public void doSomething() {
System.out.println("和高翠兰见面");
}
}
class SWK extends Aspect{
String name = "孙悟空变的高翠兰";
boolean qual;
public SWK(boolean qual){
this.qual = qual;
doSomething();
}
@Override
public void doSomething() {
System.out.println("判断是否有资格和高翠兰见面");
if(qual){//有资格,就让猪八戒见真的高翠兰
new GCL();
}else {
System.out.println("死🐖给我滚!");
}
}
}
动态代理(接口级别代理)
动态代理又叫JDK动态代理是实现JDK里的InvocationHandler接口的invoke方法,但注意的是代理的是接口,也就是你的业务类必须要实现接口,通过Proxy里的newProxyInstance得到代理对象。
JDK动态代理是基于反射的,效率比较低。
动态代理不知道要代理什么东西,只有在运行时才知道。
类图关系:
代码:
/**
* 动态代理
* 代理类不用实现接口,利用JDK的api动态在内存中构建对象
*/
public class JDKProxy {
public static void main(String[] args) {
// GCL swk = (GCL) new SWK(new GCL()).getSWK();
// 必须强转成接口类型,否则报错 $Proxy0 cannot be cast to GCL
Aspect swk = (Aspect) new SWK(new GCL()).getSWK();
swk.doSomething();
swk.doSomeThing2();
}
}
//例子
// 注意动态代理,一定要是接口
interface Aspect{
public void doSomething();
public void doSomeThing2();
}
class GCL implements Aspect{
@Override
public void doSomething() {
System.out.println("和高翠兰见面");
}
@Override
public void doSomeThing2() {
System.out.println("做一些不可描述的事情");
}
}
//此时孙悟空由JDK来进行创建
class SWK {
// 目标对象
Object target;
public SWK(Object target){
this.target = target;
}
// 通过JDK提供的API创建一个代理对象
public Object getSWK(){
/**
* public static Object newProxyInstance(ClassLoader loader,//指定当前代理对象的类加载器
* Class<?>[] interfaces,//目标对象的接口类型
* InvocationHandler h)//事件处理,执行目标方法时,会触发事件处理器的方法
*/ //会把当前执行目标对象的方法作为参数传入
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("JDK代理开始");
System.out.println("前置通知");
Object returnVal = method.invoke(target);
System.out.println("后置通知");
return returnVal;
}
}
);
}
}
扩展一点cgilib 代理。。。
cgilib 代理——方法级别代理
① 查找A上的所有非final的public类型的方法定义
② 将这些方法的定义转换成字节码
③ 将组成的字节码转换成相对应的代理的class对象
④ 实现MethodInterceptor接口,用来处理对代理类上所有方法的请求
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibProxyDemo implements MethodInterceptor {
// cglib中的Enhancer对象
private Enhancer enhancer = new Enhancer();
public Object getProxy(Class clazz) {
// 代理类的父类
enhancer.setSuperclass(clazz);
// 添加Callback对象
enhancer.setCallback(this);
// 通过cglib动态创建子类实例并返回
return enhancer.create();
}
// intercept()方法中实现了方法拦截
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
System.out.println("before operation...");
// 调用父类中的方法
Object result = proxy.invokeSuper(obj, args);
System.out.println("after operation...");
return result;
}
}
public class CglibMainDemo { // 父类,也是代理的目标类
public String method(String str) { // 被代理的目标方法
System.out.println(str);
return "CglibMainDemo:" + str;
}
public static void main(String[] args) {
CglibProxyDemo proxy = new CglibProxyDemo();
// 获取CglibMainDemo的代理对象
CglibMainDemo proxyImp = (CglibMainDemo) proxy.getProxy(CglibMainDemo.class);
// 执行代理对象的method()方法
String result = proxyImp.method("test");
System.out.println(result);
}
}
cgilib 可以实现没有接口的目标类的增强,它的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,采用的是继承,所以不能对final修饰的类进行代理。
特点:基于继承,代理对象继承真实的对象
效率:基于字节码文件动态生成,效率高
坏处:第三方提供,需要导入jar包