AOP介绍

发表时间:2017-07-11 17:08:49 浏览量( 58 ) 留言数( 0 )

学习目标:

1、了解Java的历史

2、为什么要学习Java语言

3、端正学习态度


学习过程:

一、什么是AOP编程

1、AOP思想

AOP是OOP的延续,是Aspect Oriented Programming的缩写,意思是面向切面编程。如何理解AOP,我们可以先看看在什么情况下使用AOP,比如:开发人员编写程序时,通常包含两种代码:1.和业务逻辑有关的代码,2.和业务逻辑关系不大的代码,例如日志、权限、异常处理、事务处理等。

当这两种代码都是写在一起时,不利于程序的维护。而AOP就是使这两种代码分离的思想。使用AOP,就不用在业务逻辑中实现与业务功能关系不大的代码,从而降低了两种代码的耦合性,达到易于维护和重用的目的。

AOP是一种思想,它和具体的实现技术无关。一般实现AOP可以使用代理模式,JDK1.3以后,Java提供了动态代理机制。通过动态代理机制,就可以很容易的实现AOP的思想。实际上,Spring的AOP就是建立在Java的代理机制之上。

AOP编程和OOP编程不是竞争关系,AOP不能替代OOP编程,OOP(Object Oriented Programming)对现代编程产生了深远的影响,已经非常成熟了,它能很好的解决软件系统中角色划分的问题。借助于面向对象的分析、设计和实现,开发人员可以将现实领域的实体转换成软件系统中的对象,从而很自然的完成从现实到软件的转换。但是OOP并不完美,也有其不足的地方。比如日志、权限、异常处理、事务处理等方面,当应用OOP将这些内容封装为对象的行为时,会产生大量的重复代码。

AOP编程可以把一个应用程序分为核心关注点和横切关注点,核心关注点和具体应用的功能相关,而横切关注点存在于整个系统范围内。OOP中组合的流向是从主关注点到横切关注点,而AOP组合流向是从横切关注点到主关注点,两者多关注的对象是不同的,所有两者能很好的共存,AOP是OOP的有益补充。

2、AOP的术语

AOP是从另外一个视角看待应用程序,和OOP一样有很多新的概念和看法,为了让大家能够更好的理解AOP思想,这里就简单介绍一下一些基本的概念。

(1)Aspect 切面

指在方法顺序执行的过程中,突然在其中加入一些业务操作(如日志,事务等),这样就类似在顺序的过程中添加了一个切面.切面是一个大的称呼,它可能还包括很多的小切点。一个关注点的模块化,这个关注点可能会横切多个对象。事务管理是J2EE应用中一个关于横切关注点的很好的例子。


(2)Joinpoint 连接点

在程序执行过程中某个特定的点,比如某方法调用的时候或者处理异常的时候。这个点可以是一个方法、一个属性、构造函数、类静态初始化块,甚至一条语句。在Spring AOP中,一个连接点总是表示一个方法的执行。

(3)Advice 通知

在切面(Aspect)的某个特定的连接点(Joinpoint)上执行的动作。Advice定义了切面中的实际逻辑(即实现),比如日志的写入的实际代码。换一种说法Advice是指在定义好的切入点处,所要执行的程序代码。

(4)Pointcut 切入点

切入点指一个或多个连接点(joinpoint),可以理解成连接点的集合.指明Advice在什么条件下才能被触发。Advice是通过Pointcut来连接和介入进你的JointPoint的。日志例子里面的doWork()就是切入点。表示要在这里加入Advice。

(5)Advisor (Pointcut和Advice的配置器)

包括Pointcut和Advice,是将Advice注入程序中Ponitcut位置的代码。

(6)Target Object

被一个或者多个切面(aspect)所通知(advise)的对象。也有人把它叫做 被通知(advised) 对象。 既然Spring AOP是通过运行时代理实现的,这个对象永远是一个 被代理(proxy)对象。

二、配置通知模式

下面我们先介绍一下使用通知模式的AOP的实现,比如有下面这样一个业务方法。

  public class LoginBizImpl implements LoginBiz{
	public Boolean login(String name, String password) {
		String str=null;
		//System.out.println(str.length());
		System.out.println(name+"登陆的核心逻辑");
		return true;	
	}
}

这个就是我们的核心关注点,那么先你该如何才能不修改原来的代码,但是可以在执行这个代码之前或者之后,又或者出现异常的时候执行其他的功能呢?比如需要记录日志等功能。这里我们可以分别配置通知模式,当然不是说一定要配置全部的通知,根据实际情况就可以了,这个是教学实例我们就把前置通知、返回后通知、抛出后通知和环绕通知都简单介绍一下。

