aop即面向切面的编程,将业务逻辑代码和琐碎逻辑代码分开,达到重用或者解耦的目的。springboot中添加完依赖即默认启用了aop。
starter-aop依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
贴出完整pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.edu.spring</groupId> <artifactId>springboot_web</artifactId> <version>1.0.0</version> <name>springboot_web</name> <!-- FIXME change it to the project's website --> <url>http://www.example.com</url> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.4.RELEASE</version> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> </dependencies> </project>
springboot中aop有两个配置:spring.aop.auto和spring.aop.proxy-target-class,请看源码:
spring.aop.auto默认是true,默认启动aop。
spring.aop.proxy-target-class默认是true,默认使用cglib动态代理。
(一)基于jdk的动态代理实现,必须同时满足两个条件
(1)application.properties文件中设置spring.aop.proxy-target-class=false
spring.aop.proxy-target-class=false
(2)必须有接口类
IUserDao.java
package com.edu.spring.springboot; public interface IUserDao { public void add(String username,String password); }
UserDao.java
package com.edu.spring.springboot; import org.springframework.stereotype.Component; @Component public class UserDao implements IUserDao { public void add(String username,String password){ System.out.println("add [username="+username+",password="+password+"]"); } }
LogAspect.java,使用@Aspect、@Before、@After注解
package com.edu.spring.springboot; import java.util.Arrays; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; @Aspect @Component public class LogAspect { @Before("execution(* com.edu.spring.springboot..*.*(..))") public void log(){ System.out.println("before method log done"); } @After("execution(* com.edu.spring.springboot..*.*(..))") public void afterLog(JoinPoint point){ System.out.println("after method log done "+point.getTarget().getClass()+",args: "+Arrays.asList(point.getArgs())+", method: "+point.getSignature()); } }
App.java
package com.edu.spring.springboot; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; @SpringBootApplication public class App { public static void main(String[] args) throws Exception{ ConfigurableApplicationContext context=SpringApplication.run(App.class, args); System.out.println(context.getBean(IUserDao.class).getClass()); context.getBean(IUserDao.class).add("sl","1234"); context.close(); } }
运行结果如下:
(二)基于cglib的动态代理实现。可以使用接口,也可以不用接口,spring.aop.proxy-target-class=true或者去掉该配置,运行结果如下:
说明:LogAspect是切面类,封装了横切关注点,使用@Aspect标记,并且装配到spring容器中。
@Before是前置通知,调用add方法前先执行
@After是后置通知,调用完add方法后执行
参数execution(* com.edu.spring.springboot..*.*(..))表示该包(或者其子包)下的任何类中的任何方法都会被动态代理。
JoinPoint类中封装了被代理的对象,包括目标类、方法、参数。前置通知、后置通知都可以使用JoinPoint。