结构型设计模式
- 代理模式(Proxy Pattern)
定义:为其他对象提供一种代理,以控制这个对象的访问,在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端与目标对象之间起到中介的作用
应用目的:保护目标对象,动态增强目标对象,代理模式分为静态代理和动态代理
角色:抽象目标角色,目标角色,代理角色
代理对象通过实现和目标对象所实现的接口,然后在实现类中,持有目标对象,于是在代理类中对抽象方法实现,并在调用目标对象的行为的基础上,增加自己的行为增强
通用静态代理方式实现:
抽象目标角色
package com.ProxyPattern;
/**
* @author yangxiaozhen
* @date 2022/5/8-20:23
*/
public interface Isubject {
void request();
void doSomthing();
}
目标角色
package com.ProxyPattern;
/**
* @author yangxiaozhen
* @date 2022/5/8-20:23
*/
public class RealSubject implements Isubject {
@Override
public void request() {
System.out.println("真实角色的自带行为");
}
@Override
public void doSomthing() {
System.out.println("真实角色自带的另一个行为");
}
}
代理类和测试
package com.ProxyPattern;
/**
* @author yangxiaozhen
* @date 2022/5/8-20:25
* 代理类通用写法
*/
public class ProxySubject implements Isubject {
private Isubject real;
@Override
public void request() {
before();
real.request();
after();
}
@Override
public void doSomthing() {
System.out.println("啥玩意儿");
}
private void after() {
System.out.println("在目标行为运行前代理织入行为增强");
}
private void before() {
System.out.println("在目标行为运行后代理织入行为增强");
}
public ProxySubject(Isubject real){
this.real=real;
}
public static void main(String[] args) {
new ProxySubject(new RealSubject()).request();
}
}
JDK中有内置的Proxy类(java.lang.reflect)做动态代理从原理上看是对对象的的代理
使用上主要有InvocationHandler,Method,Proxy三个类和接口
Proxy的newProxyInstance()方法返回代理类,需要提供三个参数:目标对象的类加载器,目标对象类所实现的接口,InvocationHandler接口的实例
InvocationHandler接口定义了invoke()方法,入参:目标实例,目标对象的方法(Method类型 ),参数Object数组,需要用户去是实现,invoke()方法是对目标对象所有方法的增强逻辑 我的理解,Proxy.newProxyInstance()通过入参用户实现的InvocationHandler对象中的Invoke()方法,来构造代理对象所代理的目标对象的方法的代理方法,在Invoke()方法里面,可以通过入参的methon,用method.invoke调用目标对象的方法,也可以不调用,完全去“覆盖”目标对象的方法。在定义InvocationHandler的实现时,经常习惯性的把获取代理类的方法封装在其中,然后在使用时直接调用即可
package com.ProxyPattern;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @author yangxiaozhen
* @date 2022/5/8-21:03
*/
public class JDKproxy implements InvocationHandler {
private Isubject target;
public Isubject getInstance(Isubject target){
this.target=target;
Class<? extends Isubject> aClass = target.getClass();
return (Isubject) Proxy.newProxyInstance(aClass.getClassLoader(),aClass.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 after() {
System.out.println("在目标行为运行前代理织入行为增强");
}
private void before() {
System.out.println("在目标行为运行后代理织入行为增强");
}
public static void main(String[] args) {
Isubject proxy = new JDKproxy().getInstance(new RealSubject());
proxy.request();
proxy.doSomthing();
}
}
JDK动态代理以及Cglib动态代理其实底层实现原理都是字节码的重组,不过各自对应的代理场景和实现方式不同,手动也可实现jdk动态代理
package com.ProxyPattern;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @author yangxiaozhen
* @date 2022/5/10-14:19
*/
public interface MyInvocationHandle {
Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException;
}
package com.ProxyPattern;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @author yangxiaozhen
* @date 2022/5/10-20:53
*/
public class MyInvocationHandleImpl implements MyInvocationHandle {
private static Isubject taget;
static {
taget=new RealSubject();
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
System.out.println("方法之前增强");
Object o = method.invoke(taget,args);
return o;
}
}
package com.ProxyPattern;
import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
/**
* @author yangxiaozhen
* @date 2022/5/10-14:20
*/
public class MyProxy {
private static final String ln = "\r\n";
public static Object getNewInstance(MyInvocationHandle handle, Class[] i, ClassLoader classLoader) {
try {
String codeSrc = generateSrc(i[0]);
String path = MyProxy.class.getResource("").getPath();
File file = new File(path + "$MyProxy01.java");
FileWriter fileWriter = new FileWriter(file);
fileWriter.write(codeSrc);
fileWriter.flush();
fileWriter.close();
// 编译源代码,并且生成.class文件
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager manager = compiler.getStandardFileManager(null, null, null);
Iterable iterable = manager.getJavaFileObjects(file);
JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, null, null, iterable);
task.call();
manager.close();
//
Class $MyProxy01 = classLoader.loadClass("com.ProxyPattern.$MyProxy01");
// Class $MyProxy01 = Class.forName("$MyProxy01");
Constructor constructor = $MyProxy01.getConstructor(MyInvocationHandle.class);
Object o = constructor.newInstance(handle);
return o;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 生成代理类源代码
*
* @param aClass 接口类型
*/
private static String generateSrc(Class aClass) {
StringBuilder src = new StringBuilder();
src.append("package com.ProxyPattern;" + ln);
//引入反射相关的包
src.append("import java.lang.reflect.Method;" + ln);
src.append("import com.ProxyPattern.MyInvocationHandle;"+ln);
//动态代理类实现被代理接口,在此为Person类
src.append("public class $MyProxy01 implements " + aClass.getName() + "{" + ln);
src.append("MyInvocationHandle h;" + ln);
src.append("public $MyProxy01(MyInvocationHandle h) {" + ln);
src.append("this.h = h;" + ln);
src.append("}" + ln);
for (Method m : aClass.getMethods()){
src.append("public " + m.getReturnType().getName() + " " + m.getName() + "(){" + ln);
src.append("try{" + ln);
src.append("Method m = " + aClass.getName() + ".class.getMethod(\"" + m.getName() + "\",new Class[]{});" + ln);
src.append("this.h.invoke(this,m,null);" + ln);
src.append("}catch(Throwable e){e.printStackTrace();}" + ln);
src.append("}" + ln);
}
src.append("}");
return src.toString();
}
public static void main(String[] args) {
((Isubject) MyProxy.getNewInstance(new MyInvocationHandleImpl(), RealSubject.class.getInterfaces(), MyInvocationHandle.class.getClassLoader())).doSomthing();
}
}
CGLIB相比于JDK动态代理更加强大,JDK动态代理虽然简单易用,但是其有一个致命缺陷是,只能对接口进行代理。如果要代理的类为一个普通类、没有接口,那么Java动态代理就没法使用了,CGLIB底层使用了ASM(一个短小精悍的字节码操作框架)来操作字节码生成新的类,这里只举例使用
package com.ProxyPattern;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* @author yangxiaozhen
* @date 2022/5/8-20:23
*/
public class RealSubject implements Isubject {
@Override
public void request() {
System.out.println("真实角色的自带行为");
}
@Override
public void doSomthing() {
System.out.println("真实角色自带的另一个行为");
}
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(RealSubject.class);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
if (method.getName().equals("request")){
System.out.println("target方法调用前织入");
}
methodProxy.invokeSuper(o,objects);
System.out.println("target方法调用后织入");
return null;
}
});
RealSubject realSubject = (RealSubject) enhancer.create();
realSubject.doSomthing();
}
}