引言:AOP(面向切面的编程),如雷贯耳,听的多但是从来都没有接触过。我想作为一个程序员,这块内容是务必要学习和了解的,下面我们一起来简单的学习下AOP的使用。

 AOP简介:
     AOP(Aspect-Oriented Programming,面向切面的编程),它是可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。它是一种新的方法论,它是对传统OOP编程的一种补充。OOP是关注将需求功能划分为不同的并且相对独立,封装良好的类,并让它们有着属于自己的行为,依靠继承和多态等来定义彼此的关系;AOP是希望能够将通用需求功能从不相关的类当中分离出来,能够使得很多类共享一个行为,一旦发生变化,不必修改很多类,而只需要修改这个行为即可。

      简单的说就是aop可以把我们在某个方面的功能提出来与一批对象进行隔离,这样与一批对象之间降低了耦合性,同时可以为我们省掉大量的重复代码,让业务逻辑变的更加清晰。所谓面向切面我们可以这样形象的去理解,就好比“嫁接”这个东西,往主干上砍一刀,出现了一个口(切面),然后我们把想要加入的东西加进去就行了。
 

  下面我们来看看普通的OOP和AOP使用后不同的效果:

  大致功能是这样的,有两个方法add和mul,要求在调用方法的时候将参数和结果写入日志。

普通的OOP:

package impl;

import org.apache.log4j.Logger;
import service.StudentInfoService;

public class StudentInfoServiceImpl implements StudentInfoService {
	private static Logger log = Logger.getLogger(StudentInfoServiceImpl.class);

	@Override
	public int add(int i, int j) {
		try {
			argValidtor(i);
			argValidtor(j);
		} catch (Exception e) {
			e.printStackTrace();
		}
		log.debug("输入的两个参数分别是:" + i + "----" + j);
		int m = i + j;
		log.debug("最后的结果是:" + m);
		return m;
	}

	@Override
	public int mul(int i, int j) {
		try {
			argValidtor(i);
			argValidtor(j);
		} catch (Exception e) {
			e.printStackTrace();
		}
		log.debug("输入的两个参数分别是:" + i + "----" + j);
		int m = i * j;
		log.debug("最后的结果是:" + m);
		return m;
	}

	private void argValidtor(int arg) throws Exception {
		if (arg < 0)
			throw new Exception("参数不能为负数!");
	}
}

   上面的程序一个很直观的特点就是,好多重复的代码,并且当加入越来越多的非业务需求(例如日志记录和参数验证),原有的计算器方法变得膨胀冗长。这里有一件非常痛苦的事情,无法使用原有的编程方式将他们模块化,从核心业务中提取出来。例如日志记录和参数验证,AOP里将他们称为横切关注点(crosscutting concern),它们属于系统范围的需求通常需要跨越多个模块。
  在使用传统的面向对象的编程方式无法理想化的模块化横切关注点,程序员不能不做的就是将这些横切关注点放置在每一个模块里与核心逻辑交织在一起,这将会导致横切关注点在每一个模块里到处存在。使用非模块化的手段实现横切关注将会导致,代码混乱,代码分散,代码重复。你想想看如果日志记录需要换一种显示方式,那你要改多少代码,一旦漏掉一处(概率很高),将会导致日志记录不一致。这样的代码很维护。种种原因表明,模块只需要关注自己原本的功能需求,需要一种方式来将横切关注点冲模块中提取出来。

       AOP,它是一个概念,一个规范,本身并没有设定具体语言的实现,也正是这个特性让它变的非常流行,现在已经有许多开源的AOP实现框架了。本次不是介绍这些框架的,我们将不使用这些框架,而是使用底层编码的方式实现最基本的AOP解决上面例子出现的问题。AOP实际是GoF设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,AOP可以说也是这种目标的一种实现。AOP可以使用"代理模式"来实现。

下面是使用AOP的例子:

StudentInfoService接口:

package service;

public interface StudentInfoService {
	 int add(int i,int j);
	 int mul(int i,int j);
}

