springboot怎樣使用aop呢?我們知道aop的實現(xiàn)一種是jdk動態(tài)代理實現(xiàn)aop右锨,一種是cglib動態(tài)代理實現(xiàn)的aop括堤。
先看一個demo,加入依賴:
<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>
定義一個controller
@Controller
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/user/queryUserById/{id}")
@ResponseBody
private String queryUserById(@PathVariable int id){
return userService.queryUserById(id);
}
}
定義一個service層接口:
public interface UserService {
String queryUserById(int id);
}
其實現(xiàn):
@Service("userService")
public class UserServiceImpl implements UserService{
@Override
public String queryUserById(int id) {
return "user home";
}
}
定義具體的aop功能,封裝橫切關(guān)注點悄窃,配置通知等等讥电,怎么在aop中拿到橫切的對象。
@Aspect
@Component
public class LogAspect {
private Logger logger = LoggerFactory.getLogger(getClass());
@Before("execution(* com.zhihao.miao.service..*.*(..))")
public void log(){
//logger.info("before method log done"+ AopContext.currentProxy().getClass());
logger.info("before method log done");
}
//可以通過JoinPoint取到aop的類名轧抗,方法參數(shù)恩敌,方法簽名
@After("execution(* com.zhihao.miao.service..*.*(..))")
public void logAfter(JoinPoint joinPoint){
logger.info("after method log done "+joinPoint.getTarget().getClass()+",args="+ Arrays.asList(joinPoint.getArgs())+",method="+joinPoint.getSignature());
}
}
啟動類啟動:
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
}
訪問:http://localhost:8080/user/queryUserById/1
打印結(jié)果,
很簡單的實現(xiàn)了aop的功能横媚。
AOP開發(fā)流程
- spring-boot-starter-aop纠炮,加入依賴,默認(rèn)就開啟了AOP的支持
- 寫一個Aspect灯蝴,封裝橫切關(guān)注點(日志恢口,監(jiān)控等等),需要配置通知(前置通知穷躁、后置通知等等)和 切入點(哪些包的哪些類的哪些方法等等)
- 這個Aspect需要納入到spring容器管理耕肩,并且需要加入@Aspect
深入
可以使用spring.aop.auto配置決定是否啟用AOP,默認(rèn)啟用问潭。
application.properties配置如下屬性猿诸,則aop實效,
spring.aop.auto=false
看了AopAutoConfiguration源碼發(fā)現(xiàn)我們可以指定是JDK的動態(tài)代理還是cglib的動態(tài)代理狡忙,通過設(shè)置spring.aop.proxy-target-class這個屬性梳虽。不設(shè)置(默認(rèn)屬性值是false)則默認(rèn)是jdk的代理。但是如果不設(shè)置或者設(shè)置為false去枷,但是代理的類沒有實現(xiàn)接口的話也是cglib代理怖辆。
比如再定義一個類OrderService,并沒有實現(xiàn)接口删顶,而上面的UserServiceImpl是實現(xiàn)UserService接口竖螃,我們看看其使用了什么動態(tài)代理
@Service("orderService")
public class OrderService {
public String name(){
return "order home";
}
}
修改啟動類啟動,
@SpringBootApplication
public class Application {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Application.class,args);
String userServiceClassname = context.getBean("userService").getClass().getName();
System.out.println("userServiceClassname==="+userServiceClassname);
String orderServiceclassname = context.getBean("orderService").getClass().getName();
System.out.println("orderServiceclassname===="+orderServiceclassname);
}
}
我們看到當(dāng)spring.aop.proxy-target-class=false(缺省的時候也是false)逗余,代理的類如果實現(xiàn)接口那么就使用的是jdk的動態(tài)代理特咆,如果沒有實現(xiàn)接口那么就使用的是cglib的動態(tài)代理。
我們在application.properties中設(shè)置spring.aop.proxy-target-class設(shè)為true录粱,
spring.aop.proxy-target-class=true
啟動啟動類腻格,發(fā)現(xiàn)二個類都是使用的cglib代理的
總結(jié):
默認(rèn)是使用基于JDK的動態(tài)代理來實現(xiàn)AOP,spring.aop.proxy-target-class=false 或者不配置啥繁,表示使用JDK的動態(tài)代理菜职,pring.aop.proxy-target-class=true表示使用cglib,如果配置了spring.aop.proxy-target-class=false旗闽,但是代理類沒有實現(xiàn)接口酬核,則依然使用cglib蜜另。
拓展
也可以使用
@EnableAspectJAutoProxy(proxyTargetClass=true,exposeProxy=true)這個注解,注解表示啟用AOP嫡意,當(dāng)然默認(rèn)也是啟用AOP的举瑰,因為springboot的自動裝配機制,第一個參數(shù)表示是使用JDK的動態(tài)代理還是Cglib的代理蔬螟,第二個參數(shù)表示可以使用AopContext這個類進(jìn)行一些操作此迅。
注釋之前所有的application.properties中的配置,在啟動類上加上
@EnableAspectJAutoProxy(proxyTargetClass=true,exposeProxy=true)
@SpringBootApplication
@EnableAspectJAutoProxy(proxyTargetClass=true,exposeProxy=true)
public class Application {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Application.class,args);
String userServiceClassname = context.getBean("userService").getClass().getName();
System.out.println("userServiceClassname==="+userServiceClassname);
String orderServiceclassname = context.getBean("orderService").getClass().getName();
System.out.println("orderServiceclassname===="+orderServiceclassname);
}
}
表示自己啟動cglib代理旧巾,并且exposeProxy配置為true表示可以橫切關(guān)注點中使用AopContext這個類耸序,修改一下上面的LogAspect的log方法,
@Before("execution(* com.zhihao.miao.service..*.*(..))")
public void log(){
logger.info("before method log done"+ AopContext.currentProxy().getClass());
//logger.info("before method log done");
}
啟動并測試菠齿,
可以通過AopContext得到當(dāng)前的代理對象佑吝,更一步得到一些方法簽名,方法的參數(shù)等一些具體信息绳匀。