在运行时动态对某些东西代理,代理它做了其他事情。
public interface Developer{
void code();
void debug();
}
public class JavaDeveloper implements Developer{
private String name;
JavaDeveloper(String name){
this.name = name;
}
@Override
public void code(){
System.out.println(this.name+"is coding java");
}
@Override
public void debug(){
System.out.println(this.name+"is debugging java");
}
}
但是有一个叫Zack的程序员他在开发时候,通常没有bug,这是他后天养成的,我们应该在这个程序员的成长期去实现这个特性,而不是在他出生之前就定义。
public class JavaDynamicProxy{
public static void main(String[] args){
JavaDeveloper zack= new JavaDeveloper("Zack");
Developer zackProxy=(Developer) Proxy.newProxyInstance(zack.getClass().getClassLoader(),
zack.getClass().getInterfaces(),(proxy,method,agrs)->{
if(method.getName().equals("code")){
System.out.println("Zack is praying for the code!");
return method.invoke(zack,agrs);
}
if(method.getName().equals("debug")){
System.out.println("Zack's have no bug! No need to debug!");
return null;
}
return null;
});
zackProxy.code();
zackProxy.debug();
}
}
代理后显示结果:
Zack is praying for the code!
Zack is coding java
Zack's have no bug!No need to debug!
分析
如何使用动态代理,生成一个实例对象,然后用Proxy的newInstance方法对这个实例对象代理生成一个代理对象。
zackProxy的类型是Developer接口,而不是一个实现类,因为Zack在被代理后生成的对象,并不属于Developer接口的任何一个实现类。但是他是基于Developer接口和Zack类加载代理出来的。
public static Object newProxyInstance(ClassLoaderloader,Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException
// load the class loader to define the proxy class
// interfaces the list of interfaces for the proxy class to implement
// the invocation handler to dispatch method invocations to
loader,选用的类加载器,因为代理的是zack,所以一般都会用加载zack的类加载器
interfaces,被代理类所实现的接口,这个接口可以使多个
h,绑定代理类的一个方法。
简而言之:loader和interfaces基本决定了这个类到底是个怎么样的类。而h是invocationHandler,决定了这个代理类到底是多了什么功能。所以动态代理的内容重点就是这个invocationHandler,InvocationHandler作用就是,当代理对象的原本方法被调用时候,会绑定执行一个方法,这个方法就是Invocationhandler里面定义的内容,同时会替代原本方法的结果返回。
InvocationHandler接收三个参数:
proxy 代理后的实例对象
method 对象呗调用方法
args 调用时的参数
使用场景:
可以在运行的时候才切入改变类的方法,而不需要预先定义它。
动态代理一般很少去手写,但用的其实很多,在Spring项目中用的注解,例如历来注入的@Bean,@Autowired 事务注解@Transactional等都有用到,换言之就是Spring的AOP(切面编程)
动态代理第二种实现-CGlib
cglib是针对类来实现代理的,原理是对指定的业务类生成一个子类,并覆盖其中业务方法实现代理。
因为采用的是继承,所以不能对final修饰的类进行代理
1 首先定义业务类,无需实现接口(也可以实现接口,无所谓)
public class BookFacadeImpl1{
public void addBook(){
System.out.println("新增图书...");
}
}
2 实现MehodInterceptor方法代理接口,创建代理类
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;
}
3 创建业务类和代理类对象,然后通过代理类对象.getInstance(业务类对象)返回一个动态代理类对象(它是业务类的子类,可以用业务类引用指定它)。最后通过动态代理类对象进行方法调用
public static void main(String[] args) {
BookFacadeImpl1 bookFacade=new BookFacadeImpl1();
BookFacadeCglib cglib=new BookFacadeCglib();
BookFacadeImpl1 bookCglib=(BookFacadeImpl1)cglib.getInstance(bookFacade);
bookCglib.addBook();
}
比较:
JDK动态代理是通过接口中的方法名,在动态生成的代理类中调用业务实现类的同名方法
CGlib动态代理是通过继承业务类,生成的动态代理类是业务类的子类,通过重写业务方法进行代理。