代理模式之静态代理与动态代理(java动态代理、Cglib动态代理)

目录

 

一、代理模式

二、静态代理

三、Java动态代理

四、Cglib动态代理


一、代理模式

在现实生活中,当我们需要去做一些事情,但是自己有没时间去做,或者自己做的不一定很好,那么通常就会发一定的代价去请求别人的帮助。像客户租房子,找中介。明星找经济人等等。此时中介、经纪人就成了代理商,帮我们做一些事情。

同样,在开发过程中,我们不想在去重构一些已经封装好的类,但我们又需要在类的基础之上加上一些服务。这个时候,我们就可以请求代理者帮我们代理该对象,需要访问该对象的类,现在只能通过访问代理类,而达到当下的目的。

代理类并不真正实现业务处理,所谓业务处理的实现是代理类调用委托类实现的,而代理类真正所做的是在业务处理的基础上添加一些服务,像日志记录、权限校验等等服务。

二、静态代理

静态代理实现的前提条件是,与委托类同实现一个接口,意思是说,委托类实现的接口,代理类也要实现。如果,委托类没有接口,则要创建接口,接口里的方法必须是委托类中的所有方法,这样才能帮委托类实现静态代理。

【案例】:

父接口:

public interface UserService {
	public void query();
	public void update();
}

委托类:

public class UserServiceImpl implements UserService{
	@Override
	public void query() {
		System.out.println("查询信息.....");
	}
	@Override
	public void update() {
		System.out.println("更新信息.....");
	}
}

代理类:

public class UserServiceImpl implements UserService{
	@Override
	public void query() {
		System.out.println("查询信息.....");
	}
	@Override
	public void update() {
		System.out.println("更新信息.....");
	}
}

测试方法:

@Test
	public void test01(){
		UserService userService = new UserServiceImplStatic();
		userService.query();
	}

以后凡是用到UserServiceImpl类中的方法,则不能直接使用UserServiceImpl方法,应该去找它的代理对象,从而实现对一些方法的添加的附加服务。

静态代理方法的优缺点

优点:结构清晰 易于理解
缺点:如果被代理者有多个方法,则代理者也需要开发多个方法,其中,往往存在大量重复代码,仍然存在代码重复。

三、Java动态代理

在JDK中提供了动态代理实现的工具类,直接使用该工具类就可以创建出代理者。该工具类实现代理的原理是实现委托类的接口的方法。从而实现对委托类的增加或修改。

调用Proxy.newProxyInstance()创建代理对象。

仍然以上述的父接口与委托类为案例

代理类:

public class UserServiceJavaProxy {

	private UserService userService = new UserServiceImpl();
	public UserService getProxy(){
		
		UserService proxy = (UserService)Proxy.newProxyInstance(
				/**
				 *【1】classLoader:用来生成代理者类的类加载器,通常可以传入委托者的类加载器
				 *【2】interfaces: 要求生成的代理者实现的接口们,通常就是实现和委托者相同的接口,
				  保证具有和被代理者相同的方法
				 *【3】invocationHandler: 用来设定回调函数的回调接口,使用者需要写一个类实现此接口,
				 从而实现其中的invoke方法,在其中编写代码处理代理者调用方法时的回调过程,
				 通常在这里调用真正对象身上的方法, 并且在方法之前或之后做额外操作。
				 */
				userService.getClass().getClassLoader(),
				userService.getClass().getInterfaces(),
				new InvocationHandler() {
					@Override
					public Object invoke(Object obj, Method method, Object[] args) throws Throwable {
						/**
						 * obj是代理对象
						 * method是方法对象,可以通过它来获取关于方法的信息,方法的名称,方法执行,方法变量
						 * args是方法的参数列表
						 */
						System.out.println("日志记录...");
						//method.invoke(userService, args);相当于userService.method(args);
						Object object = method.invoke(userService, args);
						return object;
					}
				});
		
		return proxy;
	}
}

测试方法:

@Test
	public void test02(){
		UserServiceJavaProxy proxy = new UserServiceJavaProxy();
		UserService userService = proxy.getProxy();
		userService.query();
	}

分析:

JDK动态代理的优缺点

优点:不需要像静态代理一样被代理方法都要实现一遍,而只需要在回调函数中进行处理就可以了,重复代码只需编写一次。

缺点:java的动态代理是通过代理者实现和委托者相同的接口,来保证两者具有相同的方法的。(userService.getClass().getInterfaces()就是为了实现相同的接口)。如果被代理者想要被代理的方法不属于任何接口,则生成的代理者自然无法具有这个方法,也就无法实现对该方法的代理。所以,java的动态代理机制是基于接口进行的受制于要代理的方法是否有接口的支持。

大家请参照代码上的注释。

四、Cglib动态代理

Cglib动态代理是通过继承委托类的方式,对委托类进行方法的增强。它克服了JDK动态代理实现接口的局限性,同样又保留了其优点。

实现动态代理

  1. 创建Enhancer增强器
  2. 实现委托类的接口
  3. 继承委托类
  4. 设置回调函数,并传入MethodInterceptor对象,实现其中intercept方法
  5. 生成代理对象

使用Cglib实现动态代理,需要先导入其jar包,才可以使用。(大家可以,自寻百度)

public class UserServiceCglibProxy {

	private UserService userService = new UserServiceImpl();
	public UserService getProxy(){
		//创建增强器
		Enhancer enhancer = new Enhancer();
		
		//实现委托类的接口, 此方法要求生成的动态代理额外实现指定接口们 ,
		//cglib动态代理不是靠接口实现的,所以可以不设置
		enhancer.setInterfaces(userService.getClass().getInterfaces());
		
		//继承代理类,此处要传入被代理者的类,
		//cglib是通过继承被代理者的类来持有和被代理者相同的方法的,此方法必须设置
		enhancer.setSuperclass(userService.getClass());
		
		//为增强器设定回调函数,之后通过增强器生成的代理对象调用任何方法都会走到此回调函数中,
		//实现调用真正被代理对象的方法的效果
		enhancer.setCallback(new MethodInterceptor() {
			//重写intercept方法,对我们需要的方法进行重构
			/**
			 * Object obj,	代理对象
			 * Method method, 需要重构的方法对象 
			 * Object[] args, 方法的参数 
			 * MethodProxy arg3 方法的代理对象
			 */
			@Override
			public Object intercept(Object obj, Method method, Object[] args, MethodProxy arg3) throws Throwable {
				System.out.println("日志记录");
				Object object = method.invoke(userService, args);
				return object;
			}
		});
		//生成代理对象
		UserService proxy = (UserService) enhancer.create();
		return proxy;
	}
}

测试方法:

@Test
	public void test03(){
		UserServiceCglibProxy proxy = new UserServiceCglibProxy();
		UserService userService = proxy.getProxy();
		userService.query();
	}

Cglib动态代理优缺点

优点:无论是否有接口都可以实现动态代理,使用场景基本不受限
缺点:第三方提供的动态代理机制,不是原生的,需要导入第三方开发包才可以使用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章