一、静态代理
静态代理通常用于对原有程序进行功能扩充,在编译的时候就将接口、实现类、代理类都手动完成。
简单例子:
接口:
public interface UserDao {
void save();
}
实现类:
public class UserImpl implements UserDao {
@Override
public void save() {
System.out.println("保存");
}
}
代理类:
public class ProxyClass implements UserDao {
private UserImpl user;
public ProxyClass(UserImpl user){
this.user = user;
}
@Override
public void save() {
System.out.println("开启事务");
user.save();
System.out.println("提交事务");
}
}
测试:
public class Main {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
UserDao user = new UserImpl();
ProxyClass proxyClass = new ProxyClass(user);
proxyClass.save();
}
}
/*
打印结果:
开启事务
保存
提交事务
*/
可以看到,代理类和被代理类要实现同一个接口,这会产生多余的接口实现,而且当接口增多之后,静态代理就会变得很困难,会显得很臃肿;除此之外,当接口增加方法之后,代理类和被代理类都要进行修改,这不是我们想要的,因此,静态代理从某种程度上来说不够好。
二、动态代理
当我们需要很多代理的时候,如果每一个都是手动去实现,岂不是太难受了,浪费了大量的时间不说,还产生很多冗余代码,此时,动态代理就是一个比较好的选择了。
动态代理,又被称为JDK代理,底层是反射,只在需要代理的地方才会加载,不会再编译期生成.class文件,动态代理要求目标对象必须实现接口,但是代理对象不必实现接口。
动态代理必须用到的JDK API有一个类 java.lang.reflect Proxy,和一个接口 java.lang.reflect InvocationHandler,InvocationHandler接口中只有一个invoke方法(JDK1.8),如下
public Object invoke(Object proxy, Method method, Object[] args) //参数含义分别为代理实例、调用的方法、方法的参数列表
throws Throwable;
InvocationHandler接口的invoke方法用于处理方法调用并返回结果。
而Proxy类主要动到的是这个静态方法
public static Object newProxyInstance(ClassLoader loader, //类加载器
Class<?>[] interfaces, //目标对象实现的接口
InvocationHandler h)
throws IllegalArgumentException
看下动态代理的demo
接口类
public interface UserDao {
void save();
}
目标对象类
public class UserDaoImpl implements UserDao {
@Override
public void save() {
System.out.println("保存");
}
}
代理对象类
public class ProxyClass {
private Object target;
public ProxyClass(Object target){
this.target = target;
}
public Object getProxyInstance(){
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开启事务");
Object o = method.invoke(target,args);
System.out.println("关闭事务");
return null;
}
});
}
}
测试
public class Main {
public static void main(String[] args){
UserDao userDao = new UserDaoImpl(); //多态,向上转型
UserDao userDao1 = (UserDao) new ProxyClass(userDao).getProxyInstance();
userDao1.save();
}
}
动态代理实现过程:
因为通过使用接口指向实现类的实例的多态实现方式,可以有效的将具体的实现与调用之间解耦,便于后期修改与维护。具体的说就是我们在代理类中创建一个私有成员变量(private修饰),使用接口来指向实现类的对象,然后在该代理类中的方法中使用这个创建的实例来调用实现类中的相应方法来完成业务逻辑功能。
private Object target;
public ProxyClass(Object target){
this.target = target;
}
上面代码通过这个构造器可以创建代理类的实例,创建的同时还能将具体实现类的实例与之绑定(target指的就是实现类的实例,这个实例需要在测试类中创建并作为参数来创建代理类的实例)。
静态代理中我们测试类中直接创建代理类的对象,使用代理类的对象来调用其方法即可,若是别的接口(这里指的是别的调用方)要调用Iuser的方法,也可以使用此法
动态代理中要复杂的多,首先我们要将之前提到的实现类的实例创建(补充完整),然后利用这个实例作为参数,调用代理来的带参构造器来创建“代理类实例对象”,这里加引号的原因是因为它并不是真正的代理类的实例对象,而是创建真正代理类实例的一个参数,这个实现了InvocationHandler接口的类严格意义上来说并不是代理类,我们可以将其看作是创建代理类的必备中间环节,这是一个调用处理器,也就是处理方法调用的一个类,不是真正意义上的代理类,可以这么说:创建一个方法调用处理器实例。
下面才是真正的代理类实例的创建,之前创建的”代理类实例对象“仅仅是一个参数
UserDao userDao1 = (UserDao) new ProxyClass(userDao).getProxyInstance();
三、cglib代理
JDK代理在某种程度上来说已经足够完美了,但是它仍有不足之处就是面向接口编程,如果为了实现代理而为每个需要代理的类创建一个毫无意义的接口是很烦的,也是没必要的,于是就产生了cglib代理。cglib代理不必面向接口,在Spring AOP就使用了cglib代理。
CGLIB是一个强大的高性能的代码生成包,它可以在运行期扩展Java类与实现Java接口。CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。CGLIB代理真正的达到了代理类无侵入。
使用cglib代理之前别忘了引入jar包或者maven依赖。
UserDao
public class UserDao{
public void save() {
System.out.println("保存");
}
}
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class ProxyFactory implements MethodInterceptor{
private Object target;//维护一个目标对象
public ProxyFactory(Object target) {
this.target = target;
}
//为目标对象生成代理对象
public Object getProxyInstance() {
//工具类
Enhancer en = new Enhancer();
//设置父类
en.setSuperclass(target.getClass());
//设置回调函数
en.setCallback(this);
//创建子类对象代理
return en.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("开启事务");
// 执行目标对象的方法
Object returnValue = method.invoke(target, args);
System.out.println("关闭事务");
return null;
}
}
测试
public class TestProxy {
@Test
public void testCglibProxy(){
//目标对象
UserDao target = new UserDao();
//代理对象
UserDao proxy = (UserDao) new ProxyFactory(target).getProxyInstance();
//执行代理对象方法
proxy.save();
}
}
/*
结果:
开启事务
保存数据
关闭事务
*/
注意:
cglib代理无需实现接口,通过生成类字节码实现代理,比反射稍快,不存在性能问题,但cglib会继承目标对象,需要重写方法,所以目标对象不能为final类。