Spring对AOP的实现

AOP引入:

我们首先通过下面这一段代码看看传统编程存在的问题

package com.hnu.service;

public class SomeServiceImpl implements SomeService {

	@Override
	public void doFirst() {
		SystemService.doTx();  //系统级事务服务
		System.out.println("执行doFirst()方法");
		SystemService.doLog();  //系统级日志服务
	}

	@Override
	public void doSecond() {
		SystemService.doTx();
		System.out.println("执行doSecond()方法");
		SystemService.doLog();
	}

}

       可以看到,我们在主业务逻辑中穿插了系统级服务,这样会使得主业务逻辑过于复杂,编码时需要考虑的东西太多,不利于我们编码。

 

使用代理可以有效解决上面存在的问题,我们还是通过代码说明

代理通常有两种:

(1)若目标对象实现了若干接口,spring使用JDK的java.lang.reflect.Proxy类代理。 
           优点:因为有接口,所以使系统更加松耦合 
           缺点:为每一个目标类创建接口
(2)若目标对象没有实现任何接口,spring使用CGLIB库生成目标对象的子类。 
           优点:因为代理类与目标类是继承关系,所以不需要有接口的存在。 
           缺点:因为没有使用接口,所以系统的耦合性没有使用JDK的动态代理好。

本例中使用JDK的java.lang.reflect.Proxy类代理:

package com.hnu.service;

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

public class MyTest {

	public static void main(String[] args) {
		final SomeService target = new SomeServiceImpl();
		SomeService service = (SomeService)Proxy.newProxyInstance(
				      target.getClass().getClassLoader(),   //这里target.getClass()拿到的是SomeServiceImpl.Class对象
				      target.getClass().getInterfaces(), 
					  new InvocationHandler() {
									
						@Override
						public Object invoke(Object proxy, Method method, Object[] args)
								throws Throwable {
							SystemService.doTx();  //系统级事务服务
							Object result = method.invoke(target, args);
							SystemService.doLog();  //系统级日志服务
										
							return result;
						}
					  });
		
		service.doFirst();
		System.out.println("=======================");
		service.doSecond();
	}

}

 

AOP简介:

         AOP(Aspect Orient Programming),面向切面编程,是面向对象编程OOP的一种补充。面向对象编程是从静态角度考虑程序结构,而面向切面编程是从动态角度考虑程序运行过程。

        AOP是一种编程思想,而Spring实现了这种思想,Spring的 AOP编程底层就是采用动态代理模式实现的。采用了两种代理:JDK的Proxy动态代理与CGLIB的动态代理。

        Spring的AOP编程,就是将交叉业务逻辑封装层切面,利用AOP容器的功能将切面织入到主业务逻辑中。所谓交叉业务逻辑是指通用的、与主业务逻辑无关的代码,如安全检查、事务、日志等。

        若不使用AOP,则会出现代码纠缠,即交叉业务逻辑与主业务逻辑混合在一起。这样会使主业务逻辑变得混杂不清。

        例如,转账,在真正转账业务逻辑前后,需要权限控制,日志记录,加载事务,结束事务等交叉业务逻辑,而这些业务逻辑与主业务逻辑间并无直接关系。但它们的代码量所占比重能达到总代码量的一半甚至还多。它们的存在,不仅产生了大量的“冗余”代码,还大大干扰了主业务逻辑--转账。 

 

Spring的AOP编程术语:

(1)切面(Aspect)

      切面泛指交叉业务逻辑。上例中的事务,日志就可以理解为切面,实际就是对主业务逻辑的一种增强。常用的切面有通知顾问

(2)织入(Weaving)

       织入是指将切面代码插入到目标对象的过程。上例中InvocationHandler类中的invoke()方法完成的工作就可以称为织入。

(3)连接点(Join Point)

       连接点指可以被切面织入的方法。通常业务接口中的方法均为连接点。

(4)切入点(Pointcut)

       切入点指被切面真正织入的方法。假如在SomeServiceImpl中doFirst()被增强而doSecond()不被增强,则doFirst()为切入点,而doSecond()仅为连接点。

        注意:被标记为final的方法是不能作为连接点或切入点的,因为最终的是不能被修改的,也就是不能被增强的。

(5)目标对象(Target)

       目标对象指将要被增强的对象。即包含主业务逻辑的类的对象,如上例中的SomeServiceImpl类的对象如果被增强了,那么它就是目标对象。

