💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
[TOC] # 使用 接下来我们在src目录下新建一个com.service包,并在该包下创建PersonService接口,其代码为: ~~~ public interface PersonService { public void save(String name); public void update(String name, Integer id); public String getPersonName(Integer id); } ~~~ PersonServiceBean.java,其代码为: ~~~ public class PersonServiceImpl implements PersonService { @Override public void save(String name) { System.out.println("我是save()方法"); } @Override public void update(String name, Integer id) { System.out.println("我是update()方法"); } @Override public String getPersonName(Integer id) { System.out.println("我是getPersonName()方法"); return "xxx"; } } ~~~ 然后,我们就要在com.service包下创建一个切面类——MyInterceptor.java,下面我们来按照以下步骤将其写出来。 * 首先用`@Aspect`注解声明整个类是一个切面: ~~~ @Aspect public class MyInterceptor { ... } ~~~ * 接着用@Pointcut注解声明一个切入点 ~~~ @Aspect public class MyInterceptor { @Pointcut("execution (* com.service.PersonServiceImpl.*(..))") private void anyMethod() {} // 声明一个切入点,anyMethod为切入点名称 ... } ~~~ 我们可利用方法签名来编写切入点表达式。最典型的切入点表达式是根据方法的签名来匹配各种方法: * `execution (* com.service.impl.PersonServiceImpl.*(..))`:匹配PersonServiceImpl类中声明的所有方法。第一个*代表任意修饰符及任意返回值类型,第二个*代表任意方法,..匹配任意数量任意类型的参数,若目标类与该切面在同一个包中,可以省略包名。 * `execution public * com.service.PersonServiceImpl.*(..)`:匹配PersonServiceImpl类中的所有公有方法。 * `execution public double com.service.PersonServiceImpl.*(..)`:匹配PersonServiceImpl类中返回值类型为double类型的所有公有方法。 * `execution public double com.service.PersonServiceImpl.*(double, ..)`:匹配PersonServiceImpl类中第一个参数为double类型,后面不管有无参数的所有公有方法,并且该方法的返回值类型为double类型。 * `execution public double com.service.PersonServiceImpl.*(double, double)`:匹配PersonServiceImpl类中参数类型为double,double类型的,并且返回值类型也为double类型的所有公有方法。 然后声明前置通知方法。 前置通知方法在目标方法开始之前执行 ~~~ @Aspect public class MyInterceptor { @Pointcut("execution (* cn.itcast.service.impl.PersonServiceImpl.*(..))") private void anyMethod() {} // 声明一个切入点,anyMethod为切入点名称 // 声明该方法是一个前置通知:在目标方法开始之前执行 @Before("anyMethod()") public void doAccessCheck() { System.out.println("前置通知"); } } ~~~ 注意:若是将一个类声明为一个切面,那么需要把该类放到IOC容器管理。 接下来,我们理应要修改Spring的配置文件——beans.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:context="http://www.springframework.org/schema/context" 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/context http://www.springframework.org/schema/context/spring-context-4.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd"> <aop:aspectj-autoproxy /> <bean id="myInterceptor" class="com.service.MyInterceptor" /> <bean id="personService" class=" com.service.PersonServiceImpl"></bean> </beans> ~~~ 从上面可看出我们并没有让Spring自动扫描和管理Bean。 最后,在src目录下新建一个junit.test包,并在该包中新建一个单元测试类——SpringAOPTest.java,其代码为: ~~~ public class SpringAOPTest { @Test public void interceptorTest() { ApplicationContext cxt = new ClassPathXmlApplicationContext("beans.xml"); PersonService personService = (PersonService) cxt.getBean("personService"); personService.save("xxx"); } } ~~~ # 细节一 如果我需要得到输入参数,如在前置通知里面,得到用户输入的数据。此时,须将前置通知方法修改为 ~~~ @Before("anyMethod() && args(name)") public void doAccessCheck(String name) { System.out.println("前置通知:" + name); } ~~~ `@Before("anyMethod() && args(name)")`匹配的是PersonServiceImpl类中参数为String类型的方法,即save()方法。 # 细节二 如我要获得PersonServiceImpl类中的getPersonName()方法的返回参数。此时,须将后置通知方法修改为 ~~~ @AfterReturning(pointcut="anyMethod()", returning="result") public void doAfterReturning(String result) { System.out.println("后置通知:" + result); } ~~~ `@AfterReturning(pointcut="anyMethod()", returning="result")`匹配的是PersonServiceImpl类中返回值类型为String的方法,并且returning属性能将返回值传入进后置通知方法里面。 测试方法修改 ~~~ public class SpringAOPTest { @Test public void interceptorTest() { ApplicationContext cxt = new ClassPathXmlApplicationContext("beans.xml"); PersonService personService = (PersonService) cxt.getBean("personService"); personService.getPersonName(2); } } ~~~ # 细节三 在目标方法出现异常时,得到抛出的异常。为了便于试验,我们须将PersonServiceImpl类的代码修改为: ~~~ public class PersonServiceImpl implements PersonService { @Override public void save(String name) { throw new RuntimeException("我是异常"); // System.out.println("我是save()方法"); } @Override public void update(String name, Integer id) { System.out.println("我是update()方法"); } @Override public String getPersonName(Integer id) { System.out.println("我是getPersonName()方法"); return "xxx"; } } ~~~ 然后将异常通知方法修改为: ~~~ @AfterThrowing(pointcut="anyMethod()", throwing="e") public void doAfterThrowing(Exception e) { System.out.println("异常通知:" + e); } ~~~ 最后,我们还要将SpringAOPTest类的代码改为: ~~~ public class SpringAOPTest { @Test public void interceptorTest() { ApplicationContext cxt = new ClassPathXmlApplicationContext("beans.xml"); PersonService personService = (PersonService) cxt.getBean("personService"); personService.save("xxx"); } } ~~~ 打印 ![](https://box.kancloud.cn/3f5a50d3e307d3398762450b5d0d993b_1706x462.png)