Spring:AOP
跳到导航
跳到搜索
关于AOP
Aspect Oriented Programming:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的。
- AOP是OOP的延续。
- 可以在不修改源码的情况下,对程序进行增强;
- 应用如:权限校验,日志记录,性能监控,事务控制;
AOP 实现
Spring 的AOP 的底层用到两种代理机制:
- JDK 动态代理:(实现了接口)使用动态代理创建接口实现类代理对象;
- CGLIB 动态代理:(没有实现接口)使用动态代理创建类的子类代理对象;(在子类中调用父类的方法,来完成增强)
- 应用的是底层的字节码增强的技术生成当前类的子类对象;
JDK 动态代理
- 参见:JavaCore:接口(代理)中的代理部分;
public class MyJDKProxy implements InvocationHandler {
private UserDao userDao;
public MyJDKProxy(UserDao userDao) {
this.userDao = userDao;
}
// 编写工具方法:生成代理:
public UserDao createProxy() {
UserDao userDaoProxy = (UserDao)Proxy.newProxyInstance(userDao.getClass().getClassLoader(),
userDao.getClass().getInterfaces(), this);
return userDaoProxy;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if("save".equals(method.getName())) {
System.out.println("权限校验================");
}
return method.invoke(userDao, args);
}
}
CGLIB 动态代理
public class MyCglibProxy implements MethodInterceptor {
private CustomerDao customerDao;
public MyCglibProxy(CustomerDao customerDao) {
this.customerDao = customerDao;
}
// 生成代理的方法:
public CustomerDao createProxy() {
// 创建Cglib 的核心类:
Enhancer enhancer = new Enhancer();
// 设置父类:
enhancer.setSuperclass(CustomerDao.class);
// 设置回调:
enhancer.setCallback(this);
// 生成代理:
CustomerDao customerDaoProxy = (CustomerDao) enhancer.create();
return customerDaoProxy;
}
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
if("delete".equals(method.getName())) {
Object obj = methodProxy.invokeSuper(proxy, args);
System.out.println("日志记录================");
return obj;
}
return methodProxy.invokeSuper(proxy, args);
}
}
关于AspectJ
AspectJ,(Eclipse AspectJ)是一个易用的功能强大的AOP框架;可以单独使用(需要专门的编译器“ajc”),也可以整合到其它框架中。
- 官网:“http://www.eclipse.org/aspectj/”;
- AspectJ是AOP编程的完全解决方案,可以做Spring AOP干不了的事情;
- AspectJ不是spring一部分,和spring一起使用进行aop操作(Spring2.0以后新增了对AspectJ支持);
关于织入
AOP编程,存在三种织入方式:
- 编译期织入(Compile-time weaving):(编译期、编译后)在Java编译期,采用特殊的编译器,将切面织入到Java类中;
- 编译后:对已经生成的“.class”文件、“jar”包进行织入;
- 类加载期织入(Load-time weaving):通过特殊的类加载器,在类字节码加载到JVM时,织入切面;
- 或在JVM启动的时候指定 AspectJ 提供的“agent:-javaagent:xxx/xxx/aspectjweaver.jar”;【?】
- 运行期织入:采用CGLib工具或JDK动态代理进行切面的织入;
AspectJ 与 Spring AOP
Spring AOP 和 AspectJ 之间的关键区别:
AspectJ | Spring AOP |
---|---|
静态织入
|
运行期织入 |
支持所有切入点
|
仅支持方法执行切入点
|
使用 Java 编程语言的扩展实现
|
在纯 Java 中实现
|
更好的性能 | 较 AspectJ 慢 |
- Spring AOP使用了Aspect的Annotation,但是并没有使用它的编译器和织入器;
- “@Before”、“@After”等是Aspect的Annotation;
- AspectJ在实际运行之前就完成了织入,所以其生成的类没有额外运行时开销。
Spring:AOP
相关术语
- Joinpoint(连接点):所谓连接点是指那些被拦截到的点。【可以被切入的方法】
- 在 Spring 中,这些点指的是方法,因为,Spring 只支持方法类型的连接点;
- Pointcut(切入点):所谓切入点是指我们要对哪些Joinpoint 进行拦截的定义;【用来切入的方法】
- Advice(通知/增强):所谓通知是指拦截到Joinpoint 之后所要做的事情就是通知;【切面处理】
- “前置通知”:;
- “后置通知”:;
- “异常通知”:;
- “最终通知”:无论程序是否正常执行,最终通知的代码会得到执行;
- “环绕通知”:;
- Introduction(引介):引介是一种特殊的通知在不修改类代码的前提下, Introduction 可以在运行期为类动态地添加一些方法或 Field;
- Target(目标对象):代理的目标对象;
- Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程;【应用增强生成代理对象】
- spring采用“动态代理织入”,而AspectJ采用“编译期织入”和“类装载期织入”;【?】
- Proxy(代理):一个类被 AOP 织入增强后,就产生一个结果代理类;
- Aspect(切面): 是 Pointcut(切入点)和 Advice(增强)/Introduction(引介)的结合;
jar包
spring 的传统 AOP 的开发的包: “spring-aop-4.2.4.RELEASE.jar” “com.springsource.org.aopalliance-1.0.0.jar” aspectJ 的开发包: “com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar” “spring-aspects-4.2.4.RELEASE.jar”
配置文件
需要在“applicationContext.xml”中引入 AOP 相关约束:
<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"> </beans>
切入点表达式
注意:Spring 中,切入点是“方法”,而非“类”
execution([方法访问修饰符] <方法返回类型> <包名.类名.方法名>(<方法的参数>) [异常])
定义切点:
- 通过方法签名:
- 示例:
- execution(public * *(..)):匹配所有目标类的 public方法;
- execution(* *To(..)):匹配目标类所有以To为后缀的方法;
- 通过类:
- 示例:
- execution(* com.baobaotao.Waiter.*(..)):匹配Waiter接口的所有方法;
- execution(* com.baobaotao.Waiter+.*(..)):匹配Waiter接口及其所有实现类的方法;
- 通过类包:
- “.”:表示包下的所有类;
- “..”:表示包、子孙包下的所有类;
- 示例:
- execution(* com.baobaotao.*(..)):匹配com.baobaotao包下所有类的所有方法;
- execution(* com.baobaotao..*(..)):匹配com.baobaotao包、子孙包下所有类的所有方法;
- execution(* com...*Dao.find(..)):匹配:前缀为com的任何包下,后缀为Dao的任何类中,以find为前缀的方法;
- 通过方法入参:
- “”:表示任意类型的参数;
- “..”:表示任意类型、任意个数的参数;
- 如果参数类型是“java.lang”包下的类,可以直接使用类名;否则必须使用全限定类名;
- 示例:
- execution(* joke(String,int))):匹配第一参数是 String,第二个参数是 int 的 joke()方法;
- execution(* joke(String,*))):
- execution(* joke(String,..))):
- execution(* joke(Object+))):匹配参数是“Object”或“Object”子类 的 joke()方法;
基于AspectJ:XML
步骤:
- 配置Bean对象;
- 配置aop操作:
- 配置切入点;
- 配置切面;(AspectJ)
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="book" class="cn.itcast.aop.Book"></bean>
<bean id="myBook" class="cn.itcast.aop.MyBook"></bean>
<!-- 2 配置aop操作 -->
<aop:config>
<!-- 2.1 配置切入点 -->
<aop:pointcut expression="execution(* cn.itcast.aop.Book.*(..))" id="pointcut1"/>
<!-- 2.2 配置切面 -->
<aop:aspect ref="myBook">
<!-- 配置增强(“method”:增强;“pointcut-ref”:切入点)-->
<aop:before method="before1" pointcut-ref="pointcut1"/>
<aop:after-returning method="after1" pointcut-ref="pointcut1"/>
<aop:around method="around1" pointcut-ref="pointcut1"/>
</aop:aspect>
</aop:config>
</beans>
- 切入点“execution(* cn.itcast.aop.Book.*(..))”匹配到“book”类中的所有方法;(切入点是方法,而非类)
- 增强类“myBook”的“before1”、“after1”、“around1”分别被织入到切入点的前、后、环绕;
增强类示例:
public class MyBook {
public void before1() {
System.out.println("前置增强......");
}
public void after1() {
System.out.println("后置增强......");
}
//环绕通知
public void around1(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
//方法之前
System.out.println("方法之前.....");
//执行被增强的方法
proceedingJoinPoint.proceed();
//方法之后
System.out.println("方法之后.....");
}
}
- “环绕通知”中,调用执行切入点;(切入点是方法,而非类)
基于AspectJ:注解
步骤:
- 创建对象;
- 开启aop操作(“applicationContext.xml”中);
<!-- 1 配置对象 --> <bean id="book" class="cn.itcast.aop.Book"></bean> <bean id="myBook" class="cn.itcast.aop.MyBook"></bean> <!-- 2 开启aop操作 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
- 在增强类中使用注解;
- “@Aspect”:注解增强类;
- “@Before”、“@After”、“@Around”、“@AfterReturning”、“@AfterThrowing”:注解增强;
- 需要配置“切入点表达式”(value属性中);
@Aspect public class MyBook { //在方法上面使用注解完成增强配置 @Before(value="execution(* cn.itcast.aop.Book.*(..))") public void before1() { System.out.println("before.............."); } }
事务增强
<!-- dataSource -->
...
<!-- 第一步 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入dataSource -->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 第二步 配置事务增强 -->
<tx:advice id="txadvice" transaction-manager="transactionManager">
<!-- 做事务操作 -->
<tx:attributes>
<!-- 设置进行事务操作的方法匹配规则 -->
<tx:method name="account*" propagation="REQUIRED"/>
<!-- <tx:method name="insert*" /> -->
</tx:attributes>
</tx:advice>
<!-- 第三步 配置切面 -->
<aop:config>
<!-- 切入点 -->
<aop:pointcut expression="execution(* cn.itcast.service.OrdersService.*(..))" id="pointcut1"/>
<!-- 切面 -->
<aop:advisor advice-ref="txadvice" pointcut-ref="pointcut1"/>
</aop:config>