利用JDK的反射机制(proxyJDK)
而spring默认使用的就是AspectJ来实现的动态代理,spring自己的AOP就是使用AspectJ来实现的!
Java 代理-- 静态代理和动态代理区别
静态代理
简单来讲代理类和委托类要实现相同的接口,代理类定义目标对象并通过构造方法传入该目标对象执行具体的方法时可以添加一些其他操作,如输入日志等,在调用目标对象的真正方法
优点:代理使客户端不需要知道实现类是什么,怎么做,客户端只需要知道代理即可(解耦合)
缺点:代理类和委托类实现相同接口,需要实现相同代码,大量重复;一旦规模变大,无法胜任
动态代理
动态代理是在运行时,通过反射机制实现动态代理,并且能够代理各种类型的对象,主要是 InvocationHandler 接口和 Proxy 类的支持
一、jdk的动态代理(必须接口)
对于从Object中继承的方法,JDK Proxy会把hashCode()、equals()、toString()这三个非接口方法转发给InvocationHandler,其余的Object方法则不会转发。
//Object proxy:被代理的对象
//Method method:要调用的方法
//Object[] args:方法调用时所需要参数
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
// 定义一个接口
public interface Subject {
public void rent();
public void hello(String str);
}
// 接口实现类
public class RealSubject implements Subject {
@Override
public void rent()
{
System.out.println("I want to rent my house");
}
@Override
public void hello(String str)
{
System.out.println("hello: " + str);
}
}
/**
* 动态代理类
*/
public class DynamicProxy implements InvocationHandler {
// 要代理的真实对象
private Object subject;
public DynamicProxy(Object subject) {
this.subject = subject;
}
@Override
public Object invoke(Object object, Method method, Object[] args) throws Throwable {
System.out.println(" before rent house ");
System.out.println(" Method "+ method );
// 当代理对象调用真实对象的方法时,会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
return method.invoke(subject,args);
}
}
class Client{
public static void main(String[] args) {
// 我们要代理的真实对象
Subject realSubject = new RealSubject();
// 我们要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的
InvocationHandler handler = new DynamicProxy(realSubject);
// 通过 Proxy 的 newProxyInstance 方法来创建我们的代理对象
// 第一个参数 代理对象的类加载器
// 第二个参数 目标对象的接口
// 第三个参数 表明这些被拦截的方法在被拦截时执行哪个InvocationHandler 的 invoke 方法
Subject subject = (Subject)Proxy.newProxyInstance(handler.getClass().getClassLoader(),
realSubject.getClass().getInterfaces(),handler);
System.out.println(subject.getClass().getName());
subject.rent();
subject.hello("world");
}
}
总结
一个典型的动态代理创建对象过程可分为以下四个步骤:
1、通过实现InvocationHandler接口创建自己的调用处理器 IvocationHandler handler = new InvocationHandlerImpl(…);
2、通过为Proxy类指定ClassLoader对象和一组interface创建动态代理类
Class clazz = Proxy.getProxyClass(classLoader,new Class[]{…});
3、通过反射机制获取动态代理类的构造函数,其参数类型是调用处理器接口类型
Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});
4、通过构造函数创建代理类实例,此时需将调用处理器对象作为参数被传入
Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler));
为了简化对象创建过程,Proxy类中的newInstance方法封装了2~4,只需两步即可完成代理对象的创建。
生成的ProxySubject继承Proxy类实现Subject接口,实现的Subject的方法实际调用处理器的invoke方法,而invoke方法利用反射调用的是被代理对象的的方法(Object result=method.invoke(proxied,args))
比较好的参考文章:https://blog.csdn.net/qq_24399533/article/details/51051258
二、cglib的动态代理
CGLIB(Code Generation Library)是一个基于ASM的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成。CGLIB通过继承方式实现代理。
// I'm back
// 定义一个没有实现任何接口的类
class HelloConcrete{
public String sayHello(String str) {
return "HelloConcrete: " + str;
}
}
class MyMethodInterceptor implements MethodInterceptor {
// 1. 首先实现一个MethodInterceptor,方法调用会被转发到该类的intercept()方法。
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println(" you say : " + Arrays.asList(args));
return methodProxy.invokeSuper(obj,args);
}
}
class Test{
public static void main(String[] args) {
// 通过CGLIB的Enhancer来指定要代理的目标对象、实际处理代理逻辑的对象,最终通过调用create()方法得到代理对象
// 注意 对这个对象所有非final方法的调用都会转发给MethodInterceptor.intercept()方法
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(HelloConcrete.class);
enhancer.setCallback(new MyMethodInterceptor());
HelloConcrete hello = (HelloConcrete) enhancer.create();
hello.sayHello(" hello ");
System.out.println(" end ");
}
}
JDK原生动态代理是Java原生支持的,不需要任何外部依赖,但是它只能基于接口进行代理;CGLIB通过继承的方式进行代理,无论目标对象有没有实现接口都可以代理,但是无法处理final的情况。