复习_SpringAOP编程

SpringAOP编程的作用是:对spring管理的所有类,在不改变类代码的情况下,对类中的方法进行增强

1.什么是AOP

AOP (Aspect Oriented Programing) 称为:面向切面编程,它是一种编程思想。

AOP思想: 基于代理思想,对原来目标对象,创建代理对象,在不修改原对象代码情况下,通过代理对象,调用增强功能的代码

AOP应用场景:

  • 场景一: 记录日志
  • 场景二: 监控方法运行时间 (监控性能)
  • 场景三: 权限控制
  • 场景四: 缓存优化 (第一次调用查询数据库,将查询结果放入内存对象, 第二次调用, 直接从内存对象返回,不需要查询数据库 )
  • 场景五: 事务管理 (调用方法前开启事务, 调用方法后提交或者回滚、关闭事务 )

2.Spring AOP编程两种方式

  • Spring 1.2 开始支持AOP编程 (传统SpringAOP 编程),编程非常复杂 ---- 更好学习Spring 内置传统AOP代码
  • Spring 2.0 之后支持第三方 AOP框架(AspectJ ),实现另一种 AOP编程 – 推荐

3.AOP编程相关术语

  • Aspect(切面)
    是通知和切入点的结合,通知和切入点共同定义了关于切面的全部内容—它的功能、在何时和何地完成其功能
  • joinpoint(连接点)
    所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点.
  • Pointcut(切入点)
    所谓切入点是指我们要对哪些joinpoint进行拦截的定义.
    通知定义了切面的”什么”和”何时”,切入点就定义了”何地”.
  • Advice(通知)
    所谓通知是指拦截到joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)
  • Target(目标对象)
    代理的目标对象
  • 织入
    是指把切面应用到目标对象来创建新的代理对象的过程.切面在指定的连接点织入到目标对象
  • Introduction(引入)
    在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field.

4.Spring AOP方式

参考spring第三天

  • 传统的AOP编程
  • xml方式
  • 注解方式
4.1 传统的AOP编程(了解即可)

面向切面编程开发步骤(动态织入)

  • 1、确定目标对象(target—>bean)
  • 2、编写Advice通知方法 (增强代码)
  • 3、配置切入点和切面

传统SpringAOP的Advice 必须实现对应的接口!

Spring按照通知Advice在目标类方法的连接点位置,可以分为5类

  • 1.前置通知 org.springframework.aop.MethodBeforeAdvice
    在目标方法执行前实施增强
  • 2.后置通知 org.springframework.aop.AfterReturningAdvice
    在目标方法执行后实施增强
  • 3.环绕通知 org.aopalliance.intercept.MethodInterceptor
    在目标方法执行前后实施增强
  • 4.异常抛出通知 org.springframework.aop.ThrowsAdvice
    在方法抛出异常后实施增强
  • 5.引介通知 org.springframework.aop.IntroductionInterceptor
    在目标类中添加一些新的方法和属性

步骤1.编写传统AOP的通知类(切面要做的事情)
这里使用环绕通知

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.log4j.Logger;
//传统的aop的advice通知,增强类,必须实现org.aopalliance.intercept.MethodInterceptor接口(注意和cglib代理接口区分开)
public class TimeLogInterceptor implements MethodInterceptor {
	//log4j记录器
	private static Logger LOG=Logger.getLogger(TimeLogInterceptor.class);

	//回调方法
	//参数:目标方法回调函数的包装类,获取调用方法的相关属性、方法名、调用该方法的对象(即封装方法、目标对象,方法的参数)
	public Object invoke(MethodInvocation methodInvocation) throws Throwable {
		//业务:记录目标的方法的运行时间
		//方法调用之前记录时间
		long beginTime = System.currentTimeMillis();
		
		//目标对象原来的方法的调用,返回目标对象方法的返回值。
		Object object = methodInvocation.proceed();//类似于invoke
		
		//方法调用之后记录时间
		long endTime = System.currentTimeMillis();
		
		//计算运行时间
		long runTime=endTime-beginTime;
		
		//写日志:
		/**
		 * 1:记录日志到数据库(优势:便于查询;劣势:要占用数据库空间,日志一般都非常庞大)
		 * 2:记录日志到log4j(优势:文件存储,可以记录非常大的日志数据,而且还有日志级别的特点;劣势:不便于查询)
		 */
		LOG.info("方法名为:"+methodInvocation.getMethod().getName()+"的运行时间为:"+runTime+"毫秒");
		
		return object;
	}

}

要增强的target对象,对于spring来说,目标:就是bean对象

public class CustomerServiceImpl implements ICustomerService {
    @Override
    public void save() {
        System.out.println("客户保存了。。。。。");
    }