(1)前置通知(Before advice):在切入点匹配的方法执行之前运行使用,

需要实现接口:implements MethodBeforeAdvice  

并实现方法:

public void before(Method arg0, Object[] arg1, Object arg2)

throws Throwable {System.out.println(arg1[0]+ " 开始工作..");}

(2)返回后通知(After returning advice):在切入点匹配的方法返回的时候执行(之后运行)。

需要实现接口:Implements AfterReturningAdvice  

并实现方法:

 public void afterReturning(Object arg0, Method arg1, Object[] arg2,Object arg3) throws Throwable {System.out.println(arg2[0]+ " 完成工作..");}

(3)抛出后通知(throwing advice): 在切入点匹配的方法执行时抛出异常的时候运行。

需要实现接口:Implements ThrowsAdvice

 并实现方法:

public void afterThrowing(Method method,Object[] args,Object target,Throwable subclass)

throws Throwable{

  System.out.println(args[0]+ " 工作中抛出异常....");}

(4)环绕通知(Interception Around Advice):环绕通知既在切入点匹配的方法执行的前后都运行。并且,它可以决定这个方法在什么时候执行,如何执行,甚至是否执行。在环绕通知中,除了可以自由添加需要的横切功能以外,还需要负责主动调用连接点(通过proceed)来执行激活连接点的程序。 请尽量使用最简单的满足你需求的通知。(比如如果前置通知也可以适用的情况下,就不要使用环绕通知)

需要实现接口:Implements MethodInterceptor

分别建立四个类实现上面几个通知。

1、前置通知

  public class BeforeAdvice implements MethodBeforeAdvice {

	public void before(Method method, Object[] arg1, Object arg2)
			throws Throwable {

		System.out.println(method.getName());
		System.out.println("权限判断");

		System.out.println( "尝试登陆了系统--日志记录" + new Date());

	}

}

2、返回后通知

  public class AfterAdviceMy implements AfterReturningAdvice{
                               
	public void afterReturning(Object returnobject, Method arg1, Object[] arg2,
			Object arg3) throws Throwable {
		System.out.println("后置通知"+returnobject);
		
	}

}

3、异常通知

  public class ThrowAdvice implements ThrowsAdvice{
	
	public void afterThrowing(Throwable throwable){
	
		System.out.println("异常通知");
	}

}

4、环绕通知

  public class MethodInter implements MethodInterceptor {

	public Object invoke(MethodInvocation methodInvocation) throws Throwable {

		System.out.println("环绕通知--前置");
		Object object = null;
		try {
			object = methodInvocation.proceed();// 执行核心方法
		} catch (Exception e) {
			System.out.println("环绕通知--异常");
		}
		System.out.println("环绕通知--后置");

		return object;
	}

}

然后就需要在spring容器中配置,先配置核心逻辑类

<!-- 核心业务逻辑 -->
	<bean name="loginBiz" class="com.biz.impl.LoginBizImpl"></bean>

把上面几个通知类也配置在spring容器中。

<!-- 前置通知 -->
	<bean name="beforeAdvice" class="com.advice.BeforeAdvice"></bean>

	<!-- 后置通知 -->
	<bean name="afterAdviceMy" class="com.advice.AfterAdviceMy"></bean>

	<!-- 异常通知 -->
	<bean name="throwAdvice" class="com.advice.ThrowAdvice"></bean>

	<!-- 环绕通知 -->
	<bean name="methodInter" class="com.advice.MethodInter"></bean>

在配置一个代理工程类

	<!-- 代理类 代理工厂 -->
	<bean name="loginBizProxySpring" class="org.springframework.aop.framework.ProxyFactoryBean">
		<!-- 要代理的类 -->
		<property name="target" ref="loginBiz"></property>
		<!--通知 -->
		<property name="interceptorNames">
			<list>
				<value>beforeAdvice</value>
				<value>afterAdviceMy</value>
				<value>throwAdvice</value>
				<value>methodInter</value>
			</list>
		</property>
	</bean>

写一个测试类,注意这个时候不能去原来的bean的业务了,得有代理类执行。

  public class RunSpring {
    public static void main(String[] args) {
        ApplicationContext context=new FileSystemXmlApplicationContext("src/loginContext.xml");
       //有代理类执行
        LoginBiz loginBiz=(LoginBiz)context.getBean("loginBizProxySpring"); 
        
        loginBiz.login("liubao", ""); 
        
    }
}