Spring AOP
什么是AOP
AOP(Aspect Oriented Programming)意为面向切面编程。
一种通过预编译方式和运行期动态代理实现程序功能的统一维护的技术。
对业务逻辑各个部分隔离,降低耦合度,提高重用性,开发效率。
主要功能:日志记录,性能统计,安全控制,事务处理,异常处理等。
底层实现原理是JDK动态代理和CGLIB动态代理。前者必须实现接口,后者直接操纵字节码。
AOP与OOP
OOP面向对象编程,对业务处理过程的实体及其属性,行为进行抽象封装。
AOP面向切面编程,对业务处理过程中的切面提取。
AOP术语
连接点(Joinpoint)
AOP用来标记插入代码的特定位置(方法调用前,方法调用后,方法调用前后,抛出异常)
切点(Pointcut)
AOP查询连接点的方式,一个切点可以匹配一个或多个连接点。
增强(Advice)
AOP插入连接点的一段代码,同时包括方位信息。结合方位信息和切点信息可以查询到目标连接点。
引介(Introduction)
一种特殊的增强,可以为类添加属性和方法。一个业务类原本没有实现某接口,通过引介,动态地为该业务添加接口的实现逻辑,使得该业务成为这个接口的实现类。
织入(Weaving)
插入代码的过程,包括以下三种:
编译器织入
需要特殊编译器
类装载器织入
需要特殊的类装载器
动态代理织入
切面(Aspect)
由切点和增强组成
目标对象(Target)
增强逻辑的织入目标类。
代理(proxy)
一个类被AOP织入增强以后,产生结果类,该结果类融合了原类和增强逻辑
切点表达式
execution(返回类型 类全名 方法名 (形参列表))
-
(*):代表任意字符,匹配上下文中某元素。
-
(..):代表任意字符,可表示多个元素,表示类时不能单独使用,必须和*配合,代表形参时可以单独使用。
-
(+):代表按类型匹配指定类的所有类(包括子类)只能跟在类名之后。
切面类型
StaticMethodMacherPointcutAdvice(静态切面)
代表一个静态方法匹配切面,通过StaticMethodMacherPointcut定义切点,通过类活驴和方法名匹配定义切点,需继承该类切面,提供匹配类过滤和方法匹配的规则。
DynamicMethodMacherPointcut(动态切面)
抽象类,将isRuntime()方法设置为final ,且返回true,这样其继承子类就一定是一个动态的切点。该抽象类默认匹配所有类和方法,实现切面需继承该类
Spring AOP框架
纯Java实现,不需专门编译过程,类装载器。运行期通过代理的方式向目标类织入增强代码。
包含两种代理机制:
JDK 动态代理和CGLib 动态代理
均为 Spring AOP 底层技术。
使用方式:基于XML配置文件;基于注解
<!– 开启 AOP 注解开关 -->
<aop:aspectj-autoproxy />
-
@Aspect 注解:放置于切面类上,用于注解定义一个切面
-
@Before 注解:放置于切面类方法上,用于注解实现前置增强
-
@After 注解:放置于切面类方法上,用于注解实现后置增强
-
@Around 注解:放置于切面类方法上,用于注解实现环绕增强
-
@AfterThrowing 注解:放置于切面类方法上,用于注解实现抛出异常增强
切点表达式
实例
Dynamic proxy(动态代理)
Chinese
package proxy_pattern.dynamic_proxy;
public class Chinese implements IPerson{
@Override
public void study() {
System.out.println("中国人爱学习");
}
@Override
public void think() {
System.out.println("中国人爱思考");
}
}
ChineseProxyTest
package proxy_pattern.dynamic_proxy;
import java.lang.reflect.Proxy;
public class ChineseProxyTest {
public static void main(String[] args){
Chinese chinese = new Chinese();
MyInvocationHandler handler= new MyInvocationHandler(chinese);
IPerson person = (IPerson) Proxy.newProxyInstance(
chinese.getClass().getClassLoader(),
chinese.getClass().getInterfaces(),
handler //返回代理对象
//代理对象自动执行接口,即9行的 person
);
person.study();
person.think();
}
}
IPerson
package proxy_pattern.dynamic_proxy;
public interface IPerson {
void study();
void think();
}
MyInvocationHandler
package proxy_pattern.dynamic_proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyInvocationHandler implements InvocationHandler {
private Object target;//目标对象
public MyInvocationHandler(Object target){
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method.getName()+"方法开始执行");//前置增强
Object result = method.invoke(target,args);//调用目标对象的方法
System.out.println(method.getName()+"方法结束执行");//后置增强
return result;
}
}
static proxy(静态代理)
DogProxy
package proxy_pattern.static_proxy;
public class DogProxy implements IDog {
public IDog target;
public DogProxy(IDog target){
this.target=target;
}
@Override
public void bark(){
System.out.println("bark方法开始执行");
target.bark();
System.out.println("bark方法结束执行");
}
@Override
public void run(){
System.out.println("run方法开始执行");
target.run();
System.out.println("run方法结束执行");
}
}
DogProxyTest
package proxy_pattern.static_proxy;
public class DogProxyTest {
public static void main(String[] args){
DogProxy dogProxy= new DogProxy(new Hiromi());
dogProxy.bark();
dogProxy.run();
System.out.println("------------");
DogProxy dogProxy2=new DogProxy(new Huskie());
dogProxy2.bark();
dogProxy2.run();
}
}
Hiromi
package proxy_pattern.static_proxy;
public class Hiromi implements IDog {
@Override
public void bark(){
System.out.println("博美狗叫");
}
@Override
public void run(){
System.out.println("博美跑");
}
}
Huskie
package proxy_pattern.static_proxy;
public class Huskie implements IDog {
@Override
public void bark(){
System.out.println("哈士奇狗叫");
}
@Override
public void run(){
System.out.println("哈士奇在跑");
}
}
IDog
package proxy_pattern.static_proxy;
public interface IDog {
void bark();
void run();
}
aop ioc(ioc容器)
<?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: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/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--开启自动扫描Bean-->
<context:component-scan base-package="aop"/>
<aop:aspectj-autoproxy/>
</beans>