    @Override
    public int find() {
        System.out.println("客户查询数量了。。。。。");
        return 100;
    }
}

public class ProductService {
    public void save() {
        System.out.println("商品保存了。。。。。");

    }

    public int find() {
        System.out.println("商品查询数量了。。。。。");
        return 99;
    }
}

使用SpringTest进行测试

//springjunit集成测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:applicationContext.xml")
public class SpringTest {
	//注入要测试bean
	@Autowired
	private ICustomerService customerService;
	@Autowired
	private ProductService productService;
	
	//测试
	@Test
	public void test(){
		//基于接口
		customerService.save();
		customerService.find();
		//基于类的
		productService.save();
		productService.find();
	}

}

在这里插入图片描述
但是发现,此时并没有执行TimeLogInterceptor 类的invoke()方法,也就是说,并没有计算执行Service类的时间,那怎么办呢?我们往下看,需要在spring容器中配置spring的aop。

这是因为我们并没有配置切入点和切面
我们只编写了通知类,知道了要对目标对象进行如何增强,但通知类和目标对象还需要通过xml建立关系

步骤2.配置切入点和切面
目的:让哪个类(切面)、哪个方法(切入点),进行怎样的增强(通知)。

<!-- 基于接口类 -->
    <bean id="customerService" class="cn.itcast.advice.CustomerServiceImpl"/>
    <!-- 基于一般类(没有实现接口的类) -->
    <bean id="productService" class="cn.itcast.advice.ProductService"/>

    <!--
        2.配置增强:原则bean能增强bean
		Advice:通知,增强
	-->
    <bean id="timeLogAdvice" class="cn.itcast.advice.TimeLogInterceptor"/>

<!-- 配置切入点和切面 :aop:config-->
<aop:config>
	<!-- 
		配置切入点:即你要拦截的哪些 连接点(方法)
			* expression:表达式:匹配方法的,语法:使用aspectj的语法,相对简单
				* 表达式:bean(bean的名字),你要对哪些bean中的所有方法增强
				* bean(customerService):要对customerservice的bean中的所有方法进行增强
	 -->
	<!-- 
		<aop:pointcut expression="bean(customerService)" id="myPointcut"/> 
			* expression=bean(*Service):在spring容器中,所有以Service单词结尾的bean的都能被拦截
			* id="myPointcut":为切入点定义唯一标识
	-->
	<!-- 
		<aop:advisor advice-ref="timeLogAdvice" pointcut-ref="myPointcut"/>
			* 配置切面:通知(增强的方法)关联切入点(目标对象调用的方法)
			* 告诉:你要对哪些方法(pointcut),进行怎强的增强 (advice)
	 -->
	<aop:pointcut expression="bean(*Service)" id="myPointcut"/>
	<aop:advisor advice-ref="timeLogAdvice" pointcut-ref="myPointcut"/>
</aop:config>

切入点表达式的语法整理如下(aspectj的语法):

  • bean(bean Id/bean name)
    例如 bean(customerService) 增强spring容器中定义id属性/name属性为customerService的bean中所有方法
  • execution(<访问修饰符>?<返回类型>空格<方法名>(<参数>)<异常>?)
    例如:
    execution(* cn.itcast.spring.a_jdkproxy.CustomerServiceImpl.(…)) 增强bean对象所有方法
    execution(
    cn.itcast.spring….(…)) 增强spring包和子包所有bean所有方法
    提示:最灵活的

4.2 AspectJ 切面编程(xml方式)

普通的pojo即可。(不需要实现接口)
AspectJ提供不同的通知类型:

  • Before 前置通知,相当于BeforeAdvice
  • AfterReturning 后置通知,相当于AfterReturningAdvice
  • Around 环绕通知,相当于MethodInterceptor
  • AfterThrowing抛出通知,相当于ThrowAdvice
  • After 最终final通知,不管是否异常,该通知都会执行
  • DeclareParents 引介通知,相当于IntroductionInterceptor (不要求掌握)

第一步:确定目标对象,即确定bean对象:

 <!-- 1.确定了要增强的target对象 -->
<!-- 对于spring来说,目标对象:就是bean对象 -->
<!-- 基于接口类 -->
<bean id="customerService" class="cn.itcast.spring.a_proxy.CustomerServiceImpl"/>
<!-- 基于一般类 -->
<bean id="productService" class="cn.itcast.spring.a_proxy.ProductService"/>

第二步:编写Before 前置通知Advice增强 :
不用实现接口

//aspectj的advice通知增强类,无需实现任何接口
public class MyAspect {
	
	//前置通知
	//普通的方法。方法名随便,但也不能太随便,一会要配置
	public void firstbefore(){
		System.out.println("------------第一个个前置通知执行了。。。");
	}
}