(6)通知(Advice)

        通知是切面的一种实现,可以完成简单的织入功能。换个角度来说,通知定义了增强代码切入到目标代码的时间点,是在目标方法之前执行还是之后执行等。通知类型不同,切入时间不同。

        切入点定义切入的位置,通知定义切入的时间。

(7)顾问(Advisor)

        顾问是切面的另一种实现,能够将通知以更复杂的方式织入到目标对象中,是将通知包装为更复杂切面的装配器。

 

 

通知Advice:

搭建AOP编程环境:

导入AOP联盟和Spring关于AOP的两个jar包

AOP联盟的jar包是一套关于AOP编程的规范(类似于JDBC规范),是一组接口,而Spring的AOP jar包是对这套规范的实现。

 

1.前置通知

       定义前置通知,需要实现MethodBeforeAdvice接口。该接口中有一个方法before()(返回值为void),会在目标方法执行之前执行。

前置通知的特点:

(1)在目标方法之前先执行

(2)不改变目标方法的执行流程,前置通知不能阻止目标方法执行

(3)不改变目标方法的执行结果

 

2.后置通知

       定义后置通知,需要实现AfterReturningAdvice接口。该接口中有一个方法afterReturing()(返回值为void),会在目标方法执行之后执行。

后置通知的特点:

(1)在目标方法之后执行

(2)不改变目标方法的执行流程,前置通知不能阻止目标方法执行

(3)不改变目标方法的执行结果(虽然可以获取到目标方法的返回值,但因为afterReturing()的返回值为void,所以对目标方法的返回值的修改不能传递回去)

 

3.环绕通知

       定义环绕通知,需要实现MethodInterceptor接口。环绕通知,也叫方法拦截器,可以在目标方法调用之前及之后做处理,可以改变目标方法的返回值,也可以改变程序执行流程。

注意:org.aopalliance.intercept.MethodInterceptor才是需要的包。

 

4.异常通知

      定义异常通知,需要实现ThrowsAdvice接口。需要自己实现public void afterThrowing(Exception ex)方法。当目标方法抛出与指定类型的异常具有is-a关系的异常时,执行当前方法。

 

省略知识点:

为目标对象织入多个通知,只需要在配置ProxyFactoryBean时注入多个通知即可。

有接口默认使用Proxy,无接口自动切换CGLIB。如果有接口时想使用CGLIB只需要做一点配置即可。

 

 

顾问Advisor:

       通知是Spring提供的一种切面,但是其功能过于简单,只能将切面织入到目标类的所有目标方法中,无法完成将切面织入到指定目标的方法中

       顾问是Spring提供的另一种切面,它将通知进行了包装,其可以完成更为复杂的切面织入功能。

 PointcutAdvisor是顾问的一种,PointcutAdvisor接口有两个较为常用的实现类:

(1)NameMatchMathodPointcutAdvisor     名称匹配方法切入点顾问

(2)RegexpMethodPointcutAdvisor            正则表达式匹配法切入点顾问

 

使用顾问,只需要在applicationContext.xml里面稍作配置即可:

名称匹配方法切入点顾问:匹配的是简单方法名

正则表达式匹配法切入点顾问:匹配的是全限定性方法名

 

自动代理生成器:

ProxyFactoryBean类的功能太过简单,导致以下两个问题:

1.若有多个目标对象需要增强,就需要使用多次ProxyFactoryBean来创建多个代理对象,这会使得配置文件过于庞大。

2.用户真正想要调用的是目标对象,而真正可以调用的却是代理对象,这不符合正常逻辑。

 

使用自动代理生成器可以解决上面的问题,常用的自动代理生成器有两个:

1.默认Advisor自动代理生成器        DefaultAdvisorAutoProxyCreator

2.Bean名称自动代理生成器           BeanNameAutoProxyCreator

自动代理生成器底层是使用Bean后处理器对Bean进行了增强

 

默认Advisor自动代理生成器:只要一行配置就可以了

DefaultAdvisorAutoProxyCreator存在三个问题:

1.不能选择目标对象(默认作用于全部目标对象)

2.不能选择切面类型,即切面只能是Advisor而不能是Advice

3.不能指定Advisor,所以所有的Advisor都会被作为切面织入到目标方法

 

Bean名称自动代理生成器:可以解决上面三个问题:

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值