Spring之AOP详解
一、概述
放在最前边,Spring AOP官方文档,传送门:spring-aop。个人感觉官方文档就是最好的学习文档,上边有详细的说明和细节的解释。其中将OOP和AOP做了简单对比。
- OOP(Object-oriented Programming):面向对象编程,一切皆对象,最小单元为class
- AOP(Aspect-oriented Programming):面向切面编程,一切皆切面,最小单元为aspect
1、何为AOP
AOP名为面向切面编程,其实跟OOP是类似的,都是一种编程的模型思想,在这种思想模式下就产生了相应的实现,AspectJ就是其中的一种。AOP其实是为了解决我们在实际生产过程中所遇到的问题,比如在不影响业务逻辑代码的前提下加入日志(日志审计),数据库的事务管理(声明式事务)等等。从设计的角度可以理解就是为了减少重复代码的耦合,聚焦业务逻辑(高内聚,低耦合),降低“侵入式编程”的风险。
2、几个重要概念
- 切面(aspect)
- 连接点(join point)
- 切点(pointcut)
- 通知(advice)
- introduction
- 目标对象(target object)
- AOP代理(AOP proxy)
- 织人(weaving)
二、示例
spring的AOP编程支持xml和注解两种方式。个人而言,比较喜欢注解,简单方便。
1、pom.xml中引入相关依赖
<dependencies>
<!-- spring core-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<!-- aop-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
</dependencies>
2、编写切面类
package com.zero.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
* 自定义切面
* @author zero
*/
@Component
@Aspect
public class ZeroAspect {
@Pointcut("execution(* com.zero.dao.*.*(..))")
public void pointcut() {}
@Before("pointcut()")
public void before() {
System.out.println("before");
}
}
3、在配置类或xml中开启AOP代理
package com.zero.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@EnableAspectJAutoProxy
@ComponentScan("com.zero")
public class AppConfig {
}
4、需要增强的类
package com.zero.dao;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Repository;
@Repository
public class TestDao implements Dao {
@Override
public void query() {
System.out.println("testdao");
}
}
package com.zero.dao;
public interface Dao {
void query();
}
5、测试
package com.zero;
import com.zero.config.AppConfig;
import com.zero.dao.TestDao;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
* Hello world!
*
*/
public class App
{
public static void main( String[] args )
{
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
TestDao dao = (TestDao) annotationConfigApplicationContext.getBean(TestDao.class);
dao.query();
}
}
三、其他说明(重要)
1、AOP代理
Spring Aop默认使用的是JDK 动态代理(戳链接代理模式)作为AOP代理,同时还支持CGLIB代理。如官方文档所说:
Spring AOP defaults to using standard JDK dynamic proxies for AOP proxies. This enables any interface (or set of interfaces) to be proxied.
Spring AOP can also use CGLIB proxies. This is necessary to proxy classes rather than interfaces. By default, CGLIB is used if a business object does not implement an interface. As it is good practice to program to interfaces rather than classes, business classes normally implement one or more business interfaces. It is possible to force the use of CGLIB, in those (hopefully rare) cases where you need to advise a method that is not declared on an interface or where you need to pass a proxied object to a method as a concrete type.
It is important to grasp the fact that Spring AOP is proxy-based. See Understanding AOP Proxies for a thorough examination of exactly what this implementation detail actually means.
如果想开启CGLIB代理可以使用proxyTargetClass
属性。
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ComponentScan("com.zero")
public class AppConfig {
}
由于CGLIB是通过类型实例化bean,所以在getBean的时候可以使用加载类的方式 。
Dao dao = (Dao) annotationConfigApplicationContext.getBean(TestDao.class);
而JDK动态代理使用name的方式进行实例化bean,所以在创建类的时候要给定一个名称(使用@Qualifier),并在getBean的时候需要指定这个名称。
@Repository
@Qualifier("testDao")
public class TestDao implements Dao {
@Override
public void query() {
System.out.println("testdao");
}
}
Dao dao = (Dao) annotationConfigApplicationContext.getBean("testDao");