StudentInfoServiceImpl类实现StudentInfoService接口:

package impl;

import service.StudentInfoService;

public class StudentInfoServiceImpl implements StudentInfoService {

	@Override
	public int add(int i, int j) {
		int m = i + j;
		return m;
	}

	@Override
	public int mul(int i, int j) {
		int m = i * j;
		return m;
	}

}

MyHandler 继承InvocationHandler

package handle;

import org.apache.log4j.Logger;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.lang.reflect.Method;

public class MyHandler implements InvocationHandler {
	private Object proxyObj;
	private static Logger log = Logger.getLogger(MyHandler.class);
	public Object bind(Object obj) {
		this.proxyObj = obj;
		return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
				.getClass().getInterfaces(), this);

	}

	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		Object result = null;
		try {
			// 请在这里插入代码,在方法前调用
            log.debug("输入的两个参数分别是:"+args[0]+"----"+args[1]);
			log.info("调用log日志方法" + method.getName());
			 for(Object arg : args)//在这里做参数的验证
		        {
		            this.argValidtor((Integer) arg);
		        }
			result = method.invoke(proxyObj, args); // 原方法
			log.debug("最后的结果是:"+result);
			// 请在这里插入代码,方法后调用
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;

	}
	 private void argValidtor(int arg) throws Exception
	    {
	        if(arg < 0)
	            throw new Exception("参数不能为负数!");
	    }
}


AOPFactory类:

package factory;

import handle.MyHandler;

public class AOPFactory{

	private static Object getClassInstance(String clzName){

	Object obj=null;

	try{
	Class cls=Class.forName(clzName);
	obj=(Object)cls.newInstance();
	}catch(ClassNotFoundException cnfe){
	System.out.println("ClassNotFoundException:"+cnfe.getMessage());
	}catch(Exception e){
	e.printStackTrace();
	}
	return obj;
	}

	public static Object getAOPProxyedObject(String clzName){
	Object proxy=null;
	MyHandler handler=new MyHandler();
	Object obj=getClassInstance(clzName);
	if(obj!=null) {
	proxy=handler.bind(obj);
	}else{
	System.out.println("Can't get the proxyobj");
	//throw
	}
	return proxy;
	}
	}

测试类ClientTest:

package bean;

import service.StudentInfoService;
import factory.AOPFactory;

public class ClientTest{
	public static void main(String[] args){
		//调用方法的时候通过代理去调用
	StudentInfoService studentInfo= (StudentInfoService)AOPFactory.getAOPProxyedObject("impl.StudentInfoServiceImpl");
	studentInfo.add(1,2);
	studentInfo.mul(1,2);
	}

	}

后台打印:

2013-12-25 14:11:54,336 DEBUG [handle.MyHandler] [main] - <输入的两个参数分别是:1----2>
2013-12-25 14:11:54,336 INFO [handle.MyHandler] [main] - <调用log日志方法add>
2013-12-25 14:11:54,336 DEBUG [handle.MyHandler] [main] - <最后的结果是:3>
2013-12-25 14:11:54,336 DEBUG [handle.MyHandler] [main] - <输入的两个参数分别是:1----2>
2013-12-25 14:11:54,336 INFO [handle.MyHandler] [main] - <调用log日志方法mul>
2013-12-25 14:11:54,336 DEBUG [handle.MyHandler] [main] - <最后的结果是:2>

       比较前后两个StudentInfoServiceImpl类,发现使用AOP之后的代码会非常简洁,并且StudentInfoServiceImpl也不知道对参数进行了验证和日志的写入。如果以后需要对日志进行修改,那只需要改一处就可以了,对后期的维护也减轻了负担,整个业务逻辑部分看起来也清晰了很多。

    个人感觉AOP还是很好的一个东西,初次接触,学的也不深,还望各位多多指点,相互交流、相互学习!!!

DEMO下载地址http://download.csdn.net/detail/javaweiming/6859337



Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