1.Aop概述
在业务开发中,总有一些功能与业务代码耦合度不高(例如保存日志,提交事务,权限验证,异常处理等),我们就可以将它们提取到一个工具类中进行管理,在哪使用时就在哪调用。(这是我们以前的做法)
弊端:
这样代码已经开发完成,后期需要添加公共性功能,就需要改动原来的代码,在每个需要调用它的地方都调用以下,一旦调用量变大,就十分麻烦。
需求:
基于以上问题,我们能不能在不修改原来代码的情况下,额外的添加其他公共功能?
解决办法:
我们引入Aop面向切面编程,使用代理对象(需要配置,动态代理模式),我们需要注意,AOP并不是spring特有的设计思想。
面向切面编程:
AOP 为 Aspect Oriented Programming 的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP 是 OOP 的延续,是软件开发中的一个热点,是 java 开发中的一个重要内容。利用 AOP 可以对业务逻辑和非业务逻辑进行隔离,从而使得各部分之间的 耦合度降低,提高程序的可重用性,同时提高了开发的效率。 AOP、OOP 在字面上虽然非常类似,但却是面向不同领域的两种设计思想。OOP (面向对象编程)针对业务处理过程的实体及其属性和行为进行抽象封装,以获 得更加清晰高效的逻辑单元划分。 而 AOP 则是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的 某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。这两种设计思想在目标上有着本质的差异。
面向切面编程优点:
代码复用、降低模块间的耦合度、专注业务。
例如下图:共用的代码(打印日志、事务模块)
总结:
面向对象编程(OOP)的出现让开发者能够实现纵向的业务逻辑处理,但面向对象编程(OOP)并不适合用于定义横向业务逻辑的关系,这样的设计会导致系统出现大量重复代码,复用性极差,如最常用的日志以及事务功能,它们都可能是横向的分布在不通的业务层级(对象层级)中,但是又和具体的核心业务无直接关系,诸如这样类型的代码,在程序中被称作横切(cross cutting),我们应该考虑将这一类代码进行统一管理,提高复用性。
面向切面编程(AOP)就是将这类与核心业务无关的,但又影响着多个类的公共行为抽取、封装到一个可重用模块,从而实现代码复用和模块解耦的目的,这种开发思想则被称为面向切面编程。
2.Aop基本概念
-
连接点(Joinpoint):类中可以被增强的方法,这个方法就被称为连接点
-
切入点(pointcut):类中有很多方法可以被增强,但实际中只有 add 和 update 被增了,那么 add 和 update 方法就被称为切入点(即实际实现的连接点)
-
通知(Advice): 通知是指一个切面在特定的连接点要做的事情(增强的功能)。通知分为方法执行前通知,方法执行后通知,环绕通知等.
-
切面(Aspect):把通知添加到切入点的整个过程称为切面.
-
目标(Target): 代理的目标对象(连接点,切入点所在类)
-
代理(Proxy): 向目标对象应用通知时创建的代理对象
3.Aop实现
对于 AOP 这种编程思想,很多框架都进行了实现。Spring 就是其中之一,可以完成面向切面编程。 AspectJ 是一个基于 Java 语言的 AOP 框架,它提供了强大的 AOP 功能,且其实现方式更为简捷,使用更为方便, 而且还支持注解式开发。所以,Spring 又将 AspectJ 的对于 AOP 的实现也引入到了自己的框架中。
1.配置
首先导入jar包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
基于xml的Aop实现配置
<!--让spring管理装有增强功能的类-->
<bean id="commonUtil" class="com.ffyc.spring.util.CommonUtil"></bean>
<aop:config>
<!-- 配置切入点 -->
<aop:pointcut expression="execution(*com.ffyc.spring.dao.AdminDao.saveAdmin(..))" id="saveAdmin"/>
<aop:pointcut expression ="execution(*com.ffyc.spring.dao.AdminDao.updateAdmin(..))" id="updateAdmin"/>
<!-- 配置通知和切入点 -->
<aop:aspect ref="commonUtil">
<aop:before method="savelog" pointcut-ref="saveAdmin"/>
<aop:after method="savelog" pointcut-ref="updateAdmin"/></aop:aspect>
</aop:config>
配置切入点应注意:
AspectJ 中常用的通知有五种类型:
前置通知before,方法执行前的通知
后置通知after,方法执行后的通知
异常通知after exception,方法出现异常后的通知
环绕通知around,包括了以上三个通知,有环绕通知就不需要上面三个通知。
最终通知.
基于注解方式的AOP实现
首先启动 AspectJ 支持:(在spring.xml中)
<aop:aspectj-autoproxy />
定义通知:
注:通知所在的类需要有
@Component @Aspect
@Component
@Aspect
public class CommonUtil {
//称为通知,指实际要增强的功能
//前置通知
//下面的配置具体含义:在AdminDao类中的每个方法执行之前都会先执行savelog方法
@Before("execution(* com.ffyc.spring.dao.AdminDao.*(..))")
public void savelog(){
System.out.println("保存日志");
}
//异常通知
//下面的配置具体含义:在AdminDao类中的每个方法执行时出现异常都会执行此方法
@AfterThrowing(value = "execution(* com.ffyc.spring.dao.AdminDao.*(..))",throwing = "e")
public void exception(Throwable e){
System.out.println("系统错误");
System.out.println(e.getMessage());
}
//环绕通知
//下面的配置具体含义:在AdminDao类中的每个方法执行时会先执行point.proceed()前的前置通知,然后就是执行point.proceed(),指的就是我们的业务代码,出现异常就执行catch中代码,继续向下执行,即后置通知
@Around("execution(* com.ffyc.spring.dao.AdminDao.*(..))")
public void around(ProceedingJoinPoint point){
System.out.println("之前");
try {
point.proceed();//实际上执行的就是saveAdmin()
} catch (Throwable e) {
e.printStackTrace();
System.out.println("系统错误");
System.out.println(e.getMessage());
}
System.out.println("之后");
}
}
注:同一个通知上只能加一个注解,即一个通知只能是一种类型。
切入点:
@Repository
public class AdminDao {
@Autowired
JdbcTemplate jdbcTemplate;
//这些方法都可以被增强(即添加其他功能,称为连接点)
// saveadmin实际被增强了,那么他就称为切入点
public void saveAdmin(){
// jdbcTemplate.update("insert into admin(account,password,gender) values(?,?,?)", "sdf","adf","男");
System.out.println("保存admin");
System.out.println(10/0);//当出现异常时,会走异常通知
}
public void updateAdmin(){
System.out.println("修改admin");
}
public void add(){
jdbcTemplate.update("UPDATE personvalue SET money=money+10 WHERE id=1" );
}
public void sub(){
jdbcTemplate.update("UPDATE personvalue SET money=money-10 WHERE id=2" );
}
}
以上就是AOP在spring中的具体实现,我们通常使用注解形式来实现AOP,便捷并且不直接对业务代码进行操作,只对特定功能即通知处进行注解配置即可。