将分散在各个方法中的重复代码提取出来,在程序编译或运行时,再提取代码应用到需要执行的地方。
一个AOP程序(基于代理类,配置麻烦)
- 创建目标类,待增强的类。
//接口
public interface UserDao {
public void addUser();
public void deleteUser();
}
import org.springframework.stereotype.Repository;
// 目标类
@Repository("userDao")
public class UserDaoImpl implements UserDao {
public void addUser() {
// int i = 10/0;
System.out.println("添加用户");
}
public void deleteUser() {
System.out.println("删除用户");
}
}
- 创建切面类MyAspect,实现org.aopalliance.intercept.Methodinterceptor接口,并实现其中invoke()方法。注意不同的通知要继承不同的接口,包括org.springframework.aop.MethodBeforeAdvice,org.springframework.aop.AfterReturningAdvice等等
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
// 切面类
public class MyAspect implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
check_Permissions();
// 执行目标方法
Object obj = mi.proceed();
log();
return obj;
}
public void check_Permissions(){
System.out.println("模拟检查权限...");
}
public void log(){
System.out.println("模拟记录日志...");
}
}
- 配置bean,并指定代理对象。
<!-- 1 目标类 -->
<bean id="userDao" class="com.itheima.jdk.UserDaoImpl" />
<!-- 2 切面类 -->
<bean id="myAspect" class="com.itheima.factorybean.MyAspect" />
<!-- 3 使用Spring代理工厂定义一个名称为userDaoProxy的代理对象 -->
<bean id="userDaoProxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 3.1 指定代理实现的接口-->
<property name="proxyInterfaces"
value="com.itheima.jdk.UserDao" />
<!-- 3.2 指定目标对象 -->
<property name="target" ref="userDao" />
<!-- 3.3 指定切面,织入环绕通知 -->
<property name="interceptorNames" value="myAspect" />
<!-- 3.4 指定代理方式,true:使用cglib,false(默认):使用jdk动态代理 -->
<property name="proxyTargetClass" value="true" />
</bean>
- 测试调用,和一般调用一致,但是结果被增强了。
public static void main(String args[]) {
String xmlPath = "com/itheima/factorybean/applicationContext.xml";
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext(xmlPath);
// 从Spring容器获得内容
UserDao userDao =
(UserDao) applicationContext.getBean("userDaoProxy");
// 执行方法
userDao.addUser();
userDao.deleteUser();
}
}
AOP术语 | 解释 |
---|---|
Aspect(切面) | 封装的用于横向插入系统功能的类。 |
Joinpoint(连接点) | 可以被拦截的点,可以被增强的方法。 |
Pointcut(切入点) | 真正被拦截的点。 |
Advice(增强、通知) | 增强的方法。(方法前后) |
Introduction(引介) | 类层面的增强。(加属性或方法) |
Target(目标) | 被增强的对象。 |
Proxy(代理) | 将通知应用到目标对象之后,被动态创建的对象。 |
Weaving(织入) | 将切面代码插入目标对象,生成代理对象的过程。 |
AspectJ:AOP框架(更简便)
一个AspectJ示例程序
- 创建切面类
public class MyAspect {
// 前置通知
public void myBefore(JoinPoint joinPoint) {
System.out.print("前置通知 :模拟执行权限检查...,");
System.out.print("目标类是:"+joinPoint.getTarget() );
System.out.println(",被织入增强处理的目标方法为:"
+joinPoint.getSignature().getName());
}
}
- 配置bean,以及<aop:config>
<!-- 1 目标类 -->
<bean id="userDao" class="com.itheima.jdk.UserDaoImpl" />
<!-- 2 切面 -->
<bean id="myAspect" class="com.itheima.aspectj.xml.MyAspect" />
<!-- 3 aop编程 -->
<aop:config>
<!-- 配置切面 -->
<aop:aspect ref="myAspect">
<!-- 3.1 配置切入点,通知最后增强哪些方法 -->
<aop:pointcut expression="execution(* com.itheima.jdk.*.*(..))"
id="myPointCut" />
<!-- 3.2 关联通知Advice和切入点pointCut -->
<!-- 3.2.1 前置通知 -->
<aop:before method="myBefore" pointcut-ref="myPointCut" />
</aop:aspect>
</aop:config>
- 测试调用
使用注解
AspectJ注解 | 解释 |
---|---|
@Aspect | 切面类 |
@Pointcut | 切入点表达式 |
@before | 前置通知 |
@AfterReturning | 后置通知 |
@Around | 环绕通知 |
@AfterThrowing | 异常通知 |
@after | final通知,无论异常否,都会执行 |
@DeclareParents | 引介通知 |
一个使用注解的示例
- 创建切面类
@Aspect
@Component
public class MyAspect {
// 定义切入点表达式
@Pointcut("execution(* com.itheima.jdk.*.*(..))")
// 使用一个返回值为void、方法体为空的方法来命名切入点
private void myPointCut(){}
// 前置通知
@Before("myPointCut()")
public void myBefore(JoinPoint joinPoint) {
System.out.print("前置通知 :模拟执行权限检查...,");
System.out.print("目标类是:"+joinPoint.getTarget() );
System.out.println(",被织入增强处理的目标方法为:"
+joinPoint.getSignature().getName());
}
}
- 配置文件中
<!-- 指定需要扫描的包,使注解生效 -->
<context:component-scan base-package="com.itheima" />
<!-- 启动基于注解的声明式AspectJ支持 -->
<aop:aspectj-autoproxy />
- 测试调用
// 测试类
public class TestAnnotationAspectj {
public static void main(String args[]) {
String xmlPath =
"com/itheima/aspectj/annotation/applicationContext.xml";
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext(xmlPath);
// 1 从spring容器获得内容
UserDao userDao = (UserDao) applicationContext.getBean("userDao");
// 2 执行方法
userDao.addUser();
}
}
execution表达式
[访问修饰符] 方法返回值 包名.类名.方法名(参数)
execution(* com.itheima.jdk.*.*(…)),*表示任意返回类型类型,括号中(…)表示任意输入参数