aop详解

这几天研究了一下面向切面编程,它是OOP的延续。通过预编译和运行期的动态代理实现程序功能的统一维护。AOP可以对程序的各个部分进行分离,从而达到低耦合,一高程序的可重用性。

它主要的用处有,日志的记录,事务的处理,安全控制,异常处理。

实现原理其实就是将日志记录,事务处理这些功能从主导的业务逻辑中分离出来,从而在不改变业务逻辑的情况下添加一些额外的处理。在查找资料的时候,有介绍它是gof设计模式的延续,设计模式知道集中但是因为不太用所以没怎么明白,等抽时间看一下。

之前看的时候,一直觉得AOP不就是比较像把程序中的可重用的代码拿出来单独作为一个功能模块吗,那它和面向对象编程有什么区别?

OOP针对的是业务处理过程的实体类以及他们的属性和行为进行的封装,以获得更清晰的逻辑单元的划分。

AOP是针对业务逻辑处理过程中的切面进行的提取,它所面对的是逻辑处理过程中的某个步骤,以获得逻辑过程中的各部分之间的低耦合。

有人认为设计良好的OOP完全可以完成AOP的功能,但是对于 OOP 中的接口来说,它仍然需要我们在相应的模块中去调用该接口中相关的方法,这是 OOP 所无法避免的,并且一旦接口不得不进行修改的时候,所有事情会变得一团糟;AOP 则不会这样,你只需要修改相应的 Aspect,再重新编织(weave)即可。 当然,AOP 也绝对不会代替 OOP。核心的需求仍然会由 OOP 来加以实现,而 AOP 将会和 OOP 整合起来,以此之长,补彼之短。

举个百度上的例子来说,

假设在一个应用系统中,有一个共享的数据必须被并发同时访问,首先,将这个 数据封装数据对象中,称为Data Class,同时,将有多个访问类,专门用于在同一时刻访问这同一个数据对象。
为了完成上述并发访问同一资源的功能,需要引入锁Lock的概念,也就是说,某个时刻,当有一个访问类访问这个 数据对象时,这个数据对象必须上锁Locked,用完后就立即解锁unLocked,再供其它访问类访问。
使用传统的 编程习惯,我们会创建一个 抽象类,所有的访问类继承这个抽象父类,如下:
1
2
3
4
5
abstractclassWorker{
abstractvoidlocked();
abstractvoidaccessDataObject();
abstractvoidunlocked();
}
accessDataObject()方法需要有“锁”状态之类的相关代码。
Java只提供了单继承,因此具体访问类只能继承这个父类,如果具体访问类还要继承其它父类,比如另外一个如Worker的父类,将无法方便实现。
重用被打折扣,具体访问类因为也包含“锁”状态之类的相关代码,只能被重用在相关有“锁”的场合,重用范围很窄。
仔细研究这个应用的“锁”,它其实有下列特性:
“锁”功能不是具体访问类的首要或主要功能,访问类主要功能是访问数据对象,例如读取数据或更改动作。
“锁”
“锁”功能其实是这个系统的一个纵向切面,涉及许多类、许多类的方法。如右图:
因此,一个新的程序结构应该是关注系统的纵向切面,例如这个应用的“锁”功能,这个新的程序结构就是aspect(方面)
在这个应用中,“锁”方面(aspect)应该有以下职责:
提供一些必备的功能,对被访问对象实现加锁或解锁功能。以保证所有在修改 数据对象的操作之前能够调用lock()加锁,在它使用完成后,调用unlock()解锁。



上面这个例子应该很好的说明了AOP。

AOP的实现有两种方式:

(一)JDK提供的动态代理实现

这种方式是在绑定接口的情况下实现的,所以有一定局限性。被代理的对象必须实现接口。

1)首先需要一个接口

public interface Student {
	 public void sayHello();  
}

2)然后就是实现该接口的类:

public class StudentImpl implements Student{
	 @Override  
	    public void sayHello() {  
	        System.out.println("Student");  
	    }  
}

3)代理类(可以把多个方法卸载多个接口中,所以这边可以有接口)

public interface Aspect {

	 /** 
     * 事先执行 
     */  
    public void doBefore();  
      
    /** 
     * 事后执行 
     */  
    public void doAfter();  
}
public class StudentAspectOne implements Aspect{

	 @Override  
	    public void doAfter() {  
	        System.out.println("do After One");  
	          
	    }  
	  
	    @Override  
	    public void doBefore() {  
	        System.out.println("do Before One");  
	          
	    }  
	  
	}  

public class StudentAspectTwo implements Aspect{
	 @Override  
	    public void doAfter() {  
	        System.out.println("do After Two");  
	          
	    }  
	  
