我查阅了一些资料 aop还是有些难以理解的,oop面向对象编程,aop是oop的一个完善,oop定义了了程序从上到下的关系,而aop定义了程序左右关系,oop中如果想给分散的对象引入一个公共的功能,例如日志功能,日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。这种散布在各处的与核心功能无关的代码被称为横切(cross-cutting)代码,在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。
而aop它使用了一种“横切”技术,把程序横向切开,并且将影响了多个类的公共行为封装起来,变成一个可重用的模块,并将其命名为“Aspect“ 这样就不会重复造轮子,减少了代码的重复,降低了模块之间的耦合度,并且有利于后期的维护
AOP的相关概念:
1.关注点:增加的是什么功能,如日志,安全等
2.切面:aspect一个关注点的模块化,这个关注点实现可能另外横切多个对象
3.通知:在切面某个特定的连接点上执行的动作
4.织入:把切面连接到其他应用程序类型或对象上,并创建一个被通知的对象
四种通知类型
第一种 前置通知
实现一个接口MethodBeforeAdvice
public class BeforeAdvice implements MethodBeforeAdvice{
/**
* method:被调用的 方法对象
* args:参数列表
* target:目标对象
*/
@Override
public void before(Method method, Object[] args, Object target)
throws Throwable {
System.out.println("前置通知");
}
}
第二种后置通知
实现的接口:AfterReturningAdvice
public class AfterAdvice implements AfterReturningAdvice{
/**
* values:返回值
* method:要执行的方法对象
* args:参数列表
* target:目标对象
*/
@Override
public void afterReturning(Object values, Method method, Object[] args,
Object target) throws Throwable {
System.out.println("后置通知");
}
}
第三种异常通知
实现的接口是ThrowsAdvice
public class ExceptionAdvice implements ThrowsAdvice{
/**
* method:被调用的 方法对象
* args:参数列表
* target:目标对象
* Exception:异常类型
*/
public void afterThrowing(Method method, Object[] args, Object target, Exception e){
System.out.println("方法 " + method.getName() + " 异常出现 : " + e.getClass().getName());
}
public void afterThrowing(Throwable e){
System.out.println("异常出现 : " + e.getClass().getName());
}
}
第四种环绕通知,环绕通知是上面三种通知的结合体,我们可以二选一,写了上面三个通知就不用使用环绕通知,当然我们也可以只写一个环绕通知
public class AroundAdvice implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Object obj = null;
System.out.println("环绕通知");
try {
// 前置通知
System.out.println("前置通知"+invocation.getMethod().getName());
// 调用具体方法
obj = invocation.proceed();
// 后置通知
System.out.println("后置通知"+invocation.getMethod().getName());
} catch (Exception e) {
// 异常通知
e.printStackTrace();
System.out.println("异常通知" + e.getClass().getName());
throw e;
}
return obj;
}
}
下面是beans.xml的配置 (两种方式)
第一种方式 AspectJ方式(直接在下配置)
<?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="service" class="cn.bjsxt.service.impl.UserServiceImpl"></bean>
<bean id="before" class="cn.bjsxt.avice.BeforeAdvice"></bean>
<bean id="after" class="cn.bjsxt.avice.AfterAdvice"></bean>
<bean id="e" class="cn.bjsxt.avice.ExceptionAdvice"></bean>
<bean id="around" class="cn.bjsxt.avice.AroundAdvice"></bean>
<aop:config>
<!-- expression:代表在什么情况下执行advice功能,属性值为一个表达式
execution代表执行
* cn.bjsxt.service.impl.*.*(..)
第一个*代表所有返回类型
cn.bjsxt.service.impl代表需要执行advice功能的包
第二个*代表包中所有的类
第三个*代表类中所有的方法
(..)代表任意参数结构
-->
<aop:pointcut expression="execution(* cn.bjsxt.service.impl.*.*(..))" id="pointcut"/>
<!-- advice-ref:通知对象的引用,pointcut-ref:切入点对象的引用 -->
<aop:advisor advice-ref="before" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="after" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="e" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="around" pointcut-ref="pointcut"/>
</aop:config>
</beans>
上面我把前置通知,后置通知注释了,因为同时配置这三个通知会产生干扰,所以执行的时候看一下,需要执行什么。
第二种配置方式 :POJO方式(在下配置)
首先创建一个Logs类
public class Logs {
public void before(){
System.out.println("前置通知");
}
public void after(){
System.out.println("后置通知");
}
public void around(){
System.out.println("环绕通知");
}
}
配置:配置时注意顺序,后置通知如果配置在前置通知前将不会执行
<!-- 配置srvice -->
<bean id="userService" class="cn.bjsxt.service.impl.UserServiceImpl"/>
<!-- 基于pojo配置 -->
<bean id="logs" class="cn.bjsxt.avdvice.Logs"/>
<aop:config>
<!--引用pojo -->
<aop:aspect ref="logs">
<aop:pointcut expression="execution( * cn.bjsxt.service.impl.*.*(..))" id="aspect"/>
<aop:before method="before" pointcut-ref="aspect"/>
<aop:after method="after" pointcut-ref="aspect"/>
<aop:around method="around" pointcut-ref="aspect"/>
</aop:aspect>
</aop:config>
还有一种是注解形式,不需要配置<aop:config>
就可以使用,直接创建一个切入的类,和一个引入的类
使用注解时记住在类前加入@Aspect,否则不会执行通知,
@Aspect
public class Loggs {
//配置切入点
@Before("execution ( * cn.bjsxt.service.impl.*.*(..))")
public void before(){
System.out.println("方法执行前");
}
@After("execution ( * cn.bjsxt.service.impl.*.*(..))")
public void after(){
System.out.println("方法执行后");
}
@Around("execution ( * cn.bjsxt.service.impl.*.*(..))")
public Object aroud(ProceedingJoinPoint jp ) throws Throwable{
System.out.println("环绕前");
System.out.println("签名:"+jp.getSignature());
Object obj = jp.proceed();
System.out.println("环绕后");
return obj;
}
}
注解的配置
<!-- 配置自定义注解方式 -->
<!-- 配置srvice -->
<bean id="userService" class="cn.bjsxt.service.impl.UserServiceImpl"/>
<bean id="loggs" class="cn.bjsxt.log.Loggs"></bean>
<aop:aspectj-autoproxy/>
补充:expression=”execution( * cn.bjsxt.service.impl.* .* (..))”,这个属性值中的 * 类似正则中的 * ,是全部的意思,也可以写为print* ,这代表切面中只要是以print开头的方法都会拦截到
以上是个人的认识,不足之处请之处,欢迎补充 互相学习 谢谢