AOP的概念和理解:使用画图的方式理解面向切面编程(AOP)
AOP的术语以及XML配置方式快速入门
(1)创建目标接口以及实现类
package com.ssm.aop.service; import com.ssm.aop.game.Role; public interface RoleService { public void printRole(Role role); }
package com.ssm.aop.service.Impl; import com.ssm.aop.game.Role; import com.ssm.aop.service.RoleService; import org.springframework.stereotype.Component; @Component public class RoleServiceImpl implements RoleService { @Override public void printRole(Role role) { System.out.println("{id: "+role.getId()+", " +"role_name:"+role.getRoleName()+", " +"note:"+role.getNote()+"}"); } }
(2)创建切面类(用于增强目标类的方法)
package com.ssm.aop.aspect; import org.aspectj.lang.annotation.*; @Aspect public class RoleAspect { @Before("execution(* com.ssm.aop.service.Impl.RoleServiceImpl.printRole(..))") public void before() { System.out.println("before..."); } @After("execution(* com.ssm.aop.service.Impl.RoleServiceImpl.printRole(..))") public void after() { System.out.println("after..."); } @AfterReturning("execution(* com.ssm.aop.service.Impl.RoleServiceImpl.printRole(..))") public void afterReturning() { System.out.println("afterReturning..."); } @AfterThrowing("execution(* com.ssm.aop.service.Impl.RoleServiceImpl.printRole(..))") public void afterThrowing() { System.out.println("afterThrowing..."); } }
在类的上方加上@Aspect注解,就表明该类为切面类
@Before:添加该注解的方法表明该方法在被代理对象的方法前调用,即前置通知
@After:添加该注解的方法表明该方法在被代理对象的方法后调用,即后置通知
@AfterReturning:添加该注解的方法表明该方法在被代理对象的方法正常返回后调用。
@AfterThrowing:添加该注解的方法表明该方法在被代理对象的方法抛出异常后调用。
方法上的注解括号中的配置项(定义了execution的正则表达式,来匹配对应的切点)用于定义切点:
表达式的语法:
execution([修饰符]返回值类型 包名.类名.方法名(参数))
以下面的表达式为例:
execution(* com.ssm.aop.service.Impl.RoleServiceImpl.printRole(..))
execution:代表执行方法时会触发
*:代表任意返回类型的方法
com.ssm.aop.service.Impl.RoleServiceImpl:代表类的全限定名
printRole:被拦截的方法名称
(..):任意的参数
在一个没有任何逻辑的方法上方定义@Pointcut注解,配置项为切点的表达式即可,在使用时只要在配置项配置没有逻辑的方法名即可。
改进后的代码如下
package com.ssm.aop.aspect; import org.aspectj.lang.annotation.*; @Aspect public class RoleAspect { @Pointcut("execution(* com.ssm.aop.service.Impl.RoleServiceImpl.printRole(..))") public void print() { } @Before("print()") public void before() { System.out.println("before..."); } @After("print()") public void after() { System.out.println("after..."); } @AfterReturning("print()") public void afterReturning() { System.out.println("afterReturning..."); } @AfterThrowing("print()") public void afterThrowing() { System.out.println("afterThrowing..."); } }
(3)配置
要使得上述的注解有效,要先启动AspectJ自动代理,这样Spring才会生成动态代理对象。
配置代码如下:
package com.ssm.aop.config; import com.ssm.aop.aspect.RoleAspect; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; @Configuration @EnableAspectJAutoProxy @ComponentScan("com.ssm.aop") public class AopConfig { @Bean public RoleAspect getRoleAspect() { return new RoleAspect(); } }
@EnableAspectJAutoProxy
@Bean
public RoleAspect getRoleAspect()
{
return new RoleAspect();
}
该部分代码就代表了启动了AdpectJ框架的自动代理
另一种启用自动代理是通过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 "> <aop:aspectj-autoproxy/> <bean id="roleAspect" class="com.ssm.aop.aspect.RoleAspect"/> <bean id="roleService" class="com.ssm.aop.service.Impl.RoleServiceImpl"/> </beans>
可以看到XML中将RoleAspect切面类和RoleServiceImpl目标类作为bean加入Ioc容器,相当于注解中的@Bean和@Component
<aop:aspectj-autoproxy>跟注解@EnableAspectJautoProxy作用相同,启用自动代理功能。
(4)测试
测试代码如下:
import com.ssm.aop.config.AopConfig; import com.ssm.aop.game.Role; import com.ssm.aop.service.RoleService; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class Main { public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(AopConfig.class);//使用config类配置 // ApplicationContext ctx=new ClassPathXmlApplicationContext("application-config.xml");//使用xml配置文件 RoleService roleService = (RoleService) ctx.getBean(RoleService.class); Role role = new Role(); role.setId(1L); role.setRoleName("role_name_1"); role.setNote("note_1"); roleService.printRole(role); System.out.println("####################"); //测试异常通知 role = null; roleService.printRole(role); } }
运行结果如下图;
在测试代码中特意创建了一个空对象来触发异常通知。
在没有错误的情况下,可以看到首先调用前置通知,然后调用printRole方法,再调用了成功返回的通知,最后调用后置通知。
在抛出异常的情况下,首先调用了前置通知,然后调用pringRole方法时出现异常,则调用异常通知输出afterThrowing,最后调用后置通知。
也就是说无论调用pringRole成功与否,都会调用后置通知。