	    @Override  
	    public void doBefore() {  
	        System.out.println("do Before Two");  
	          
	    }  
}
因为这里是将切面处理的多个不同的对象放在一个数组中。这里的切面接口,实现该接口就可以再里面做自己想做的处理。
public class DynamicProxyFactory {

	 /** 
     * 私有构造方法 
     */  
    private DynamicProxyFactory() {}  
      
    /** 
     * 工厂方法 
     *  
     * @param instance 代理目标类实例对象 
     * @param aspect 切面对象 
     */  
    public static Object newInstance(Object instance, Aspect aspect) {  
          
        List<Aspect> aspectList = new ArrayList<Aspect>();  
        aspectList.add(aspect);  
          
        return newInstance(instance, aspectList);  
    }  
      
    /** 
     * 工厂方法 
     *  
     * @param instance 代理目标类实例对象 
     * @param aspectList 切面集合 
     */  
    public static Object newInstance(Object instance, List<Aspect> aspectList) {  
        SimpleInvocationHandler hander = new SimpleInvocationHandler();  
        hander.setAspectList(aspectList);  
        hander.setSource(instance);  
        return Proxy.newProxyInstance(instance.getClass().getClassLoader(),   
                                      instance.getClass().getInterfaces(),   
                                      hander);  //第一个参数是类加载器(加载class目录下面的文件,第二个参数是类的所有接口)
        //要绑定接口,这就是jdk代理的缺点,因为它只代理实现接口的类。cglib弥补了这个缺陷
    }  
}
</pre><pre name="code" class="html">代理类的实际操作需要实现InvocationHandler接口,在这个接口中有个委托方法,incoke()方法,这个方法是在实际调用被代理类的方法是触发的
</pre><pre name="code" class="html">public class SimpleInvocationHandler implements InvocationHandler{

	private Object source = null;  
    
    private List<Aspect> aspectList = null;  
      
      
    public Object getSource() {  
        return source;  
    }  
  
  
    public void setSource(Object source) {  
        this.source = source;  
    }  
  
  
    public List<Aspect> getAspectList() {  
        return aspectList;  
    }  
  
  
    public void setAspectList(List<Aspect> aspectList) {  
        this.aspectList = aspectList;  
    }  
  
    /** 
     * 委托方法 
     *  
     * @param proxy 被代理的对象 
     * @param method 代理方法(调用的方法) 
     * @param args 方法参数 
     */  
    public Object invoke(Object proxy, Method method, Object[] args)  
            throws Throwable {  
          
        for (Aspect aspect : aspectList) {  
            aspect.doBefore();  
        }  
          
        Object retObj = method.invoke(getSource(), args);  
          
        for (int index = aspectList.size() - 1; index >= 0; index--) {  
            aspectList.get(index).doAfter();  
        }  
          
        return retObj;  
    }  
}

5)测试类:

public class Test {

	 public static void main(String[] args) {  
         
		 
		 //jdk提供的动态代理实现
	        List<Aspect> aspectList = new ArrayList<Aspect>();  
	          
	        aspectList.add(new StudentAspectOne());  
	        aspectList.add(new StudentAspectTwo());  
	        //生成代理对象()
	        Student s = (Student)DynamicProxyFactory.newInstance(new StudentImpl(), aspectList);  
	        s.sayHello();  
	    }  
}

代理类和为拖累有同样的接口,它主要负责为委托类预处理消息,过滤消息,把消息传给委托类,以及事后处理等。 一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。 

按照代理的创建时期,代理类可以分为两种。 
静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。 
动态代理:在程序运行时,运用反射机制动态创建而成。


spring aop通过反射机制,读取配置文件中的代理机制,获取被代理类对象,并且设置连接点,最后返回的是代理类。

<?xml version="1.0" encoding="UTF-8"?>

<beans>
	<!-- <bean id="businessObj" class="org.springaop.proxy.BusinessObjImpl"
		aop="org.springaop.proxy.BeforeMethodAdvisor" aopType="before" /> -->
		<bean id="businessObj" class="org.springaop.proxy.BusinessObjImpl"
		aop="org.springaop.proxy.AfterMethodAdvisor" aopType="after" />
		
