1、写代码的原则
开闭原则
开闭原则就是说对扩展开放,对修改关闭。
2、静态代理
以找对象为例子讲解静态代理
张三写代码、玩游戏没空找对象
张三他爹着急,年龄大了,想抱孙子了。张三他爹拿着张三的照片(持有张三的引用)给张三满世界找对象
张三他爹就是张三的一个代理
1、关键是持有被代理类的引用
2、一开始就被加载到内存了,不是运行时动态生成的
代码
package design.proxy.staticproxy;
public interface People {
void zhaoduixiang();
}
package design.proxy.staticproxy;
public class ZhangSan implements People {
@Override
public void zhaoduixiang() {
System.out.println("我只要漂亮的");
}
}
package design.proxy.staticproxy;
public class HisDaddy implements People {
// 父亲持有儿子的引用
private ZhangSan zhangSan;
public HisDaddy(ZhangSan zhangSan) {
this.zhangSan = zhangSan;
}
@Override
public void zhaoduixiang() {
before();
zhangSan.zhaoduixiang();
after();
}
public void before() {
System.out.println("老子是张三的老子, 先要过老子这关, 要有房子, 车子, 票子...");
}
public void after() {
System.out.println("这个姑娘是一个好姑娘!");
}
}
package design.proxy.staticproxy;
public class MainClass {
public static void main(String[] args) {
People daddy = new HisDaddy(new ZhangSan());
daddy.zhaoduixiang();
People zhangSan = new ZhangSan();
zhangSan.zhaoduixiang();
}
}
3、动态代理
写一个jdk的动态代理
1. 动态代理的动态体现在代理类是动态生成的
2. 动态代理类的声明才是我们写业务代码的地方, 实现了InvocationHandler接口的类叫做声明
3. **jdk内存中的动态代理类是没有对被代理类进行任何操作的**
问题
- 为什么会调到invovationHandler的invoke方法,以及为什么会传那几个参数
- 去工程里面找代理类找不到, 其实代理类在内存中
- 用jdk的api去生成一个代理类,用反编译工具看代理类
public final class $Proxy0 extends java.lang.reflect.Proxy implements design.proxy.dynamicproxy.People {...}
代理类继承了Proxy 并实现了People 接口
public final void eat() throws Throwable{
//h 为父类Proxy中的 protected InvocationHandler h
//m4 为 Method对象 在代理类的静态代码块中 通过Class.forName("接口名").getMethod('eat'/*方法名*/,new Class[0])取得 null 该方法为参数
//可以看出 是由代理类调用了我们声明中的invoke方法
this.h.ivoke(this,m4,null);
}
代码
package design.proxy.dynamicproxy;
public interface People {
void eat();
void sleep();
void sport();
}
package design.proxy.dynamicproxy;
public class ZhangSan implements People {
@Override
public void eat() {
System.out.println("张三吃饭的时候喜欢看美女");
}
@Override
public void sleep() {
System.out.println("张三睡觉了");
}
@Override
public void sport() {
System.out.println("张三运动");
}
}
package design.proxy.dynamicproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class ProxyHandler implements InvocationHandler {
private People people;
public ProxyHandler(People people) {
this.people = people;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
method.invoke(people, args);
after();
return null;
}
private void before() {
System.out.println("吃饭之前要洗手");
}
private void after() {
System.out.println("吃饭之后要洗碗");
}
}
package design.proxy.dynamicproxy;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.lang.reflect.Proxy;
import sun.misc.ProxyGenerator;
@SuppressWarnings("restriction")
public class MainClass {
public static void main(String[] args) throws Exception {
People proxy = (People) Proxy.newProxyInstance(People.class.getClassLoader(), //
ZhangSan.class.getInterfaces(), //
new ProxyHandler(new ZhangSan()));
proxy.eat();// 方法得到了增强 调用了InvocationHandler的invoke方法
System.out.println(proxy.getClass().getName());// com.sun.proxy.$Proxy0
createProxyClassFile();
// proxy.sleep();
}
// 还原我们的代理类
public static void createProxyClassFile() throws Exception {
byte[] generateProxyClass = ProxyGenerator.generateProxyClass("$Proxy0", new Class[] { People.class });
// 将字节数组输出到$Proxy0.class文件中
OutputStream outputStream = new FileOutputStream("$Proxy0.class");
outputStream.write(generateProxyClass);
outputStream.close();
}
}
不用jdk的动态代理,自己写一个动态代理
1、Proxy.newProxyInstance里面到底干了些什么呢?
2、自己定义代理类,MyProxy,自己在运行时用流的方式生成一个代理类的java文件,用jdk的编译器编译java文件生成class文件,把class文件加载到内存
3、new 一个代理类的实例并返回代码
package design.proxy.dynamicproxy.my;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
//自定义Proxy
public class MyProxy {
// 定义回车键
private static String rt = "\r\n";
public static Object createProxyInstance(ClassLoader classLoader, //
Class clazz, //
MyInvocationHandler handler) {
// 首先拿到 被代理类实现的接口中的 方法数组(动态代理类要实现这些方法)
Method[] methods = clazz.getMethods();
// 用流的方式创建一个Java文件
String proxyClass = "package design.proxy.dynamicproxy.my;" + rt//
+ "import java.lang.reflect.Method;" + rt//
+ "public class $Proxy0 implements " + clazz.getName() + "{" + rt//
+ "MyInvocationHandler h;" + rt//
+ "public $Proxy0(MyInvocationHandler h){ this.h=h; }" + rt//
+ getMethodString(methods, clazz) + "}";
// 把类生成文件
String fileName = "D:/Users/workspace-sts-3.7.1.RELEASE/demo/src/main/java/design/proxy/dynamicproxy/my/$Proxy0.java";
try {
FileWriter fileWriter = new FileWriter(fileName);
fileWriter.write(proxyClass);
fileWriter.flush();
fileWriter.close();
// 编译java文件 存在于磁盘中
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();// 获取系统编译器
StandardJavaFileManager standardFileManager = compiler.getStandardFileManager(null, null, null);// 标准Java文件管理器
Iterable<? extends JavaFileObject> javaFileObjects = standardFileManager.getJavaFileObjects(fileName);
CompilationTask task = compiler.getTask(null, standardFileManager, null, null, null, javaFileObjects);
task.call();
standardFileManager.close();
// 加载到内存中
MyClassLoader myClassLoader = new MyClassLoader("D:/Users/workspace-sts-3.7.1.RELEASE/demo/src/main/java/design/proxy/dynamicproxy/my/");
Class<?> proxy0Class = myClassLoader.findClass("$Proxy0");
Constructor<?> constructor = proxy0Class.getConstructor(MyInvocationHandler.class);
Object newInstance = constructor.newInstance(handler);
return newInstance;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private static String getMethodString(Method[] methods, //
Class clazz) {
String proxyMethod = "";
for (int i = 0; i < methods.length; i++) {
proxyMethod += "public void " + methods[i].getName() + "() throws Throwable {" + rt//
+ "Method md = " + clazz.getName() + ".class.getMethod(\"" + methods[i].getName() + "\",new Class[]{});" + rt//
+ "this.h.invoke(this,md,null);" + rt//
+ "}" + rt;
}
return proxyMethod;
}
}
package design.proxy.dynamicproxy.my;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
//将流文件加载到内存中
public class MyClassLoader extends ClassLoader {
private File dir;
public MyClassLoader(String path) {
dir = new File(path);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
if (dir != null) {
File clazzFile = new File(dir, name + ".class");
if (clazzFile.exists()) {
FileInputStream input = null;
try {
input = new FileInputStream(clazzFile);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = input.read(buffer)) != -1) {
byteArrayOutputStream.write(buffer, 0, len);
}
return defineClass("design.proxy.dynamicproxy.my." + name, byteArrayOutputStream.toByteArray(), 0, byteArrayOutputStream.size());
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (input != null) {
input.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
return super.findClass(name);
}
}
package design.proxy.dynamicproxy.my;
import java.lang.reflect.Method;
public interface MyInvocationHandler {
Object invoke(Object proxy, Method method, Object args) throws Throwable;
}
package design.proxy.dynamicproxy.my;
import java.lang.reflect.Method;
import design.proxy.dynamicproxy.jdk.People;
//自定义声明类
public class MyProxyHandler implements MyInvocationHandler {
private People people;
public MyProxyHandler(People people) {
this.people = people;
}
@Override
public Object invoke(Object proxy, Method method, Object args) throws Throwable {
before();
method.invoke(people, args);
after();
return null;
}
private void before() {
System.out.println("吃饭之前要洗手");
}
private void after() {
System.out.println("吃饭之后要洗碗");
}
}
package design.proxy.dynamicproxy.jdk;
import design.proxy.dynamicproxy.my.MyProxy;
import design.proxy.dynamicproxy.my.MyProxyHandler;
public class MainClass {
public static void main(String[] args) throws Throwable {
People proxy = (People) MyProxy.createProxyInstance(People.class.getClassLoader(), //
People.class, //
new MyProxyHandler(new ZhangSan()));
proxy.eat();
}
}
- 思维发散
1、Mybatis中只有接口定义没有实现类
2、Spring的aop的事务管理
3、持久带和heap space溢出,持久带溢出原因,动态加载了大量的java类而导致溢出