【Spring专题-②】AOP浅析(一)

声明:这是转载的

来源地址:http://helloxuweifu.iteye.com/blog/688806


AOP的核心是代理模式



前言

{2017-02-17 补充}:

AOP(Aspect-Oriented Programming)其实是OOP(Object-Oriented Programing)思想的补充和完善。

AOP的体现

日志功能、事务管理、权限控制等。

其中事务管理在日常项目中完美体现了AOP特性。


AOP的个人理解:

他采用一种‘横切’的技术,将具有独立业务逻辑的类并且与具体(主体)业务无关的公共行为封装成独立的模块-称为切面。

更妙的是,他又巧妙的将切面无缝、不留痕迹的融入到主体业务逻辑中-织入


基本概念

join point(连接点):
是程序执行中的一个精确执行点,例如类中的一个方法。它是一个抽象的概念,在实现AOP时,并不需要去定义一个join point。

point cut(切入点):

本质上是一个捕获连接点的结构。在AOP中,可以定义一个point cut,来捕获相关方法的调用。


*注解 pointcut 声明?

advice(通知):

是point cut的执行代码,是执行“方面”的具体逻辑。

aspect(方面):

point cut和advice结合起来就是aspect,它类似于OOP中定义的一个类,但它代表的更多是对象间横向的关系。

*参考

http://jingyan.baidu.com/article/fd8044fafe35a35031137a3f.html


--------------------------------------------------------------------------------------------------------

应用:

AOP的主要应用就是体现在了对持久层事务的管理。


1 一个最简单的AOP实现例子

最通用的例子:为业务类加入日志,在不改变原码的情况下。


接口

public interface IHello {
	public void sayHello(String name);
}

实现类

public class Hello implements IHello {
	Logger log = Logger.getLogger(Hello.class);

	@Override
	public void sayHello(String name) {
		// TODO Auto-generated method stub
		DOMConfigurator.configure("log4j.xml");
		log.info("hello " + name);
	}

}

代理类

public class HelloProxy implements IHello{
	private IHello hello;
	public HelloProxy(IHello hello){
		this.hello = hello;
	}
	Logger log = Logger.getLogger(HelloProxy.class);
	
	public void sayHello(String name){
		DOMConfigurator.configure("log4j.xml");
		log.info("mehtod start------------->>");
		hello.sayHello(name);
		log.info("method end!");
	}
}


log4j.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
  <appender name="appender" class="org.apache.log4j.ConsoleAppender">
    <layout class="org.apache.log4j.PatternLayout">
      <param name="ConversionPattern" value="%d [%t] %p - %m%n"/>
    </layout>
  </appender>
  <root>
    <priority value ="debug"/>
    <appender-ref ref="appender"/>
  </root>
</log4j:configuration>


测试:

public class Test {
	public static void main(String[] args){
		IHello hello = new HelloProxy(new Hello());
		hello.sayHello("Hi, Justin...");
	}
}


2  改进 java.lang.reflect.InvocationHandler

我们会发现一个问题,如果我们像Hello这样的类很多,那么,我们是不是要去写很多个HelloProxy这样的类呢.没错,是的.其实也是一种很麻烦的事.在jdk1.3以后.jdk跟我们提供了一个API   java.lang.reflect.InvocationHandler的类. 这个类可以让我们在JVM调用某个类的方法时动态的为些方法做些什么事.让我们把以上的代码改一下来看看效果. 


写一个代理类.只不过.让这个类去实现java.lang.reflect.InvocationHandler接口,代码如下:

public class DynaProxyHello implements InvocationHandler {
	//要处理的对象 (要在前后加上业务逻辑的对象 如hello)
	private Object delegage;
	Logger log = Logger.getLogger(DynaProxyHello.class);
	public DynaProxyHello(){
		DOMConfigurator.configure("log4j.xml");
	}
	
	
	/*动态生成方法被处理过后的对象*/
	public Object bind(Object delegate) {
		this.delegage = delegate;
		return Proxy.newProxyInstance(
				this.delegage.getClass().getClassLoader(), this.delegage
						.getClass().getInterfaces(), this);
	}
	
