代理模式

给某个对象提供一个代理对象,并由代理对象控制对于原对象的访问,即客户不直接操控原对象,而是通过代理对象间接地操控原对象。

代理模式的本质是控制对象访问,可以在具体的目标对象前后,附加很多操作,从而进行功能扩展。

代理模式的的实现方式有两种:

  • 静态代理 代理类是在编译时就实现好的。也就是说Java编译完成后代理类是一个实际的class文件。

  • 动态代理 代理类是在运行时生成的。也就是说Java编译完之后并没有实际的class文件,而是在运行时动态生成的类字节码,并加载到JVM中。

TODO:代理模式的分类以及更深入的实现方式

静态代理代码示例 接口UserService和该接口的实现类UserServiceImpl

public interface UserService {

    User getUser(String userId);

    void updateUser(User user);

    void deleteUser(String userId);

    void addUser(User user);

}

public class UserServiceImpl implements UserService {
    @Override
    public User getUser(String userId) {

        System.out.println("---getUser-----");
        return null;
    }

    @Override
    public void updateUser(User user) {

        System.out.println("---updateUser-----");
    }

    @Override
    public void deleteUser(String userId) {

        System.out.println("---deleteUser-----");
    }

    @Override
    public void addUser(User user) {

        System.out.println("---addUser-----");
    }

}

现在,假如要对UserService中的所有方法进行安全检查(即增加一个方法,假设为checkSecurity()),则实现类UserServiceImpl的代码需要修该为这样:

public class UserServiceImpl implements UserService {
    @Override
    public User getUser(String userId) {
        checkSecurity();
        System.out.println("---getUser-----");
        return null;
    }

    @Override
    public void updateUser(User user) {
        checkSecurity();
        System.out.println("---updateUser-----");
    }

    @Override
    public void deleteUser(String userId) {
        checkSecurity();
        System.out.println("---deleteUser-----");
    }

    @Override
    public void addUser(User user) {
        checkSecurity();
        System.out.println("---addUser-----");
    }

    /**
     * 校验安全性的方法
     */
    private void checkSecurity(){
        System.out.println("----checkSecurity-----");
    }
}

假如以后还要对每个方法进行日志记录、缓存等,都需要对其中的每个方法添加相应的逻辑代码,给代码维护带来很大麻烦。

现在使用动态代理来解决该问题:为UserServiceImpl添加一个代理类UserServiceImplProxy;同时让这个代理类UserServiceImplProxy实现UserService的接口:

public class UserServiceImplProxy implements UserService {

    private UserService userService;

    //构造器
    public UserServiceImplProxy(UserService userService){
        this.userService = userService;
    }

    @Override
    public User getUser(String userId) {
        checkSecurity();
        return userService.getUser(userId);
    }

    @Override
    public void updateUser(User user) {
        checkSecurity();
        userService.updateUser(user);
    }

    @Override
    public void deleteUser(String userId) {
        checkSecurity();
        userService.deleteUser(userId);
    }

    @Override
    public void addUser(User user) {
        checkSecurity();
        userService.addUser(user);
    }

    /**
     * 校验安全性的方法
     */
    private void checkSecurity(){
        System.out.println("----checkSecurity-----");
    }
}

UserServiceImpl中还是原来的方法:

public class UserServiceImpl implements UserService {
    @Override
    public User getUser(String userId) {

        System.out.println("---getUser-----");
        return null;
    }

    @Override
    public void updateUser(User user) {

        System.out.println("---updateUser-----");
    }

    @Override
    public void deleteUser(String userId) {

        System.out.println("---deleteUser-----");
    }

    @Override
    public void addUser(User user) {

        System.out.println("---addUser-----");
    }

}

从上面的代码可以看出,使用了静态代理模式之后,与User紧密相关的增删改查方法都在UserServiceImpl中,类似检查安全或日志记录等非User相关操作都在代理类中实现。 当要实现User相关的操作时,不使用UserServiceImpl,而是通过代理UserServiceImpProxy进行:

public static void main(String[] args) {
        UserService userService = new UserServiceImplProxy(new UserServiceImpl());
        userService.deleteUser("user的id");
}

静态代理虽然让User相关操作在一个类中(UserServiceImpl),非User相关操作在另一个类中的(即一定程度提高代码内聚性),但需要为每个目标类(上面例子中的UserServiceImpl)写一个代理类。假如我们有很多目标类都要实现同样的安全检查这类的操作,就要创建多个代理类,并且写很多重复的代码,显然我们需要更简便的方式。

动态代理示例(使用JDK的方式) 还是以上面的静态代理为基础,继续看动态代理是怎么解决上述问题的(动态代理的实现有多种:JDK 自带的动态处理、CGLIBJavassist或者 ASM库,下文中演示的是JDK 自带的动态代理实现)。

首先,接口UserService和该接口的实现类UserServiceImpl与演示静态代理时一致:

public interface UserService {

    User getUser(String userId);

    void updateUser(User user);

    void deleteUser(String userId);

    void addUser(User user);

}

public class UserServiceImpl implements UserService {
    @Override
    public User getUser(String userId) {

        System.out.println("---getUser-----");
        return null;
    }

    @Override
    public void updateUser(User user) {

        System.out.println("---updateUser-----");
    }

    @Override
    public void deleteUser(String userId) {

        System.out.println("---deleteUser-----");
    }

    @Override
    public void addUser(User user) {

        System.out.println("---addUser-----");
    }

}

之后,创建一个代理类(UserServiceImpl)的调用处理器,起个名字叫:SecurityHandler,并让它实现InvocationHandler接口(该接口是JDK1.3之后自带的)

public class SecurityHandler  implements InvocationHandler {
    //使用一个通用的对象
    private Object targetObject;

    public Object createProxyInstance(Object object){
        this.targetObject = targetObject;
        //根据目标接口生成代理
        //第一个参数是代理对象的classloader,第二个是代理对象实现的接口,第三个参数可以理解为回调。
        return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
                targetObject.getClass().getInterfaces(),
                this);

    }

    @Override
    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("----checkSecurity-----");
    }
}

好了,现在就可以调用了,调用时与静态代理类似:

public static void main(String[] args) {
        SecurityHandler handler = new SecurityHandler();
        UserService userService = (UserService)handler.createProxyInstance(new UserServiceImpl());
        userService.deleteUser("user的id");
}

参考

理解Spring AOP

Last updated