Java总结坚持一百天2--JDK动态代理和CGLIB动态代理

1、什么是动态代理?
2、JDK动态代理模式
3、CGLIB动态代理模式
4、动态代理实现拦截器

----------------------------------------------什么是动态代理?-------------------------------------------------------------
动态代理技术是整个java技术中最重要的一个技术,它是学习java框架的基础,不会动态代理技术,那么在学习Spring这些框架时是学不明白的。

动态代理技术就是用来产生一个对象的代理对象的。在开发中为什么需要为一个对象产生代理对象呢?
  举一个现实生活中的例子:歌星或者明星都有一个自己的经纪人,这个经纪人就是他们的代理人,当我们需要找明星表演时,不能直接找到该明星,只能是找明星的代理人。比如刘德华在现实生活中非常有名,会唱歌,会跳舞,会拍戏,刘德华在没有出名之前,我们可以直接找他唱歌,跳舞,拍戏,刘德华出名之后,他干的第一件事就是找一个经纪人,这个经纪人就是刘德华的代理人(代理),当我们需要找刘德华表演时,不能直接找到刘德华了(刘德华说,你找我代理人商谈具体事宜吧!),只能是找刘德华的代理人,因此刘德华这个代理人存在的价值就是拦截我们对刘德华的直接访问!
  这个现实中的例子和我们在开发中是一样的,我们在开发中之所以要产生一个对象的代理对象,主要用于拦截对真实业务对象的访问。那么代理对象应该具有什么方法呢?代理对象应该具有和目标对象相同的方法
所以在这里明确代理对象的两个概念:
 1、代理对象存在的价值主要用于拦截对真实业务对象的访问。
 2、代理对象应该具有和目标对象(真实业务对象)相同的方法。刘德华(真实业务对象)会唱歌,会跳舞,会拍戏,我们现在不能直接找他唱歌,跳舞,拍戏了,只能找他的代理人(代理对象)唱歌,跳舞,拍戏,一个人要想成为刘德华的代理人,那么他必须具有和刘德华一样的行为(会唱歌,会跳舞,会拍戏),刘德华有什么方法,他(代理人)就要有什么方法,我们找刘德华的代理人唱歌,跳舞,拍戏,但是代理人不是真的懂得唱歌,跳舞,拍戏的,真正懂得唱歌,跳舞,拍戏的是刘德华,在现实中的例子就是我们要找刘德华唱歌,跳舞,拍戏,那么只能先找他的经纪人,交钱给他的经纪人,然后经纪人再让刘德华去唱歌,跳舞,拍戏。

----------------------------------------------JDK动态代理模式-----------------------------------------------------------
JDK动态代理必须需要接口。

JDK代理实现

package com.dp.jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class JDKPoxyExampleImpl implements InvocationHandler {

	private Object target;


	/**
	 * 
	 * 返回  代理对象
	 * 
	 * @param target
	 * @return
	 */
	public Object bind(Object target) {
		this.target =target;
		return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
	}
	
	/**
	 * 代理逻辑
	 */
	@Override
	public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
		before();
		Object obj = method.invoke(target, params);
		after();
		return obj;
	}
	
	
	public void before() {
		System.out.println("before");
	}
	
	public void after() {
		System.out.println("after");
	}

}

接口

package com.dp.jdk;

public interface TestInterface {
	
	public String display();

}

实现

package com.dp.jdk;
public class TestBean implements TestInterface {
	String name;
	String sex;
	String status ="锁定";
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getSex() {
		return sex;
	}
	public void setSex(String sex) {
		this.sex = sex;
	}
	public String getStatus() {
		return status;
	}
	public void setStatus(String status) {
		this.status = status;
	}
	@Override
	public String toString() {
		return "TestBean [name=" + name + ", sex=" + sex + ", status=" + status + "]";
	}
	public String display() {
//		System.out.println("name:"+name);
//		System.out.println("sex:"+sex);
		System.out.println(status);
		return "展示成功";
	}
}

测试

package com.dp.test;

import com.dp.jdk.JDKPoxyExampleImpl;
import com.dp.jdk.TestBean;
import com.dp.jdk.TestInterface;

public class Test {
	
	public static void main(String[] args) {
		
		//保存生成的代理类的字节码文件
		//System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
		
		//JDK 动态代理
		JDKPoxyExampleImpl jdkPoxyExampleImpl = new JDKPoxyExampleImpl();
		
		//返回代理对象
		TestInterface bind = (TestInterface) jdkPoxyExampleImpl.bind(new TestBean());
		
		bind.display();
	}
}

测试结果

before
锁定
after

----------------------------------------------CGLIB动态代理模式-------------------------------------------------------
CGLIB优势就是不必须提供接口,只要一个非抽象接口就可以实现动态代理。

CGLIB实现的需要jar包(可以通过http链接在maven上获取)

<!-- https://mvnrepository.com/artifact/cglib/cglib -->
	<dependency>
	    <groupId>cglib</groupId>
	    <artifactId>cglib</artifactId>
	    <version>3.2.10</version>
	</dependency>
	
	<!-- https://mvnrepository.com/artifact/org.ow2.asm/asm -->
	<dependency>
	    <groupId>org.ow2.asm</groupId>
	    <artifactId>asm</artifactId>
	    <version>7.0</version>
	</dependency>

CGLIB实现动态代理

