Spring-AOP

什么是切面?

首先理解什么是切点:就是我们要作用的那个方法。

接着理解什么是通知:通知就是在方法之前,方法之后进行额外业务。

最后理解什么是织入:织入就是将切点和通知进行联系。

切面就是这三概念的总和

理解什么是aop

知道动态代理没有?aop做的就是跟动态代理一样的事情。

aop原理就是动态代理。

aop面向切面编程是一种在方法前后或者异常时输出一段逻辑而不改变原来的方法。是一种增强逻辑的思想。

前置通知:before

使用前记得导入包aop的包。

Spring知识点(xml方式实现的五种aop通知)_方法名

我们先实现一个类

package daoimpl;
//定义一个say类
public class Say {
//定义一个说出一句话的方法SayAWord()
public String SayAWord() {
return "你好aop";
}
}


再定义一个people类

package serviceimpl;
import daoimpl.Say;
public class People {
//人具备说话的属性
private Say say;

public String PeopleSay() {
return this.say.SayAWord();
}
public Say getSay() {
return say;
}

public void setSay(Say say) {
this.say = say;
}
}


建立applicationContent.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:p="http://www.springframework.org/schema/p"
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 ">
<!--注册say类 -->
<bean id="say" class="daoimpl.Say"></bean>
<!--注册people类 -->
<bean id="people" class="serviceimpl.People" autowire="byName"></bean>


</beans>


建立测试类,实例化一个people

package test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import serviceimpl.People;

public class TestBefore {
public static void main(String[] args) {
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContent.xml");
People people=(People)applicationContext.getBean("people");
people.PeopleSay();
}
}


Spring知识点(xml方式实现的五种aop通知)_spring_02

现在已经可以建立一个人说话的例子了。

接下来在实现前置通知before

建立一个aop包,在里面建立UseServiceLog类

Spring知识点(xml方式实现的五种aop通知)_xml_03

package aop;


import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
public class UseServiceLog {
private Logger logger=Logger.getLogger(UseServiceLog.class);
//前置通知的方法
//这里的参数JoinPoint 通过他可以得到方法的信息
public void before(JoinPoint p) {
logger.info("调用了"+p.getTarget()+"的"+p.getSignature().getName()+"方法");
}

}


这个before就是调用的前置方法

为了节省代码量下面只讲重点

为applicationContent.xml多添加下面的配置

 <!-- 把before引入 -->
<bean id="UseServiceLog" class="aop.UseServiceLog"></bean>
<!--开启aop-->
<aop:config>
<!-- 确定切点(匹配方法名字叫public String PeopleSay()的方法) -->
<aop:pointcut expression="execution(public String PeopleSay())" id="pointcut"></aop:pointcut>

<!--对这个方法织入也就是做切点和before关联 -->
<aop:aspect ref="UseServiceLog">
<aop:before method="before" pointcut-ref="pointcut"></aop:before>
</aop:aspect>
</aop:config>


控制台输出结果

Spring知识点(xml方式实现的五种aop通知)_方法名_04

后置通知:after

类似于前置通知

public void after(JoinPoint p) {
logger.info("-----调用了"+p.getTarget()+"的"+p.getSignature().getName()+"方法");
}
<aop:config>
<!-- 确定切点(匹配方法名字叫public String PeopleSay()的方法) -->
<aop:pointcut expression="execution(public String PeopleSay())" id="pointcut"></aop:pointcut>

<!--对这个方法织入也就是坐切点和before关联 -->
<aop:aspect ref="UseServiceLog">
<aop:before method="before" pointcut-ref="pointcut"></aop:before>
<!--后置就是前置换了一个单词 -->
<aop:after method="after" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>


返回通知:afterruntime

 
public void afterreturn(JoinPoint p,Object result) {
logger.info("-----调用了"+p.getTarget()+"的"+p.getSignature().getName()+"方法"
+result.toString()
);
System.out.println("返回通知");
}
<!--开启aop-->
<aop:config>
<!-- 确定切点(匹配方法名字叫public String PeopleSay()的方法) -->
<aop:pointcut expression="execution(public String PeopleSay())" id="pointcut"></aop:pointcut>