	@Override
	/**
	 * 要处理的对象中的每个方法会被此方法送去JVM调用,也就是说,要处理的对象的方法只能通过此方法调用
	 * 此方法是动态的,不是手动调用的
	 */
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		// TODO Auto-generated method stub
		Object result = null;
		try{
			log.info("===> " + method.getName() + " start -->>>");
			//JVM通过这条语句执行原来的方法(反射机制)
			result = method.invoke(this.delegage, args);
			log.info("===> " + method.getName() + " end.");
		}catch(Exception e){
			e.printStackTrace();
		}
		return result;
	}

}

测试:

IHello hello = (IHello)new DynaProxyHello().bind(new Hello());
		hello.sayHello("Hi, Justin!");


3 继续改进:DynaPoxyHello对象和日志操作对象(Logger)解藕

1>抽象出一个日志接口

public interface IOperation {
	public void start(Method method);
	public void end(Method method);
}

2>日志接口实现类

public class LoggerOperation implements IOperation {
	Logger log = Logger.getLogger(LoggerOperation.class);
	public LoggerOperation(){
		DOMConfigurator.configure("log4j.xml");
	}
	
	@Override
	public void start(Method method) {
		// TODO Auto-generated method stub
		log.info("===> " + method.getName() + " start -->>>");
	}

	@Override
	public void end(Method method) {
		// TODO Auto-generated method stub
		log.info("===> " + method.getName() + " end.");
	}

}

3> 修改一下代理对象DynaProxyHello中的代码.如下:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class DynaProxyHello2 implements InvocationHandler {
	private Object proxy;          /*操作者*/
	private Object delegate;       /*要处理的对象 (要在前后加上业务逻辑的对象 如hello)*/
	
	public Object bind(Object delegate, Object proxy) {
		this.proxy = proxy;
		this.delegate = delegate;
		return Proxy.newProxyInstance(
				this.delegate.getClass().getClassLoader(), this.delegate
						.getClass().getInterfaces(), this);
	}
	
	@Override
	/**
	 * 要处理的对象中的每个方法会被此方法送去JVM调用,也就是说,要处理的对象的方法只能通过此方法调用
	 * 此方法是动态的,不是手动调用的
	 */
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		// TODO Auto-generated method stub
		Object result = null;
		try{
			//反射得到操作者的实例
			Class clazz = this.proxy.getClass();
			//反射得到操作者的start方法
			Method start = clazz.getDeclaredMethod("start", new Class[]{Method.class});
			//反射执行start方法
			start.invoke(this.proxy, new Object[]{method});
			//执行要处理对象原本方法
			result = method.invoke(this.delegate, args);
			//反射得到操作者的start方法
			Method end = clazz.getDeclaredMethod("end", new Class[]{Method.class});
			//反射执行end方法
			end.invoke(this.proxy, new Object[]{method});
			
		}catch(Exception e){
			e.printStackTrace();
		}
		return result;
	}

}

测试:

IHello hello = (IHello)new DynaProxyHello2().bind(new Hello(), new LoggerOperation());
		hello.sayHello("Hi, bruce.");

====================================================================================

二 AOP底层实现

类:org.springframework.aop.framework.JdkDynamicAopProxy


