AOP 是Aspect-Oriented Programming的简称,意思是面向切面编程。
其核心思想为将程序中涉及的公共问题集中解决
怎么个意思?
struts!
web应用程序中 有很多公共问题(重复的东东) 例如设置字符集 提取参数 数据类型转换 异常处理等等。。。 怎么办?
拦截器!
这就是aop思想的一个典型实现。
AOP 是Aspect-Oriented Programming的简称,意思是面向切面编程。比如刚才的问题,这些公共的处理如果放在每个业务方法里,系统会变的臃肿,而且很难去维护。
AOP的思想就是把这些公共部分从业务方法中提取出来,集中处理。编写代码的时候,“切面”代码并不放在业务方法中,但是程序运行的时候,Spring会拦截到方法的执行,并运行这些“切面”代码。
这样就可以使我们集中精力在业务上。
AOP是对OOP的补充和完善。
其重点在于发现和提取公共问题(横切行关注点)进行集中处理
还有aop的应用吗???
例如。。。
示例: 记录日志:
AOP 中包含许多新的概念与术语,说明如下:
(1)切面 (Aspect):切面是系统中抽象出来的的某一个功能模块。
(2)连接点 (Joinpoint):程序执行过程中某个特定的点,如调用某方法或者处理异常时。在SpringAOP中,连接点总是代表某个方法的执行。
(3)通知 (Advice):通知是切面的具体实现,是放置“切面代码”的类。
(4)切入点 (Pointcut):多个连接点组成一个切入点,可以使用切入点表达式来表示。
(5)目标对象 (Target Object):包含一个连接点的对象,即被拦截的对象。
(6)AOP代理 (AOP Proxy):AOP框架产生的对象,是通知和目标对象的结合体。
(7)织入 (Weaving):将切入点和通知结合的过程称为织入。
示例 : spring aop 实现
1 导入命名空间
2 配置bean 和切面类
3 aop配置。将切面和bean连接起来
4 切入点表达式。
5 运行。
aop的原理 :通过反射看清代理 。
有借口 基于接口 没有借口 基于集成
如果有借口 注意 不要转型成具体类 会错的
也可以指定代理方式
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
Spring有两种代理方式
默认使用JDK动态代理实现AOP代理,主要用于代理接口
CGLIB代理,实现类的代理,而不是接口 更多相关内容 结合事务进行讲解
切入点表达式:
切入点指示符:如execution,最常用的切入点指示符,表示方法的执行。
方法可见性修饰符:将选择连接点的范围缩小到某种可见性的方法。如public的。
方法返回类型:如void、int、String等,也可以使用*,表示所有类型。
类名:指定完整的类名(包括完整的包名称,如java.lang.String),将选择连接点的范围缩小到某个目标类。可以结合通配符使用
方法名称:可以是全名。也可以是*(任何名称)。也可以是部分名称结合通配符,如get*,即所有名称以get开头的方法。
参数列表
方法声明抛出的异常:如 throws java.lang.IOException。
使用execution切入点指示符描述连接点范围时,除了方法返回类型、方法名称和方法参数是必须描述的以外, 其它所有的部分都是可选的(方法可见性修饰符、类名、方法声明抛出的异常)。
<aop:config>
<aop:pointcut expression="execution(public int com.zzzy.biz.impl.UserBizImpl.add(String ,int) throws java.io.IOException)" id="pc1"/>
<aop:aspect id="a" ref="log">
<aop:before method="log" pointcut-ref="pc1"/>
</aop:aspect>
</aop:config>
通配符:星号(*),用于匹配任何方法、类名或者参数类型。双点号(..),用于表示0个或者多个参数类型。
布尔运算符:AND(&&)、OR(||)和NOT(!),可以将多个表达式组合成一个新的表达式来缩小选择连接点的范围。
execution( public void s3spring.ch2.biz.impl.PhoneBizImpl.SalePhone(int) )
execution( public void s3spring.ch2.biz.im*) )
execution(* s3spring.ch2.biz.impl.*.* (..) )
execution(* s3spring.ch2.biz.impl..*(..))
示例2.9也可以用within切入点指示符进行简化。within表示某一范围内的连接点,如某包内,某类内。示例2.10表示s3spring.ch2.biz.impl包中所有的连接点:
示例2.10
within( s3spring.ch2.biz.impl. )
示例2.11表示s3spring.ch2.biz.impl.PhoneBizImpl类中所有的连接点:
示例2.11
within( s3spring.ch2.biz.impl.PhoneBizImpl )
示例2.12表示s3spring.ch2.biz.impl 包及其所有子孙包中所有的连接点:
示例2.12
within( s3spring.ch2.biz.impl.PhoneBizImpl..* )
<aop:pointcut expression="execution(* add*(..) ) and execution(int *(..))" id="pc1"/>
<aop:pointcut expression="execution(* add*(..) ) or execution(* del*(..))" id="pc1"/>
通知(Advice)
前置
后置
环绕
异常
最终
示例1 :
做日志 记录访问的方法名和时间。
Object[] getArgs():以对象数组的形式返回所有方法参数
Signature getSignature():返回方法签名,通过Signature的getName()方法可以得到方法名称;getModifiers()方法可以得到方法修饰符。
String getKind():返回当前连接点的类型,如:“method-execution”,方法执行。
Object getTarget():返回连接点所在的目标对象。
Object getThis():返回AOP自动创建的代理对象。
示例2
获取方法运行时间。
小总结:
前置通知(Before):在某连接点之前执行的通知,它可以阻止连接点的执行。例如检查权限,决定是否执行连接点。有joinpoint参数
可以获取目标相关信息。
后置通知(AfterReturning):在某连接点正常完成后执行的通知:例如,一个方法没有抛出任何异常,正常返回。
有joinpoint参数
可以获取目标相关信息。
它可以访问方法返回值。
<aop:after-returning method="log" pointcut-ref="pc1" returning="i"/>
public void log(JoinPoint pj,Object i)
注意 pj要放在前面
异常通知(AfterThrowing):在方法抛出异常退出时执行的通知。它可以访问抛出的异常。
有joinpoint参数
可以获取目标相关信息。
可以获得异常信息
<aop:after-throwing method="log" pointcut-ref="pc1" throwing="e"/>
public void log(JoinPoint pj,Exception e)
最终通知(After ):当某连接点退出的时候执行的通知(不论是正常返回还是异常退出 类似finally)。 这是传统spring AOP中没有的新的通知类型。
它不能访问返回值或抛出的异常。可以用来执行释放资源,日志记录等操作。
可以获取目标相关信息。
环绕通知(Around):也叫方法拦截器。包围一个连接点的通知。这是最强大的一种通知类型。环绕通知可以在方法调用前后完成自定义的行为。它也会选择是否继续执行连接点或直接返回它自己的返回值或抛出异常来结束执行。 可用于实现性能测试,事物管理等。
ProceedingJoinPoint
hibernate 事务
基于注解
@Component("logaspect")
@Aspect
public class LogAspect {
@Pointcut("execution( * com.zzzy.biz.*.*(..))")
public void point(){};
@Before("point()")
public void beforelog(JoinPoint jp){
System.out.println("调用"+jp.getSignature().getName()+"方法 开始时间:"+new Date());
}
@AfterReturning (pointcut="point()",returning="re")
public void afterlog(JoinPoint jp,boolean re){
System.out.println("调用"+jp.getSignature().getName()+"方法 结束时间:"+new Date());
System.out.println("返回:"+re);
}
@AfterThrowing (pointcut="point()",throwing="e")
public void errlog(JoinPoint jp,Exception e){
System.out.println("调用"+jp.getSignature().getName()+"方法 出现异常 时间:"+new Date());
System.out.println("异常信息:"+e.getMessage());
}
public void finallog(JoinPoint jp){
System.out.println("调用"+jp.getSignature().getName()+"方法 调用结束 执行最终通知 。 时间:"+new Date());
}
public Object aroundlog(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("调用"+pjp.getSignature().getName()+"方法 调用开始 时间:"+new Date());
Object o = pjp.proceed();
return o;
}
关于代理的方式
如果是基于接口实现代理。在实现多种接口的时候 会给每个接口的方法都加上事务
另外 只如果实现了无关接口 那么就会出错
解决办法 使用基于类的代理。