代理模式

一,概述

1)代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式,即通过代理访问目标对象.

2)代理的好处:可以在目标对象实现的基础上,增加额外的功能操作,即扩展目标对象的功能.

3)分类:静态代理,动态代理,cglib代理.

4)代理模式的关键点:代理对象与目标对象.

5)举例:商家是直接联系不到明星的,一般情况下,都是商家找明星经纪人,然后明星经纪人通知明星.明星是目标对象,明星经纪人就是代理对象.

二,静态代理

1)要点:代理对象要实现与目标对象一样的接口.

2)举例:模拟保存用户

          UserDao-->直接保存

          UserDaoProxy-->给保存方法添加事务管理

3)接口:IUserDao.java

public interface IUserDao {

	void save();
}
4)目标对象:UserDao.java,实现了IUserDao接口

public class UserDao implements IUserDao {

	@Override
	public void save() {
		System.out.println("***保存用户数据到数据库");
	}

}
5)代理对象:UserDaoProxy.java,与目标对象一样实现了IUserDao接口
public class UserDaoProxy implements IUserDao{

	private IUserDao target;
	public UserDaoProxy(IUserDao target) {
		this.target = target;
	}
	
	@Override
	public void save() {
		System.out.println("开启事务...");
		//执行目标对象的方法
		target.save();
		System.out.println("提交事务...");
	}
}
6)测试:

public class App {

	@Test
	public void testProxy() throws Exception {
		
		IUserDao userDao=new UserDao();
		
		UserDaoProxy proxy=new UserDaoProxy(userDao);
		
		proxy.save();
	}
}
结果:

开启事务...
***保存用户数据到数据库
提交事务...
综上,扩展了UserDao的save接口.

7)总结静态代理:

a)优点:可以做到在不修改目标对象的前提下,对目标对象的功能进行扩展.

b)缺点:因为代理对象需要与目标对象实现一样的接口,所以会造成很多代理类,类太多;一旦接口增加方法,目标对象与代理对象都要维护.(动态代理就可以避免这些问题,借助代理工厂完成)

三,动态代理

1)代理对象不需要实现接口.

2)代理对象的生成利用了JDK API,动态地在内存中构建代理对象(需要我们指定创建代理对象 目标对象 实现的接口的类型)

3)动态代理也叫JDK代理,或者接口代理.

4)接口和目标对象还是用上面代码,代理工厂代码如下:

package com.bighuan.b_dynamic;

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

/**
 * 给所有Dao创建代理对象(动态代理)
 * 
 * @author bighuan
 * 
 */
public class ProxyFactory {

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

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

	// 给目标对象生成代理对象
	public Object getProxyInstance() {
		return Proxy.newProxyInstance(target.getClass().getClassLoader(),
				target.getClass().getInterfaces(),// 得到目标对象实现的接口类型
				// new Class[]{IUserDao.class}
				new InvocationHandler() {// 事件处理器

					@Override
					public Object invoke(Object proxy, Method method,
							Object[] args) throws Throwable {
						// 开启事务
						System.out.println("***开启事务***");

						String name = method.getName();// 拿到方法名

						System.out.println(name);
						// 执行目标对象方法
						Object returnValue = method.invoke(target, args);

						// 提交事务
						System.out.println("***提交事务***");
						return returnValue;
					}
				});
	}
}
5)测试:

@Test
	public void testProxy() throws Exception {
		//目标对象
		IUserDao userDao=new UserDao();
		//原始类型:class com.bighuan.b_dynamic.UserDao
		System.out.println(userDao.getClass());
		
		//给目标对象创建代理对象
		IUserDao proxy=(IUserDao) new ProxyFactory(userDao).getProxyInstance();
		
		//class $Proxy4,内存中动态生成的代理对象
		System.out.println(proxy.getClass());
		//执行方法[代理对象]
		proxy.save();
	}
效果和上面的静态代理一致,但却规避了静态代理的缺点.

6)动态代理总结:代理对象不需要实现接口,但是目标对象一定要实现接口;否则不能使用动态代理!

如果一个目标对象需要实现功能扩展,但是目标对象却没有实现接口,该怎样实现功能扩展呢?

四,Cglib代理

1)Cglib代理也叫子类代理,在内存中构建一个子类对象从而实现对目标对象的功能扩展.

2)JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口.如果想代理没有实现接口的类,就可以使用CGLIB实现.
3)CGLIB是一个强大的高性能的代码生成包,它可以在运行期扩展Java类与实现Java接口.它广泛的被许多AOP的框架使用,例如Spring AOP和dynaop,为他们提供方法的interception(拦截).
4)CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类.不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉.

5)开发准备:需要引入Cglib的相关jar文件,但是Spring的核心包中已经包括了cglib功能,所以自接导入spring-core-3.2.5.jar即可;引入功能包后,就可以在内存中动态构建子类.

6)代理类不能为final类(final类没有子类),目标对象的方法如果为final或static(final或static修饰的方法,子类不能重写),那么就不会拦截,即不会执行除目标对象之外的业务方法.

7)UserDao.java

public class UserDao {

	public void save() {
		System.out.println("***保存用户数据到数据库");
	}

}
ProxyFactory.java

package com.bighuan.c_cglib;

import java.lang.reflect.Method;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

/**
 * Cglib子类代理工厂
 * (对UserDao,在内存中动态构建一个子类对象)
 * @author bighuan
 *
 */
public class ProxyFactory implements MethodInterceptor{
	
	//维护目标对象
	private Object target;
	public ProxyFactory(Object target){
		this.target=target;
	}
	
	//给目标对象创建代理对象
	public Object getProxyInstance(){
		//1,工具类
		Enhancer hancer=new Enhancer();
		//2,设置父类
		hancer.setSuperclass(target.getClass());
		//3,设置回调函数
		hancer.setCallback(this);
		//4,创建子类(代理对象)
		return hancer.create();
	}

	@Override
	public Object intercept(Object obj, Method method, Object[] args,
			MethodProxy proxy) throws Throwable {
		System.out.println("**********开启事务*****");
		//执行目标对象的方法
		Object returnValue = method.invoke(target, args);
		
		System.out.println("**********提交事务*****");
		
		return returnValue;
	}

}
8)测试:

@Test
public void testProxy() throws Exception {
	//目标对象
	UserDao target=new UserDao();
	//class com.bighuan.c_cglib.UserDao
	System.out.println(target.getClass());
		
	//代理对象
	UserDao proxy=(UserDao) new ProxyFactory(target).getProxyInstance();
	//class com.bighuan.c_cglib.UserDao$$EnhancerByCGLIB$$b6d37fe1
	System.out.println(proxy.getClass());
		
	//执行代理对象的方法
	proxy.save();
}
结果:

**********开启事务*****
***保存用户数据到数据库
**********提交事务*****

五,总结

下一篇,Spring的AOP编程,期待吧,boys!





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值