对于静态代理和动态代理,了解AOP基于的JDK动态代理机制.
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()---------------");
}
}
3.若要在执行方法前进行安全性的验证(即下面这段代码),该如何做.
private void checkSecurity()
{
System.out.println("---------UserManagerImpl.checkSecurity()---------------");
}
4.是这样做?修改每个方法,将安全验证的方法,插入在方法执行前或者执行后?这样好吗,明显不好.修改方法,不仅违背了开放封闭原则,而且需求一旦修改,或浪费很多的时间来修改.
public void addUser(String username, String password) {
checkSecurity();
System.out.println("---------UserManagerImpl.addUser()---------------");
//checkSecurity();
}
5.那这样做好吗?不修改原来的方法,而是加一个代理,在原来的方法上面包一层,加上安全验证的方法.这种是静态代理的方式,没有违背开放封闭,但是还是有着大量重复工作的问题.所以还是需要改进的.之所以称为静态代理,是因为你还是可以看到代理类的.
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()---------------");
}
}
6.所以现在改成这样,可以吗?改成动态代理,事先并不知道调用的会是哪个方法,但是不管你是哪个方法,都能给你转过去.
写一个SecurityHandler类,实现InvocationHanlder接口.实现接口的invoke方法,参数是一个代理,一个方法,还有参数数组.可以理出,代理,方法和参数数组对应的都是你需要调用的方法的类的代理,方法和参数,也就是目标对象代理和目标方法.在invoke方法中,统一添加安全性验证方法,并且将调用方法转向调用目标方法.
然后就是目标对象和目标对象的代理如何来?需要根据对应的目标对象来生成.声明一个属性targetObject目标对象,传入方式使用createProxyInstance(ObjecttargetObject)传入,并在该方法中将目标对象转为目标对象的代理对象返回.
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()---------------");
}
}
7.代码写完了,如何使用呢?实例化一个SecurityHanlder对象,用createProxyInstance,传入UserManagerImpl目标对象,返回UserManagerImpl的代理对象,强制转型为UserManager,使用该方法的addUser方法,就会调到invoke方法上,加上安全性验证的方法,并且去调用目标方法,真正的去添加用户.
public static void main(String[] args) {
SecurityHandler handler = new SecurityHandler();
UserManager userManager =(UserManager)handler.createProxyInstance(new UserManagerImpl());
userManager.addUser("张三","123");
}
8.这样就可以做到又不用违背开发封闭原则,又可以方便的应对修改.那么对于什么样的情况,可以采用这样的方式来做?对于那种到处都需要使用,但是又和方法本身没有什么关系的独立服务,可以将其提取出来作为一个方法,放到一个类中.
这个类可以称为Aspect,这个方法可以称为advice.你可以选择将这个方法,放到目标方法执行前或者执行后调用,也可以选择目标方法出了异常再调用.那么目标方法是什么,比如只有增删改才需要安全性验证,查询就不用了,那么目标方法就是增删改的所有方法了,如何做到控制?使用一些表达式如:execution(* add*(..)),这叫做PointCut.这样就可以产生AOP了.