</beans>

	public void init(String xml) {
		try {
			// 读取指定的配置文件
			SAXReader reader = new SAXReader();
			ClassLoader classLoader = Thread.currentThread()
					.getContextClassLoader();
			InputStream ins = classLoader.getResourceAsStream(xml);
			Document doc = reader.read(ins);
			Element root = doc.getRootElement();
			Element foo;
			// 创建AOP处理器
			AopHandler aopHandler = new AopHandler();
			// 遍历bean
			for (Iterator i = root.elementIterator("bean"); i.hasNext();) {
				foo = (Element) i.next();
				// 获取bean的属性id、class、aop以及aopType
				Attribute id = foo.attribute("id");
				Attribute cls = foo.attribute("class");
				Attribute aop = foo.attribute("aop");
				Attribute aopType = foo.attribute("aopType");
				// 配置了aop和aopType属性时,需进行拦截操作
				if (aop != null && aopType != null) {
					// 根据aop字符串获取对应的类
					Class advisorCls = Class.forName(aop.getText());
					// 创建该类的对象
					Advisor advisor = (Advisor) advisorCls.newInstance();
					// 根据aopType的类型来设置前置或后置顾问
					if ("before".equals(aopType.getText())) {
						aopHandler.setBeforeAdvisor(advisor);
					} else if ("after".equals(aopType.getText())) {
						aopHandler.setAfterAdvisor(advisor);
					}
				}
				// 利用Java反射机制,通过class的名称获取Class对象
				Class bean = Class.forName(cls.getText());
				// 获取对应class的信息
				java.beans.BeanInfo info = java.beans.Introspector
						.getBeanInfo(bean);
				// 获取其属性描述
				java.beans.PropertyDescriptor[] pd = info
						.getPropertyDescriptors();
				// 设置值的方法
				Method mSet = null;
				// 创建一个对象
				Object obj = bean.newInstance();
				// 遍历该bean的property属性
				for (Iterator ite = foo.elementIterator("property"); ite
						.hasNext();) {
					Element foo2 = (Element) ite.next();
					// 获取该property的name属性
					Attribute name = foo2.attribute("name");
					String value = null;
					// 获取该property的子元素value的值
					for (Iterator ite1 = foo2.elementIterator("value"); ite1
							.hasNext();) {
						Element node = (Element) ite1.next();
						value = node.getText();
						break;
					}
					for (int k = 0; k < pd.length; k++) {
						if (pd[k].getName().equalsIgnoreCase(name.getText())) {
							mSet = pd[k].getWriteMethod();
							// 利用Java的反射机制调用对象的某个set方法,并将值设置进去
							mSet.invoke(obj, value);
						}
					}
				}
				// 为对象增加前置或后置顾问
				obj = (Object) aopHandler.setObject(obj);
				// 将对象放入beanMap中,其中key为id值,value为对象
				beanMap.put(id.getText(), obj);
			}
		} catch (Exception e) {
			System.out.println(e.toString());
		}
	}

/**
 * AOP处理器.
 * @author dinghui
 * @Creation date: 2008-9-16
 */
public class AopHandler implements InvocationHandler {
	// 需要代理的目标对象
	private Object target;

	// 方法前置顾问
	Advisor beforeAdvisor;

	// 方法后置顾问
	Advisor afterAdvisor;

	/**
	 * 设置代理目标对象,并生成动态代理对象.
	 * 
	 * @param target 代理目标对象
	 * @return 返回动态代理对象
	 */
	public Object setObject(Object target) {
		// 设置代理目标对象
		this.target = target;
		// 根据代理目标对象生成动态代理对象
		Object obj = Proxy.newProxyInstance(target.getClass().getClassLoader(),
				target.getClass().getInterfaces(), this);
		return obj;
	}

	/**
	 * 若定义了前置处理,则在方法执行前执行前置处理, 若定义了后置处理,则在方法调用后调用后置处理.
	 * 
	 * @param proxy
	 *            代理对象
	 * @param method
	 *            调用的业务方法
	 * @param args
	 *            方法的参数
	 * @return 返回结果信息
	 * @throws Throwable
	 */
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		// 进行业务方法的前置处理
		if (beforeAdvisor != null) {
			beforeAdvisor.doInAdvisor(proxy, method, args);
		}
		// 执行业务方法(反射机制)
		Object result = method.invoke(target, args);
		// 进行业务方法的后置处理
		if (afterAdvisor != null) {
			afterAdvisor.doInAdvisor(proxy, method, args);
		}
		// 返回结果对象
		return result;
	}

	/**
	 * 设置方法的前置顾问.
	 * 
	 * @param advisor
	 *            方法的前置顾问
	 */
	public void setBeforeAdvisor(Advisor advisor) {
		this.beforeAdvisor = advisor;
	}

	/**
	 * 设置方法的后置顾问.
	 * 
	 * @param advisor
	 *            方法的后置顾问
	 */
	public void setAfterAdvisor(Advisor advisor) {
		this.afterAdvisor = advisor;
	}
}






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值