Java AOP编程

来源:互联网 发布:淘宝店铺入驻规则 编辑:程序博客网 时间:2024/06/07 08:44
Java AOP编程
     AOP是Aspect Oriented Programming的缩写,意思是面向方面编程,与OOP(Object Oriented Programming)面向对象编程对等,都是一种编程思想。从OOP角度分析,我们关注业务的处理逻辑,是属于纵向的行为,从AOP角度分析,我们关注的是对象行为发生时的问题,是横向行为。
     AOP一般用于如下两个方面:
l   监控函数(方法)的调用
l   捕获异常的发生
在实际应用中主要为:事务、安全、日志等横切关注点。
例如:
     有这样一个商业逻辑:要求实现针对用户User对象的所有操作之前和之后都打印一句话!
用户实体类:
package com.mirror.mode.aop;
 
public class User {
    private Long id;
    private String userName;
    private String address;
    private Double salary;
    public User() {
    }
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    public Double getSalary() {
        return salary;
    }
    public void setSalary(Double salary) {
        this.salary = salary;
    }
}
用户实体服务层接口:
package com.mirror.mode.aop;
 
public interface IUserService {
    public void saveUser(User user);
    public void deleteUser(Long id);
    public User findUser(Long id);
    public void updateUser(User user);
}
用户实体服务层实现类:
package com.mirror.mode.aop;
 
public class UserServiceImpl implements IUserService {
    @Override
    public void saveUser(User user) {
        System.out.println("saveUser method invoked!");
    }
    @Override
    public void deleteUser(Long id) {
        System.out.println("deleteUser method invoked!");
    }
    @Override
    public User findUser(Long id) {
        System.out.println("findUser method invoked!");
        return new User(id, "Louis");
    }
    @Override
    public void updateUser(User user) {
        System.out.println("updateUser method invoked!");
    }
}
用户实体服务层实现代理类:
package com.mirror.mode.aop;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
 
public class UserServiceImplProxy implements InvocationHandler {
    private Object userService;
    public UserServiceImplProxy() {
    }
    public UserServiceImplProxy(Object userService) {
        this.userService = userService;
    }
    public static Object factory(Object targetObject) {
        Class cls = targetObject.getClass();
        return Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), new UserServiceImplProxy(targetObject));
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        this.preInvoke();
        if (null == userService) {
            userService = new UserServiceImpl();
        }
        Object o = method.invoke(userService, args);
        this.postInvoke();
        return o;
    }
    private void preInvoke() {
        System.out.println("This is preInvoke method...");
    }
    private void postInvoke() {
        System.out.println("This is postInvoke method...");
    }
}
进行如下测试:
package com.mirror.mode.aop;
 
public class AOPTestCase {
    public static void main(String[] args) {
        IUserService userService = new UserServiceImpl();
        userService.saveUser(new User());
        System.out.println("------------------------");
        userService = (IUserService) UserServiceImplProxy.factory(userService);
        userService.saveUser(new User());
    }
}
测试结果为:
saveUser method invoked!
------------------------
This is preInvoke method...
saveUser method invoked!
This is postInvoke method...
     可以看出,在没有使用服务层实现代理类UserServiceImp之前进行的用户保存操作saveUser(),指输出了接口保存方法的核心业务逻辑,而使用服务层实现代理类UserServiceImplProxy后,按照要求,对所调用的方法前输出了一句话,调用方法后输出了一句话,实现了“对用户User对象的所有操作之前和之后都打印一句话”的系统设计要求。
     现在问题又来了,软件设计与需求不可分,需求是随用户而不断变化的,但好的设计可以使程序主体架构稳定(高内聚),修改少量的代码即可完成需求变更(低耦合)。现在用户要求在原来的基础上,增加对用户姓名进行更新时需要记录用户原来的姓名和修改后的姓名到操作日志中,这又该如何设计?
     从上面的程序设计可以得出:目前我们能实现针对用户操作的所有的方法调用前后输出一句话,如何在保证不改变原来功能的基础上,只对用户的updateUser()方法进行修改呢?
日志操作接口:
package com.mirror.mode.aop;
 
public interface IOptLogService {
    public void logOpt(String oldName, String newName);
}
日志操作实现类:
package com.mirror.mode.aop;
 
public class OptLogServiceImpl implements IOptLogService {
    @Override
    public void logOpt(String oldName, String newName) {
        System.out.println("this is logOpt method, oldName:" + oldName + ",newName:" + newName);
    }
}
修改我们的服务层代理类UserServiceImplProxy:
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        this.preInvoke();
        if (null == userService) {
            userService = new UserServiceImpl();
        }
        if (method.getName().equals("updateUser")) {
            User newUser = (User) args[0];
            User oldUser = ((UserServiceImpl)userService).findUser(newUser.getId());
            IOptLogService optLogService = new OptLogServiceImpl();
            optLogService.logOpt(oldUser.getUserName(), newUser.getUserName());
        }
        Object o = method.invoke(userService, args);
        this.postInvoke();
        return o;
}
测试:
package com.mirror.mode.aop;
 
