java中的代理(静态代理与动态代理)
静态代理
静态代理通常用于对原有业务逻辑的扩充,其实也就是代理模式的一种实现,通过对真实对象的封装,来实现扩展性。
缺点:同时代理多个方法时候冗余。
class StaticUserDaoProxy implements IUserDao {
//接收保存目标对象
private IUserDao target;
public StaticUserDaoProxy(IUserDao target) {
this.target = target;
}
public void save() {
System.out.println("before StaticUserDaoProxy...");
target.save();//执行目标对象的方法
System.out.println("after StaticUserDaoProxy...");
}
}
动态代理
动态代理的好处:实现无侵入式的代码扩展,也就是方法的增强,也就是说让程序员在不用修改源码的情况下,增强一些方法;
(1)jdk动态代理:
jdk动态代理是通过实现java中的InvocationHandler通过反射的方式来进行方法的调用。
//定义接口
public interface Subject {
public void doSomething();
}
//要被代理的类
public class RealSubject implements Subject {
@Override
public void doSomething() {
System.out.println("call doSomething()");
}
}
//代理类
public class ProxyHandler implements InvocationHandler {
private Object tar;
public Object bind(Object tar)
{
this.tar = tar;
//绑定该类实现的所有接口,取得代理类
return Proxy.newProxyInstance(tar.getClass().getClassLoader(), tar.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
Object result = null;
//这里就可以进行所谓的AOP编程了
//在调用具体函数方法前,执行功能处理
System.out.println("start time:"+System.currentTimeMillis());
result = method.invoke(tar,args);
//在调用具体函数方法后,执行功能处理
System.out.println("end time:"+System.currentTimeMillis());
return result;
}
}
//测试类
public class TestDynamicProxy {
public static void main(String args[]){
ProxyHandler proxy = new ProxyHandler();
//绑定该类实现的所有接口
Subject sub = (Subject) proxy.bind(new RealSubject());
sub.doSomething();
}
}
上面的main方法代码中,有proxy.bind()的方法调用了Proxy类的静态方法newProxyInstance()。proxy类中的newProxyInstance方法就是在创建代理类实例对象。
Proxy.newProxyInstance(tar.getClass().getClassLoader(), tar.getClass().getInterfaces(), this);
newProxyInstance有三个参数:
Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h>
- loader,被代理类的类加载器。
- interfaces,被代理的类所实现的接口,这个接口可以是多个。
- h,绑定代理类的一个方法。
具体的来说,这个方法执行了下面三步:
1.生成一个实现了参数interfaces里所有接口且继承了Proxy的代理类的字节码,然后用参数里的classLoader加载这个代理类。
2.使用代理类父类的构造函数 Proxy(InvocationHandler h)来创造一个代理类的实例,将我们自定义的InvocationHandler的实现类传入,自定义的InvocationHandler中的invoke方法里放入需要增强的功能。
3.返回这个代理类实例,因为我们构造的代理类实现了参数中的接口组,可以将newProxyInstance生成的对象强制转换为被代理对象的接口实现类。这也是jdk代理会出现的弊端,被代理的类必须实现了接口,否则无法被动态代理。
(2)cglib动态代理:
CGLIB与JDK代理不同,是针对类来实现代理的,原理是对指定的业务类生成一个子类,并覆盖其中业务方法实现代理。因为采用的是继承,所以不能对final修饰的类进行代理。
//首先定义业务类,无需实现接口
public class BookFacadeImpl1 {
public void addBook() {
System.out.println("新增图书...");
}
}
//实现 MethodInterceptor方法代理接口,创建代理类
public class BookFacadeCglib implements MethodInterceptor {
private Object target;//业务类对象,供代理方法中进行真正的业务方法调用
//相当于JDK动态代理中的绑定
public Object getInstance(Object target) {
this.target = target; //给业务对象赋值
Enhancer enhancer = new Enhancer(); //创建加强器,用来创建动态代理类
enhancer.setSuperclass(this.target.getClass()); //为加强器指定要代理的业务类(即:为下面生成的代理类指定父类)
//设置回调:对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法进行拦
enhancer.setCallback(this);
// 创建动态代理类对象并返回
return enhancer.create();
}
// 实现回调方法
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("预处理——————");
proxy.invokeSuper(obj, args); //调用业务类(父类中)的方法
System.out.println("调用后操作——————");
return null;
}
//创建业务类和代理类对象,然后通过代理类对象.getInstance(业务类对象) 返回一个动态代理类对象(它是业务类的子类,可以用业务类引用指向它)。最后通过动态代理类对象进行方法调用。
public static void main(String[] args) {
BookFacadeImpl1 bookFacade=new BookFacadeImpl1();
BookFacadeCglib cglib=new BookFacadeCglib();
BookFacadeImpl1 bookCglib=(BookFacadeImpl1)cglib.getInstance(bookFacade);
bookCglib.addBook();
}
(3)两种方式的比较
jdk动态代理是由java内部的反射机制来实现的,cglib动态代理底层则是借助asm来实现的。
JDK动态代理是通过接口中的方法名,在动态生成的代理类中调用业务实现类的同名方法;
CGlib动态代理是通过继承业务类,生成的动态代理类是业务类的子类,通过重写业务方法进行代理;