Spring 静态代理与动态代理

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Stephen_mu/article/details/87691574

首先关于代理模式我们拿生活中的例子来让大家便于理解

静态代理

明星  <<------ 经纪人<<----------大老板

现实世界中,如果一个大老板需要找明星代言或者表演是不会直接找到明星本人的,一般都是直接找明星的经纪人,让他再去找明星商谈代言和表演的相关事项。在这个模式中,真正表演和代言的是明星本人,而经纪人只是一个传达消息或者说帮明星处理一些杂事的人。

实现:

//明星类
public class Celebrity implements ICelebrity {

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.ncs.staticproxy.ICelebrity#show()
	 */
	public void show() {

		System.out.println("开始表演");

	}

}
//明星所要实现的接口
public interface ICelebrity {

	void show();

}
//经纪人类(代理类)
public class Proxy implements ICelebrity {
	
	
	private Celebrity target;
	
	public Proxy(Celebrity target) {
		
		this.target=target;
	}

	public void show() {
		
		System.out.println("开始事务");
		
		target.show();
		
		System.out.println("结束事务");
		
	}

}
//大老板类
public class Boss {

	public static void main(String[] args) {
		
		//实例化Celebrity
		Celebrity target =new Celebrity();
		//实例化Proxy对象
		ICelebrity proxy =new Proxy(target);
		proxy.show();
	}

}

总结静态代理:

  1.        经纪人(代理对象)在不影响明星(目标对象)的前提下,扩展了明星(目标对象)的功能。

     缺点:

  1.   经纪人(代理对象)需要实现和明星(目标对象)一样的接口
  2.   有多少明星(目标对象)就需要多少经纪人(代理对象)。

     解决方案:经纪人公司(工厂模式)出现(不是最优) 。 动态代理模式出现(较优的解决方案)

动态代理

使用JDK自带的API实现

java.lang.reflect.Proxy

static obejct newProxyInstance​(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

Classloader loader :  指定目标对象类加载器 

Class<?>[] interfaces : 目标对象实现的接口的类型,使用泛型方式确认类型

InvocationHandler h : 事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入

静态代理例子实现

//明星类(目标对象)
public class Celebrity implements ICelebrity {
	
	/* (non-Javadoc)
	 * @see com.ncs.dynamicproxy.ICelebrity#show()
	 */
	public void show() {
		
		System.out.println("开始演出");
		
	}

}
//明星类需要实现的接口
public interface ICelebrity {

	void show();

}
//代理工厂类(动态生成代理对象)
public class ProxyFactory {

	private Object target;

	public ProxyFactory(Object target) {
		this.target = target;
	}

	public Object getInstanceProxy() {
        //生成一个目标对象的代理对象
		return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
				new InvocationHandler() {
                    //事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入
					public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
						System.out.println("开始事务");
						// 通过反射执行目标对象的方法
						Object invoke = method.invoke(target, args);
						System.out.println("结束事务");
						return invoke;
					}
				});
	}

}
//老板类
public class Boss {

	public static void main(String[] args) {
		
		//创建目标对象
		Celebrity target=new Celebrity();
		System.out.println(target.getClass());
		//创建代理对象工厂
		ProxyFactory factory=new ProxyFactory(target);
		//使用动态代理获取代理对象
		ICelebrity proxy = (ICelebrity) factory.getInstanceProxy();
		System.out.println(proxy.getClass());
		
		proxy.show();
        		
	}

}

动态代理总结:

  1. 动态代理不需要实现目标对象所对应的接口
  2. 代理对象的生成是通过JDK api在内存中动态的生成,(需要我们指定创建代理对象实现的接口的类型)
  3. 动态代理,JDK 代理 接口代理
  4. 代理对象不需要实现接口,但是目标对象必须要实现接口,否则不能使用动态代理

Cglib代理

     首先我们需要思考一个这样子的问题,在静态代理和动态代理中目标对象(明星)都需要实现特定的接口来扩充功能。

然而有一天我们突然发现存在一个目标对象并未实现任何接口,那么我们该如何通过构建代理对象来扩充它的功能呢?

     解决方案: 通过在内存中构建目标对象的子类来实现对于目标对象功能的扩充。

     Cglib:
                Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用,例如Spring AOP和synaop,为他们提供方法的interception(拦截)
                Cglib包的底层是通过使用一个小而块的字节码处理框架ASM来转换字节码并生成新的类

      使用: 可以引入cglib的jar文件,但是Spring的核心包中已经包括了Cglib功能,所以直接引入spring-core.jar

重写动态代理的例子:

//明星类(目标对象)
public class Celebrity {

	public void show() {
		System.out.println("开始演出");
	}

}

/**
 * cglib通过在内存中构建一个子类对象来实现对于目标对象功能的构建
 *  
 * @author P1310989
 *
 */
//代理工厂类
public class CglibProxyFactory {

	// 首先维护一个目标对象
	private Object target;

	public CglibProxyFactory(Object target) {
		this.target = target;
	}

	// 得到一个目标对象的代理对象
	public Object getProxyInstance() {
		// 1.得到一个工具类
		Enhancer enhancer = new Enhancer();
		// 2.设置父类
		enhancer.setSuperclass(target.getClass());
		// 3.设置回调函数
		enhancer.setCallback(new MethodInterceptor() {
			@Override
			public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {

				System.out.println("开始事务");
				// 执行目标对象的方法
				Object result = method.invoke(target, args);

				System.out.println("结束事务");

				return result;
			}
		});
		// 4.创建子类代理对象
		return enhancer.create();
	}

}

或者实现MethodInterceptor接口

/**
 * cglib通过在内存中构建一个子类对象来实现对于目标对象功能的构建
 * 
 * @author P1310989
 *
 */
//代理工厂类
public class CglibProxyFactory implements MethodInterceptor {

	// 首先维护一个目标对象
	private Object target;

	public CglibProxyFactory(Object target) {
		this.target = target;
	}

	// 得到一个目标对象的代理对象
	public Object getProxyInstance() {
		// 1.得到一个工具类
		Enhancer enhancer = new Enhancer();
		// 2.设置父类
		enhancer.setSuperclass(target.getClass());
		// 3.设置回调函数
		enhancer.setCallback(this);
		// 4.创建子类代理对象
		return enhancer.create();
	}

	@Override
	public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {

		System.out.println("开始事务");
		// 执行目标对象的方法
		Object result = method.invoke(target, args);

		System.out.println("结束事务");

		return result;
	}

}
//老板类(测试类)
public class Boss {

	public static void main(String[] args) {

		// 创建明星对象(目标对象)
		Celebrity target = new Celebrity();
		// 创建代理对象工厂
		CglibProxyFactory factory = new CglibProxyFactory(target);
		// 获取代理对象实例
		Celebrity proxy = (Celebrity) factory.getProxyInstance();

		proxy.show();

	}

}

总结:     

                代理模式被广泛使用在Spring AOP编程中
                如果加入容器的目标对象存在实现接口,采用JDK代理(动态代理)
                如果目标对象没有实现接口,采用cglib代理    

猜你喜欢

转载自blog.csdn.net/Stephen_mu/article/details/87691574