1. Spring 中的 AOP 简介
- AOP Aspect Oriented Programing 面向切面编程
- AOP 采取横向抽取机制,取代了传统纵向继承体系重复性代码(性能监视、事务管理、安全检查、缓存)
- Spring 中的 Aop 是纯 Java 来实现的,使用动态代理的方式增强代码
- Spring 使用动态代理的机制是判断委托类是否实现了接口,如果实现了接口则使用 jdk 的动态代理,如果没有实现接口则使用 cglib 的动态代理
- AOP 不是由 Spring 提出来的,是由 AOP 联盟定义的
2. 代理模式
- 不使用对象来进行真实操作,使用我们自己创建的代理对象来操作
- 为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
组成:
- 抽象角色:通过接口或抽象类声明真实角色实现的业务方法。
- 代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
- 真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用
2.1 静态代理
1 | //抽象角色,声明真实角色实现的业务方法 |
1 | package com.uplooking.aop.agent; |
1 | /** |
1 | //发起者 |
1 | public class App { |
2.2 动态代理
2.2.1 基于原生的 JDK 的动态代理
1 | Pan pan = new Pan(); |
2.2.2 基于CGLIB的动态代理
- 因为原生的 jdk 的动态代理存在缺陷,代理类和委托类必须实现同一个接口
- 所以有个开源的动态代理框架出现了(CGLIB)
- CGLIB不要求委托类必须实现接口===>因为CGLIB底层是基于继承实现的
2.2.3 jdk的动态代理与cglib
- jdk 的动态代理底层实现是基于接口实现的
- cglib 的动态代理底层是基于继承的
3. Aop的专业术语
- Joinpoint(连接点) :目标类中可以被增强的方法(例如有四个方法)
- **Pointcut(切入点,切点)**:目标类中要被增强的方法
- Advice(通知/增强) :增强的代码
- 前置增强(通知)
- 后置增强(通知)
- 环绕增强(通知)
- **Target(目标对象)**:目标对象
- Weaving(织入) :把增强应用切点的过程
- **Proxy(代理)**: 一个类被 AOP 织入增强后,就产生一个结果代理类
- Aspect(切面):是切点和通知的结合
4. 基于 Jdk 动态代理实现自定义AOP
使用 JDK 的动态代理,使用 Proxy 类里面的方法创建代理对象
调用 newProxyInstance(1:ClassLoder,2:类<?>[] interfances,3:InvocationHand)
参数的作用:1. 类的加载器;2. 增强方法所在类,这个类实现的接口,支持多个接口,所以以数组的形式展出;3. 实现这个接口InvocationHand,创建代理的对象,写增强的方法。
方法的作用:将方法调用到处理程序,具有指定的代理类,调用处理程序的代理实例,该代理类,由指定的类加载器定义并实现指定的接口
method.invoke:调用 method 类代表的方法,其中 obj 是对象名,args 是传入 method 方法的参数
pom依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.3.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.10</version>
</dependency>
</dependencies>创建接口
UserService
1
2
3
4public interface UserService {
void addUser();
void deleteUser();
}定义目标类
UserServiceImpl
1
2
3
4
5
6
7
8
9
10
11public class UserServiceImpl implements UserService {
public void addUser() {
System.out.println("添加用户");
}
public void deleteUser() {
System.out.println("删除用户");
}
}定义切面
MyAspect
1
2
3
4
5
6
7
8
9
10
11public class MyAspect {
public void before() {
System.out.println("前置通知");
}
public void after() {
System.out.println("后置通知");
}
public void around() {
System.out.println("环绕通知");
}
}定义目标对象的工厂类(用于创建目标对象说对应的代理对象)
UserServiceFactory
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20public class UserServiceFactory {
public UserService newUserServiceProxy() {
//创建目标对象
UserServiceImpl userService = new UserServiceImpl();
//创建切面对象
MyAspect myAspect = new MyAspect();
//创建代理对象
UserService userServiceProxy = (UserService) Proxy.newProxyInstance(UserServiceFactory.class.getClassLoader(), UserServiceImpl.class.getInterfaces(), new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
myAspect.before();
Object result = method.invoke(userService, args);
myAspect.after();
return result;
}
});
return userServiceProxy;
}
}入口类
1
2
3
4
5public static void main(String[] args) {
UserServiceFactory factory = new UserServiceFactory();
UserService userService = factory.newUserServiceProxy();
userService.addUser();
}
5. AOP联盟定义的通知类型
Spring 按照通知 (Advice) 在目标类方法的切点的位置,分为5类:
- 前置通知
org.springframework.aop.MethodBeforeAdvice
- 在目标方法执行前实施增强
- 后置通知
org.springframework.aop.AfterReturningAdvice
- 在目标方法执行后实施增强
- 环绕通知
org.aopalliance.intercept.MethodInterceptor
- 在目标方法执行前后实施增强,环绕通知,必须手动执行目标方法
- 异常抛出通知
org.springframework.aop.ThrowsAdvice
- 在方法抛出异常后实施增强
- 引介通知
org.springframework.aop.IntroductionInterceptor
- 在目标类中添加一些新的方法和属性
6. Spring中的AOP(不建议使用)
6.1 Spring中的半自动AOP
定义接口
1
2
3
4public interface UserService {
void addUser();
void deleteUser();
}定义目标类
1
2
3
4
5
6
7
8
9
10
11public class UserServiceImpl implements UserService {
public void addUser() {
System.out.println("添加用户");
}
public void deleteUser() {
System.out.println("删除用户");
}
}定义切面(实现Spring提供的拦截器)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24public class MyAspect implements org.aopalliance.intercept.MethodInterceptor {
//定义三个通知
public void before() {
System.out.println("前置通知");
}
public void after() {
System.out.println("后置通知");
}
public void around() {
System.out.println("环绕通知");
}
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
before();
Object result = methodInvocation.proceed();
after();
return result;
}
}配置Spring中自带的工厂
applicationContext.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!--<bean id="userServiceFactory" class="com.it.framework.UserServiceFactory"></bean>-->
<!--定义目标对象-->
<bean id="userServiceImpl" class="com.it.framework.UserServiceImpl"></bean>
<!--定义切面对象-->
<bean id="myAspect" class="com.it.framework.MyAspect"></bean>
<!--定义工厂对象-->
<bean id="userServiceproxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!--配置接口-->
<property name="interfaces" value="com.it.framework.UserService"/>
<!--配置目标对象-->
<property name="targetName" value="userServiceImpl"/>
<!--配置拦截器-->
<property name="interceptorNames" value="myAspect"/>
<!--配置动态代理方式,true是使用cglib的方式,默认使用jdk的动态代理-->
<property name="optimize" value="true"></property>
</bean>
</beans>
6.2 Spring中的全自动AOP
pom依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.3.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.10.RELEASE</version>
</dependency>
<!--spring全自动aop依赖aspectj联盟的依赖-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.10</version>
</dependency>
</dependencies>定义接口===>同半自动AOP一致
定义目标类===>同半自动AOP一致
定义切面(实现Spring提供的拦截器)===>同半自动AOP一致
配置
applicationContext.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14<!--定义目标对象-->
<bean id="userServiceImpl" class="com.it.framework.UserServiceImpl"></bean>
<!--定义切面对象-->
<bean id="myAspect" class="com.it.framework.MyAspect"></bean>
<!--
proxy-target-class=true表示使用cglib动态代理
-->
<aop:config proxy-target-class="true">
<!--定义切点表达式-->
<aop:pointcut id="myPointCut" expression="execution(* com.it.framework.UserServiceImpl.*(..))"/>
<aop:advisor advice-ref="myAspect" pointcut-ref="myPointCut"></aop:advisor>
</aop:config>入口类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15<!--定义目标对象-->
<bean id="userServiceImpl" class="com.it.framework.UserServiceImpl"></bean>
<!--定义切面对象-->
<bean id="myAspect" class="com.it.framework.MyAspect"></bean>
<!--
proxy-target-class=true表示使用cglib动态代理
-->
<aop:config proxy-target-class="true">
<!--定义切点表达式-->
<aop:pointcut id="myPointCut" expression="execution(* com.it.framework.UserServiceImpl.*(..))"/>
<aop:advisor advice-ref="myAspect" pointcut-ref="myPointCut"></aop:advisor>
</aop:config>
7. 切点表达式
用于描述需要增强的方法(切点)
语法
1
execution(修饰符 返回值 包.类.方法名(参数) throws异常)
修饰符,一般省略
1
2public 公共方法
* 任意返回值类型,不能省略
1
2
3void 返回没有值
String 返回值字符串
* 任意包 可以省略
1
2com.it.spring 固定包
com.it.spring.. spring包下面的所有子包(含自己)类,可以省略
1
2
3
4UserServiceImpl 指定类
*Impl 以Impl结尾
User* 以User开头
* 任意方法名,不能省略
1
2
3
4addUser 固定方法
add* 以add开头
*Do 以Do结尾
* 任意(参数)
1
2
3
4() 无参
(int) 一个整型
(int ,int) 两个
(..) 参数任意throws ,可省略,一般不写
eg
1
UserServiceImpl类中任意参数的方法 * com.it.framework.UserServiceImpl.*(..)
9. 基于 Aspectj 的 AOP
- AspectJ 是一个基于 Java 语言的面向切面的 AOP 框架
- Spring2.0 以后新增了对 AspectJ 切点表达式支持
- @AspectJ 是 AspectJ1.5 新增功能,通过 JDK5 注解技术,允许直接在 Bean 类中定义切面
- 新版本 Spring 框架,建议使用 AspectJ 方式来开发 AOP
9.1 AspectJ 的增强类型
- @Before 前置通知,相当于 BeforeAdvice
1 |
|
- @AfterReturning 后置通知,相当于AfterReturningAdvice
1 |
|
- @Around 环绕通知,相当于MethodInterceptor
1 |
|
- @AfterThrowing抛出通知,相当于ThrowAdvice
1 |
|
- @After 最终final通知,不管是否异常,该通知都会执行
1 |
|
9.2. 基于 Aspectj 的Aop
- pom依赖
1 | <dependency> |
定义切面
切面 = 切点 + 通知
1 | /** |
- 定义要增强的类
1 |
|
- 配置
applicationContext.xml
1 | <!--组件扫描器--> |
- 测试程序
1 |
|