<!--对这个方法织入也就是坐切点和before关联 -->
<aop:aspect ref="UseServiceLog">
<aop:before method="before" pointcut-ref="pointcut"></aop:before>

<aop:after method="after" pointcut-ref="pointcut"/>
<!--返回通知 -->
<aop:after-returning method="afterreturn" pointcut-ref="pointcut" returning="result"/>
</aop:aspect>
</aop:config>


异常通知:after-throwing

//添加异常通知方法
public void afterthrowing(JoinPoint p,RuntimeException exception) {
logger.info("-----调用了"+p.getTarget()+"的"+p.getSignature().getName()+"方法"
+exception.toString()
);
System.out.println("异常通知");
}
添加异常通知的配置
 <!-- 把before引入 -->
<bean id="UseServiceLog" class="aop.UseServiceLog"></bean>
<!--开启aop-->
<aop:config>
<!-- 确定切点(匹配方法名字叫public String PeopleSay()的方法) -->
<aop:pointcut expression="execution(public String PeopleSay())" id="pointcut"></aop:pointcut>

<!--对这个方法织入也就是坐切点和before关联 -->
<aop:aspect ref="UseServiceLog">

<aop:before method="before" pointcut-ref="pointcut"></aop:before>

<aop:after method="after" pointcut-ref="pointcut"/>

<aop:after-returning method="afterreturn" pointcut-ref="pointcut" returning="result"/>

<aop:after-throwing method="afterthrowing" pointcut-ref="pointcut" throwing="exception"/>

</aop:aspect>
</aop:config>


在调用方法时候出现异常会调用这个方法。

环绕通知:around

环绕通知做的就是动态代理

//环绕通知方法
public Object around(ProceedingJoinPoint pjd) {

Object result = null;//返回值
String methodName = pjd.getSignature().getName();//得到方法名

try {
//前置通知
System.out.println("The method " + methodName + " begins with " + Arrays.asList(pjd.getArgs()));
result = pjd.proceed();//执行方法 //返回通知
System.out.println("The method " + methodName + " ends with " + result);
} catch (Throwable e) {
//异常通知
System.out.println("The method " + methodName + " occurs exception:" + e);
throw new RuntimeException(e);
}
//后置通知
System.out.println("The method " + methodName + " ends");
return result;
}


需要开启

 <!-- 把before引入 -->
<bean id="UseServiceLog" class="aop.UseServiceLog"></bean>
<!--开启aop-->
<aop:config>
<!-- 确定切点(匹配方法名字叫public String PeopleSay()的方法) -->
<aop:pointcut expression="execution(public String PeopleSay())" id="pointcut"></aop:pointcut>

<!--对这个方法织入也就是坐切点和before关联 -->
<aop:aspect ref="UseServiceLog">
<!--前置-->
<aop:before method="before" pointcut-ref="pointcut"></aop:before>
<!--后置-->
<aop:after method="after" pointcut-ref="pointcut"/>
<!--返回通知-->
<aop:after-returning method="afterreturn" pointcut-ref="pointcut" returning="result"/>
<!--异常-->
<aop:after-throwing method="afterthrowing" pointcut-ref="pointcut" throwing="exception"/>
<!--环绕-->
<aop:around method="around" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>


关于5种通知的位置:(下面详细讲了5个通知的位置)

System.out.println("整个代码块合起来叫环绕通知");
{
System.out.println("执行方法“1+1”之前叫前置");
try {
int a=1+1;}catch (Exception e) {
System.out.println("执行方法“1+1”之中出现异常叫异常通知");
}
System.out.println("执行方法“1+1”之后叫后置");
return a;
System.out.println("执行return之后叫返回后通知");
}

写得比我好太多,可以跟好理解aop的通知

整篇文章从静态代理--->JDK代理---->aop的Aspeactj给我很多帮助