final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
... ...
/**
	 * Implementation of InvocationHandler.invoke.
	 * Callers will see exactly the exception thrown by the target, unless a hook
	 * method throws an exception.
	 */
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		MethodInvocation invocation = null;
		Object oldProxy = null;
		boolean setProxyContext = false;

		TargetSource targetSource = this.advised.targetSource;
		Class targetClass = null;
		Object target = null;

		try {
			// Try special rules for equals() method and implementation of the
			// Advised AOP configuration interface.

			if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
				// What if equals throws exception!?
				// This class implements the equals(Object) method itself.
				return equals(args[0]) ? Boolean.TRUE : Boolean.FALSE;
			}
			if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
				// This class implements the hashCode() method itself.
				return new Integer(hashCode());
			}
			if (Advised.class == method.getDeclaringClass()) {
				// service invocations on ProxyConfig with the proxy config
				return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
			}

			Object retVal = null;

			if (this.advised.exposeProxy) {
				// make invocation available if necessary
				oldProxy = AopContext.setCurrentProxy(proxy);
				setProxyContext = true;
			}

			// May be <code>null</code>. Get as late as possible to minimize the time we "own" the target,
			// in case it comes from a pool.
			target = targetSource.getTarget();
			if (target != null) {
				targetClass = target.getClass();
			}

			// get the interception chain for this method
			List chain = this.advised.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
					this.advised, proxy, method, targetClass);

			// Check whether we have any advice. If we don't, we can fallback on direct
			// reflective invocation of the target, and avoid creating a MethodInvocation.
			if (chain.isEmpty()) {
				// We can skip creating a MethodInvocation: just invoke the target directly
				// Note that the final invoker must be an InvokerInterceptor so we know it does
				// nothing but a reflective operation on the target, and no hot swapping or fancy proxying
				retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
			}
			else {
				// We need to create a method invocation...
				// invocation = advised.getMethodInvocationFactory().getMethodInvocation(
				//		 proxy, method, targetClass, target, args, chain, advised);

				invocation = new ReflectiveMethodInvocation(
						proxy, target, method, args, targetClass, chain);

				// proceed to the joinpoint through the interceptor chain
				retVal = invocation.proceed();
			}

			// massage return value if necessary
			if (retVal != null && retVal == target && method.getReturnType().isInstance(proxy)) {
				// Special case: it returned "this" and the return type of the method is type-compatible
				// Note that we can't help if the target sets
				// a reference to itself in another returned object.
				retVal = proxy;
			}
			return retVal;
		}
		finally {
			if (target != null && !targetSource.isStatic()) {
				// must have come from TargetSource
				targetSource.releaseTarget(target);
			}

			if (setProxyContext) {
				// restore old proxy
				AopContext.setCurrentProxy(oldProxy);
			}
		}
	}
... ...
}


=====================================================================================

三 AOP基本概念

Aop 是面向切面的编程。
Aspect 切面:不改变原有应用系统代码,为了解决现有问题而插入的新的业务逻辑,比如日志,权限,安全等
joinpoint  连接点:在Spring AOP中,一个连接点总是代表一个方法的执行。通过声明一个org.aspectj.lang.JoinPoint类型的参数可以使通知(Advice)的主体部分获得连接点信息

Join point: a point during the execution of a program, such as the execution of a method or the
handling of an exception. In Spring AOP, a join point always represents a method execution


Pointcut  切入点:jointpoint的表达式,表示拦截哪些方法。一个Pointcut对应多个Joinpoint。

Pointcut: a predicate that matches join points. Advice is associated with a pointcut expression and runs
at any join point matched by the pointcut (for example, the execution of a method with a certain name).
The concept of join points as matched by pointcut expressions is central to AOP, and Spring uses the
AspectJ pointcut expression language by default.


Advice 通知: 定义了切面中的实际逻辑(即实现),比如日志的写入的实际代码,或是安全检查的实际代码。这里理解为例子中的SayHello

Advice: action taken by an aspect at a particular join point. Different types of advice include "around,"
"before" and "after" advice. (Advice types are discussed below.) Many AOP frameworks, including
Spring, model an advice as an interceptor, maintaining a chain of interceptors around the join point


Interceptor 拦截器: 拦截器是用来实现对连接点进行拦截,从而在连接点前或后加入自定义的切面模块功能。

Target:一个Advice被应用的对象或目标对象

Target object: object being advised by one or more aspects. Also referred to as the advised object.
Since Spring AOP is implemented using runtime proxies, this object will always be a proxied object.


Proxy:将通知应用到目标对象后创建的对象,我理解为例子中的HelloProxy类对象
Weave:Advice被应用至对象之上的过程称之为织入(Weave)。

Weaving: linking aspects with other application types or objects to create an advised object. This can be
done at compile time (using the AspectJ compiler, for example), load time, or at runtime. Spring AOP,
like other pure Java AOP frameworks, performs weaving at runtime.

--------------------------------------------------------------------------------------

【参考&鸣谢】

http://www.iteye.com/topic/39912

初探spring aop内部实现


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值