【AOP】借助容器将服务与代理类分离

我们所希望的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的一个简单程序,还可以继续实现多个业务颗粒,多个服务,以及每个业务颗粒和服务中有多个类,每个类中有多个方法的例子.写代码和写文章是一样一样的,没有最好的代码只有更完善的代码.

时间: 2024-08-28 21:19:47

【AOP】借助容器将服务与代理类分离的相关文章

Spring AOP 实现原理(三) 使用 使用 CGLIB 生成代理类

CGLIB(Code Generation Library),简单来说,就是一个代码生成类库.它可以在运行时候动态是生成某个类的子类. 此处使用前面定义的 Chinese 类,现在改为直接使用 CGLIB 来生成代理,这个代理类同样可以实现 Spring AOP 代理所达到的效果. 下面先为 CGLIB 提供一个拦截器实现类: public class AroundAdvice implements MethodInterceptor { public Object intercept(Obje

AOP 代理类的创建

AOP 代理类的创建 入口:AnnotationAwareAspectJAutoProxyCreator#postProcessAfterInitialization 和 AnnotationAwareAspectJAutoProxyCreator#getEarlyBeanReference /** * 代理配置:保证所有的代理创建者都有一致的属性配置 */ public class ProxyConfig implements Serializable { /** use serialVers

Spring AOP(基于代理类的AOP实现)

#基于代理类的AOP实现:step1: 1 package com.sjl.factorybean; 2 /**切面类*/ 3 import org.aopalliance.intercept.MethodInterceptor; 4 import org.aopalliance.intercept.MethodInvocation; 5 6 public class MyAspect implements MethodInterceptor { 7 @Override 8 public Obj

带你手写基于 Spring 的可插拔式 RPC 框架(四)代理类的注入与服务启动

上一章节我们已经实现了从客户端往服务端发送数据并且通过反射方法调用服务端的实现类最后返回给客户端的底层协议. 这一章节我们来实现客户端代理类的注入. 承接上一章,我们实现了多个底层协议,procotol 有 netty,http,和 socket 三个实现类,每个实现类都有启动服务端和客户端发送数据两个方法. 问题 如何实现底层协议的选择那? 可以通过配置文件来选择协议. 单独的配置文件还是和 Spring 的配置文件结合起来那? 我们选择与 Spring 结合的配置文件,自定义一些属性的标签,

客户端使用自定义代理类访问WCF服务 z

通常在客户端访问WCF服务时,都需要添加服务引用,然后在客户端app.config或 web.config文件中产生WCF服务的客户端配置信息.若是每添加一个服务都是这样做,这样势必会将比较麻烦,能否简单在app.config或 web.config文件增加WCF服务地址,然后直接通过此地址访问WCF服务呢?可以,那就是通过自定义客户端代理类来实现.本文是通过继承 ClientBase<T>类实现的自定义客户端代理类,来实现同过简单在app.config或web.config文件增加wcf服务

WCF服务引用之后自动生成的泛型代理类名称太长的解决方案

问题:WCF服务引用之后会将原来的泛型类自动生成一个代理类,但是有时候名称太长怎么办? 解决方案: 1.方案一: 调用客户端同样也引用这个泛型类的类库. 2.方案二: 找到这个泛型类,然后在上面的[DataContract]中添加Name属性. 如下,{#}会被自动替换为Hash码.{0}和{1}会自动变成泛型值的名称. namespace DataContractSerializerDemos { DataContract(Name="BillOf{0}{1}{#}")] publi

EF容器---代理类对象

1 #region 修改--官方的修改是,先查询,然后修改 2 /// <summary> 3 /// 修改--官方的修改是,先查询,然后修改 4 /// </summary> 5 static void Modify() 6 { 7 //此时返回的是一个Student类的代理类(包装类)对象 8 Studnet stu = db.Studnets.Where(s => s.s_ID == 14).ToList().FirstOrDefault(); 9 10 //此时其实操

Spring Aop中,获取被代理类的工具

在实际应用中,顺着过去就是一个类被代理.反过来,可能需要逆向进行,拿到被代理的类,实际工作中碰到了,就拿出来分享下. 1 /** 2 * 获取被代理类的Object 3 * @author Monkey 4 */ 5 public Object getTarget(Object proxy) throws Exception { 6 7 if(!AopUtils.isAopProxy(proxy)) { 8 //不是代理对象 9 return proxy; 10 } 11 12 if(AopUt

基于代理类实现Spring AOP

目录 ProxyFactoryBean类介绍 基于JDK动态代理的Spring  AOP实现 基于CGLIB代理的Spring  AOP实现 Spring的通知类型 ProxyFactoryBean类 虽然直接使用代理就可以创建代理的实例,但需要自己写创建代理的方法,比如JDK动态代理: 1 ........ 2 //创建代理方法,参数是目标接口的实例 3 public Object createProxy(UserInterface user){ 4 this.user=user; //初始化