连接点 : 应用程序中插入切面的地点 , 可以是方法调用 , 异常抛出 , 或者要修改的字段 . 一个类或者一段代码具有一些边界性质的特定点, 这些代码中特定点就是连接点, spring只支持方法的连接点 .
切入点 : 定义了通知应该应用在哪些连接点 , 被应用的连接点就是切入点 . 通知可以应用到系统的任何连接点上 .
切面(aspect) : 要实现的交叉功能 , 由切点和增强(引入)组成, 包括对横切关注功能的定义, 也包括对连接点定义 .
通知(增强) : 切面的具体实现 ,是织入到目标连接点的一段程序代码, Spring提供的增强接口都是带方位名的 , 如 : BeforeAdvice, AfterReturningAdvice, ThrowsAdvice等,
引入 : 一种特殊增强, 它为类添加一些属性和方法, 这样即使一个业务类原本没有实现某个接口, 通过引入功能, 可以动态给业务类添加接口实现逻辑, 让业务类称为这个接口的实现类 .
织入 : 创建代理对象的过程 ,
三种织入方式 :
1>编译器织入 , 在java–>class文件时候 , 织入 . 需要特殊编译器 .
2>类装在其织入 , 将java字节码载入到jvm , 将通知织入 , 需要特殊的classloader
3>运行期 , spring就是用这种 .
上网找关于这两个概念区别:
估计很多人看了之后也是懵懵哒 . 可以从这些说法中得出, Advisor是一个特殊的Aspect, 那么为什么要有Advisor呢?
Advisor充当Advice(通知)和Pointcut的适配器 , 一般会有advice和pointcut属性 . 大多数切面是由定义切面行为的通知 和 定义切面在什么地方执行的切入点组合而成 . spring认识到这一点 , 提供了Advisor类 . 这个类将通知和切入点组合到一个对象中 .
业务类接口:
public interface UserService {
public void add();
}
业务类:
@Service("userService")
public class UserServiceImpl implements UserService {
@Override
public void add() {
System.out.println("add.......");
}
}
客户端:
public class App {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("spring/applicationContext-service.xml");
UserService userService = (UserService) ac.getBean("userService");
userService.add();
}
}
applicationContext-service.xml
<context:component-scan base-package="com.zhangyan.service"/>
<bean id="myAdvice" class="com.zhangyan.busi.MyAroundAdvice">bean>
<aop:config>
<aop:pointcut expression="execution(* com.zhangyan.service.*.*(..))" id="myPointCut"/>
<aop:advisor advice-ref="myAdvice" pointcut-ref="myPointCut"/>
aop:config>
MyAroundAdvice :
public class MyAroundAdvice implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("start...");
Object val = methodInvocation.proceed();
System.out.println("end...");
return val;
}
}
当调用App客户端类打印结果:
start...
add.......
end...
applicationContext-service.xml
<context:component-scan base-package="com.zhangyan.service"/>
<bean id="consumeAspect" class="com.zhangyan.busi.ConsumeAdvice">bean>
<aop:config>
<aop:aspect id="myAspect" ref="consumeAspect">
<aop:pointcut expression="execution(* com.zhangyan.service.*.*(..))" id="myPointCut"/>
<aop:around method="doAround" pointcut-ref="myPointCut" />
aop:aspect>
aop:config>
ConsumeAdvice通知类:
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
public class ConsumeAdvice {
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("start.......");
Object retVal = pjp.proceed();
System.out.println("end.........");
return retVal;
}
}
当执行完App客户端后, 执行结果:
start...
add.......
end...
可以很明显看到两种实现方式,
如果使用advisor, 那么定义通知类时候, 如果是环绕通知, 通知类必须实现MethodInterceptor, 当然还有其他接口 : BeforeAdvice , ThrowsAdvice, AfterReturningAdvice 等接口.
如果使用aspect, 那么配置<’aop:around method=”doAround” pointcut-ref=”myPointCut” />, 相当于声明使用环绕通知, 那么通知类不用实现任何接口, 通知类中方法名称和配置中method相同. 通知类中方法参数应该是ProceedingJoinPoint, 就ok .
所以advisor是Spring为了简化操作, 定义这种方式, 当然也有局限, 只能有一个Pointcut和一个advice .
在advice中可以定义多个Point和advice .
配置:
<bean id="consumeAspect" class="com.zhangyan.busi.ConsumeAdvice">bean>
<aop:config>
<aop:aspect id="myAspect" ref="consumeAspect">
<aop:pointcut expression="execution(* com.zhangyan.service.*.*(..))" id="myPointCut"/>
<aop:around method="doAround" pointcut-ref="myPointCut" />
<aop:before method="doBefore" pointcut-ref="myPointCut"/>
<aop:after method="doAfter" pointcut-ref="myPointCut" />
<aop:after-throwing method="doThrowing" pointcut-ref="myPointCut" throwing="ex"/>
aop:aspect>
aop:config>
通知类 :
public class ConsumeAdvice {
public void doBefore(JoinPoint jp) {
System.out.println("log Begining method: " + jp.getTarget().
getClass().getName() + "." + jp.getSignature().getName());
}
public void doAfter(JoinPoint jp) {
System.out.println("log Ending method: " + jp.getTarget().getClass()
.getName() + "." + jp.getSignature().getName());
}
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("start.......");
Object retVal = pjp.proceed();
System.out.println("end.........");
return retVal;
}
public void doThrowing(JoinPoint jp, Throwable ex) {
System.out.println("method " + jp.getTarget().getClass().getName()
+ "." + jp.getSignature().getName() + " throw exception");
System.out.println(ex.getMessage());
}
}