AOP 面向切面编程
看本文章前最好先看Spring IOCSpring IOC
1 什么是AOP
AOP(Aspect Orient Programming):面向切面编程,他是作为面向对象编程的一种补充,已经成为一种比较成熟的编程方式。其实AOP问世的时间并不太长,AOP和OOP互为补充,面向切面编程将程序运行过程分解成各个切面。在JavaEE应用中,常常通过AOP来处理一些具有横切性质的系统级服务,如性能检测、事务管理、安全检查、日志、缓存等,AOP已经成为一种非常常用的解决方案。
如果还没明白,咱们来一个 性能检测 的案例给大家说明。例如,我想统计业务方法执行的耗时。
public class TestService{
public void add(){
//新增的业务
}
void delete(){
//删除的业务
}
//其他方法....
}
我现在想统计TestService中的每个方法执行时的耗时,可以对代码做如下修改:
public class TestService{
public void add(){
Long start = System.currentTimeMillis();
//新增的业务
Long end = System.currentTimeMillis();
System.out.println("耗时为:"+(end-start));
}
void delete(){
Long start = System.currentTimeMillis();
//删除的业务
Long end = System.currentTimeMillis();
System.out.println("耗时为:"+(end-start));
}
//其他方法....
}
这样确实可以达到需求,但是代码量就很大了,每个业务方法都需要在首尾加代码,很明显不可取,如果有了AOP就可以将这部分公共的代码抽取出来,放在AOP中,可以在不修改源码的情况下做到功能的加强。
2 如何使用AOP-xml版
2.1 AspectJ实现AOP
AspectJ是一个基于Java语言的AOP框架,提供了强大的AOP功能。
<!--spring原有的依赖-->
...
<!--在原有spring依赖基础上,加入aspectj依赖-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.8</version>
</dependency>
2.2 AOP的基本概念
关于面向切面编程的一些术语:
-
连接点(Joinpoint): 程序执行过程中明确的点,如方法的调用,或者异常的抛出。在Spring AOP中,连接点总是方法的调用。
-
切入点(Pointcut): 可以插入增强处理的连接点。简而言之,当某个连接点满足指定要求时,该连接点将被添加增强处理,该连接点也就变成了切入点。
-
切面(Aspect): 切面用于组织多个Advice,Advice放在切面中定义。
-
增强处理(Advice): AOP框架在特定的切入点执行的增强处理。处理有"around",“before” 和 "after"等类型。也叫通知。
2.3 使用AOP
1.咱们以事务处理为场景来进行测试,准备一个事务管理器和一个业务类。
//业务接口
public interface IUserService {
void delete();
void update();
}
//业务实现
public class UserServiceImpl implements IUserService {
@Override
public void update() {
System.out.println("用户修改成功。。。");
}
@Override
public void delete() {
System.out.println("用户删除成功。。。");
}
}
事务管理器:模拟提供对于事务管理的相关方法。
public class TransactionManager {
//前置通知方法
public void begin(){
System.out.println("开启事务....");
}
//后置通知方法
public void commit(){
System.out.println("提交事务...");
}
//异常通知方法
public void rollback(Throwable e){
System.out.println("回滚事务....错误原因是:"+e.getMessage());
}
//最终通知方法
public void close(){
System.out.println("关闭资源....");
}
//环绕通知方法(暂时不用)
public void around(ProceedingJoinPoint pjp){
try {
begin();
pjp.proceed(); //目标方法
commit();
}catch (Throwable e){
rollback(e);
}finally {
close();
}
}
}
2.使用AOP为业务类增加事务管理功能
<?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">
<!--创建业务类的bean-->
<bean id="userServiceImpl" class="包路径.UserServiceImpl"></bean>
<!--创建事务管理器的bean 就是切面-->
<bean id="txManager" class="包路径.TransactionManager"></bean>
<!--aop的配置-->
<aop:config>
<!--切入点,id:为切入点的唯一标识,expression:切入点表达式
该表达式会切到指定包下名叫UserServiceImpl类中的方法(方法名不限,参数类型和个数不限,返回值不限)
-->
<aop:pointcut id="myPointcut" expression="execution(* 包路径.UserServiceImpl.*(..))"/>
<!--切面 ,ref:关联事务管理器的id -->
<aop:aspect ref="txManager">
<!--前置通知-->
<aop:before method="begin" pointcut-ref="myPointcut"></aop:before>
<!--后置通知,在发生异常时不会执行 -->
<aop:after-returning method="commit" pointcut-ref="myPointcut"></aop:after-returning>
<!--异常通知,异常时才会执行,throwing="e":将异常信息传递给通知方法 -->
<aop:after-throwing method="rollback" pointcut-ref="myPointcut" throwing="e"></aop:after-throwing>
<!--最终通知,正常和异常都要执行-->
<aop:after method="close" pointcut-ref="myPointcut"></aop:after>
<!--环绕通知-->
<!--<aop:around method="around" pointcut-ref="myPointcut"></aop:around>-->
</aop:aspect>
</aop:config>
</beans>
3.测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class MyTest {
@Autowired
private IUserService userService;
@Test
public void t1(){
userService.update();
}
}
//执行结果:
开启事务....
用户修改成功。。。
提交事务...
关闭资源....
进行测试后会发现使用AOP可以将公共代码抽离到外部AOP的切面中,这样原有代码就不需要做修改。分别可以实现:前置,后置,异常,最终等通知,也可以使用环绕通知来实现,建议测试环绕通知时注释掉前4种通知,方便查看。
3 如何使用AOP-注解版
1.准备业务类
//业务接口
public interface IUserService {
void delete();
void update();
}
//业务实现
@Service
public class UserServiceImpl implements IUserService {
@Override
public void update() {
System.out.println("用户修改成功。。。");
}
@Override
public void delete() {
System.out.println("用户删除成功。。。");
}
}
2.准备事务管理器,并设置 切入点等相关配置
@Component
@Aspect // 配置切面类,相当于:<aop:aspect ref="txManager">
public class TxManager {
//配置切入点
@Pointcut("execution(* 包路径.UserServiceImpl.*(..))")
public void myPointcut(){}
//前置通知, 使用切入点传入方法名即可
@Before("myPointcut()")
public void begin(){
System.out.println("开启事务....");
}
//后置通知
@AfterReturning("myPointcut()")
public void commit(){
System.out.println("提交事务...");
}
//异常通知
@AfterThrowing(value = "myPointcut()",throwing = "e")
public void rollback(Throwable e){
System.out.println("回滚事务....错误原因是:"+e.getMessage());
}
//最终通知
@After("myPointcut()")
public void close(){
System.out.println("关闭资源....");
}
//环绕通知
//@Around("myPointcut()")
public void around(ProceedingJoinPoint pjp){
try {
begin();
pjp.proceed(); //目标方法
commit();
}catch (Throwable e){
rollback(e);
}finally {
close();
}
}
}
3.使用包扫描把 业务类 和 切面配置类 都交给Spring管理
<?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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--包扫描-->
<context:component-scan base-package="包路径"/>
<!--开启注解版aop-->
<aop:aspectj-autoproxy/>
</beans>
4.测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class MyTest {
@Autowired
private IUserService userService;
@Test
public void t1(){
userService.update();
}
}
到此,AOP的两种使用,咱们就有个大致了解。
那么AOP的底层是怎么实现的呢?是动态代理实现。
我亦无他,惟手熟尔
学java找他CSDN:码赛客1024