package com.dp.cglib;

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 CglibProxyExmaple implements MethodInterceptor {

	/**
	 * 生成代理对象
	 * @param cls
	 * @return
	 */
	public Object getProxy(Class cls) {
		return Enhancer.create(cls, this);
	}
	
	/**
	 * 代理逻辑方法
	 */
	@Override
	public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
		before();
		Object invokeSuper = proxy.invokeSuper(obj, args);
		after();
		return invokeSuper;
	}
	public void before() {
		System.out.println("before");
	}
	public void after() {
		System.out.println("after");
	}
}

测试

package com.dp.cglib;

import com.dp.jdk.TestBean;

import net.sf.cglib.proxy.Enhancer;

public class TestMain {
	public static void main(String[] args) {
		CglibProxyExmaple cglibProxyExmaple = new CglibProxyExmaple();
		TestBean tb = (TestBean) cglibProxyExmaple.getProxy(TestBean.class);
		tb.display();
	}
}

----------------------------------------------动态代理实现拦截器-------------------------------------------------------

定义拦截器接口

package com.dp.Interceptor;

import java.lang.reflect.Method;

/**
 * 拦截器
 * @author jackray
 *
 */
public interface Intercepter {
	public boolean before(Object proxy,Object target,Method method, Object args[]);
	public void around(Object proxy,Object target,Method method, Object args[]);
	public void after(Object proxy,Object target,Method method, Object args[]);

}

参数含义

  1. proxy 代理对象
  2. target 真实对象
  3. method 方法
  4. args 运行方法参数

方法含义

  1. before 方法返回boolean值,他在真实对象前调用。当返回为true时,则返回真实的对象方法;当返回为false的时候,则调用around方法。
  2. 在before方法返回为false的情况下,调用around。
  3. 在返回真实对象方法或者around方法执行之后,调用after

定义拦截器实现

package com.dp.Interceptor;

import java.lang.reflect.Method;

public class MyInterceper implements Intercepter {

	@Override
	public boolean before(Object proxy, Object target, Method method, Object[] args) {
		System.out.println("反射方法前逻辑");
		return false;//不反射被代理对象原有方法
	}

	@Override
	public void around(Object proxy, Object target, Method method, Object[] args) {
		System.out.println("反射方法后逻辑");

	}

	@Override
	public void after(Object proxy, Object target, Method method, Object[] args) {
		System.out.println("取代了被代理对象的方法");

	}

}

package com.dp.Interceptor;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class IntercepterJDKProxy implements InvocationHandler {

	
	private Object target; //真实对象
	private String intercepterClass = null; //拦截器全限定名
	
	public IntercepterJDKProxy(Object target,String intercepterClass) {
		this.target = target;
		this.intercepterClass = intercepterClass;
	}
	
	/**
	 * 绑定委托对象并返回一个【代理占位】
	 * @param target【真实对象】
	 * @param intercepterClass
	 * @return 代理对象【占位】
	 */
	public static Object bind(Object target,String intercepterClass) {
		//取得代理对象
		return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new IntercepterJDKProxy(target, intercepterClass));
	}
	
	/**
	 * 通过代理对象调用方法,首先进入这个方法
	 */
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		if(intercepterClass == null) {
			//没有设置拦截器则直接返回原有对象
			return method.invoke(target, args);
		}
		Object result = null;
		//通过反射生成拦截器
		Interceptor interceptor = (Interceptor) Class.forName(intercepterClass).newInstance();
		//调用前置方法
		if(interceptor.before(proxy, target, method, args)) {
			//反射原有对象方法
			result = method.invoke(target, args);
		}else {//返回false执行aroud方法
			interceptor.around(proxy, target, method, args);
			
		}
		//调用后置方法
		interceptor.after(proxy, target, method, args);
		
		return result;
	}
	
}

IntercepterJDKProxy 分析
target是真实对象,intercepterClass拦截器的全限定名
执行步骤分析如下:

  1. 在bind方法中用JDK动态绑定了一个对象,然后返回代理对象。
  2. 如果没有拦截器,则直接反射真实对象的方法,然后结束,否则进行第三部
  3. 通过反射生成拦截器,并准备使用它
  4. 调用拦截器的before方法,如果返回为true,反射原来的方法;否则运行拦截器的around方法
  5. 返回结果。

测试

package com.dp.Interceptor;

import com.dp.jdk.TestBean;
import com.dp.jdk.TestInterface;

public class MainTest {
	public static void main(String[] args) {
		
		TestInterface proxy = (TestInterface) IntercepterJDKProxy.bind(new TestBean(), "com.dp.Interceptor.MyInterceptor");
		
		proxy.display();
	}
}

测试结果

反射方法前逻辑
反射方法后逻辑
取代了被代理对象的方法

不拦截

package com.dp.Interceptor;

import com.dp.jdk.TestBean;
import com.dp.jdk.TestInterface;

public class MainTest {
	public static void main(String[] args) {
		
		TestInterface proxy = (TestInterface) IntercepterJDKProxy.bind(new TestBean(), null);
		
		proxy.display();
	}
}

返回结果

锁定

拦截器可以进一步简化动态代理的使用方法,使程序变得更简单。

仅仅使简单的实现,深度不够,继续加油。参考地址

猜你喜欢

转载自blog.csdn.net/ppwwp/article/details/86560716