Java静态代理和动态代理(基于JDK和基于Spring的CGLib)

          常常有这样的业务需求,如在用户登录前加入日志记录,权限校验等。这些操作本身和用户登录并无太大关系,因此在业务层加入这些代码会显得冗余。这时就需要用到代理模式,就好像找工作,我们可以把复杂的寻找过程交给中介去完成,我们只需要关心自己的需求就可以了。同样,我们可以借助代理对象帮我们做一些前期和后期的操作。

         通过代理访问目标对象。如此便于在目标实现的基础上增加额外的功能操作,前拦截,后拦截等,以满足自身的业务需求。

下面假设有在用户登录前加入日志记录的需求,分别用静态代理和动态代理实现:

1、静态代理

静态代理需要被代理对象和代理对象都实现相同的接口

//要实现的接口
package com.hncj.demo;

public interface UserService {
	public void userLogin();
}
//要被代理的对象
package com.hncj.demo;

public class UserServiceImpl implements UserService{

	@Override
	public void userLogin() {
		System.out.println("用户登录");
	}

}
//代理对象
package com.hncj.demo;

public class StaticProxy implements UserService{
	//被代理对象的实例
        private UserService userService;
	
	public StaticProxy(UserService userService) {
		super();
		this.userService = userService;
	}

	@Override
	public void userLogin() {
		logging();
		userService.userLogin();
	}
	public void logging() {
		System.out.println("====日志记录====");
	}
	
	
	public static void main(String[] args) {
		new StaticProxy(new UserServiceImpl()).userLogin();
	}
}

可以看出,静态代理模式在接口方法发生变化时,其实现类和代理对象都要进行维护,而且如果要被增强的方法过多,也会造成代码的冗余。

如下这种情况:


	public void userLogin1() {
		logging();           //重复的逻辑
		userService.userLogin();
	}
	
	public void userLogin2() {
		logging();
		userService.userLogin();
	}
	public void userLogin3() {
		logging();
		userService.userLogin();
	}
	public void userLogin4() {
		logging();
		userService.userLogin();
	}
	。。。。。

2、动态代理(基于jdk)

在接口和被代理类不变的基础上,创建下面的类:

package com.hncj.demo2;

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

public class DynamicProxy implements InvocationHandler{
	//被代理对象的实例
	private UserService userService;
	public DynamicProxy(UserService userService) {
		this.userService = userService;
	}
	//生成代理对象
	public UserService createDynamicProxyObject() {
		return (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), this);
	}

	/* 
	 *method:接口中提供的方法 args:方法的参数 proxy:代理对象 
	 */
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		if(method.getName().equals("userLogin")) {
			logging();
			return method.invoke(userService, args);
		}
		return method.invoke(userService, args);
	}
	public void logging() {
		System.out.println("====日志记录====");
	}
	
	public static void main(String[] args) {
		new DynamicProxy(new UserServiceImpl()).createDynamicProxyObject().userLogin();
	}
}

这样一来,接口中再变更方法,只要在方法处理类中判断一下方法名即可,而且代理对象不要求实现接口,用起来很方便。

3、动态代理(基于spring的CGLib)

不需要接口,根据被代理对象的子类生成代理,需要spring的jar包

package com.hncj.demo3;

public class UserServiceImpl{

	public void userLogin() {
		System.out.println("用户登录");
	}

}
package com.hncj.demo3;

import java.lang.reflect.Method;

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

public class CglibProxy implements MethodInterceptor{
	private UserServiceImpl userServiceImpl;
	public CglibProxy(UserServiceImpl userServiceImpl) {
		this.userServiceImpl = userServiceImpl;
	}
	public UserServiceImpl getCgliProxy() {
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(UserServiceImpl.class);
		enhancer.setCallback(this);
		return (UserServiceImpl) enhancer.create();
	}
	@Override
	public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
		if(arg1.getName().equals("userLogin")) {
			logging();
			Object result = arg3.invokeSuper(arg0, arg2);
			logging();
			return result;
		}
		return arg3.invokeSuper(arg0, arg2);
	}
	public  void logging() {
		System.out.println("日志记录");
	}
	public static void main(String[] args) {
		new CglibProxy(new UserServiceImpl()).getCgliProxy().userLogin();
	}
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值