Spring里有个概念叫面向切面编程(AOP),我们管切入到指定类指定方法的代码片段称为切面,而切入到哪些类、哪些方法则叫切入点。有了AOP,我们可以把几个类共有的代码,抽取到一个切片中,等到需要时再切入对象中去,从而改变其原有的行为。它的基础是动态代理,这里记录下动态代理的相关使用。
比如,当我们在注册用户时,想校验用户,而需要校验用户的地方又很多,可能登录也要,如果每个方法里都放置校验码,那样代码太冗余,抽取到一个方法里再调用又会增强偶合性,这时可以用到动态代理,在程序运行过程中产生的另外对象,替原主体代码完成校验工作,只需把校验部分的代码放在代理类即可。
创建动态代理类:
public class SecunityHandler implements InvocationHandler {//实现动态代理,把要做的额外的事放这里
private Object targetObject;//传入的要代理的类的实例
public Object createProxyInstance(Object targetObect) {//创建代理实例
this.targetObject=targetObect;
/*
* newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
* 需要对要代理的对象进行封装,返回一个具备代理方法的要代理对象的实例
* 参数1:装载目标代理类
* 参数2:代理是针对接口再生成一个对象,要代理的类必须具备抽象接口
* 参数3:实现动态代理的类,因为要调用代理方法,代理方法是在本类实现,即invoke()方法,要找到这个方法传入this即可
*/
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObect.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 代理对象会自动来执行invoke()方法
System.out.println("校验权限");
Object ret=method.invoke(targetObject, args);//校验完后调用被代理类自己的方法
return ret;
}
}
Service层的业务逻辑:
public interface UserManager {//被代理类要有抽象接口
public void addUser(String name,String password);
}
public class UserManagerImpl implements UserManager {
@Override
public void addUser(String name,String password) {
// TODO Auto-generated method stub
System.out.print("添加用户");
}
}
使用时通过动态代理进一步包装对象:
public static void main(String[] args) {
SecunityHandler handler=new SecunityHandler();
//进一步加工要代理对象,返回的新实例会在调用前先执行代理类的方法
UserManager userManager=(UserManager)handler.createProxyInstance(new UserManagerImpl());
userManager.addUser("张三", "123");//再执行此方法前会走invoke下的校验代码
}