SpringAOP
1、什么是SpringAOP
- 面向切面编程 (AOP) 通过提供另一种思考程序结构的方式来补充面向对象编程 (OOP)。OOP 中模块化的关键单元是类,而 AOP 中模块化的单元是切面。切面支持跨多种类型和对象的关注点(例如事务管理)的模块化。(这种关注点在 AOP 文献中通常被称为“横切”关注点。)
- Spring 的关键组件之一是 AOP 框架。虽然 Spring IoC 容器不依赖 AOP(这意味着如果您不想使用 AOP,则无需使用 AOP),AOP 补充了 Spring IoC 以提供非常强大的中间件解决方案。
- Spring 通过使用 基于模式的方法或@AspectJ 注释样式提供了编写自定义切面的简单而强大的方法。这两种风格都提供了完全类型化的通知和使用 AspectJ 切入点语言,同时仍然使用 Spring AOP 进行织入。
AOP 在 Spring Framework 中用于:
- 提供声明式企业服务。最重要的此类服务是 声明式事务管理。
- 让用户实现自定义切面,用 AOP 补充他们对 OOP 的使用。
Spring AOP术语:
- Aspect:跨多个类的关注点的模块化。事务管理是企业 Java 应用程序中横切关注点的一个很好的例子。在 Spring AOP 中,切面是通过使用常规类(基于模式的方法)或使用
@Aspect
注解注释的常规类(@AspectJ 样式)来实现的。 - Join point :程序执行过程中的一个点,例如方法的执行或异常的处理。在 Spring AOP 中,一个连接点总是代表一个方法执行。
- Advice :切面在特定连接点采取的行动。不同类型的通知包括“环绕”、“之前”和“之后”通知。包括 Spring 在内的许多 AOP 框架将通知建模为拦截器,并在连接点周围维护一个拦截器链。
- Pointcut :匹配连接点的谓词。Advice 与切入点表达式相关联,并在与切入点匹配的任何连接点处运行(例如,执行具有特定名称的方法)。切入点表达式匹配的连接点的概念是 AOP 的核心,Spring 默认使用 AspectJ 切入点表达式语言。
- Introduction :代表一个类型声明额外的方法或字段。Spring AOP 允许您向任何通知的对象引入新接口(和相应的实现)。例如,您可以使用介绍使 bean 实现 IsModified接口,以简化缓存。(介绍在 AspectJ 社区中称为类型间声明。)
- Target object :一个或多个切面通知的对象。也称为“通知对象”。由于 Spring AOP 是使用运行时代理实现的,因此该对象始终是代理对象。
- AOP proxy :由 AOP 框架创建的对象,用于实现切面协定(通知方法执行等)。在 Spring Framework 中,AOP 代理是 JDK 动态代理或 CGLIB 代理。
- Weaving :将切面与其他应用程序类型或对象链接以创建通知对象。这可以在编译时(例如,使用 AspectJ 编译器)、加载时或运行时完成。Spring AOP 与其他纯 Java AOP 框架一样,在运行时执行织入。
Spring AOP 包括以下类型的通知:
- 前置通知(Before advice):在连接点之前运行但不能阻止执行流继续到连接点的通知(除非它抛出异常)。
- 后置返回通知(After returning advice):在连接点正常完成后运行的通知(例如,如果方法返回而没有引发异常)。
- 后置抛出通知(After throwing advice):如果方法因抛出异常而退出,则要执行的通知。
- 后置通知(After(finally)advice):无论连接点以何种方式退出(正常或异常返回),都要执行的通知。
- 环绕通知(Around advice):环绕连接点的通知,例如方法调用。这是最有力的建议。环绕通知可以在方法调用之前和之后执行自定义行为。它还负责选择是继续到连接点还是通过返回自己的返回值或抛出异常来缩短通知的方法执行。
2、使用Spring实现AOP
第一种方式:
导入依赖
<!--单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!--aop-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--spring相关包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--aspectj-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.8</version>
</dependency>
项目结构
UserMapper接口
public interface UserMapper {
void select();
void add();
void update();
void delete();
}
UserMapperImpl
public class UserMapperImpl implements UserMapper{
@Override
public void select() {
System.out.println("查询用户");
}
@Override
public void add() {
System.out.println("添加用户");
}
@Override
public void update() {
System.out.println("更新用户");
}
@Override
public void delete() {
System.out.println("删除用户");
}
}
BeforeLog通知
public class BeforeLog implements MethodBeforeAdvice {
/*
* method:要执行的目标对象的方法
* objects:要被调用的方法的参数
* object:目标对象
* */
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println(o.getClass().getName()+"的"+method.getName()+"执行了");
}
}
AfterLog通知
public class AfterLog implements AfterReturningAdvice {
/*
* returnValue:返回值
* method:要执行目标的对象的方法
* args:执行方法的参数
* target:执行目标
* */
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName()+"的"+method.getName()+"执行了");
}
}
applicationContext.xml配置文件
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userMapperImpl" class="com.yinzi.dao.UserMapperImpl"/>
<bean id="beforeLog" class="com.yinzi.log.BeforeLog"/>
<bean id="afterLog" class="com.yinzi.log.AfterLog"/>
<!--aop配置-->
<aop:config>
<!--切入点-->
<aop:pointcut id="pointcut" expression="execution(* com.yinzi.dao.*Impl.*(..))"/>
<!--通知-->
<aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
</beans>
测试
public class MyTest {
@Test
public void test1() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper userMapper = context.getBean("userMapperImpl", UserMapper.class);
userMapper.select();
}
}
测试结果
第二种方式:
项目结构
UserMapper接口
public interface UserMapper {
void select();
void add();
void update();
void delete();
}
UserMapperImpl
public class UserMapperImpl implements UserMapper{
@Override
public void select() {
System.out.println("查询用户");
}
@Override
public void add() {
System.out.println("添加用户");
}
@Override
public void update() {
System.out.println("更新用户");
}
@Override
public void delete() {
System.out.println("删除用户");
}
}
DiyPointcut
//自定义切入点
public class DiyPointcut {
public void before(){
System.out.println("======方法执行之前==========");
}
public void after(){
System.out.println("======方法执行之后==========");
}
}
applicationContext.xml
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userMapperImpl" class="com.yinzi.dao.UserMapperImpl"/>
<bean id="diyPointcut" class="com.yinzi.log.DiyPointcut"/>
<!--aop配置-->
<aop:config>
<!--切面-->
<aop:aspect ref="diyPointcut">
<!--切点-->
<aop:pointcut id="pointcut" expression="execution(* com.yinzi.dao.*Impl.*(..))"/>
<!--前置通知-->
<aop:before method="before" pointcut-ref="pointcut"/>
<!--后置通知-->
<aop:after method="after" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
</beans>
测试
public class MyTest2 {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper userMapper = context.getBean("userMapperImpl", UserMapper.class);
userMapper.select();
}
}
测试结果
第三种方法(注解实现)
项目结构
UserMapper接口
public interface UserMapper {
void select();
void add();
void update();
void delete();
}
UserMapperImpl
public class UserMapperImpl implements UserMapper{
@Override
public void select() {
System.out.println("查询用户");
}
@Override
public void add() {
System.out.println("添加用户");
}
@Override
public void update() {
System.out.println("更新用户");
}
@Override
public void delete() {
System.out.println("删除用户");
}
}
AnnotationPointcut
@Aspect
public class AnnotationPointcut {
@Before("execution(* com.yinzi.dao.*Impl.*(..))")
public void before(){
System.out.println("========注解:方法执行前=======");
}
@After("execution(* com.yinzi.dao.*Impl.*(..))")
public void after(){
System.out.println("========注解:方法执行后=======");
}
@Around("execution(* com.yinzi.dao.*Impl.*(..))")
public Object around(ProceedingJoinPoint pj) throws Throwable {
System.out.println("环绕前");
System.out.println("签名:"+pj.getSignature());
//执行目标方法proceed
Object proceed = pj.proceed();
System.out.println("环绕后");
// System.out.println(proceed);
return proceed;
}
}
applicationContext.xml
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userMapperImpl" class="com.yinzi.dao.UserMapperImpl"/>
<bean id="annotationPointcut" class="com.yinzi.log.AnnotationPointcut"/>
<!--
过aop命名空间的<aop:aspectj-autoproxy />声明自动为spring容器中那些配置@aspectJ切面
的bean创建代理,织入切面。当然,spring 在内部依旧采用
AnnotationAwareAspectJAutoProxyCreator进行自动代理的创建工作,但具体实现的细节已经被
<aop:aspectj-autoproxy />隐藏起来了
<aop:aspectj-autoproxy />有一个proxy-target-class属性,默认为false,表示使用jdk动态
代理织入增强,当配为<aop:aspectj-autoproxy poxy-target-class="true"/>时,表示使用
CGLib动态代理技术织入增强。不过即使proxy-target-class设置为false,如果目标类没有声明接
口,则spring将自动使用CGLib动态代理。
-->
<aop:aspectj-autoproxy/>
</beans>
测试
public class MyTest3 {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper userMapper = context.getBean("userMapperImpl", UserMapper.class);
userMapper.select();
}
}
测试结果
以上就是spring-aop的简单理解了~~