什么是AOP
AOP的全称是Aspect-Orented Progamming,即面切面编程(也称面向方向编程)。它是面向对象(OOP)的一种补充,目前已经成为一种比较成熟的编程方式。
在传统的业务处理代码中,通常会进行事务处理、日志记录等操作。虽然使用OOP可以通过组合或者继承的方式达到代码的重用,但是如果要实现某个功能(如记录日中),同样的代码依然会分散到各个方法中。这样如果想要关闭某个功能,或者对其进行修改,就必须要修改所有的相关方法。这不但增加了开发人员的工作量,而且提高了代码的出错率。
为了解决这一问题,AOP思想随之产生。AOP采取横向抽取机制,将分散在各个方法中的重复代码提取出来,然后在程序编译或运行时,再将这些提取出来的代码应用到需要执行的地方。父子关系的纵向重用。虽然AOP是一种新的编程思想,但却不是OOP的代替品,它只是OOP的延伸和补充。
在AOP思想中,类与切面的关系如图。
从上图可以看出,通过Aspect(切面)分别在Class1和Class2的方法中加入了事务、日志、权限和异常功能。
Aop的使用,使开发人员在编写业务逻辑时可以专心于核心业务,而不用过对地关注其他业务逻辑实现,这不但提高了开发效率,而且增强了代码的可维护性。
目前最流行的AOP框架有两个,分别为Spring AOP和AspectJ。Spring AOP使用纯Java实现,不需要专门的编译过程和类加载器,在运行期间通过代理的方式向目标类织入增强的代码。AspectJ是一个基于Java语言的AOP框架,从Spring2.0开始,SpringAop引入了对AspectJ的支持,AspectJ扩展了Java语言,提供了专门的编译器,在编译时横向代码织入。
AOP术语
通知、增强处理(Advice)
通知、增强处理(Advice) 就是你想要的功能,也就是上说的权限、事务、日志等。你给先定义好,然
后再想用的地方用一下。包含Aspect的一段处理代码。
连接点(JoinPoint)
连接点(JoinPoint) 这个就更好解释了,就是spring允许你是通知(Advice)的地方,那可就真多了,
基本每个方法的钱、后(两者都有也行),或抛出异常是时都可以是连接点,spring只支持方法连接
点。其他如AspectJ还可以让你在构造器或属性注入时都行,不过那不是咱们关注的,只要记住,和方法
有关的前前后后都是连接点。
切入点(Pointcut)
切入点(Pointcut) 上面说的连接点的基础上,来定义切入点,你的一个类里,有15个方法,那就有十
几个连接点了对吧,但是你并不想在所有方法附件都使用通知(使用叫织入,下面再说),你只是想让
其中几个,在调用这几个方法之前、之后或者抛出异常时干点什么,那么就用切入点来定义这几个方
法,让切点来筛选连接点,选中那几个你想要的方法。
切面(Aspect)
切面(Aspect) 切面是通知和切入点的结合。现在发现了吧,没连接点什么事,链接点就是为了让你好
理解切点搞出来的,明白这个概念就行了。通知说明了干什么和什么时候干(什么时候通过方法名中的
befor,after,around等就能知道),二切入点说明了在哪干(指定到底是哪个方法),这就是一个完
整的切面定义。
引入(introduction)
引入(introduction) 允许我们向现有的类添加新方法属性。这不就是把切面(也就是新方法属性:通
知定义的)用到目标类中吗
目标(target)
引入(introduction) 允许我们向现有的类添加新方法属性。这不就是把切面(也就是新方法属性:通
知定义的)用到目标类中吗
织入(weaving)
织入(weaving) 把切面应用到目标对象来创建新的代理对象的过程。
SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice:
即 Aop 在 不改变原有代码的情况下 , 去增加新的功能 .
使用Spring 实现AOP
写一个切入类
public class LogAspect {
public void before(){
System.out.println("执行方法前打印一条日志-----自定义形式");
}
public void after(){
System.out.println("执行方法后打印一条日志-----自定义形式");
}
public void returning(){
System.out.println("returning-----自定义形式");
}
public void throwing(){
System.out.println("throwing-----自定义形式");
}
}
在配置文件中配置如下内容:
<bean name="logAspect" class="com.shen.aop.LogAspect"/>
<aop:config>
<aop:aspect ref="logAspect">
<!--切入点 expression:表达式匹配要执行的方法-->
<aop:pointcut id="pointcut" expression="(execution(* com.shen.service.impl.*.*(..)))"/>
<aop:before method="before" pointcut-ref="pointcut"/>
<aop:after method="after" pointcut-ref="pointcut" />
<aop:after-throwing method="throwing" pointcut-ref="pointcut"/>
<aop:after-returning method="returning" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
上述配置代码片段中execution(* com.shen.service.impl..(…))就是定义的切入点表达式,该切入点表达式的意思是匹配com.shen.service.impl包中 任意类的任意方法执行。其中execution()是表达式的主体,第一个表示的是返回类型,使用表示所有类型;com.shen.service.impl表示的是需要拦截的包名,后面的2个表示的是类名,使用表示的是所有类;第三个表示的是方法名,使用表示所有方法;后面(. .)表示方法的参数其中“. .”表示任意参数。需要注意的是第1个*与包名之间有一个空格。
Spring AOP中切入点表达式的基本格式如下。
execution(modifiers-pattren?ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
上述格式中,各部分说明如下。
- modifiers-pattren:表示定义的目标方法的访问修饰符,如public、private等。
- ret-type-pattern:表示定义的目标方法的返回值类型,如void、String等。
- declaring-type-pattern:表示定义的目标方法的类路径,如com.itheima.jdk.UserDaoImpl。
- name-pattern:表示具体需要被代理的目标方法,如add()方法。
- param-pattern:表示需要被代理的目标方法包含的参数。
- throws-pattern:表示需要被代理的目标方法抛出的异常类型。
其中带有问号(?)的部分,如modifiers-pattern、declaring-type-pattern、throws-pattern表示可配置项,而其他部分表示必须配置项。
注解实现AOP
与基于代理类的AOP实现相比,基于XML声明式AspectJ要便捷的多,但它也存在着一些缺点,那就是要在Spring文件中配置大量的代码信息。为了解决这个问题,Aspectj框架为AOP的实现提供了一套注解,用以取代Spring配置文件中为实现AOP功能所配置的臃肿代码。
关于AspectJ注解的介绍,如表3-5所示
注解名称 | 描述 |
---|---|
@Aspect | 用于定义一个切面 |
@Printcut | 用于定义切入点表达式。 |
@Before | 前置通知,在连接点方法前调用 |
@After | 后置通知,在连接点方法后调用 |
@AfterReturningt | 返回通知,在连接点方法执行并正常返回后调用,要求连接点方法在执行过程中没有发生异常 |
@AfterThrowingt | 异常通知,当连接点方法异常时调用 |
使用注解实现AOP:
@Aspect
public class LogAspect {
@Before("execution(* com.shen.service.impl.*.*(..))")
public void before( ){
System.out.println("执行方法前打印一条日志-----自定义形式");
}
@After("execution(* com.shen.service.impl.*.*(..))")
public void after(){
System.out.println("执行方法后打印一条日志-----自定义形式");
}
@AfterReturning("execution(* com.shen.service.impl.*.*(..))")
public void returning(){
System.out.println("returning-----自定义形式");
}
@AfterThrowing("execution(* com.shen.service.impl.*.*(..))")
public void throwing(){
System.out.println("throwing-----自定义形式");
}
}
@Service
public class User_uService implements IUserService {
@Autowired
IUser_uDao userUDao;
@Override
public User_u findUserById(Integer id) {
System.out.println("方法在执行");
return userUDao.findUserById(id);
}
}
测试类:
public class Test {
@org.junit.Test
public void TestUser(){
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("application.xml");
IUserService user = applicationContext.getBean(IUserService.class);
user.findUserById(1);
}
}
执行结果