1. AOP入门
1). 创建项目并导入Jar包:spring-beans-5.0.5.RELEASE.jar、spring-context-5.0.5.RELEASE.jar、spring-core-5.0.5.RELEASE.jar、spring-expression-5.0.5.RELEASE.jar、spring-aop-5.0.5.RELEASE.jar、spring-aop-5.0.5.RELEASE.jar、spring-aspects-5.0.5.RELEASE.jar、commons-logging-1.2.jar、aopalliance-1.0.jar、aspectjweaver-1.8.13.jar.
2). 引入aop命令空间
<?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">
</beans>
3). 创建PersonService接口
public interface PersonService {
public void save(String name);
public void update(String name, int id);
public String getPersonName(int id);
}
4). 创建实现了PersonService的接口PersonServiceImpl
public class PersonServiceImpl implements PersonService{
@Override
public void save(String name) {
System.out.println("我是save方法");
}
@Override
public void update(String name, int id) {
System.out.println("我是update方法");
}
@Override
public String getPersonName(int id) {
System.out.println("我是getPersonName方法");
return "mazaiting";
}
}
5). 创建切面类——MyInterceptor
最典型的切入点表达式是根据方法的签名来匹配各种方法:
-
execution (* cn.itcast.service.impl.PersonServiceImpl.*(..))
:匹配PersonServiceImpl类中声明的所有方法。第一个代表任意修饰符及任意返回值类型,第二个代表任意方法,..匹配任意数量任意类型的参数,若目标类与该切面在同一个包中,可以省略包名。 -
execution public * cn.itcast.service.impl.PersonServiceImpl.*(..)
:匹配PersonServiceImpl类中的所有公有方法。 -
execution public double cn.itcast.service.impl.PersonServiceImpl.*(..)
:匹配PersonServiceImpl类中返回值类型为double类型的所有公有方法。 -
execution public double cn.itcast.service.impl.PersonServiceImpl.*(double, ..)
:匹配PersonServiceImpl类中第一个参数为double类型,后面不管有无参数的所有公有方法,并且该方法的返回值类型为double类型。 -
execution public double cn.itcast.service.impl.PersonServiceImpl.*(double, double)
:匹配PersonServiceImpl类中参数类型为double,double类型的,并且返回值类型也为double类型的所有公有方法。
// 声明当前类为切面类
@Aspect
public class MyInterceptor {
// 声明一个切入点,anyMethod为切入点名称
@Pointcut("execution (* com.mazaiting.test.service.impl.PersonServiceImpl.*(..))")
private void anyMethod(){
}
// 声明该方法是一个前置通知,在目标方法开始之前执行
@Before("anyMethod()")
public void doAccessCheck() {
System.out.println("前置通知");
}
}
注意:若是将一个类声明为一个切面,那么需要把该类放到IOC容器管理。
6). 在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: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.mazaiting.test.MyInterceptor"></bean>
<bean id="personService" class="com.mazaiting.test.service.impl.PersonServiceImpl"></bean>
</beans>
7). 创建测试类
public class TestAop {
@Test
public void test() {
// 加载配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
// 创建对象
PersonService service = (PersonService) context.getBean("personService");
service.save("xxx");
}
}
8). 执行结果
2. AOP细节(前置通知、后置通知、异常通知、最终通知、环绕通知等)
1). 添加后置通知
// 声明当前类为切面类
@Aspect
public class MyInterceptor {
// 声明一个切入点,anyMethod为切入点名称
@Pointcut("execution (* com.mazaiting.test.service.impl.PersonServiceImpl.*(..))")
private void anyMethod(){
}
// 声明该方法是一个前置通知,在目标方法开始之前执行
@Before("anyMethod()")
public void doAccessCheck() {
System.out.println("前置通知");
}
// 声明一个后置通知
@AfterReturning("anyMethod()")
public void doAfterRunning() {
System.out.println("后置通知");
}
}
执行测试代码,打印结果:
2). 添加最终通知
// 声明当前类为切面类
@Aspect
public class MyInterceptor {
// 声明一个切入点,anyMethod为切入点名称
@Pointcut("execution (* com.mazaiting.test.service.impl.PersonServiceImpl.*(..))")
private void anyMethod(){
}
// 声明该方法是一个前置通知,在目标方法开始之前执行
@Before("anyMethod()")
public void doAccessCheck() {
System.out.println("前置通知");
}
// 声明一个后置通知
@AfterReturning("anyMethod()")
public void doAfterRunning() {
System.out.println("后置通知");
}
// 声明一个最终通知
@After("anyMethod()")
public void doAfter() {
System.out.println("最终通知");
}
}
执行测试代码,打印结果:
3). 添加异常通知
I. 修改PersonServiceImpl中的save代码实现
public class PersonServiceImpl implements PersonService{
@Override
public void save(String name) {
throw new RuntimeException("异常");
// System.out.println("我是save方法");
}
@Override
public void update(String name, int id) {
System.out.println("我是update方法");
}
@Override
public String getPersonName(int id) {
System.out.println("我是getPersonName方法");
return "mazaiting";
}
}
II. 修改MyInterceptor类中代码
// 声明当前类为切面类
@Aspect
public class MyInterceptor {
// 声明一个切入点,anyMethod为切入点名称
@Pointcut("execution (* com.mazaiting.test.service.impl.PersonServiceImpl.*(..))")
private void anyMethod(){
}
// 声明该方法是一个前置通知,在目标方法开始之前执行
@Before("anyMethod()")
public void doAccessCheck() {
System.out.println("前置通知");
}
// 声明一个后置通知
@AfterReturning("anyMethod()")
public void doAfterRunning() {
System.out.println("后置通知");
}
// 声明一个最终通知
@After("anyMethod()")
public void doAfter() {
System.out.println("最终通知");
}
// 声明一个异常通知
@AfterThrowing("anyMethod()")
public void doAfterThrowing() {
System.out.println("异常通知");
}
}
III. 执行测试代码,打印结果:
4). 添加环绕通知
I. 将代码重置回第二步结束时样子
II. 修改MyInterceptor文件
// 声明当前类为切面类
@Aspect
public class MyInterceptor {
// 声明一个切入点,anyMethod为切入点名称
@Pointcut("execution (* com.mazaiting.test.service.impl.PersonServiceImpl.*(..))")
private void anyMethod(){
}
// 声明该方法是一个前置通知,在目标方法开始之前执行
@Before("anyMethod()")
public void doAccessCheck() {
System.out.println("前置通知");
}
// 声明一个后置通知
@AfterReturning("anyMethod()")
public void doAfterRunning() {
System.out.println("后置通知");
}
// 声明一个最终通知
@After("anyMethod()")
public void doAfter() {
System.out.println("最终通知");
}
// 声明一个异常通知
@AfterThrowing("anyMethod()")
public void doAfterThrowing() {
System.out.println("异常通知");
}
// 环绕通知,环绕通知方法的写法是固定
@Around("anyMethod()")
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("进入方法");
Object result = pjp.proceed();
System.out.println("退出方法");
return result;
}
}
注意:环绕通知内部一定要确保执行proceed()该方法,如果不执行该方法,业务bean中被拦截的方法就不会被执行。当执行该方法,如果后面还有切面的话,它的执行顺序应该是这样的:先执行后面的切面,如果后面没有切面了,再执行最终的目标对象的业务方法。若不执行该方法,则后面的切面,业务bean的方法都不会被执行。 其实我们仅使用环绕通知就可以实现前置通知、后置通知、异常通知、最终通知等的效果。
III. 执行测试代码,打印结果:
3. 前置通知获取参数
I. 修改MyInterceptor中的doAccessCheck方法
// 声明当前类为切面类
@Aspect
public class MyInterceptor {
// 声明一个切入点,anyMethod为切入点名称
@Pointcut("execution (* com.mazaiting.test.service.impl.PersonServiceImpl.*(..))")
private void anyMethod(){
}
// 声明该方法是一个前置通知,在目标方法开始之前执行
@Before("anyMethod() && args(name)")
public void doAccessCheck(String name) {
System.out.println("前置通知" + name);
}
// 声明一个后置通知
@AfterReturning("anyMethod()")
public void doAfterRunning() {
System.out.println("后置通知");
}
// 声明一个最终通知
@After("anyMethod()")
public void doAfter() {
System.out.println("最终通知");
}
// 声明一个异常通知
@AfterThrowing("anyMethod()")
public void doAfterThrowing() {
System.out.println("异常通知");
}
// 环绕通知,环绕通知方法的写法是固定
@Around("anyMethod()")
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("进入方法");
Object result = pjp.proceed();
System.out.println("退出方法");
return result;
}
}
II. 执行测试代码,打印结果:
4. 获取getPersonName()方法的返回参数
I. 修改MyInterceptor代码
// 声明当前类为切面类
@Aspect
public class MyInterceptor {
// 声明一个切入点,anyMethod为切入点名称
@Pointcut("execution (* com.mazaiting.test.service.impl.PersonServiceImpl.*(..))")
private void anyMethod(){
}
// 声明该方法是一个前置通知,在目标方法开始之前执行
@Before("anyMethod() && args(name)")
public void doAccessCheck(String name) {
System.out.println("前置通知" + name);
}
// 声明一个后置通知
@AfterReturning(pointcut = "anyMethod()", returning = "result")
public void doAfterRunning(String result) {
System.out.println("后置通知" + result);
}
// 声明一个最终通知
@After("anyMethod()")
public void doAfter() {
System.out.println("最终通知");
}
// 声明一个异常通知
@AfterThrowing("anyMethod()")
public void doAfterThrowing() {
System.out.println("异常通知");
}
// 环绕通知,环绕通知方法的写法是固定
@Around("anyMethod()")
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("进入方法");
Object result = pjp.proceed();
System.out.println("退出方法");
return result;
}
}
II. 修改测试代码
public class TestAop {
@Test
public void test() {
// 加载配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
// 创建对象
PersonService service = (PersonService) context.getBean("personService");
// service.save("xxx");
service.getPersonName(2);
}
}
III. 执行测试代码,打印结果:
5. 获取异常
I. 修改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, int id) {
System.out.println("我是update方法");
}
@Override
public String getPersonName(int id) {
System.out.println("我是getPersonName方法");
return "mazaiting";
}
}
II. 修改异常通知代码
// 声明当前类为切面类
@Aspect
public class MyInterceptor {
// 声明一个切入点,anyMethod为切入点名称
@Pointcut("execution (* com.mazaiting.test.service.impl.PersonServiceImpl.*(..))")
private void anyMethod(){
}
// 声明该方法是一个前置通知,在目标方法开始之前执行
@Before("anyMethod() && args(name)")
public void doAccessCheck(String name) {
System.out.println("前置通知" + name);
}
// 声明一个后置通知
@AfterReturning(pointcut = "anyMethod()", returning = "result")
public void doAfterRunning(String result) {
System.out.println("后置通知" + result);
}
// 声明一个最终通知
@After("anyMethod()")
public void doAfter() {
System.out.println("最终通知");
}
// 声明一个异常通知
@AfterThrowing(pointcut = "anyMethod()", throwing = "e")
public void doAfterThrowing(Exception e) {
System.out.println("异常通知" + e);
}
// 环绕通知,环绕通知方法的写法是固定
@Around("anyMethod()")
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("进入方法");
Object result = pjp.proceed();
System.out.println("退出方法");
return result;
}
}
III. 修改测试代码
public class TestAop {
@Test
public void test() {
// 加载配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
// 创建对象
PersonService service = (PersonService) context.getBean("personService");
service.save("xxx");
// service.getPersonName(2);
}
}
IV. 执行测试代码,打印结果:
6. 配置文件实现AOP
1). 创建Person接口
public interface Person {
void save();
}
2). 创建实现Person接口的PersonImpl类
public class PersonImpl implements Person {
@Override
public void save() {
System.out.println("我是save方法");
}
}
3). 创建MyInterceptor类
public class MyInterceptor {
public void doAccessCheck() {
System.out.println("前置通知");
}
public void doAfterReturning() {
System.out.println("后置通知");
}
public void doAfter() {
System.out.println("最终通知");
}
public void doAfterThrowing() {
System.out.println("异常通知");
}
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("进入方法");
Object result = pjp.proceed();
System.out.println("退出方法");
return result;
}
}
4). 修改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: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="myInterceptor1" class="com.mazaiting.test1.MyInterceptor"></bean>
<bean id="person" class="com.mazaiting.test1.service.impl.PersonImpl"></bean>
<aop:config>
<aop:aspect id="asp" ref="myInterceptor1">
<aop:pointcut expression="execution(* com.mazaiting.test1.service.impl.PersonImpl.*(..))" id="mycut"/>
<aop:before pointcut-ref="mycut" method="doAccessCheck"/>
<aop:after pointcut-ref="mycut" method="doAfter"/>
<aop:after-returning pointcut-ref="mycut" method="doAfterReturning"/>
<aop:after-throwing pointcut-ref="mycut" method="doAfterThrowing"/>
<aop:around pointcut-ref="mycut" method="doBasicProfiling"/>
</aop:aspect>
</aop:config>
</beans>
5). 创建测试代码
public class TestPerson {
@Test
public void test() {
// 加载配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
// 创建对象
Person person = (Person) context.getBean("person");
person.save();
}
}
6). 执行测试代码,打印结果:
7). 修改PersonImpl代码
public class PersonImpl implements Person {
@Override
public void save() {
// System.out.println("我是save方法");
throw new RuntimeException("yichang");
}
}
8). 执行测试代码,打印结果: