Spring-面向切面编程(AOP)

面向切面编程(AOP)

1.什么是AOP?

**用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,**这个模块被命名为“切面”(Aspect)。

例如:

日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来。允许你把遍布应用各处的功能分离出来形成可重用组件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5nCdBfV9-1570362511719)(C:\Users\Leisure\AppData\Roaming\Typora\typora-user-images\1569571356919.png)]

2. AOP组成

切面(Aspect): 横切关注点(跨越应用程序多个模块的功能)被模块化的特殊对象
通知(Advice): 切面必须要完成的工作
目标(Target): 被通知的对象
**代理(Proxy):**向目标对象应用通知之后创建的对象
**连接点(Joinpoint):**程序执行的某个特定位置:如类某个方法调用前、调用后、方法抛出异常后等。连接点由两个信息确定:方法表示的程序执行点;相对点表示的方位。例如 ArithmethicCalculator#add() 方法执行前的连接点,执行点为 ArithmethicCalculator#add(); 方位为该方法执行前的位置
切点(pointcut):每个类都拥有多个连接点:例如 ArithmethicCalculator 的所有方法实际上都是连接点,即连接点是程序类中客观存在的事务。AOP 通过切点定位到特定的连接点。类比:连接点相当于数据库中的记录,切点相当于查询条件。切点和连接点不是一对一的关系,一个切点匹配多个连接点,切点通过 org.springframework.aop.Pointcut 接口进行描述,它使用类和方法作为连接点的查询条件。

3. AOP实现方式:

基于 AspectJ 注解或基于 XML 配置的 AOP

3.1 基于注解方式

导入相关的项目架包

spring-aop-4.0.0.RELEASE.jar
spring-aspects-4.0.0.RELEASE.jar
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
  1. 要在 Spring 中声明 AspectJ 切面, 切面一个IOC中的Bean,即加入**@Component注解**

  2. 在 AspectJ 注解中, 切面一个带有 @Aspect 注解的 Java 类, 即加入 @Aspect注解

  3. xml配置文件中要添加 **<aop:aspectj-autoproxy/>**实现aop的自动代理

       <!-- 建立aop的自动代理 -->
    	<aop:aspectj-autoproxy/>
    
  4. 通知是标注有某种注解的简单的 Java 方法,写在切面类中。
    AspectJ 支持 5 种类型通知注解:
    @Before:

    前置通知, 在方法执行之前执行

    // 	前置通知:在目标方法开始之前执行
    	//	比@Aruond的前置要前
    	@Before("execution(* com.nchu.service.ProductService.*())")
    	public void before(JoinPoint joinPoint){
    		String methodName = joinPoint.getSignature().getName();
    		System.out.println("Start:"+ methodName);
    	}
    

    @After:

    后置通知, 在方法执行之后执行

    // 	后置通知:在目标方法开始之后(不管是否发生异常)执行
    	//	不能访问到方法的结果
    	//	比@Aruond的后置要前
    	@After("execution(* com.nchu.service.ProductService.*())")
    	public void after(JoinPoint joinPoint){
    		String methodName = joinPoint.getSignature().getName();
    		System.out.println("End:"+ methodName);
    	}
    

    @AfterRunning:

    返回通知, 在方法返回结果之后执行

    //	返回通知:在方法正常结束执行后的代码
    	//	可以访问到方法的返回值
    	//	在后置通知之后
    	@AfterReturning(value="execution(* com.nchu.service.ProductService.*())",
    			returning="result")
    	public void afterReturning(Object result){
    		System.out.println("Return :"+result);
    	}
    

    @AfterThrowing:

    异常通知, 在方法抛出异常之后

    //	异常通知:在目标方法出现异常时,会执行的代码
    	//	可以访问到异常对象;且可以指定在出现指定异常时再执行通知代码
    	@AfterThrowing(value="execution(* com.nchu.service.ProductService.*())",
    			throwing="ex")
    	public void afterReturning(Exception ex){
    		System.out.println("Exception :"+ex);
    	}
    

    @Around:

    环绕通知, 围绕着方法执行

    //	环绕通知:需要携带 ProceedingJoinPoint 类型的参数
    	//	环绕通知类类似于动态代理的全过程:ProceedingJoinPoint 类型的参数可以决定是否执行目标方法
    	//	必须需要返回值,即为目标方法的返回值
    	@Around("execution(* com.nchu.service.ProductService.*(..))")
    	// 	要抛异常
    	public Object performance(ProceedingJoinPoint joinPoint) throws Throwable{
    		//	前置通知
    		System.out.println("***************************+1");
    		//	执行目标方法
    		Object object = joinPoint.proceed();
    		//	后置通知
    		System.out.println("###########################+1");
    		return object;
    	}
    	
    	//	对service类中的不同方法进行切面编程
    	@Around(value="execution(* com.nchu.service.ProductService.*(..))")
    	public Object performance2(ProceedingJoinPoint joinPoint){
    
    		Object object=null;
    		try {
    //			前置通知
    			System.out.println("***************************+2");
    			//	执行目标方法
    			object = joinPoint.proceed();
    			//	后置通知
    			System.out.println("###########################+2");
    		} catch (Throwable e) {
    			//	异常通知
    			System.out.println("Around:Exception");
    		}
    
    		return object;
    	}
    
  5. 当同时有好几个切面时,可以使用@Order()注解进行指定切面优先级

    //	同时有好几个切面时,可以使用  @Order 注解指定切面优先级
    //	值越小,优先级越高
    @Order(1)
    @Component
    @Aspect
    public class PerformanceAspect {
    	
    	// 	前置通知:在目标方法开始之前执行
    	//	比@Aruond的前置要前
    	@Before("execution(* com.nchu.service.ProductService.*())")
    	public void before(JoinPoint joinPoint){
    		String methodName = joinPoint.getSignature().getName();
    		System.out.println("Start:"+ methodName);
    	}
    }
    
  6. 可以使用切入点表达式,实现重用

    **注意:**如果运行会抛出"error at ::0 can’t find referenced pointcut “错误,原因可能是当前的aspectj.weaver架包版本不行。

    **解决方法:**可以尝试将com.springsource.org.aspectj.weaver.RELEASE.jar包替换为

    com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar或后续版本,即可解决问题。

    //	可定义切入点表达式,实现重用
    @Pointcut("execution(* com.nchu.service.ProductService.*())")
    public void defineJoinPointCut(){}	
    
    // 	前置通知:在目标方法开始之前执行
    //	比@Aruond的前置要前
    @Before("defineJoinPointCut()")
    public void before(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("Start:"+ methodNme);
    }
    	
    
3.2 基于XML文件方式

直接在xml文件中进行配置:

 
   <!-- 1. 创建服务类的bean,用于测试 -->
   <bean name="s" class="com.nchu.service.ProductService"/>
   
   <!-- 2. 创建切面类的bean,用于后面的使用 -->
   <bean id="aspect" class="com.nchu.aspect.PerformanceAspect"/>
   
   <!-- 3. 使用aop:config来设置aop配置 -->
   <aop:config>
   		<!-- 3.1 使用aop:pointcut 标识切点 -->
   		<aop:pointcut expression="execution(* com.nchu.service.ProductService.*()) " id="toPerformance"/>
   		<!-- 3.2 配置对应的切面依赖 -->
   		<aop:aspect id="perfAspect" ref="aspect">
   			<!-- 3.2.1 配置pointcut-ref切点依赖、 method配置切点触发对应的切面方法-->
   			<aop:around pointcut-ref="toPerformance" method="performance2"/>
   		</aop:aspect>
   </aop:config>
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值