在spring中使用aspectj有2种方式:
- xml配置
- 注解配置(推荐)
这2种方式需要添加的jar包都一样(待修改):
- spring-aop.RELEASE.jar
- aspectjweaver.jar
在spring中使用aspectj,不需要添加aspectjrt.jar,也不需要专门的ajc编译器,使用javac编译即可。
xml配置方式
(1)目标接口、目标类
新建包com.chy.dao,包下新建接口UserDao、实现类UserDaoImpl:
public interface UserDao { public void addUser(); public void deleteUser(); }
public class UserDaoImpl implements UserDao{ @Override public void addUser() { System.out.println("正在添加用户..."); } @Override public void deleteUser() { System.out.println("正在删除用户..."); } }
(2)切面
新建包com.chy.aspect,包下新建类UserDaoAspect:
public class UserDaoAspect { //前置通知要调用的方法 public void before(){ System.out.println("正在执行前置通知..."); } //后置通知要调用的方法 public void after(){ System.out.println("正在执行后置通知..."); } //返回通知要调用的方法 public void afterReturning(Object obj){ System.out.println("正在执行返回通知..."); System.out.println("目标方法的返回值是:"+obj); } // 异常通知要调用的方法 public void afterThrowing(JoinPoint point,Exception e){ System.out.println("异常信息:"+e.getMessage()); } }
(3)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 https://www.springframework.org/schema/aop/spring-aop.xsd"> <!--目标对象--> <bean name="userDaoImpl" class="com.chy.dao.UserDaoImpl" /> <!--切面--> <bean name="userDaoAspect" class="com.chy.aspect.UserDaoAspect" /> <!--AOP配置--> <aop:config> <!--全局切入点,所有切面都可以引用。配置切入点只能用id,不能用name --> <!-- <aop:pointcut id="pointCut" expression="execution(* com.chy.dao.UserDaoImpl.*(..))" /> --> <!--一个<aop:aspect>配置一个切面--> <aop:aspect ref="userDaoAspect"> <!--局部切入点,只能在此切面中引用--> <aop:pointcut id="pointCut" expression="execution(* com.chy.dao.UserDao.*(..))" /> <!--前置通知--> <aop:before method="before" pointcut-ref="pointCut" /> <!--可以引用切入点,也可以现配--> <!-- <aop:before method="before" pointcut="execution(* com.chy.dao.UserDao.*(..))"/> --> <!--后置通知--> <aop:after method="after" pointcut-ref="pointCut" /> <!--返回通知--> <!--如果要使用目标方法的返回值,可以用returning要将目标方法的返回值传递给返回通知要调用的方法的哪个形参--> <aop:after-returning method="afterReturning" pointcut-ref="pointCut" returning="obj"/> <!--异常通知--> <!--如果要使用捕获的异常对象,可以用throwing指定要将异常对象传递给哪个形参--> <aop:after-throwing method="afterThrowing" pointcut-ref="pointCut" throwing="e"/> </aop:aspect> </aop:config> </beans>
5种通知对应的方法都可以传递JoinPoint型的参数,不用传递实参,只有目标方法的返回值、捕获的异常需要传实参。
returning是返回通知的特有属性,throwing是异常通知的特有属性。
环绕通知
环绕通知与其它通知有重叠,通常不与其它通知一起使用。
// 环绕通知 public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { //前增强 System.out.println("正在执行前增强..."); //调用目标方法 Object object=proceedingJoinPoint.proceed(); //后增强 System.out.println("正在执行后增强..."); return object; }
<!--环绕通知--> <!--ProceedingJoinPoint是JoinPoint的子类,不必手动传参--> <aop:around method="around" pointcut-ref="pointCut" />
注解配置方式
(1)目标接口、目标类
public interface UserDao { public void addUser(); public void deleteUser(); }
@Repository public class UserDaoImpl implements UserDao{ @Override public void addUser() { System.out.println("正在添加用户..."); } @Override public void deleteUser() { System.out.println("正在删除用户..."); } }
(2)切面
@Component @Aspect public class UserDaoAspect { //配置切入点 @Pointcut("execution(* com.chy.dao.UserDao.*(..))") private void pointCut(){} /* 前置通知 @Before(value="pointCut()"),只有一个属性时可以只写属性值 @Before("execution(* com.chy.dao.UserDao.*(..))"),属性值可以引用切入点,也可以现配 */ @Before("pointCut()") public void before(){ System.out.println("正在执行前置通知..."); } //后置通知 @After("pointCut()") public void after(){ System.out.println("正在执行后置通知..."); } //返回通知。可传递目标方法的返回值。 @AfterReturning(value = "pointCut()",returning = "obj" ) public void afterReturning(Object obj){ System.out.println("正在执行返回通知..."); System.out.println("目标方法的返回值是:"+obj); } // 异常通知。可传递异常对象。 @AfterThrowing(value = "pointCut()",throwing = "e") public void afterThrowing(JoinPoint point,Exception e){ System.out.println("异常信息:"+e.getMessage()); } }
环绕通知:
// 环绕通知 @Around("pointCut()") public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { //前增强 System.out.println("正在执行前增强..."); //调用目标方法 Object object=proceedingJoinPoint.proceed(); //后增强 System.out.println("正在执行后增强..."); return object; }
(3)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 https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <!--使用包扫描,要扫描多个包时用逗号分隔--> <context:component-scan base-package="com.chy.dao,com.chy.aspect" /> <!--启用AspectJ的注解--> <aop:aspectj-autoproxy /> </beans>
使用注解十分方便,xml文件也更加简洁,推荐。
使用
不管是xml方式,还是注解方式,使用时都是一样的:
(1)如果目标类实现了接口,不管切入点配置为接口、还是实现类:
execution(* com.chy.dao.UserDao.*(..)) execution(* com.chy.dao.UserDaoImpl.*(..))
使用时都只能使用接口(代理的是接口,不是具体的实现类):
UserDao userDao=applicationContext.getBean("userDaoImpl", UserDao.class); userDao.addUser();
红色标出的两处都只能使用接口。
(2)如果目标类没有实现接口,那切入点只能配置为目标类:
execution(* com.chy.dao.UserDaoImpl.*(..))
使用时自然只能使用目标类:
UserDaoImpl userDao=applicationContext.getBean("userDaoImpl", UserDaoImpl.class); userDao.addUser();
调用方法时会自动增强。
AOP常见术语
- Joinpoint (连接点):目标类中的所有方法
- Pointcut(切入点):目标类中被增强的方法。有时候我们只增强目标类的部分方法。
- Weaving(织入):把增强应用到目标对象创建代理对象的过程。spring aop采用动态代理织入,aspectj采用在编译期、类装载期织入。