1.类加载过程模拟(先明白类加载过程,方可模拟类运行期间加载-创建代理类,调用目标方法)
public class Programmer {
public void code() {
System.out.println("I'm a Programmer,Just Coding.....");
}
}
/**
* 自定义一个类加载器,用于将字节码转换为class对象
*/
public class MyClassLoader extends ClassLoader {
public Class<?> defineMyClass(byte[] b, int off, int len) {
//TODO SOURCE CODE
return super.defineClass(null,b, off, len);
}
}
public class MyTest {
public static void main(String[] args) throws IOException {
//读取本地的class文件内的字节码,转换成字节码数组
File file = new File(".");
InputStream input = new FileInputStream(file.getCanonicalPath() +
"\\target\\classes\\com\\max\\dproxy\\loadseq\\Programmer.class");
byte[] result = new byte[1024];//字节型
int count = input.read(result);
// 使用自定义的类加载器将 byte字节码数组转换为对应的class对象
MyClassLoader loader = new MyClassLoader();
Class clazz = loader.defineMyClass(result, 0, count);
//测试加载是否成功,打印class 对象的名称
System.out.println(clazz.getCanonicalName());
try {
//实例化一个Programmer对象
Object o = clazz.newInstance();
//调用Programmer的code方法
clazz.getMethod("code", null).invoke(o, null);
} catch (IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
}
2.类加载过程
注:绿色椭圆内即JVM虚拟机状态,具体不做赘述。
静态代理:手动编写代理类代理目标类方法。缺点:手动创建;代理类越来越多,系统规模增大,不易维护;
动态代理:由于JVM通过字节码的二进制(byte-code)信息加载类的,那么,如果我们在运行期系统中,1.遵循Java编译系统组织.class文件的格式和结构,2.生成相应的二进制数据,3.然后再把这个二进制数据加载转换成对应的类,这样,就完成了在代码中,动态创建一个类的能力了。
public interface Vehicle {
void drive();
}
public interface Rechargable {
void recharge();
}
public class ElectricCar implements Rechargable, Vehicle {
@Override
public void drive() {
System.out.println("Electric Car is Moving silently...");
}
@Override
public void recharge() {
System.out.println("Electric Car is Recharging...");
}
}
public class InvocationHandlerImpl implements InvocationHandler {
private ElectricCar car;
public InvocationHandlerImpl(ElectricCar car) {
this.car = car;
}
@Override
public Object invoke(Object paramObject, Method paramMethod, Object[] paramArrayOfObject) throws Throwable {
System.out.println("You are going to invoke " + paramMethod.getName() + " ...");
paramMethod.invoke(car, null);
System.out.println(paramMethod.getName() + " invocation Has Been finished...");
return null;
}
}
public class Test {
public static void main(String[] args) {
ElectricCar car = new ElectricCar();
// 1.获取对应的ClassLoader
ClassLoader classLoader = car.getClass().getClassLoader();
// 2.获取ElectricCar 所实现的所有接口
Class[] interfaces = car.getClass().getInterfaces();
// 3.设置一个来自代理传过来的方法调用请求处理器,处理所有的代理对象上的方法调用
InvocationHandler handler = new InvocationHandlerImpl(car);
/*
4.根据上面提供的信息,创建代理对象 在这个过程中,
a.JDK会通过根据传入的参数信息动态地在内存中创建和.class文件等同的字节码
b.然后根据相应的字节码转换成对应的class,
c.然后调用newInstance()创建实例
*/
Object o = Proxy.newProxyInstance(classLoader, interfaces, handler);
Vehicle vehicle = (Vehicle) o;
vehicle.drive();
Rechargable rechargeable = (Rechargable) o;
rechargeable.recharge();
}
}
1.通过传入的类信息(加载器、接口、处理器-增强方法)动态的在内存中创建和.class文件同等的字节码(代理类字节码)
2.将该字节码转换成对应类(生成代理类的步骤)
3.通过newInstance创建类,而后调用1中传入的所有接口方法。
对比类的加载过程,123步骤。
public class Programmer {
public void code() {
System.out.println("I'm a Programmer,Just Coding.....");
}
}
/*
* 实现了方法拦截器接口 Spring AOP实现方式
*/
public class Hacker implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("**** I am a hacker,Let's see what the poor programmer is doing Now...");
proxy.invokeSuper(obj, args);
System.out.println("**** Oh,what a poor programmer.....");
return null;
}
}
public class Test {
public static void main(String[] args) {
Programmer progammer = new Programmer();
Hacker hacker = new Hacker();
//cglib 中加强器,用来创建动态代理
Enhancer enhancer = new Enhancer();
//设置要创建动态代理的类
enhancer.setSuperclass(progammer.getClass());
// 设置回调,这里相当于是对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实行intercept()方法进行拦截
enhancer.setCallback(hacker);
Programmer proxy = (Programmer) enhancer.create();
proxy.code();
}
}
public class Programmer {
public void code() {System.out.println("I'm a Programmer,Just Coding.....");
}
}
public class MyGenerator {
public static void main(String[] args) throws Exception {
ClassPool pool = ClassPool.getDefault();
//创建Programmer类
CtClass cc = pool.makeClass("com.max.dproxy.javassist.Programmer");
//定义code方法
CtMethod method = CtNewMethod.make("public void code(){}", cc);
//插入方法代码 增强
method.insertBefore("System.out.println(\"I'm a Programmer,Just Coding.....\");");
cc.addMethod(method);
//保存生成的字节码
cc.writeFile("d://temp");
}
}
???中间有一点没有实践,暂时遗留???
1.类的加载:
.java编译形成.class bytecode;而后将byte code通过类加载期load到运行期中进行解释、编译、运行
2.动态代理
实现原理:在运行期模拟类的加载过程,将增强类生成字节码,加载转成类(代理类)
jdk:基于接口?从何说起?因为在newPorxy创建代理类时,传入被代理类的所有接口。
InvocationHandler作用?
1)自定义的方法处理器,在invoke方法中加入被代理类的增强逻辑。
2)通过InvocationHandler统一管理器,在调用接口方法(drive、recharge时)进行拦截,统一调用invoke方法,根据invoke中传入的method参数,确定调用被代理类的具体方法。
缺点:被代理类与代理类都继承自同一接口,无法实现随机自由组合增强。
cglib:两个无关类,增强类只需实现MethodInterceptor,通过enhancer的setCallback,调用intercept增强方法。intercept写法与jdk类似。
javassist:采用类的包名加载类,编译形成字节码,设置接口、字段信息,添加构造方法、接口方法,最后使用构造器实例化类,调用接口方法。
https://blog.csdn.net/daybreak1209/article/details/80402895