版权声明:本文为博主原创文章,转载请注明出处!!!!!!!!!! https://blog.csdn.net/qq_21434959/article/details/83120258
1. Spring 的AOP介绍
Spring 中 的AOP联盟为通知Advice定义了org.aoplliance.aop.Advice
Spring按照通知Advice在目标方法的连接点位置,可以分为5类:
- 前置通知 org.springframework.aop.MethodBeforeAdvice
在目标方法执行前实施增强 - 后置通知 org.springframework.aop.AfterReturningAdvice
在目标方法执行后实施增强 - 环绕通知 org.aopalliance.intercept.MethodInterceptor
在目标方法执行前后实施增强 - 异常抛出通知 org.springframework.aop.ThrowsAdvice
在方法抛出异常后实施增强 - 引介通知 org.springframework.aop.IntroductionInterceptor
在目标类中添加一些新的方法和属性
1.1 MethodBeforeAdvice源码分析:
/**
* Interceptor to wrap am {@link org.springframework.aop.MethodBeforeAdvice}.
* Used internally by the AOP framework; application developers should not need
* to use this class directly.
*
* @author Rod Johnson
*/
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable {
private MethodBeforeAdvice advice;
/**
* Create a new MethodBeforeAdviceInterceptor for the given advice.
* @param advice the MethodBeforeAdvice to wrap
*/
public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
Assert.notNull(advice, "Advice must not be null");
this.advice = advice;
}
public Object invoke(MethodInvocation mi) throws Throwable {
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
return mi.proceed();
}
}
如上图源码:
invoke方法中先执行了advice 通知的方法,而后执行了目标方法。
- 环绕通知,需要手动执行目标方法:
try{
//前置通知
//执行目标方法
//后置通知
} catch(){
//抛出异常通知
}
其他通知请自行查看源码!!!
2. 基于aop 的切面编程
- 让spring 创建代理对象,从spring容器中手动的获取代理对象。
2.1 demo样例如下:
需要导入的包如下:
核心:4 + 1
aop联盟(规范)、spring-aop(实现)
2.2 需要编写的文件
- beans.xml
- AopTest.java
- MyAspect.java
- UserService.java
- UserServiceImp.java
主要说下UserServiceImpl、beans.xml 、 AopTest.java、MyAspect.java:(如有不清楚,请自行查看码云源码 )
UserServiceImpl.java
public class UserServiceImp implements UserService {
public void addUser() {
System.out.println("d_aspect.a_xml.............a_jdk");
}
public void updateUser() {
int i= 1/0;
System.out.println("d_aspect.a_xml.............a_jdk");
}
public void deleteUser() {
System.out.println("d_aspect.a_xml.............a_jdk");
}
}
MyAspect切面类如下:
/**
* @author liupenghao 切面类
*/
public class MyAspect implements MethodInterceptor{
public Object invoke(MethodInvocation mi) throws Throwable {
System.out.println("前3");
Object obj = mi.proceed();
System.out.println("后3");
return obj;
}
}
beans.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 definitions here -->
<!--配置目标类 -->
<bean id="UserService" class ="com.uu.c_spring_aop.UserServiceImp"></bean>
<!-- 配置切面类 -->
<bean id="myaspect" class="com.uu.c_spring_aop.MyAspect"> </bean>
<!--
aop 编程
1. 导入命名空间
2. 使用<app:config> 进行配置
proxy-target-class 声明是否使用cglib
aop:pointcut 切入点,从哪目标对象获得具体方法
aop:advisor 特殊的切面,只有一个通知和一个切入点
advice-ref 通知引入
pointcut-ref 切入点引用
3. 切入点表达式
execution(* com.uu.c_spring_aop.*.*(..))
选择方法 返回值任意 包
-->
<aop:config proxy-target-class="true">
<aop:pointcut expression="execution(* com.uu.c_spring_aop.*.*(..))" id="myPointCut"></aop:pointcut>
<aop:advisor advice-ref="myaspect" pointcut-ref="myPointCut"/>
</aop:config>
</beans>
AopTest 如下:
public class AopTest {
@Test
public void Test() {
String xmlPath = "com/uu/c_spring_aop/beans.xml";
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
xmlPath);
UserService service = context.getBean("UserService",
UserService.class);
service.addUser();
service.updateUser();
service.deleteUser();
}
}
2.3 基于AspectJ 的切面编程
与基于AOP编程不同的是,AspectJ提供了 按照通知Advice在目标方法的连接点位置的不同 而生成的实现类(如:AfterReturningAdviceInterceptor),用的时候只需配置通知方法即可。
可以简单的理解为:根据用户要在目标方法 实时操作的位置的不同,而实现的方法拦截器。
2.3.1 demo小案例
程序的实现需要的文件如下:
- MyAspect.java
- UserService.java
- UserServiceImp.java
- xmlTest.java
- beans.xml
UserSerice、UserServiceImpl、xmlTest 文件和2.2的一致,不做赘述
MyAspect.javaq 如下:
public class MyAspect {
public void myBefore(JoinPoint joinPoint) {
System.out.println("前置通知:" + joinPoint.getSignature().getName());
}
public void MyAfterRetruning(JoinPoint joinPoint, Object ret) {
System.out.println("后置通知: " + joinPoint.getSignature().getName() + ","
+ ret);
}
public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println(" 前");
Object obj = joinPoint.proceed();
System.out.println("后");
return obj;
}
public void myAfterThrowing(JoinPoint joinPoint,Throwable e){
System.out.println("抛出异常 通知:"+ e.getMessage());
}
public void myAfter(JoinPoint joinPoint){
System.out.println("最终通知");
}
}
beans.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">
<!-- 1 创建目标类 -->
<bean id="userServiceId" class="com.uu.d_aspect.a_xml.UserServiceImp"></bean>
<!-- 2 创建切面类(通知) -->
<bean id="myAspectId" class="com.uu.d_aspect.a_xml.MyAspect"></bean>
<!-- 3 aop编程 <aop:aspect> 将切面类 声明“切面”,从而获得通知(方法) ref 切面类引用 <aop:pointcut>
声明一个切入点,所有的通知都可以使用。 expression 切入点表达式 id 名称,用于其它通知引用 -->
<aop:config>
<aop:aspect ref="myAspectId">
<aop:pointcut
expression="execution(* com.uu.d_aspect.a_xml.UserServiceImp.*(..))"
id="myPointCut" />
<!-- 前置通知: method: 通知,及方法名 pointcut: 切入点表达式 pointcut-ref: 切入点引用 -->
<!-- <aop:before method="myBefore" pointcut-ref="myPointCut" /> -->
<!-- <aop:after-returning method="MyAfterRetruning" pointcut-ref="myPointCut"
returning="ret"/> -->
<!-- <aop:around method="myAround" pointcut-ref="myPointCut" /> -->
<aop:after-throwing method="myAfterThrowing"
pointcut-ref="myPointCut" throwing="e" />
<aop:after method="myAfter" pointcut-ref="myPointCut" />
</aop:aspect>
</aop:config>
</beans>
2.4 基于注解的配置
与基于xml配合相比,注解配置较为简单,可以做到强耦合,基于method进行切面的编程。再加上spring 强大的IOC(控制反转),DI(依赖注入),代码在实现功能的基础上,变得简洁明了,完美!!!
2.4.1demo小案例
所需的文件如下:
- beans.xml
- AnnoTest.java
- MyAspect.java
- UserService.java
- UserServiceImp.java
UserServiceImpl.java
@Service("userServiceId")
public class UserServiceImp implements UserService {
public void addUser() {
System.out.println("d_aspect.b_anno.............a_jdk");
}
public void updateUser() {
//int i= 1/0;
System.out.println("d_aspect.b_anno.............a_jdk");
}
public void deleteUser() {
System.out.println("d_aspect.b_anno.............a_jdk");
}
}
MyAspect.java
@Component
@Aspect
public class MyAspect {
@Pointcut("execution(public * com.uu.d_aspect.b_anno.UserServiceImp.*(..))")
public void myPointCut(){
}
@After("myPointCut()")
public void myAfter(JoinPoint joinPoint){
System.out.println("最终通知");
}
}
beans.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 1. 扫描 注解类 -->
<context:component-scan base-package="com.uu.d_aspect.b_anno"></context:component-scan>
<!-- 2. 确定 aop 注解生效 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>