标签:
我们所希望的AOP是这样的:
业务单独开发,服务也单独开发.将希望被切入的业务颗粒扔到容器中,通过AOP这种思想(AOP的实现有多种)将服务切进去,换句话说,就是在AOP提供的切面类上配置服务与业务间的切入关系,然后将业务和服务都分别交给容器管理.
原来我们一直把它做成了这样:
这种实现确实也能够满足业务和服务单独开发,但是AOP不是作为工作的bean存在,切面类不可复用,不灵活.
为了让系统更灵活,我们首先要把写死在代理类(切面类)中的服务分离出去.
问题一:现在业务和服务已经彻底分离,如何让二者在需要的时候联合起来?
问题二:当有多个服务同时要切入业务系统,各个服务的方法不能写到代理类即切面类里,切面如何被复用?
我们来看一下不借助任何框架的简单实现.
业务:
public class CourseManager { public void addCourse(){ System.out.println("The course has been added successfully !"); } }
日志服务:
public class LogUtil { public void Info(){ System.out.println("Info: this is Infolog."); } }
封装服务的容器:
import java.lang.reflect.Method; import java.util.HashMap; /** * 日志,权限,工作流等类和方法的封装类 * @author ghy * version 3.0.0 , 2015年5月25日 上午10:57:31 */ public class ProxyMethods { private HashMap<String, Object> logBeans; private HashMap<String, String> logMethodBeans; public void writeLog(){ try { for(HashMap.Entry<String,Object> entry : logBeans.entrySet()){ String key =entry.getKey(); Object value =entry.getValue(); Method writeLogMethod=value.getClass().getMethod(logMethodBeans.get(key)); writeLogMethod.invoke(value); } } catch (Exception ex) { ex.printStackTrace(); } } public HashMap<String, Object> getLogBeans() { return logBeans; } public void setLogBeans(HashMap<String, Object> logBeans) { this.logBeans = logBeans; } public HashMap<String, String> getLogMethodBeans() { return logMethodBeans; } public void setLogMethodBeans(HashMap<String, String> logMethodBeans) { this.logMethodBeans = logMethodBeans; } }
容器中定义了两个hashmap,分别是类和方法的集合,简单的实现只有一个日志类,这个类中只有一个Info()方法.而且它们保存的key值相同,我们遍历集合,得到这个类里面的方法.
代理类:
import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; /** * 一个业务类,一个方法 * 代理类,即AOP的切面类,连接核心业务类和日志等功能类的方法,实现了拦截 * @author ghy * version 3.0.0 , 2015年5月25日 上午10:59:11 */ public class CGLibDynamicProxy implements MethodInterceptor{ //单例模式获得代理对象 public static CGLibDynamicProxy instance=new CGLibDynamicProxy(); private CGLibDynamicProxy(){ } public static CGLibDynamicProxy getInstance(){ return instance; } //持有对proxyMethod的引用 private ProxyMethods proxyMethods; //泛型方法,得到代理类 public <T> T getProxy(Class<T> cls){ return (T)Enhancer.create(cls, this); } //拦截核心业务的方法,在核心业务方法执行前添加写日志的方法 @Override public Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { proxyMethods.writeLog(); Object result=methodProxy.invokeSuper(target, args); return result; } public ProxyMethods getProxyMethods() { return proxyMethods; } public void setProxyMethods(ProxyMethods proxyMethods) { this.proxyMethods = proxyMethods; } }
实现原理是CGLib动态代理,代理类中持有对proxyMethod容器的引用,这里可以调用容器的方法,容器就是一个空壳子,在运行时具体执行的类和方法才被装载.
客户端:
public class Client { public static void main(String[] args){ //定义两个hashmap分别盛放类和方法 HashMap<String, Object> logBeans=new HashMap(); HashMap<String, String> logMethodBeans=new HashMap(); //将日志类添加到盛放类的hashmap,将写日志的方法添加到盛放方法的hashmap //两个hashmap内均有一个类和一个方法,key值相同 logBeans.put("LogUtil", new LogUtil()); logMethodBeans.put("LogUtil", "Info"); //实例化一个类和方法的封装类的对象,将盛放日志类和写日志的方法的两个hashmap放到对象中 ProxyMethods proxyMethods=new ProxyMethods(); proxyMethods.setLogBeans(logBeans); proxyMethods.setLogMethodBeans(logMethodBeans); //实例化代理对象 CGLibDynamicProxy cglib =CGLibDynamicProxy.getInstance(); //将对象和方法封装类的对象放到代理中 cglib.setProxyMethods(proxyMethods); //声明一个业务类对象,并获得它的代理 CourseManager courseManager=cglib.getProxy(CourseManager.class); //调用方法 courseManager.addCourse(); } }
客户端手动new了一个容器,将日志服务注册到容器中,实例化代理对象,把容器挂到代理对象上,然后获得添加课程业务类的代理,调用方法.
运行结果:
如果我们希望在添加课程方法的后面切入日志方法,没关系,只要把容器的方法调用写在添加课程方法的后面就行了.类比springAOP的before,after,around等,我们这里都可以实现.另外要说的是,这只是写活了AOP的一个简单程序,还可以继续实现多个业务颗粒,多个服务,以及每个业务颗粒和服务中有多个类,每个类中有多个方法的例子.写代码和写文章是一样一样的,没有最好的代码只有更完善的代码.
标签:
原文地址:http://blog.csdn.net/zhuanzhe117/article/details/46240715