SpringAOP—— 深入AOP

目录

 

1、切入点表达式

配置:

Spring基于xml的aop配置:

2、常见的四种通知类型

3、环绕通知

问题:

解决:

4、通用化切入点表达式

5、spring基于注解的aop配置

纯注解


1、切入点表达式

配置:

Spring基于xml的aop配置:

1)把通知bean交给spring来管理

2)使用 aop:config 标签开始aop配置

3)使用 aop: aspect标签,开始切面配置   id:切面的id    ref:引用通知bean的id

4)使用 aop:before 配置前置通知

method:指定通知类中哪个方法是前置通知

pointcut:用于指定切入点表达式

写法:execution(访问修饰符  返回值  包名.类名.方法名(  参数列表 ))

          访问修饰符可以省略

         返回值用 * ,可以用来表示任意返回值

         execution(* anli.AccountServiceImpl.save())

         包名用 * ,可以表示任意包。几级包就用几个 *

         包名也可以用 .. 表示当前包及其子包

          类名和方法名也可以使用 *,意思同上

        参数基本类型:execution(* anli.AccountServiceImpl.*(int))

        参数引用类型:execution(* anli.AccountServiceImpl.*(java.lang.String))

        参数列表可以使用 * ,但是必须有参数:execution(* anli.AccountServiceImpl.*(*))

        参数列表可以用 .. ,表示有无参数均可:execution(* anli.AccountServiceImpl.*(..))

         实际开发中:execution(* anli.*.*(..))   可以根据实际需求进行修改

 

bean.xml

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
        
        <!-- 配置业务层 -->
        <bean id="accountService" class="anli.AccountServiceImpl"></bean>
        
        <bean id="logger" class="utils.Logger"></bean>
        
        <aop:config>
        	<aop:aspect id="LogAdvice" ref="logger">
        		<aop:before method="beforeLog" pointcut="execution(public void anli.AccountServiceImpl.save())"/>
        	</aop:aspect>
        </aop:config>
        
</beans>

AccountServiceImpl:

package anli;

public class AccountServiceImpl implements IAccountService{

	@Override
	public void save() {
		System.out.println("保存了账户");
		
	}

	@Override
	public void update(int id) {
		System.out.println("修改了账户");
		
	}

	@Override
	public int delete() {
		System.out.println("删除了账户");
		return 0;
	}

}

Client:

package anli;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Client {
	public static void main(String[] args) {
		
		ApplicationContext ac = new ClassPathXmlApplicationContext("anli/bean.xml");
		IAccountService as = ac.getBean("accountService", IAccountService.class);
		
		as.save();
	}
}

Logger:

package utils;


//一个通知类,对业务层方法进行增强,模拟记录日志
public class Logger {

	public void beforeLog() {
		System.out.println("Logger开始记录日志");
	}

}

2、常见的四种通知类型

前置通知:它永远在切入点方法执行之前执行

后置通知:当切入点方法正常执行后,再执行

异常通知:切入点方法执行出现异常后执行

最终通知:无论切入点是否正常执行,它都会在最后执行

后置通知和异常通知只能执行一个

<aop:config>
        	<aop:aspect id="LogAdvice" ref="logger">
        		<!-- 前置通知 -->
        		<aop:before method="beforeLog" pointcut="execution(* anli.AccountServiceImpl.*(..))"/>
        		<!-- 后置通知 -->
        		<aop:after-returning method="afterLog" pointcut="execution(* anli.AccountServiceImpl.*(..))"/>
        		<!-- 异常通知 -->
        		<aop:after-throwing method="exceptionLog" pointcut="execution(* anli.AccountServiceImpl.*(..))"/>
        		<!-- 最终通知 -->
        		<aop:after method="finalLog" pointcut="execution(* anli.AccountServiceImpl.*(..))"/>
        	</aop:aspect>
        </aop:config>

Logger

package utils;


//一个通知类,对业务层方法进行增强,模拟记录日志
public class Logger {

	public void beforeLog() {
		System.out.println("Logger开始记录日志");
	}
	
	public void afterLog() {
		System.out.println("后置");
	}
	
	public void exceptionLog() {
		System.out.println("异常");
	}
	
	public void finalLog() {
		System.out.println("最终");
	}

}

3、环绕通知

<!-- 环绕通知 -->
<aop:around method="aroundLog" pointcut="execution(* anli.AccountServiceImpl.*(..))"/>

问题:

为什么切入点save()没有执行?

解决:

分析:环绕通知没有调用切入点方法

public Object aroundLog(ProceedingJoinPoint pjp) {
		
		Object obj = null;
		try {
			System.out.println("前置");
			//获取方法所需的参数
			Object[] args = pjp.getArgs();
			//process明确调用切入点方法
			obj = pjp.proceed(args);
			System.out.println("后置");
		} catch (Throwable e) {
			System.out.println("异常");
			e.printStackTrace();
		}finally {
			System.out.println("最终");
		}
		
		return obj;
		
	}

它是spring为我们提供的一种可以在代码中手动控制通知何时执行的方式,上四种是通过配置的方式。

4、通用化切入点表达式

使用aop:pointcut标签可以配置通用切入点表达式,写在aspect内部,只能当前切面使用。如果要想所要切面使用,写到aop:aspect外面

        <aop:config>
        	<aop:pointcut expression="execution(* anli.AccountServiceImpl.*(..))" id="pc"/>
        	<aop:aspect id="LogAdvice" ref="logger">

        		<!-- 环绕通知 -->
        		<aop:around method="aroundLog" pointcut-ref="pc"/>
        		
        	</aop:aspect>
        </aop:config>

5、spring基于注解的aop配置

bean.xml

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
        
        <context:component-scan base-package="anli"></context:component-scan>
       	<!-- 开启spring注解对aop的支持 -->
       	<aop:aspectj-autoproxy/>
 
</beans>
package anli;

import org.springframework.stereotype.Service;

@Service("accountService")
public class AccountServiceImpl implements IAccountService{

	@Override
	public void save() {
		System.out.println("保存了账户");
		
	}

	@Override
	public void update(int id) {
		System.out.println("修改了账户");
		
	}

	@Override
	public int delete() {
		System.out.println("删除了账户");
		return 0;
	}

}
package anli;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

//一个通知类,对业务层方法进行增强,模拟记录日志
@Component("logger")
@Aspect
public class Logger {
	
	@Pointcut("execution(* anli.AccountServiceImpl.*(..))")
	private void pt1() {}

	@Before("pt1()")
	public void beforeLog() {
		System.out.println("Logger开始记录日志");
	}
	
	@AfterReturning("pt1()")
	public void afterLog() {
		System.out.println("后置");
	}
	@AfterThrowing("pt1()")
	public void exceptionLog() {
		System.out.println("异常");
	}
	@After("pt1()")
	public void finalLog() {
		System.out.println("最终");
	}
	@Around("pt1()")
	public Object aroundLog(ProceedingJoinPoint pjp) {
		
		Object obj = null;
		try {
			System.out.println("前置");
			//获取方法所需的参数
			Object[] args = pjp.getArgs();
			//process明确调用切入点方法
			obj = pjp.proceed(args);
			System.out.println("后置");
		} catch (Throwable e) {
			System.out.println("异常");
			e.printStackTrace();
		}finally {
			System.out.println("最终");
		}
		
		return obj;
		
	}

}

纯注解

package anli;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Client {
	public static void main(String[] args) {
		
		ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfig.class);
		IAccountService as = ac.getBean("accountService", IAccountService.class);
		
		as.save();

	}
}
package anli;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@ComponentScan("anli")
@EnableAspectJAutoProxy
public class SpringConfig {

}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值