第三步:配置切入点和切面(让切入点关联通知) :

<!-- 2.配置advice通知增强 -->
<bean id="myAspectAdvice" class="cn.itcast.spring.c_aspectjaop.MyAspect"/>

<!-- 3:配置aop -->
<aop:config>
	<!-- 切入点:拦截哪些bean的方法 -->
	<aop:pointcut expression="bean(*Service)" id="myPointcut"/>
	<!--
		切面:要对哪些方法进行怎样的增强  
		aop:aspect:aspejctj的方式!
		ref:配置通知
	-->
	<aop:aspect ref="myAspectAdvice">
		
		<!-- 第一个前置通知 :在访问目标对象方法之前,先执行通知的方法
			method:advice类中的方法名,
               pointcut-ref="myPointcut":注入切入点
               目的是让通知关联切入点
		-->
	    <aop:before method="firstbefore" pointcut-ref="myPointcut"/>
	</aop:aspect>
</aop:config>

和传统的aop配置相比,更灵活,advice不需要实现接口,简单的pojo就可以了;一个通知可以增强多个连接点,一个连接点可以被多次增强。
和传统aop的对比发现,aspectj更灵活,一个类中可以写N个增强方法,但传统的只能是一个类对应一个方法。


4.3 @Aspectj注解配置切面编程

1.创建目标对象

//实现类
/**
 * @Service("customerService")
 * 相当于spring容器中定义:
 * <bean id="customerService" class="cn.itcast.spring.a_aspectj.CustomerServiceImpl">
 */
@Service("customerService")
public class CustomerServiceImpl implements CustomerService{

	public void save() {
		System.out.println("客户保存了。。。。。");
		
	}

	public int find() {
		System.out.println("客户查询数量了。。。。。");
		return 100;
	}

}

//没有接口的类
/**
 * @Service("productService")
 * 相当于spring容器中定义:
 * <bean id="productService" class="cn.itcast.spring.a_aspectj.ProductService">
 */
@Service("productService")
public class ProductService {
	public void save() {
		System.out.println("商品保存了。。。。。");
		
	}

	public int find() {
		System.out.println("商品查询数量了。。。。。");
		return 99;
	}
}

使用bean注解的扫描(自动开启注解功能)

<!-- 1。确定目标 -->
<!-- 扫描bean组件 -->
<context:component-scan base-package="cn.itcast.spring"/>

2.编写通知,配置切面
编写通知类,在通知类 添加@Aspect 注解,代表这是一个切面类,并将切面类交给spring管理(能被spring扫描到@Component)
@Component(“myAspect”):将增强的类交给spring管理,才可以增强
@Aspect:将该类标识为切面类(这里面有方法进行增强),相当于<aop:aspect ref=”myAspect”>

//advice通知类增强类
@Component("myAspect")//相当于<bean id="myAspect" class="cn.itcast.spring.a_aspectj.MyAspect"/>
@Aspect//相当于<aop:aspect ref="myAspect">
public class MyAspect {

}

在切面的类,通知方法上添加

  • @Before 前置通知,相当于BeforeAdvice
  • @AfterReturning 后置通知,相当于AfterReturningAdvice
  • @Around 环绕通知,相当于MethodInterceptor
  • @AfterThrowing抛出通知,相当于ThrowAdvice
  • @After 最终final通知,不管是否异常,该通知都会执行
  • @DeclareParents 引介通知,相当于IntroductionInterceptor (不要求掌握)

在spring容器中开启AspectJ 注解自动代理机制
使用<aop:aspectj-autoproxy/>
作用:能自动扫描带有@Aspect的bean,将其作为增强aop的配置,有点相当于:<aop:config>

<!-- 1。确定目标 -->
<!-- 扫描bean组件 -->
<context:component-scan base-package="cn.itcast.spring"/>
<!-- 2:编写通知 -->

<!-- 3:配置aop的aspectj的自动代理:
		自动扫描bean组件中,含有@Aspect的bean,将其作为aop管理,开启动态代理    -->
<aop:aspectj-autoproxy/>

前置通知

//advice通知类增强类
@Component("myAspect")//相当于<bean id="myAspect" class="cn.itcast.spring.a_aspectj.MyAspect"/>
@Aspect//相当于<aop:aspect ref="myAspect">
public class MyAspect {

    //前置通知
    //相当于:<aop:before method="before" pointcut="bean(*Service)"/>
    //@Before("bean(*Service)"):参数值:自动支持切入点表达式或切入点名字
    @Before("bean(*Service)")
    public void before(JoinPoint joinPoint){
        System.out.println("=======前置通知。。。。。");
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值