代理模式详解
特点:
- 1、 执行者、 被代理人
- 2、 对于被代理人来说, 这件事情是一定要做的, 但是我自己又不想做或者没有时间做, 找代理。
- 3、 需要获取到被代理的人个人资料。
- 4、关心过程
穷举法:
- 租房中介: 中介和你
- 火车票黄牛: 黄牛和你
- 媒人: 媒婆和你
- 明星经纪人: 经纪人和明星 刘德华要开演唱会(长沙) 、 准备工作和善后工作
总结:
- 做了一件什么事情呢? 字节码重组
- 可以做一件什么事情? 可以在每一个方法调用之前加一些代码, 在方法调用之后再加一些代码
代理模式在Spring中的使用
可以做一件什么事情?
在调用方法前和方法后加入一些处理逻辑
AOP中使用场景
- 事务代理(声明式事务, 哪个方法需要加事务, 哪个方法不需要加事务)
- 日志监听
假如我们定义了一个service 方法
- 开启一个事务(open)
代理来做
- 事务的执行
执行我们的service方法
- 监听到是否有异常, 可能需要根据异常的类型来决定这个事务是否要回滚还是继续提交
代理来做
- (commit/rollback)
代理来做
- 事务要关闭(close)
代理来做
代理模式的三种玩法
- jdk代理模式
- cglib代理模式实现
- 手写一个代理模式
jdk代理模式实现
通过接口实现代理模式,先定义一个接口:
/**
*
* @author DoubleChina
* @date 2018/1/6 11:54
*/
public interface Person {
//寻找真爱、相亲
void findLove();
}
实现这个接口:
/**
*
* @author DoubleChina
* @date 2018/1/6 11:54
*/
public interface Person {
//寻找真爱、相亲
void findLove();
}
定义一个被代理人
/**
*
* @author DoubleChina
* @date 2018/1/6 11:53
*/
public class Jack implements Person {
private String sex = "男";
private String name = "Jack";
public void findLove() {
System.out.println("我叫" + this.name + ",性别是" + this.sex);
System.out.println("喜欢长的漂亮的");
System.out.println("身高165的");
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
定义一个代理:
/**
*
* @author DoubleChina
* @date 2018/1/6 11:53
*/
public class MyProxy implements InvocationHandler {
private Person person;
//获取被代理人的资料
public Object getInstance(Person target) throws Exception {
this.person = target;
Class zz = target.getClass();
return Proxy.newProxyInstance(zz.getClassLoader(), zz.getInterfaces(), this);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("我是媒婆:您的性别是:男");
System.out.println("开始海选");
method.invoke(this.person,args);
System.out.println("如何合适的话,就准备办事");
return null;
}
}
测试代理
/**
*
* @author DoubleChina
* @date 2018/1/6 11:54
*/
public class TestFindLove {
public static void main(String[] args) {
try {
Person person = (Person) new MyProxy().getInstance(new Jack());
person.findLove();
System.out.println(person.getClass());
//原理:
//1.拿到被代理对象的引用,然后获取它的接口
//2.JDK代理重新生成一个类,同时实现我们给的代理对象所实现的接口
//3.把被代理对象的引用也拿到了
//4.重新动态生成了一个class字节码
//5.然后编译
byte[] data = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{person.getClass()});
FileOutputStream os = new FileOutputStream("E:/learn/$Proxy0.class");
os.write(data);
os.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
cglib代理模式实现
maven需要导入Spring的核心库
<exclusions>
<exclusion>
<artifactId>servlet-api</artifactId>
<groupId>javax.servlet</groupId>
</exclusion>
</exclusions>
定义一个被代理人
/**
*
* @author DoubleChina
* @date 2018/1/6 11:53
*/
public class Python {
public void learnPython() {
System.out.println("看我72变");
}
}
代理执行者
/**
*
* @author DoubleChina
* @date 2018/1/6 11:52
*/
public class LanguageProxy implements MethodInterceptor {
public Object getInstance(Class clazz) throws Exception{
//通过反射机制给他实例化
Enhancer enhancer=new Enhancer();
//注入父类,告诉CGLib,生成的子类需要继承那个类
enhancer.setSuperclass(clazz);
//添加监听
enhancer.setCallback(this);
//1.生成源代码
//2.编译成.class文件
//3.加载到JVM中,返回对象
return enhancer.create();
}
//也是做了字节码重组,jdk实现需要实现接口,cglib则是通过继承的方式
//对于使用api无法感知的
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("-----------------学习开始-----------------");
System.out.println("我正在学习Python");
System.out.println("我正在学习大数据");
//JDK的动态代理是通过接口来强制转换的
//生成以后的代理对象是可以强制转化为我们的接口
//CGLib的动态代理是通过生成一个被代理对象的子类,然后重写了父类的方法
//生成的对象,可以强制为被代理的对象(也就是用户自己写的类)
//子类引用赋值给父类
//此处的 Object o对象是CGLib帮我么new出来子类的对象
//Java OOP,在new子类的同时,实际上默认先调用了我们super()的方法
//new了父类的同时,必须向new出来父类,这也就是间接持有了我们父类的引用
//我们改变了子类对象的某些属性,是可以间接的操作父类的属性的
methodProxy.invokeSuper(o,objects);
System.out.println("-----------------学习结束-----------------");
return o;
}
}
测试实现
/**
*
* @author DoubleChina
* @date 2018/1/6 11:53
*/
public class TestLanguage {
public static void main(String[] args) {
try {
Python python = (Python)new LanguageProxy().getInstance(Python.class);
python.learnPython();
} catch (Exception e) {
e.printStackTrace();
}
}
}
手写一个代理模式
定义一个被代理人
/**
*
* @author DoubleChina
* @date 2018/1/6 11:53
*/
public class Jack implements Person {
private String sex = "男";
private String name = "Jack";
public void findLove() {
System.out.println("我叫" + this.name + ",性别是" + this.sex);
System.out.println("喜欢长的漂亮的");
System.out.println("身高165的");
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
自定义一个代理
import main.proxy.jdk.Person;
import java.lang.reflect.Method;
/**
*
* @author DoubleChina
* @date 2018/1/6 11:53
*/
public class ZdyMeiPo implements MyInvocationHandler {
private Person person;
//获取被代理人的资料
public Object getInstance(Person target) throws Exception {
this.person = target;
Class zz = target.getClass();
return ZdyPorxy.newProxyInstance(new ZdyClassLoader(), zz.getInterfaces(), this);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("我是媒婆:您的性别是:男" );
System.out.println("开始海选");
method.invoke(this.person, args);
System.out.println("如何合适的话,就准备办事");
return null;
}
}
定义一个加载器
/**
*
* @author DoubleChina
* @date 2018/1/6 11:53
*/
public class ZdyClassLoader extends ClassLoader {
private File baseDir;
public ZdyClassLoader() {
String basePath = ZdyClassLoader.class.getResource("").getPath();
this.baseDir = new File(basePath);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
String className = ZdyClassLoader.class.getPackage().getName() + "." + name;
if (baseDir != null) {
File classFile=new File(baseDir,name.replaceAll("\\.","/")+".class");
if (classFile.exists()){
FileInputStream inputStream=null;
ByteArrayOutputStream outputStream=null;
try {
inputStream=new FileInputStream(classFile);
outputStream=new ByteArrayOutputStream();
byte[] buff=new byte[1024];
int len;
while ((len=inputStream.read(buff))!=-1){
outputStream.write(buff,0,len);
}
return defineClass(className,outputStream.toByteArray(),0,outputStream.size());
} catch (Exception e) {
e.printStackTrace();
}finally {
classFile.delete();
if (inputStream!=null){
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (null==outputStream){
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
return null;
}
}
底层实现代理全过程实现
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 DoubleChina
* @date 2018/1/6 11:53
*/
public class ZdyPorxy {
private final static String ln = "\n\t";
public static Object newProxyInstance(ZdyClassLoader loader,
Class<?> interfaces[],
MyInvocationHandler h)
throws IllegalArgumentException {
//1.生成源代码
String porxySrc = generateSrc(interfaces);
//2.将生成的源代码输出到磁盘,保存为.java文件
String filePath = ZdyPorxy.class.getResource("").getPath();
File file = new File(filePath + "$Proxy0.java");
System.out.println(filePath + "$Proxy0.java");
try {
FileWriter fileWriter = new FileWriter(file);
fileWriter.write(porxySrc);
fileWriter.flush();
fileWriter.close();
//3.编译源代码,生成.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();
//4.将class文件中的内容,动态加载到JVM中来
Class proClass = loader.findClass("$Proxy0");
//5.返回被代理后的对象
Constructor constructor = proClass.getConstructor(MyInvocationHandler.class);
// file.delete();
return constructor.newInstance(h);
} catch (Exception e) {
e.printStackTrace();
}
//将classh
return null;
}
private static String generateSrc(Class<?>[] interfaces) {
StringBuffer src = new StringBuffer();
src.append("package main.proxy.custom; " + ln);
src.append("import java.lang.reflect.Method; " + ln);
// src.append("public class $Proxy0 extend ZdyPorxy implements ");
src.append("public class $Proxy0 implements " + interfaces[0].getName() + "{ " + ln);
//声明变量
src.append("MyInvocationHandler h; " + ln);
//写入构造方法
src.append("public $Proxy0(MyInvocationHandler h) {");
src.append("this.h=h;" + ln);
src.append("}" + ln);
//迭代方法
for (Method method : interfaces[0].getMethods()) {
src.append("public " + method.getReturnType().getName() + " " + method.getName() + "() {" + ln);
src.append("try {" + ln);
src.append("Method m=" + interfaces[0].getName() + ".class.getMethod(\"" + method.getName() + "\",new Class[]{});" + ln);
src.append("this.h.invoke(this,m,null);" + ln);
src.append(" } catch (Throwable throwable) {" + ln);
src.append(" throwable.printStackTrace();" + ln);
src.append(" }" + ln);
src.append("}" + ln);
}
src.append("}");
return src.toString();
}
}