静态代理和动态代理

        现在有这么一个功能,对于用户管理有增删改查4个方法,后来由于对安全性的要求,对于这4个方法,需要添加这么一个验证安全性功能(见如下代码).如何做到不修改原来的代码,而将功能完成?

private voidcheckSecurity()
{
       System.out.println("---------UserManagerImpl.checkSecurity()---------------");
}

        将需要的代码列出

          1.接口UserManager,定义增删改查4个方法.

public interface UserManager {
	public void addUser(String username,String password);
	public void delUser(int userId);
	public String findUserById(int userId);
	public void modifyUser(int userId,String username,String passwrod);
}	

        2.交由UserManagerImpl来实现.

public class UserManagerImpl implements UserManager {

	public void addUser(String username, String password) {
		System.out.println("---------UserManagerImpl.addUser()---------------");
	}

	public void delUser(int userId) {
		System.out.println("---------UserManagerImpl.delUser()---------------");
	}

	public String findUserById(int userId) {
		System.out.println("---------UserManagerImpl.findUserById()---------------");
		return "张三";
	}

	public void modifyUser(int userId, String username, String passwrod) {
		System.out.println("---------UserManagerImpl.modifyUser()---------------");
	}
}

         到现在前期的准备代码完成.那么如何实现加入安全性验证的功能呢?

 

最简单的方式

public voidaddUser(String username, String password) {
       checkSecurity();
       System.out.println("---------UserManagerImpl.addUser()---------------");
       //checkSecurity();
}

        只取其中一段代码,就是上面一样.直接修改Impl类的addUser,加入安全性验证代码,这种方式无疑是最简单的,但是却有很大的问题,首先违背了开放封闭原则,并且若方法少还行,但是方法一多,就存在大量的修改,而修改往往会存在修改错误,或者修改不全的问题,所以不可取.

         那么可取的方法是,至少不可以违背开放封闭原则.如何才能不违背开放封闭原则,加个代理是个不错的选择.


静态代理

   public class UserManagerImplProxy implements UserManager {	private UserManager userManager;
	
	public UserManagerImplProxy(UserManager userManager)
	{
		this.userManager = userManager;
	}
	
	public void addUser(String username, String password) {
		checkSecurity();
		userManager.addUser(username, password);
	}

           public void delUser(int userId) {
		checkSecurity();
		userManager.delUser(userId);
	}

	 //***改查类似
	
	private void checkSecurity()
	{
		System.out.println("---------UserManagerImpl.checkSecurity()---------------");
	}
}

         不修改原来的方法,而是加一个代理,在原来的方法上面包一层,加上安全验证的方法.这种是静态代理的方式,没有违背开放封闭,但是还是有着大量重复工作的问题,所以还是需要改进的.之所以称为静态代理,是因为你还是可以看到代理类的.


动态代理

public class SecurityHandler implements InvocationHandler {
    private Object targetObject;
    public Object createProxyInstance(Object targetObject)
    {
    	this.targetObject=targetObject;
    	 
    	return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
    			targetObject.getClass().getInterfaces(),
    			this);
    }
	
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		checkSecurity();
		//调用目标方法
		Object ret = method.invoke(targetObject,args);
		return ret;
	}

	private void checkSecurity()
	{
		System.out.println("---------UserManagerImpl.checkSecurity()---------------");
	}
}

          现在改成这样,改成动态代理,事先并不知道调用的会是哪个方法,但是不管你是哪个方法,都能给你转过去.

          写一个SecurityHandler,实现InvocationHandler接口.实现接口的invoke方法,参数是一个代理,一个方法,还有方法的参数数组.可以理出,代理,方法和参数数组对应的都是你需要调用的方法的类的代理,方法和参数,也就是目标对象代理和目标方法.invoke方法中,统一添加安全性验证方法,并且将调用方法转向调用目标方法.

           然后就是目标对象和目标对象的代理如何来?需要根据对应的目标对象来生成.声明一个属性targetObject目标对象,传入方式使用createProxyInstance(ObjecttargetObject)传入,并在该方法中将目标对象转为目标对象的代理对象返回.


区别

        采用动态代理和静态代理完成了这个功能.那么他们之间的区别是什么?

        首先静态代理的可理解性高,因为方法都是明明白白写好的,而动态代理,由于将所有的方法都汇聚到一个invoke()方法中,所以代码量大大减少,复用率高,并且容易维护和修改.不像静态代理,每个类都需要写一个代理类.

        而静态代理由于在编译期间就指明了调用关系,而不用像动态代理去反射,所以效率高于动态代理.但是动态代理由于采用反射机制,在运行时创建动态代理,灵活性高,耦合性低.

        然后,对于静态代理不需要引入任何其他的类,只需要和被代理的类,实现同一个接口.但是动态代理需要,java.lang.reflect 包中的Proxy类和InvocationHandler 接口提供了生成动态代理类的能力,Proxy类中的newProxyInstance()方法用来创建动态代理类的实例,而当调用动态代理类的实例的任意方法(addUser),则该方法(addUser)会调用与它关联的InvocationHandler对象的invoke()方法.

         所以,总的来说,对于不复杂追求效率的,可以用静态代理,但是对于复杂的,用动态代理就好多了.


实现

 public static voidmain(String[] args) {
    SecurityHandlerhandler = new SecurityHandler();
    UserManager userManager=(UserManager)handler.createProxyInstance(new UserManagerImpl());
    userManager.addUser("张三","123");
}

        将动态代理的实现在客户端使用,实例化一个SecurityHanlder对象,createProxyInstance,传入UserManagerImpl目标对象,返回UserManagerImpl的代理对象,强制转型为UserManager,使用该方法的addUser方法,就会调到invoke方法上,加上安全性验证的方法,并且去调用目标方法,真正的去添加用户.


总结

         这样就可以做到又不用违背开发封闭原则,又可以方便的应对修改.那么对于什么样的情况,可以采用这样的方式来做?

         对于那种到处都需要使用,但是又和方法本身没有什么关系的独立服务,可以将其提取出来作为一个方法,放到一个类中.这个类可以称为Aspect,这个方法可以称为advice.你可以选择将这个方法,放到目标方法执行前或者执行后调用,也可以选择目标方法出了异常再调用.那么目标方法是什么,比如只有增删改才需要安全性验证,查询就不用了,那么目标方法就是增删改的所有方法了,如何做到控制?使用一些表达式如:execution(* add*(..)),这叫做PointCut.这样就可以产生AOP.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值