一、引言
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。
利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
主要功能
将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。
相关术语
目标对象target:要增强的类
连接点(join point):增强类中所有的方法
切入点(pointcut):要具体增强的方法
通知(advice):具体要增强的功能代码
切面aspect=切入点+通知
织入weaving
代理Proxy
AOP底层实现
Jdk动态代理:基于接口,代理对象与目标可理解为兄弟关系
CGLIB动态代理:目标对象与代理对象可理解为父子关系
二、知识点概要
SpringBoot AOP开发流程
1、添加 spring-boot-starter-aop,加入依赖,默认就开始了AOP 的支持
2、写一个 Aspect,封装横切关注点(日志、监控等),需要配置通知和切入点
3、这个Aspect 需要纳入到spring容器管理,并且需要加入@Aspect
spring.aop.auto 配置项决定是否启用AOP,默认启用
默认是使用基于JDK的动态代理来实现AOP
spring.aop.proxy-target-class=false 或者不配置,表示使用JDK的动态代理
=true 表示使用了 cglib
如果配置了false,而类没有接口,则依然使用cglib
三、AOP小Demo实现
1、目录结构如下
项目结构
2、添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
3、创建UserDao 编写个简单的测试方法
/**
* @author liyao
* @createTime 2018/8/14
* @description
*/
@Component
public class UserDao {
public void add(String username,String password){
System.out.println("username"+username+",password:"+password);
}
}
4、编写切面LogAspect
/**
* @author liyao
* @createTime 2018/8/14
* @description
*/
@Aspect
@Component
public class LogAspect {
@Before("execution(* com.ly.aop.dao..*.*(..))")
public void log(){
System.out.println("method log done");
}
}
5、测试结果
控制台打印
四、AOP配置设置
1、是否启用动态代理,通过application.properties 配置
通过查看:得知配置:
#spring.aop.auto 配置项决定是否启用AOP,默认启用
spring.aop.auto = true
#默认是使用基于JDK的动态代理来实现AOP
#spring.aop.proxy-target-class=false 或者不配置,表示使用JDK的动态代理
#=true 表示使用了 cglib
#如果配置了false,而类没有接口,则依然使用cglib
spring.aop.proxy-target-class = true
(1) Cglib 动态代理配置
spring.aop.auto = true
#如果配置了false,而类没有接口,则依然使用cglib
spring.aop.proxy-target-class = true
结果如图:
(2) JDK动态代理配置
spring.aop.auto = false
则不会启用
如下图
默认是使用基于JDK的动态代理来实现AOP,如果配置了false,而类没有接口,则依然使用cglib,
因为jdk 动态代理是基于接口实现的
。
spring.aop.auto = true
#如果配置了false,而类没有接口,则依然使用cglib
spring.aop.proxy-target-class = false
结果如下图:如何处理?
这里添加一个接口即可:
public interface IUserDao {
public void add(String username,String password);
}
@Component
public class UserDao implements IUserDao {
public void add(String username,String password){
System.out.println("username:"+username+",password:"+password);
}
}
结果如图:2、获取操作对象信息的一些api
/**
* @author liyao
* @createTime 2018/8/14
* @description
*/
@Aspect
@Component
public class LogAspect {
@Before("execution(* com.ly.aop.dao..*.*(..))")
public void log(){
System.out.println("before method log done");
//System.out.println("before method log done" + AopContext.currentProxy().getClass());
}
/**
* 获取通知的信息
* @param point
*/
@After("execution(* com.ly.aop.dao..*.*(..))")
public void logAfter(JoinPoint point){
System.out.println("after method log done");
System.out.println(
" 操作的对象:"+point.getTarget().getClass()+"args"
+ " 方法参数:"+Arrays.asList(point.getArgs())
+ " 方法名:"+point.getSignature().getName());
}
}
结果如图:3、注解配置
@EnableAspectJAutoProxy 注解有两个属性exproxy true ---> 可以 AopContext.currentProxy().getClass()获取代理对象
exproxy false ---> 不可以获取代理对象
@EnableAspectJAutoProxy(exposeProxy = true)
@SpringBootApplication
public class AopApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(AopApplication.class, args);
System.out.println(context.getBean(IUserDao.class).getClass());
context.getBean(IUserDao.class).add("admin","123");
context.close();
}
}