欢迎来到知了汇智!
联系电话:知了汇智-电话号码 028-62016472 / 13228113191

java学习之静态代理、JDK原生和CGLIB动态代理-知了汇智

java学习之静态代理、JDK原生和CGLIB动态代理

  一、何为代理(Proxy)

  代理,即:你不用去做,别人代替你去处理。
  代理是基本的设计模式之一,它是你为了提供额外的或不同的操作,而插入的用来代替“实际”对象的对象。
  如:在公司里要上外网,要在浏览器里设置一个 HTTP 代理;
  王宝强事业忙没空照顾妻子,被宋JJ代替照顾了(O(∩_∩)O哈哈~)。
  Spring的AOP底层原理就是通过JDK原生动态代理实现的。

  二、静态代理

  先看一个简单的例子:

public interface Star {
	void sing(String songName);
	void goShow(String city);
}

 

@Component
public class DengChao implements Star{
 
	@Override
	public void sing(String songName) {
		System.out.println("请邓超唱一首:"+songName);
	}
 
	@Override
	public void goShow(String city) {
		System.out.println("让邓超去"+city+"参加节目");
	}
 
}


  DengChao实现了Star这个接口,假如我需要在实现类中两个方法的输出语句(System.out...)前后加入一些内容,比如:日志、校验等信息时,如果直接把这些逻辑写死在这两个方法里面,感觉不太好,不够灵活,所以为了不侵入原本的业务逻辑,使用静态代理,代码如下:
 

@Component
public class StaticProxy implements Star{
	/**
	 * 委托类
	 */
	private DengChao dengChao;
	
	public StaticProxy(DengChao dengChao) {
		super();
		this.dengChao = dengChao;
	}
 
	@Override
	public void sing(String songName) {
		System.out.println("前置信息");
		dengChao.sing(songName);
		System.out.println("后置信息");
	}
 
	@Override
	public void goShow(String city) {
		System.out.println("前置信息");
		dengChao.goShow(city);
		System.out.println("后置信息");
	}
}

  测试一下:

class TestStaticProxy {
	@Test
	void test() {
		DengChao dengChao = new DengChao();
		Star star = new StaticProxy(dengChao);
		star.sing("我是超级英雄");
		star.goShow("深圳");
	}
 
}

  结果为:

前置信息
请邓超唱一首:我是超级英雄
后置信息
前置信息
让邓超去深圳参加节目
后置信息


  虽然实现了相关的功能,但是感觉还是不太好,因为
  1、代理类要跟被代理类一样实现相同的接口,多个接口的实现类要被代理的话就需要写多个代理类,那么就会出现大量重复的代码,作为一个有思想的程序员是绝对不允许的。
  2、如果接口增加一个方法时,实现类要修改,代理类也要修改,增加了代码维护的复杂度
  更好的方式就是动态代理。

java学习之静态代理、JDK原生和CGLIB动态代理

 

  三、动态代理

  动态代理:应用程序发布后,通过动态创建代理对象;动态代理可以动态地创建代理并动态地处理对所代理方法的调用。
  其中动态代理又可分为:
  1、JDK原生动态代理
  JDK动态代理只能针对实现了接口的类生成代理。
  只需要一个代理类,而不是针对每个类编写代理类。
  新增一个动态代理类实现实现了 InvocationHandler 接口,那么必须实现该接口的 invoke 方法。
  通过调用静态方法Proxy.newProxyInstance()可以创建动态代理,这个方法需要
  一个类加载器(通过可以从已经被加载的对象中获取其类加载器)
  你希望代理实现的接口列表(不是类或抽象类)
  InvocationHandler接口的一个实现
  动态代理可以将所有调用重定向到调用处理器,因此通常会向调用处理器的构造器传递给一个“实际”对象的引用,从而使得调用处理器在执行其中介任务时,可以将请求转发。
  在invoke()内部,在代理上调用方法时需要格外小心,因为对接口的调用将被重定向为对代理的调用。
  Method.invoke()将请求转发给被代理对象,并传入必需的参数。
  代码如下:

public class DynamicProxyHander implements InvocationHandler{
	
	private Object target;//用于接收具体实现类的实例对象
	/**
	 * 使用带参数的构造器来传递具体实现类的对象
	 * @param object
	 */
	public  DynamicProxyHander(Object target) {
		this.target = target;
	}
	
	@SuppressWarnings("unchecked")
	public <T> T getProxy() {
		/**
		 * this:当前对象自己
		 */
		System.out.println("this is "+this);
		System.out.println("Interfaces is" +Arrays.asList
                (target.getClass().getInterfaces()));
		return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), this);
	}
	
	/**
	 * proxy:代理对象
	 * method:原对象被调用的方法
	 * args:方法的参数
	 */
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println("前置通知");
		Object result = method.invoke(target, args);
		System.out.println("后置通知");
		return result;
	}
 
}


  测试一下:
 

@Test
	void test() {
		DynamicProxyHander dp = new DynamicProxyHander(new DengChao());
		Star starProxy = dp.getProxy();
		starProxy.sing("超级英雄");
		starProxy.goShow("南昌");
		
	}


  结果如下:
 

this is com.cb.springstudy.dynamicproxy.DynamicProxyHander@3f49dace
/**两个接口是因为我让DengChao这个 类实现了Star和Actor两个接口**/
Interfaces is[interface com.cb.springstudy.dynamicproxy.Star,
interface com.cb.springstudy.dynamicproxy.Actor]
前置通知
请邓超唱一首:超级英雄
后置通知
前置通知
让邓超去南昌参加节目
后置通知


  JDK动态代理有个局限是只能针对实现了接口的类生成代理,如果没有实现接口的类想生成代理呢,那么就能用
  CGLIB动态代理
  2、CGLIB动态代理
  CGLIB(CODE GENERLIZE LIBRARY)代理是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的所有方法,所以该类或方法不能声明称final的。
  增加一个CGLibProxy类实现MethodInterceptor接口,要实现intercept这个方法

 

public class CGLibProxy implements MethodInterceptor{
 
private static CGLibProxy instance = new CGLibProxy();
    
    private CGLibProxy() {
    }
 
    public static CGLibProxy getInstance() {
        return instance;
    }
    
    public <T> T getProxy(Class<T> cls) {
        return (T) Enhancer.create(cls, this);
    }
 
    @Override
    public Object intercept
    (Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
        System.out.println("前置日志");
        Object result = arg3.invokeSuper(arg0, arg2);
        System.out.println("后置日志");
        return result;
    }
}

        代理没有实现接口的类,如:

@Component
public class DengChao{
 
	public void sing(String songName) {
		System.out.println("请邓超唱一首:"+songName);
	}
 
	public void goShow(String city) {
		System.out.println("让邓超去"+city+"参加节目");
	}
 
}


  测试一下:

@Test
    void test() {
        DengChao dengChao = CGLibProxy.getInstance().getProxy(DengChao.class);
        dengChao.sing("中华英雄");
    }

  结果:

前置日志
请邓超唱一首:中华英雄
后置日志


        版权声明:本文来源于网络,由知了堂搜集整理,仅供大家学习Java时使用

132 2811 3191
预约免费试学
点击咨询
预约试学