什么是AOP
Aspect Oriented Programming(面向切面编程或者面向方面编程)
它是对面向对象的一个扩展,可以不修改原有代码的情况下,给原有的逻辑增加功能,降低了共通业务逻辑和原有逻辑的耦合度
因为共同业务逻辑可以通过配置手段加入到原有逻辑中
AOP中涉及到概念
切面 Aspect 封装共通业务逻辑的
连接点 JoinPoint 共通业务逻辑所要嵌入的位置 一般封装了方法的信息
切点 Pointcut 它是一堆连接点 可以看成连接点的集合 (切点表达式)
目标 Target 要嵌入共通业务逻辑的对象
代理 Proxy 被增强之后的目标对象
通知 Advice 时机 目标方法调用之前 目标方法调用之后 目标方法调用前后 目标方法最终 目标方法出现异常
实现AOP的步骤(使用配置文件bean标签)
1 2 3 4 5 6 7 8 9
| package com.xdl.service; public class XdlBankAccountService { public void login() { System.out.println("登录中..."); } public void register() { System.out.println("注册中..."); } }
|
- 在配置文件中配置这个服务器类,然后通过容器获取服务类对应的对象,测试方法调用
1 2 3
| <!-- 目标对象 --> <bean id="bankService" class="com.xdl.service.XdlBankAccountService"> </bean>
|
-
在不修改服务类代码的情况下,让服务类对应的方法调用前输出
a.定义一个切面类,里面定义输出******的方法
1 2 3 4 5 6
| package com.xdl.aspect; public class XdlLogAspect { public void printSixStarts() { System.out.println("******"); } }
|
b.在配置文件中,配置切面类型的对象
1 2
| <!-- 切面对象 --> <bean id="logAspect" class="com.xdl.aspect.XdlLogAspect"></bean>
|
c.在Spring配置文件中写AOP的配置
1 2 3 4 5 6
| <!-- AOP配置 bean()指的是配置文件bean标签,括号里的是id名--> <aop:config> <aop:aspect ref="logAspect"> <aop:before method="printSixStarts" pointcut="bean(bankService)"/> </aop:aspect> </aop:config>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| package com.xdl.test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.xdl.service.XdlBankAccountService; public class XdlBankAccountServiceTest { public static void main(String[] args) { ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml"); XdlBankAccountService bankService = app.getBean("bankService", XdlBankAccountService.class); bankService.register(); bankService.login(); } }
|
切点表达式的写法
bean(spring容器中id表达式) 支持统配,如bean(*Account)
-
类型限定表达式
within(表达式) 表达式的写法,是最后一部分必须是类型
com.xdl.dao.XdlBankAccountDAOOracleImp 对XdlBankAccountDAOOracleImp这个类型对应的所有方法都切入共通业务逻辑。
com.xdl.dao.* dao包下所有的类型,对应的方法都将被切入共通业务逻辑
com…* com包下所有的类型以及com的子包下所有的类型对应的方法都将被切入共通业务逻辑
1
| <aop:before method="printSysTime" pointcut="within(com..*)"/>
|
-
方法限定表达式
execution(方法限定) 具体如下:
execution(权限修饰 返回值类型 方法名(参数列表) throws 异常)
其中返回值类型,方法名()是必须的
1
| <aop:before method="printSysTime" pointcut="execution(* *())"/>
|
AOP中的五种通知类型
<aop:before 前置通知 目标方法调用之前调用
<aop:after 最终通知 目标方法调用后,一定会调用
<aop:after-returning 后置通知 目标方法调用之后调用
<aop:after-throwing 异常通知 目标方法调用出现异常 采用调用
<aop:around 环绕通知 目标方法调用前后都调用
实现AOP的步骤(使用组件扫描)
-
建立项目,导入jar包(ioc aop)拷贝配置文件到src
-
开启组件扫描,在applicationContext.xml里输入
1
| <context:component-scan base-package="com.xdl"></context:component-scan>
|
1 2 3 4 5
| package com.xdl.dao; public interface XdlBankAccountDAO { void insertAccount(); void deleteAccount(); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| package com.xdl.dao; import org.springframework.stereotype.Repository; @Repository("accountDao") public class XdlBankAccountDAOOracleImp implements XdlBankAccountDAO { @Override public void insertAccount() { System.out.println("增加银行账户"); }
@Override public void deleteAccount() { System.out.println("删除银行账户"); } }
|
1
| <aop:aspectj-autoproxy proxy-target-class="true" />
|
- 编写一个切面类,打上@Component和@Aspect,然后定义并标注切面方法打印######
1 2 3 4 5 6 7 8 9 10 11 12
| package com.xdl.aspect; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; @Component @Aspect public class XdlLogAspect { @Before("bean(accountDao)") public void printSixStarts() { System.out.println("######"); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| package com.xdl.test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.xdl.dao.XdlBankAccountDAO; import com.xdl.dao.XdlBankAccountDAOOracleImp; public class XdlBankAccountDAOTest { public static void main(String[] args) { ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml"); XdlBankAccountDAO accountDao = app.getBean("accountDao", XdlBankAccountDAO.class); accountDao.insertAccount(); accountDao.deleteAccount(); } }
|
AOP中五种通知对应的标注
前置通知 @Before
后置通知 @AfterReturning
最终通知 @After
异常通知 @AfterThrowing
环绕通知 @Around
- 重新编写Aspect类,显示执行的时间, 什么时间什么方法出了什么异常,统计一下方法的执行时间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| package com.xdl.aspect; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; 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.springframework.stereotype.Component; import java.text.SimpleDateFormat; import java.util.Date; @Component @Aspect
public class XdlSysTimeAspect { @AfterReturning("within(com.xdl..*)") public void printSysTime() { Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); String dateStr = sdf.format(date); System.out.println(dateStr + "@" + date.getTime()); }
@AfterThrowing(pointcut="within(com.xdl..*)",throwing="e") public void processException(JoinPoint jp,Exception e) { Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); String dateStr = sdf.format(date); System.out.println(dateStr + "||" +jp.getSignature()+"||" +e); System.out.println("出大事了"); }
@Around("within(com.xdl..*)") public Object callMethodTimes(ProceedingJoinPoint pjp) throws Throwable { long startTime = System.currentTimeMillis(); Object obj = pjp.proceed(); long endTime = System.currentTimeMillis(); System.out.println(pjp.getSignature() +":用了" + (endTime-startTime) + "毫秒" ); return obj; } }
|