文章目录
1.AOP简介
AOP 即 Aspect Oriented Program
面向切面编程
首先,在面向切面编程的思想里面,把功能分为核心业务功能,和周边功能。
所谓的核心业务,比如登陆,增加数据,删除数据都叫核心业务
所谓的周边功能,比如性能统计,日志,事务管理等等
周边功能在Spring的面向切面编程AOP思想里,即被定义为切面
在面向切面编程AOP的思想里面,核心业务功能和切面功能分别独立开发
然后把切面功能和核心业务功能 “编织” 在一起,这就叫AOP
2.什么是advice,joinpoint ,pointcut
- AOP中的一个重要术语是advance。它是特定join-point上某个aspect 所采取的操作。
- Joinpoint是程序的执行点,例如方法的执行或异常的处理。在Spring AOP中,Joinpoint始终表示方法执行。
- pointcut是匹配Joinpoint的表达式。
- advice与pointcut表达式相关联,并在pointcut匹配的任何连接点处运行。
- Spring默认使用AspectJ切入点表达式语言。
3.AOP的advice类型
- Before advice:在连接点之前执行但不能阻止执行流程进入连接点议(除非它抛出异常)。
- After returning advice:在连接点正常完成后执行:例如,如果方法返回而且不抛出异常就会执行。
- After throwing advice:如果方法通过抛出异常退出,则执行。
- After (finally) advice:无论连接点退出的方式(正常或异常返回),都要执行。
- Around advice:围绕连接点,例如方法调用。这是最有力的advice。around通知可以在方法调用之前和之后执行自定义行为。它还负责选择是继续加入点还是通过返回自己的返回值或抛出异常来加速advice的方法执行。
2.AOP基于XML的配置(已不用)
1.声明一个aspect
使用 <aop:aspect> 元素声明一个aspect。aspect使用ref引用bean,id自取。
<bean id="loggerAspect" class="com.how2java.aspect.LoggerAspect"/>
<aop:config>
<!-- 指定左边的辅助功能 然后通过aop:config把业务对象与辅助功能编织在一起。 -->
<aop:aspect id="logAspect" ref="loggerAspect">
······
</aop:aspect>
</aop:config>
2.声明一个切入点(pointcut)
一个切入点有助于确定使用不同advice执行的连接点(即方法)。
在处理基于配置的 XML 架构时,切入点将会按照如下所示定义:
<aop:config>
<aop:aspect id="logAspect" ref="loggerAspect">
<aop:pointcut id="loggerCutpoint"
expression=
"execution(* com.how2java.service.ProductService.*(..)) "/>
······
</aop:aspect>
</aop:config>
"execution( com.how2java.service.ProductService.(…)) "
这表示对满足如下条件的方法调用,进行切面操作:
* 返回任意类型
com.how2java.service.ProductService.* 包名以 com.how2java.service.ProductService 开头的类的任意方法
(…) 参数是任意数量和类型
3.声明advice
advice有五个
<!-- log()是切片类内的方法 -->
<aop:around pointcut-ref="loggerCutpoint" method="log"/>
4.总体:
</bean>
<!-- 声明日志切面 -->
<bean id="loggerAspect" class="com.how2java.aspect.LoggerAspect"/>
<aop:config>
<aop:aspect id="logAspect" ref="loggerAspect">
<aop:pointcut id="loggerCutpoint"
expression=
"execution(* com.how2java.service.ProductService.*(..)) "/>
<!-- log()是切片类内的方法 -->
<aop:around pointcut-ref="loggerCutpoint" method="log"/>
</aop:aspect>
</aop:config>
再举例子:
这里是Logging.java文件的内容。这实际上是aspect模块的一个示例,它定义了在各个点调用的方法。
package com.tutorialspoint;
public class Logging {
/**
* This is the method which I would like to execute
* before a selected method execution.
*/
public void beforeAdvice(){
System.out.println("Going to setup student profile.");
}
/**
* This is the method which I would like to execute
* after a selected method execution.
*/
public void afterAdvice(){
System.out.println("Student profile has been setup.");
}
/**
* This is the method which I would like to execute
* when any method returns.
*/
public void afterReturningAdvice(Object retVal){
System.out.println("Returning:" + retVal.toString() );
}
/**
* This is the method which I would like to execute
* if there is an exception raised.
*/
public void AfterThrowingAdvice(IllegalArgumentException ex){
System.out.println("There has been an exception: " + ex.toString());
}
}
的英文下面Student.java文件的内容:
package com.tutorialspoint;
public class Student {
private Integer age;
private String name;
public void setAge(Integer age) {
this.age = age;
}
public Integer getAge() {
System.out.println("Age : " + age );
return age;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
System.out.println("Name : " + name );
return name;
}
public void printThrowException(){
System.out.println("Exception raised");
throw new IllegalArgumentException();
}
}
的英文下面MainApp.java文件的内容:
package com.tutorialspoint;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("Beans.xml");
Student student = (Student) context.getBean("student");
student.getName();
student.getAge();
student.printThrowException();
}
}
下面是配置文件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-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">
<aop:config>
<aop:aspect id="log" ref="logging">
<aop:pointcut id="selectAll"
expression="execution(* com.tutorialspoint.*.*(..))"/>
<aop:before pointcut-ref="selectAll" method="beforeAdvice"/>
<aop:after pointcut-ref="selectAll" method="afterAdvice"/>
<aop:after-returning pointcut-ref="selectAll"
returning="retVal"
method="afterReturningAdvice"/>
<aop:after-throwing pointcut-ref="selectAll"
throwing="ex"
method="AfterThrowingAdvice"/>
</aop:aspect>
</aop:config>
<!-- Definition for student bean -->
<bean id="student" class="com.tutorialspoint.Student">
<property name="name" value="Zara" />
<property name="age" value="11"/>
</bean>
<!-- Definition for logging aspect -->
<bean id="logging" class="com.tutorialspoint.Logging"/>
</beans>
一旦你已经完成的创建了源文件和bean配置文件,让我们运行一下应用程序。如果你的应用程序一切都正常的话,这将会输出以下消息:
Going to setup student profile.
Name : Zara
Student profile has been setup.
Returning:Zara
Going to setup student profile.
Age : 11
Student profile has been setup.
Returning:11
Going to setup student profile.
Exception raised
Student profile has been setup.
There has been an exception: java.lang.IllegalArgumentException
.....
other exception content
3.AOP基于注解@AspectJ的配置
举例子:
这里是 Logging.java 文件的内容。这实际上是 aspect 模块的一个示例,它定义了在各个点调用的方法。注释写的很清楚
这里用一个selectAll匹配了所有方法,然后对selectAll进行操作。也可以对每个方法单独匹配
package com.tutorialspoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
@Aspect //!!!!
public class Logging {
/** Following is the definition for a pointcut to select
* all the methods available. So advice will be called
* for all the methods.
*/
@Pointcut("execution(* com.tutorialspoint.*.*(..))")
private void selectAll(){}
/**
* This is the method which I would like to execute
* before a selected method execution.
*/
@Before("selectAll()")
public void beforeAdvice(){
System.out.println("Going to setup student profile.");
}
/**
* This is the method which I would like to execute
* after a selected method execution.
*/
@After("selectAll()")
public void afterAdvice(){
System.out.println("Student profile has been setup.");
}
/**
* This is the method which I would like to execute
* when any method returns.
*/
@AfterReturning(pointcut = "selectAll()", returning="retVal")
public void afterReturningAdvice(Object retVal){
System.out.println("Returning:" + retVal.toString() );
}
/**
* This is the method which I would like to execute
* if there is an exception raised by any method.
*/
@AfterThrowing(pointcut = "selectAll()", throwing = "ex")
public void AfterThrowingAdvice(IllegalArgumentException ex){
System.out.println("There has been an exception: " + ex.toString());
}
}
下面是 Student.java 文件的内容:
package com.tutorialspoint;
import org.springframework.stereotype.Component;
//@Component 表示这是一个bean,由Spring进行管理
@Component
public class Student {
private Integer age;
private String name;
public void setAge(Integer age) {
this.age = age;
}
public Integer getAge() {
System.out.println("Age : " + age );
return age;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
System.out.println("Name : " + name );
return name;
}
public void printThrowException(){
System.out.println("Exception raised");
throw new IllegalArgumentException();
}
}
下面是 MainApp.java 文件的内容:
package com.tutorialspoint;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("Beans.xml");
Student student = (Student) context.getBean("student");
student.getName();
student.getAge();
student.printThrowException();
}
}
下面是配置文件 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-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">
<!--找到被注解了的切面类,进行切面配置-- >
<aop:aspectj-autoproxy/>
<!-- Definition for student bean -->
<bean id="student" class="com.tutorialspoint.Student">
<property name="name" value="Zara" />
<property name="age" value="11"/>
</bean>
<!-- Definition for logging aspect -->
<bean id="logging" class="com.tutorialspoint.Logging"/>
</beans>
一旦你已经完成的创建了源文件和 bean 配置文件,让我们运行一下应用程序。如果你的应用程序一切都正常的话,这将会输出以下消息:
Going to setup student profile.
Name : Zara
Student profile has been setup.
Returning:Zara
Going to setup student profile.
Age : 11
Student profile has been setup.
Returning:11
Going to setup student profile.
Exception raised
Student profile has been setup.
There has been an exception: java.lang.IllegalArgumentException
.....
other exception content
我们来看其中的一段:
Going to setup student profile.
Name : Zara
Student profile has been setup.
Returning:Zara
这段输出是当我们调用getName()方法时输出的。
@Before("selectAll()")
public void beforeAdvice(){
System.out.println("Going to setup student profile.");
}
在所有切入点之前调用,输出"Going to setup student profile."
public String getName() {
System.out.println("Name : " + name );
return name;
}
执行方法,输出Name
@After("selectAll()")
public void afterAdvice(){
System.out.println("Student profile has been setup.");
}
在所有切入点之后调用,输出"Student profile has been setup."
@AfterReturning(pointcut = "selectAll()", returning="retVal")
public void afterReturningAdvice(Object retVal){
System.out.println("Returning:" + retVal.toString() );
}
在方法返回后调用(有返回值才会调用)。输出"Returning:" + retVal.toString()。retVal是返回值,可以配置。