public class AOPTestCase {
    public static void main(String[] args) {
        IUserService userService = new UserServiceImpl();
        userService.saveUser(new User());
        System.out.println("------------------------");
        userService = (IUserService) UserServiceImplProxy.factory(userService);
        userService.saveUser(new User());
        System.out.println("------------------------");
        userService.updateUser(new User(10L, "Kans"));
    }
}
测试输出结果:
saveUser method invoked!
------------------------
This is preInvoke method...
saveUser method invoked!
This is postInvoke method...
------------------------
This is preInvoke method...
findUser method invoked!
this is logOpt method, oldName:Louis,newName:Kans
updateUser method invoked!
This is postInvoke method...
     由此可以看出,该需求变更实现的逻辑实际上是通过在服务层接口实现代理类中对所调用的方法名称进行了一个判断,若为updateUser方法,则进行日志记录。
     过了一段时间,客户的需求又变了,客户要求系统保留原来对用户进行的所有操作前后打印一句话的需求,但去掉更新用户updateUser操作时的日志记录,改变为记录对用户进行的保存、删除、更新操作时的时间和操作人名称。这又该如何设计?很多人可能认为,直接在上面的invoke方法中对Method方法名称进行判断即可,这不外乎是一种实现,但有没有更好的或者更OO的一种设计思想呢?没错,这里我们可以使用java中的注解,即对指定的方法进行相应注解的配置,拥有该注解的方法,我们就进行日志记录:记录当前操作时间、操作人名称。
声明一个方法注解:
package com.mirror.mode.aop;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
 
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface OptLog {
}
这里有一个问题:究竟是在接口方法上添加如上注解类还是在实现类的方法中增加注解?这里推荐在接口方法上增加注解,因为我们一般采用Java的面向接口编程,在服务层的实现代理类中,我们利用静态工厂方法创建的服务层实现类实际上是服务层实现类UserServiceImpl,但转型成了接口IUserService,这就是面向接口编程的设计思想,由此,修改我们的IUserService接口:
package com.mirror.mode.aop;
 
public interface IUserService {
    @OptLog
    public void saveUser(User user);
    @OptLog
    public void deleteUser(Long id);
    public User findUser(Long id);
    @OptLog
    public void updateUser(User user);
}
修改我们的IOptLogService:
package com.mirror.mode.aop;
 
public interface IOptLogService {
    public void logOpt(String oldName, String newName);
    public void logOpt();
}
修改我们的OptLogServiceImpl:
package com.mirror.mode.aop;
import java.text.SimpleDateFormat;
import java.util.Date;
 
public class OptLogServiceImpl implements IOptLogService {
    @Override
    public void logOpt(String oldName, String newName) {
        System.out.println("this is logOpt method, oldName:" + oldName + ",newName:" + newName);
    }
    @Override
    public void logOpt() {
        System.out.println("this is logOpt method, optTime:" +
                new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) +
                ", optUserName:" + "Kans");
    }
}
修改我们的UserServiceImplProxy:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    this.preInvoke();
    if (null == userService) {
        userService = new UserServiceImpl();
    }
    if (method.isAnnotationPresent(OptLog.class)) {
        IOptLogService optLogService = new OptLogServiceImpl();
        optLogService.logOpt();
    }
    Object o = method.invoke(userService, args);
    this.postInvoke();
    return o;
}
修改测试类:
package com.mirror.mode.aop;
 
public class AOPTestCase {
    public static void main(String[] args) {
        IUserService userService = new UserServiceImpl();
        userService.saveUser(new User());
        System.out.println("------------------------");
        userService = (IUserService) UserServiceImplProxy.factory(userService);
        userService.saveUser(new User());
        System.out.println("------------------------");
        userService.updateUser(new User(10L, "Kans"));
        System.out.println("------------------------");
        userService.deleteUser(10L);
    }
}
测试结果为:
saveUser method invoked!
------------------------
This is preInvoke method...
this is logOpt method, optTime:2014-12-31 15:48:31, optUserName:Kans
saveUser method invoked!
This is postInvoke method...
------------------------
This is preInvoke method...
this is logOpt method, optTime:2014-12-31 15:48:31, optUserName:Kans
updateUser method invoked!
This is postInvoke method...
------------------------
This is preInvoke method...
this is logOpt method, optTime:2014-12-31 15:48:31, optUserName:Kans
deleteUser method invoked!
This is postInvoke method...
原创粉丝点击