AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。OOP引入封装、继承、多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合。不过OOP允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能。日志代码往往横向地散布在所有对象层次中,而与它对应的对象的核心功能毫无关系对于其他类型的代码,如安全性、异常处理和透明的持续性也都是如此,这种散布在各处的无关的代码被称为横切(cross cutting),在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。
AOP技术恰恰相反,它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。
springAOP在ssm框架中起到了解耦作用,一般用于性能检测、访问控制、事务管理、缓存、对象池管理、日志记录等方面。
实现的关键在于AOP框架自动创建AOP代理,用到的设计模式有:工厂、单例、适配器、装饰器、代理、策略、模块、观察者。
AOP的底层实现是动态代理:JDK的动态代理主要涉及到java.lang.reflect包中的两个类:Proxy和InvocationHandler。其中InvocationHandler是一个接口,可以通过实现该接口定义横切逻辑,在并通过反射机制调用目标类的代码,动态将横切逻辑和业务逻辑编织在一起。
而Proxy为InvocationHandler实现类动态创建一个符合某一接口的代理实例。这样讲一定很抽象,我们马上着手动用Proxy和InvocationHandler这两个魔法戒对上一节中的性能监视代码进行AOP式的改造。
一 AOP的基本概念
(1)Aspect(切面):通常是一个类,里面可以定义切入点和通知
(2)JointPoint(连接点):程序执行过程中明确的点,一般是方法的调用
(3)Advice(通知):AOP在特定的切入点上执行的增强处理,有before,after,afterReturning,afterThrowing,around
(4)Pointcut(切入点):就是带有通知的连接点,在程序中主要体现为书写切入点表达式
(5)AOP代理:AOP框架创建的对象,代理就是目标对象的加强。Spring中的AOP代理可以使JDK动态代理,也可以是CGLIB代理,前者基于接口,后者基于子类
二 通知类型介绍
(1)Before:在目标方法被调用之前做增强处理,@Before只需要指定切入点表达式即可
(2)AfterReturning:在目标方法正常完成后做增强,@AfterReturning除了指定切入点表达式后,还可以指定一个返回值形参名returning,代表目标方法的返回值
(3)AfterThrowing:主要用来处理程序中未处理的异常,@AfterThrowing除了指定切入点表达式后,还可以指定一个throwing的返回值形参名,可以通过该形参名
来访问目标方法中所抛出的异常对象
(4)After:在目标方法完成之后做增强,无论目标方法时候成功完成。@After可以指定一个切入点表达式
(5)Around:环绕通知,在目标方法完成前后做增强处理,环绕通知是最重要的通知类型,像事务,日志等都是环绕通知,注意编程中核心是一个ProceedingJoinPoint
先用一个实例来简单介绍:
aop.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">
<!--核心业务 -->
<bean id="serviceA" class="services.ServiceA"></bean>
<bean id="serviceB" class="services.ServiceB"></bean>
<bean id="serviceC" class="services.ServiceC"></bean>
<!--通知业务 -->
<bean id="notice" class="notice.Notice"></bean>
<!-- aop配置 -->
<aop:config>
<!--定义一个切面,切面里可以有多个切点,但如果切点在切面的外面,则该切点可以被它下面的所有切面所共享 -->
<aop:aspect ref="notice">
<!--定义一个切入点表达式 -->
<aop:pointcut id="cutpoint" expression="execution(* services.*.*(..))"/>
<!--在切入点前执行通知的方法 -->
<aop:before method="beforeNotice" pointcut-ref="cutpoint" />
<aop:after method="afterNotice" pointcut="execution(* services.ServiceC.*(..))"/>
<aop:around method="aroundNotice" pointcut-ref="cutpoint"/>
</aop:aspect>
</aop:config>
</beans>
execution表达式:用来匹配执行方法的连接点
A:@Pointcut("execution(* com.aijava.springcode.service..*.*(..))")
第一个*表示匹配任意的方法返回值,..(两个点)表示零个或多个,上面的第一个..表示service包及其子包,第二个*表示所有类,第三个*表示所有方法,第二个..表示
方法的任意参数个数
通知类
public class Notice {
public void beforeNotice(){
SimpleDateFormat simp = new SimpleDateFormat("yyyy-mm-dd hh:MM:ss");
System.out.println(simp.format(new Date())+":做了一笔业务!!!");
}
public void afterNotice(){
System.out.println("通知结束");
}
public void aroundNotice(ProceedingJoinPoint point){
System.out.println("环绕开始");
try {
String name=point.getSignature().getName();
System.out.println("方法名"+name);
} catch (Throwable e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("环绕结束");
}
}
核心业务类,三个基本相同,列出一个
public class ServiceA {
public void addAccount(){
System.out.println("addaccount");
}
}
unit4 测试类
public class Test1 {
@Test
public void beforeNotice(){
ApplicationContext ctx = new ClassPathXmlApplicationContext("aop.xml");
ServiceA serviceA = ctx.getBean("serviceA",ServiceA.class);
ServiceB serviceB = ctx.getBean("serviceB",ServiceB.class);
ServiceC serviceC = ctx.getBean("serviceC",ServiceC.class);
System.out.println("开始");
serviceA.addAccount();
serviceB.desposit();
serviceC.withDraw();
}
}
结果为:
开始
2018-19-23 03:08:17:做了一笔业务!!!
环绕开始
方法名addAccount
环绕结束
2018-19-23 03:08:17:做了一笔业务!!!
环绕开始
方法名desposit
环绕结束
2018-19-23 03:08:17:做了一笔业务!!!
环绕开始
方法名withDraw
环绕结束
通知结束