以前说了代理模式,即为其余对象提供一种代理以控制对这个对象的访问,详情见《简说设计模式——代理模式》,而代理模式常见的实现方式为静态代理及动态代理。html
所谓静态代理类是指:由程序员建立或由特定工具自动生成源代码,再对其进行编译。在程序运行以前,代理类的.class文件就已经存在了。UML结构图以下:java
这里以持久化层的数据交互类为例,IUserDao是与数据库进行交互的接口,UserDao为IUserDao接口的实现类,UserDaoProxy为代理类,两者皆实现了IUserDao接口。说具体点就是,UserDao类是IUserDao接口的具体实现者,而UserDaoProxy类是经过调用UserDao类的相关方法来提供特定服务的。git
这里为抽象目标类/接口。程序员
1 public interface IUserDao { 2 3 public void save(); 4 5 }
这里为具体目标类或被代理的对象。实现了IUserDao中的接口方法。数据库
1 public class UserDao implements IUserDao { 2 3 @Override 4 public void save() { 5 System.out.println("数据已保存!!!"); 6 } 7 8 }
首先在静态代理类中引入IUserDao接口,经过调用UserDao类的相关方法来提供特定服务。编程
1 public class UserDaoProxy implements IUserDao { 2 3 private IUserDao iUserDao; 4 5 public UserDaoProxy(IUserDao iUserDao) { 6 this.iUserDao = iUserDao; 7 } 8 9 @Override 10 public void save() { 11 System.out.println("开始事务..."); 12 iUserDao.save(); //执行目标对象 13 System.out.println("提交事务..."); 14 } 15 16 }
1 public class Client { 2 3 public static void main(String[] args) { 4 UserDao userDao = new UserDao(); 5 UserDaoProxy proxy = new UserDaoProxy(userDao); 6 proxy.save(); 7 } 8 9 }
运行结果以下:设计模式
总结一下就是,在代理类中注入依赖,即引入须要代理的实体类,经过代理类来调用实体类中的方法来实现静态代理。框架
静态代理由咱们本身去生成固定的代码进行编译。须要定义接口或抽象的父类做为抽象目标类,具体目标类和代理类一块儿实现相同的接口或继承相同的类,而后经过调用相同的方法来调用目标对象的方法。jvm
静态代理须要目标对象和代理对象实现相同的接口。能够在不修改目标对象功能的前提下,对目标功能进行扩展。ide
虽然静态代理能够很好的对目标对象进行功能扩展,但对每个服务都须要创建代理类,工做量较大且不易管理,并且若是接口发生改变的话,代理类也得进行相应的修改,这时动态代理的做用就显现出来了。
动态代理与静态代理的区别在于:在程序运行时,动态代理类是运用反射机制建立而成的。在抽象工厂模式的最后有提到用反射来代理switch语句进行选择,这里就运用到了相似的思想。
经过动态代理,咱们再也不须要手动建立代理类,只需编写一个动态处理器便可,而真正的代理对象由JDK在运行时帮咱们建立。因此咱们也将之称为JDK动态代理。
方法步骤以下:
下面看一个例子,UML结构图以下:
被代理的接口。
1 public interface IBusiness { 2 3 public void doWork(); 4 5 }
具体实现类/被代理的对象。
1 public class Business implements IBusiness { 2 3 @Override 4 public void doWork() { 5 System.out.println("进行业务逻辑处理"); 6 } 7 8 }
BusinessHandler类实现类Invocation接口,它是方法调用接口,声明了负责调用任意一个方法的invoke()方法,参数proxy指定动态代理类实例,参数method指定被调用的方法,参数args指定向被调用方法传递的参数,而invoke()方法的返回值表示被调用方法的返回值。其中 method.invoke(iBusiness, args) 至关于 iBusiness.method(args) 。
1 public class BusinessHandler implements InvocationHandler { 2 3 private IBusiness iBusiness; 4 5 public BusinessHandler(IBusiness iBusiness) { 6 this.iBusiness = iBusiness; 7 } 8 9 @Override 10 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 11 System.out.println("before"); 12 method.invoke(iBusiness, args); 13 System.out.println("after"); 14 return null; 15 } 16 17 }
1 public class Client { 2 3 public static void main(String[] args) { 4 Business business = new Business(); 5 6 //生成代理类对象 7 IBusiness proxy = (IBusiness) Proxy.newProxyInstance( 8 business.getClass().getClassLoader(), 9 business.getClass().getInterfaces(), 10 new BusinessHandler(business)); 11 12 proxy.doWork(); 13 } 14 15 }
此处经过java.lang.reflect.Proxy类的newProxyInstance()方法来生成代理类对象,它的完整定义以下:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler handler) throws IllegalArgumentException
参数loader指定动态代理类的类加载器,参数interfaces指定动态代理类须要实现的全部接口,参数handler指定与动态代理类相关联的InvocationHandler对象。因此咱们只需调用newProxyInstance()方法就能够某一个对象的代理对象了。(有关ClassLoader类加载器的内容这里就再也不赘述了,它的做用是将class文件加载到jvm虚拟机中去)。
运行结果以下:
相比于静态代理,动态代理的优点仍是很明显的,不只减小了对业务接口的依赖,还下降了耦合度,但它仍是没法摆脱对接口的依赖。那么对于没有接口的类应该如何实现动态代理呢?
cglib是一个强大的高性能代码生成包,底层是经过使用一个小而快的字节码处理框架ASM来转换并生成新的类,因此咱们通常也称之为cglib字节码生成。
与JDK动态代理不一样,cglib是针对类来实现代理的,因此对于没有接口的类咱们能够经过cglib字节码生成来实现代理。原理是对指定的业务类生成一个子类,并覆盖其中的业务方法实现代理。但由于采用的是继承,因此不能对final修饰的类进行代理。
下面看一个使用cglib进行代理的实例,需先导入相应的jar包(asm及cglib包)。
方法步骤以下:
首先定义业务类,无需实现接口。
1 public class Hello { 2 3 public void sayHello() { 4 System.out.println("Hello World!"); 5 } 6 7 }
定义一个拦截器,经过实现MethodInterceptor接口的intercept()方法来实现回调方法,经过invokeSuper()执行目标对象的方法。
1 public class HelloMethodInterceptor implements MethodInterceptor { 2 3 @Override 4 public Object intercept(Object object, Method method , Object[] objects , MethodProxy methodProxy ) throws Throwable { 5 System.out.println("before " + method.getName()); 6 methodProxy.invokeSuper(object, objects); 7 System.out.println("after " + method.getName()); 8 return null; 9 } 10 11 }
经过Enhancer增强类来建立动态代理类,经过它的setSuperclass()方法来指定要代理的业务类(即为下方生成的代理类指定父类),而后经过create()方法生成代理类对象。
在enhance.create()建立完代理对象后,在代理类调用方法中,会被咱们实现的方法拦截器HelloMethodInterceptor拦截。若是被代理类被final修饰,则该类不能被继承,即不能被代理;一样,若是被代理类存在final修饰的方法,则该方法不能被代理。
1 public class Client { 2 3 public static void main(String[] args) { 4 Enhancer enhancer = new Enhancer(); //工具类 5 enhancer.setSuperclass(Hello.class); //继承被代理类 6 enhancer.setCallback(new HelloMethodInterceptor()); //设置回调 7 8 Hello hello = (Hello) enhancer.create(); //生成代理类对象 9 hello.sayHello(); 10 } 11 12 }
运行结果以下:
综上所述,cglib采用的是动态建立子类的方法,因此对final修饰的类不能进行代理。以Spring AOP编程为例,JDK动态代理及cglib代理的区别在于,有接口的目标对象采用JDK代理,无接口的目标对象采用cglib代理。
使用cglib的前提条件为: