1.什么是AOP
AOP(Aspect-Oriented Programming, 面向切面编程):
将分散在各个方法中的相同的功能提取出来,并在运行时动态的将切面注入到各方法中;它是OOP的有力补充,OOP是纵向的抽象,AOP是横向的抽象;
2. 使用AOP的好处
1. 每个事物逻辑位于一个位置, 代码不分散, 便于维护和升级
2. 业务模块更简洁, 只包含核心业务代码.
3.AOP术语
术语 | 说明 |
---|---|
切面(Aspect) | 横切关注点(跨越应用程序多个模块的功能)被模块化的特殊对象(Advice+PointCut=Aspect); |
增强(Advice) | 切面必须要完成的工作,也叫做通知(加入的功能即代码段); |
切点(PointCut) | 指一个具体的连接点,通常使用表达式来确定切点位置 |
目标(Target) | 被通知的对象(加入增强的对象); |
代理(Proxy) | 向目标对象加入增强(应用通知之后)创建的代理对象 |
连接点(JoinPoint) | 程序执行的某个特定位置,Spring只支持方法(method) |
织入(Weaving) | 将增强加入到目标对象切点的过程(编译期,加载期,运行期) |
4.AOP的实现者##
1. AspectJ:Java 社区里最完整最流行的 AOP 框架,能够在编译期提供横切代码织入。在 Spring2.0 以上版本中, 可以使用基于 AspectJ 注解或基于 XML 配置的 AOP
2. SpringAOP:SpringAOP使用纯java代码实现,不需要专门的编译过程,也不需要特殊的类装载器,在运行期通过代理的方式向目标对象织入增强。
3. JBossAOP:不常用
5.在Spring中启用AspectJ
1. 在maven工程中,添加依赖信息
<dependency>
<groupId>repo.org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.1.3.RELEASE</version>
</dependency>
2. 在spring的IOC容器中启用AspectJ的注解支持
1. 加入aop命名空间
2. 在bean配置文件中加入<aop:aspectj-autoproxy>,自动为与 AspectJ 切面匹配的 Bean 创建代理.
6.创建切面 使用@Aspect和@Component
1. 增强注解(Advice):具体的代码
AspectJ 支持 5 种类型的通知注解:
@Before: 前置通知, 在方法执行之前执行
@After: 后置通知, 在方法执行之后执行
@AfterRunning: 返回通知, 在方法返回结果之后执行
@AfterThrowing: 异常通知, 在方法抛出异常之后
@Around: 环绕通知, 围绕着方法执行
2. 切点(PointCut):根据表达式来匹配各种方法(也可以通过||,&&,!将多个表达式结合起来使用)
execution (访问修饰符 返回值类型 类名(包括包名).方法名.(参数类型))
例如:
execution (* com.oracle.test.*.*(..));//当访问修饰符 返回值没有要求是使用一个"*"即可,该表达式为com.oracle.test包下的任何类的任何一个方法,参数类型没有任何要求
execution (public * com.oracle.test.Landlord.rent(int));//访问修饰符为public ,类为Landlord,方法为rent();参数要求为int类型
3. 示例:
bean配置文件
<?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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<!-- 设置要扫描的包 -->
<context:component-scan base-package="com.oracle.test"></context:component-scan>
<!-- 设置自动代理 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
抽象角色:
package com.oracle.test;
/**
* 抽象角色
* @author dingshuangen
*
*/
public interface Rentable {
public void rent(int money);
}
具体角色:
package com.oracle.test;
import org.springframework.stereotype.Component;
/**
* 具体角色
* @author dingshuangen
*
*/
@Component
public class Landlord implements Rentable {
public void rent(int money) {
System.out.println("我是房东。。出租房子我收取租金:"+money);
}
}
切面:
package com.oracle.test;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
/**
* 切面
* @author dingshuangen
*
*/
@Component
@Aspect
public class LogAspectJ {
//将该方法放到切点的前面执行
@Before("execution (* com.oracle.test.*.*(..))")
public void before(){
System.out.println("---在方法之前执行");
}
@After("execution (public * com.oracle.test.Landlord.rent(int))")
public void after(){
System.out.println("---在方法之后执行");
}
}
测试:
package com.oracle.junitTest;
import org.junit.After;
import org.junit.Before;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.oracle.test.Landlord;
import com.oracle.test.Rentable;
public class Test {
//创建工厂,加载配置文件
ApplicationContext factory=new ClassPathXmlApplicationContext("applicationContext.xml");
@Before
public void setUp() throws Exception {
}
@After
public void tearDown() throws Exception {
}
@org.junit.Test
public void test() {
//自动创建代理对象
Rentable r=factory.getBean(Rentable.class);
//Rentable r=(Rentable) factory.getBean("landlord");
System.out.println("类名:"+r.getClass());//动态产生的一个类
System.out.println("父类:"+r.getClass().getSuperclass());
r.rent(3000);
}
}
输出结果:
类名:class com.sun.proxy.$Proxy15
父类:class java.lang.reflect.Proxy
---在方法之前执行
我是房东。。出租房子我收取租金:3000
---在方法之后执行
当不使用接口时Landlord l=factory.getBean(Landlord.class);
测试以下代码:
System.out.println("不使用接口产生的类名:"+l.getClass());
System.out.println("l父类:"+l.getClass().getSuperclass());
l.rent(3000);
输出结果:
不使用接口产生的类名:class com.oracle.test.Landlord$$EnhancerBySpringCGLIB$$12f910f9
l父类:class com.oracle.test.Landlord
---在方法之前执行
我是房东。。出租房子我收取租金:3000
---在方法之后执行
说明:
当使用接口时,Spring会使用JDK提供的代理模式就行自动代理,当不提供接口时,Spring使用CGLIB进行自动代理,生成一个真实对象的子类,在子类上进行织入增强
7.JoinPoint对象
JoinPoint对象封装了SpringAOP中切面方法的信息,在切面方法中添加JoinPoint参数,就可以获取到封装了该方法信息的JoinPoint对象.
常用API:
方法名 | 作用 |
---|---|
Signature getSignature(); | 获取封装了署名信息的对象,在该对象中可以获取到目标方法名,所属类的Class等信息 |
Object[] getArgs(); | 获取传入目标方法的参数对象 |
Object getTarget(); | 获取被代理的对象 |
Object getThis(); | 获取代理对象 |
测试方法:
在上文切面对象的方法中加入参数JoinPoint
@Before("execution (* com.oracle.test.*.*(..))")
public void before(JoinPoint p){
System.out.println("---在方法之前执行");
System.out.println("目标对象:"+p.getTarget().getClass());
System.out.println("代理对象:"+p.getThis().getClass());
System.out.println("目标的方法名:"+p.getSignature());
System.out.println("参数:"+Arrays.deepToString(p.getArgs()));
System.out.println(p.getKind());
}
输出结果为:
不使用接口产生的类名:class com.oracle.test.Landlord$$EnhancerBySpringCGLIB$$12f910f9
l父类:class com.oracle.test.Landlord
---在方法之前执行
目标对象:class com.oracle.test.Landlord
代理对象:class com.oracle.test.Landlord$$EnhancerBySpringCGLIB$$12f910f9
目标的方法名:void com.oracle.test.Landlord.rent(int)
参数:[3000]
method-execution
我是房东。。出租房子我收取租金:3000
---在方法之后执行