一丶JDK动态代理
/**
* 被代理接口(JDK动态代理必须有接口)
*
* @author hujun
* @date 2021/09/11
*/
public interface ProxiedInterface {
void sayHello();
void sayBye();
}
/**
* 需要代理的实现类
*
* @author hujun
* @date 2021/09/11
*/
public class ProxyInterfaceRealize implements ProxiedInterface {
@Override
public void sayHello() {
System.out.println("hello word");
}
@Override
public void sayBye() {
System.out.println("bye");
}
}
/**
* 获取代理对象
*
* @author hujun
* @date 2021/09/11
*/
public class JDKProxyFactory {
/**
* 根据接口生成代理对象
*/
public static <T> T getProxyInterfaceObject(Class<T> proxyInterface) {
if (!proxyInterface.isInterface()) {
throw new RuntimeException("no interface");
}
// 创建代理对象 类加载器 需要代理的接口的class InvocationHandler对象(具体的代理逻辑)
Object proxyInstance = Proxy.newProxyInstance(proxyInterface.getClassLoader(), new Class[]{proxyInterface}, (proxy, method, args) -> {
// proxy:代理对象 method:执行的方法对象 args:方法参数
if (method.getDeclaringClass() != proxyInterface) {
throw new NoSuchMethodException();
}
// 代理的只是接口 没有具体的实现类 根据method自定义实现
if (method.getName().equals("sayHello")) {
System.out.println("hello word");
} else {
System.out.println("bye");
}
return null;
});
return (T) proxyInstance;
}
/**
* 代理接口的具体实现类的实例对象
*/
public static <T> T getProxyObject(Object proxyObject) {
Class<?> proxyClass = proxyObject.getClass();
Class<?>[] interfaces = proxyClass.getInterfaces();
if (interfaces.length < 1) {
throw new RuntimeException("no interface");
}
Object proxyInstance = Proxy.newProxyInstance(proxyClass.getClassLoader(), interfaces, new ProxyInvocationHandler(proxyObject));
return (T) proxyInstance;
}
/**
* InvocationHandler的实现类
*/
@RequiredArgsConstructor
static class ProxyInvocationHandler implements InvocationHandler {
/**
* 被代理的对象
*/
private final Object target;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getDeclaringClass().equals(Object.class)) {
return method.invoke(target, args);
}
long now = System.currentTimeMillis();
// 执行被代理对象的方法
Object result = method.invoke(target, args);
System.out.println("消耗时间:" + (System.currentTimeMillis() - now));
return result;
}
}
}
public static void main(String[] args) {
// 根据接口生成代理对象
ProxiedInterface proxiedInterface = JDKProxyFactory.getProxyInterfaceObject(ProxiedInterface.class);
proxiedInterface.sayHello();
proxiedInterface.sayBye();
// 根据接口的实现类生成代理对象
ProxiedInterface proxyObject = JDKProxyFactory.getProxyObject(new ProxyInterfaceRealize());
proxyObject.sayHello();
proxyObject.sayBye();
}
二丶CGLIB动态代理
<!-- cglib依赖 -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
<!-- 不需要构成方法生成对象 -->
<dependency>
<groupId>org.objenesis</groupId>
<artifactId>objenesis</artifactId>
<version>3.2</version>
</dependency>
/**
* 无参构造需要代理的对象
*
* @author hujun
* @date 2021/09/11
*/
public class ByProxyClass {
public void sayHello() {
System.out.println("hello word");
}
}
/**
* 有参构造需要的代理对象
*
* @author hujun
* @date 2021/09/11
*/
@RequiredArgsConstructor
public class ProxyNoDefaultConstruction {
private final String message;
public void sayMessage() {
System.out.println(message);
}
}
/**
* 获取代理对象
*
* @author hujun
* @date 2021/09/11
*/
public class CglibProxyFactory {
/**
* 通过构造方法创建代理对象
*/
public static <T> T getProxyObjectByParams(Class<T> byProxyClass, Object... params) {
// cglib用来增强被代理对象的类
Enhancer enhancer = new Enhancer();
// 设置父类
enhancer.setSuperclass(byProxyClass);
// 设置代理拦截
enhancer.setCallback(new ProxyMethodHandle());
Class<?>[] paramsType = new Class[params.length];
for (int i = 0; i < params.length; i++) {
paramsType[i] = params[i].getClass();
}
// 创建代理对象
return (T) enhancer.create(paramsType, params);
}
/**
* 也可以直接代理接口
*/
public static <T> T getProxyObjectByInterface(Class<T> byProxyInterface) {
Enhancer enhancer = new Enhancer();
enhancer.setCallback(new InterfaceHandle());
enhancer.setInterfaces(new Class[]{byProxyInterface});
return (T) enhancer.create();
}
/**
* 当你拥有一个实例对象后,对实例对象的代理一般都会使用组合的方式,增强方法的行为写在代理拦截中,实际执行的方法是该实例对象的方法
* 这在实例对象拥有无参构造的时候,可以直接创建一个代理对象,而实例对象只有有参构造的情况下,使用cglib创建代理对象也需要执行这个有参
* 构造,这在已经拥有实例对象的情况下,还执行有参构造没有多大意义,代理对象通常是不需要父类的属性的
* 所以代理实例对象时,采用Objenesis来生成代理对象,这个可以在没有无参构造的情况下创建对象
*/
public static <T> T getProxyObjectNoDefaultConstructor(T obj) {
Enhancer enhancer = new Enhancer();
// 是否使用factory factory是cglib的一个接口,会默认实现这个接口的方法
enhancer.setUseFactory(false);
// 设置父类
enhancer.setSuperclass(obj.getClass());
// 设置代理逻辑的类型(因为是通过cglib创建Class对象,创建Class对象不能直接设置代理拦截的逻辑)
enhancer.setCallbackType(InvocationHandler.class);
// 创建一个代理Class对象
Class proxyClass = enhancer.createClass();
// 注册这个代理对象的拦截
Enhancer.registerStaticCallbacks(proxyClass, new ProxyTargetHandle[]{new ProxyTargetHandle(obj)});
// 可以根据Class对象创建对象实例 而不通过构造方法
Objenesis objenesis = new ObjenesisStd();
return (T) objenesis.newInstance(proxyClass);
}
/**
* 直接代理接口的InvocationHandler
*/
static class InterfaceHandle implements InvocationHandler {
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
// 因为只代理了接口,没有具体实现,只能有invoke实现逻辑
if (method.getName().equals("sayHello")) {
System.out.println("hello word");
} else {
System.out.println("bye");
}
return null;
}
}
/**
* 代理方法的实现
*/
static class ProxyMethodHandle implements MethodInterceptor {
/**
* @param o 代理对象
* @param method 执行的方法
* @param objects 方法参数
* @param methodProxy 方法代理
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
// 使用代理对象直接执行method时,会重新进入代理逻辑,造成递归
long now = System.currentTimeMillis();
// methodProxy可以直接调用父类方法
Object res = methodProxy.invokeSuper(o, objects);
System.out.println("消耗时间:" + (System.currentTimeMillis() - now));
return res;
}
}
/**
* 代理实例对象的拦截
*/
@RequiredArgsConstructor
static class ProxyTargetHandle implements InvocationHandler {
/**
* 代理的实例
*/
private final Object target;
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
long now = System.currentTimeMillis();
// 调用代理实例的具体方法,这个方法前后就可以实现对应的代理逻辑
Object result = method.invoke(target, objects);
System.out.println("消耗时间:" + (System.currentTimeMillis() - now));
return result;
}
}
}
public static void main(String[] args) {
// cglib代理接口
ProxiedInterface object = CglibProxyFactory.getProxyObjectByInterface(ProxiedInterface.class);
object.sayBye();
object.sayHello();
// 代理无参构造类
ByProxyClass proxyObject = CglibProxyFactory.getProxyObjectByParams(ByProxyClass.class);
proxyObject.sayHello();
// 代理有参构造类
ProxyNoDefaultConstruction proxyObjectByParams = CglibProxyFactory.getProxyObjectByParams(
ProxyNoDefaultConstruction.class, "hello message");
proxyObjectByParams.sayMessage();
// 代理有参构造 代理类不进行构造函数初始化 使用对象组合的形式
ProxyNoDefaultConstruction helloProxy = CglibProxyFactory.getProxyObjectNoDefaultConstructor(
new ProxyNoDefaultConstruction("hello proxy"));
helloProxy.sayMessage();
}
三丶JAVA agent
java提供的通过指定jar包的形式在类加载之前进行拦截,需要加拦截的话,会设计字节码的修改(以后在学习这方面的东西,现在先简单了解)
首先需要有一个拦截的jar包
/**
* 必须是静态方法,且方法名必须为premain
* agentOps:命令行传入的参数
* instrumentation:对类可以进行操作的类,这个方法可以没有这个参数,也就是可以有premain(String) premain(String,Instrumentation)
* 这两种方法,两个同时存在的时候,后者优先
* 这个是在应用的启动前执行
*/
public static void premain(String agentOps, Instrumentation instrumentation) {
System.out.println("开始agent" + agentOps);
// 获取所有加载的class
Class<?>[] classes = instrumentation.getAllLoadedClasses();
for (Class<?> cls : classes) {
System.out.println("agent main loaded class: " + cls.getName());
}
// 自定义的转换 实现ClassFileTransformer接口
instrumentation.addTransformer(new MyClassTransformer());
}
/**
* 基本同premain方法 这个可以在应用运行的时候使用,可以做到修改类
*/
public static void agentmain(String agentArgs, Instrumentation instrumentation) {
System.out.println("agent main start: " + agentArgs);
Class<?>[] classes = instrumentation.getAllLoadedClasses();
for (Class<?> cls : classes) {
System.out.println("agent main loaded class: " + cls.getName());
}
instrumentation.addTransformer(new MyClassTransformer());
}
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestEntries>
<!-- 使用maven指定premain-class和agent-class 然后可以将这个打成jar包 这两个只需要有其中一个即可 -->
<premain-class>com.demo.AgentDemo</premain-class>
<agent-class>com.demo.AgentMainDemo</agent-class>
</manifestEntries>
</archive>
</configuration>
</plugin>
运行的main方法增加-javaagent:(对应jar文件的路径)\demo.jar=arg,=后面跟的是premain或agentMain第一个string参数
public static void main(String[] args) throws Exception {
// 列出当前运行的虚拟机 tools.jar包中的VirtualMachine
List<VirtualMachineDescriptor> list = VirtualMachine.list();
String mainClass = "proxy.agent.JavaAgentTest";
String agentPath = "D:\\..\\demo.jar";
for (VirtualMachineDescriptor vmd : list) {
// 虚拟机显示的名字是指定mainClass的时候
if (vmd.displayName().equals(mainClass)) {
// attach对应的虚拟机,然后指定对应的agent jar包就会执行里面的agentmain方法
VirtualMachine attach = VirtualMachine.attach(vmd.id());
attach.loadAgent(agentPath);
}
}
}