最近在學習面向切面編程,把一個Dome貼出來,大家一起學習。
1、定義一個目標,這裏使用接口。
package com.miller.emperor.aspects;
//切面中切面的目標對象
public interface Performance {
public void perform();
}
2、目標接口的實現類
package com.miller.emperor.aspects;
/**
* @program: mybatis
* @description: 看電影
* @author: Miller.FAN
* @create: 2019-11-14 18:08
**/
public class Movie implements Performance {
@Override
public void perform() {
System.out.println("觀看《哪吒之魔童降世》");
}
}
package com.miller.emperor.aspects;
/**
* @program: mybatis
* @description: 鋼琴表演
* @author: Miller.FAN
* @create: 2019-11-14 18:11
**/
public class PianoPerformance implements Performance {
@Override
public void perform() {
System.out.println("觀看朗朗的表演");
}
}
3、定義切面
package com.miller.emperor.aspects;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* @program: mybatis
* @description: 定義切面
* @author: Miller.FAN
* @create: 2019-11-14 17:42
**/
//定義切面
@Component
@Aspect
public class Audience {
@Pointcut("execution(* com.miller.emperor.aspects.Performance.perform(..))")
public void performance() {
}
/*目標方法執行前執行以下方法體的內容*/
@Before(value = "performance()")
public void silenceCellPhones(JoinPoint jp) {
try {
String name = jp.getSignature().getName();
System.out.println(name + "請將手機調成靜音!");
System.out.println("電影播放過程中請不要走動,以免影響其它觀衆!");
}catch (Exception e) {
e.printStackTrace();
}
}
@After(value = "performance()")
public void performanceOver(JoinPoint jp) {
try{
String name = jp.getSignature().getName();
System.out.println(name + "我們的整場演出已經結束,請大家有序退出大廳!");
}catch (Exception e) {
e.printStackTrace();
}
}
@Around(value = "performance()")
public Object time(final ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
Object value;
try {
value = proceedingJoinPoint.proceed();
} catch (Throwable throwable) {
throw throwable;
} finally {
System.out.println("我的切面被執行");
}
return value;
}
}
4、將切面配置成spring 的Bean
package com.miller.emperor.config;
import com.miller.emperor.aspects.Audience;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
/**
* @program: emperor
* @description: peizhi
* @author: Miller.FAN
* @create: 2019-11-18 16:48
**/
@Configuration
@EnableAspectJAutoProxy
@ComponentScan
public class ApplicationConfiguration {
@Bean
public Audience audience() {
return new Audience();
}
}
5、調用被通知的對象
import com.miller.emperor.aspects.Performance;
import com.miller.emperor.aspects.PianoPerformance;
import org.springframework.stereotype.Service;
import java.util.HashMap;
/**
* @program: emperor
* @description: 切面服務
* @author: Miller.FAN
* @create: 2019-11-18 16:40
**/
@Service
public class AspectService {
Performance movie = new Movie();
Performance pianoPerfoemance = new PianoPerformance();
public HashMap<String,Object> play() {
HashMap<String,Object> ret = new HashMap<String,Object>();
movie.perform();
pianoPerfoemance.perform();
ret.put("OK", 1);
return ret;
}
}
package com.miller.emperor.controller;
import com.miller.emperor.service.AspectService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
/**
* @program: emperor
* @description: 控制器
* @author: Miller.FAN
* @create: 2019-11-18 16:43
**/
@RestController
@RequestMapping(path = "/test")
public class AspectController {
@Autowired
private AspectService aspectService;
@RequestMapping(value = "/play" , method = RequestMethod.GET)
public HashMap<String,Object> pay() {
return aspectService.play();
}
}
6、spring boot 的啓動類
package com.miller.emperor;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class EmperorApplication {
public static void main(String[] args) {
SpringApplication.run(EmperorApplication.class, args);
}
}
7、pom文件
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.miller</groupId>
<artifactId>emperor</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>emperor</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>1.5.4.RELEASE</version>
</dependency>
<!--
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
8、application.properties
spring.main.allow-bean-definition-overriding=true
server.port=8989
運行,並用postman測試,很遺憾,沒有像預期的那樣切面起作用。
2019-11-20 15:08:56.654 INFO 4188 --- [nio-8989-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2019-11-20 15:08:56.655 INFO 4188 --- [nio-8989-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2019-11-20 15:08:56.660 INFO 4188 --- [nio-8989-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 5 ms
觀看《哪吒之魔童降世》
觀看朗朗的表演
9、爲什麼我的切面沒有起作用呢?
項目地址:https://github.com/XianbeiEmperor/emperor,請大神們路過指點。
10、修改
package com.miller.emperor.service;
import com.miller.emperor.aspects.Movie;
import com.miller.emperor.aspects.PianoPerformance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashMap;
/**
* @program: emperor
* @description: 切面服務
* @author: Miller.FAN
* @create: 2019-11-18 16:40
**/
@Service
public class AspectService {
/* Performance movie = new Movie();
Performance pianoPerfoemance = new PianoPerformance();*/
@Autowired
private Movie movie;
@Autowired
private PianoPerformance pianoPerfoemance;
public HashMap<String,Object> play() {
HashMap<String,Object> ret = new HashMap<String,Object>();
movie.perform();
pianoPerfoemance.perform();
ret.put("OK", 1);
return ret;
}
}
同時在接口類加 @Repository註解,接口的實現類加@Component註解。
11、成功
2019-11-20 15:38:22.288 INFO 1592 --- [nio-8989-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2019-11-20 15:38:22.293 INFO 1592 --- [nio-8989-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 5 ms
perform請將手機調成靜音!
電影播放過程中請不要走動,以免影響其它觀衆!
觀看《哪吒之魔童降世》
我的切面被執行
perform我們的整場演出已經結束,請大家有序退出大廳!
perform請將手機調成靜音!
電影播放過程中請不要走動,以免影響其它觀衆!
觀看朗朗的表演
我的切面被執行
perform我們的整場演出已經結束,請大家有序退出大廳!
12、spring切面可以應用5種類型的通知
Before 前置通知、 After 後置通知、 After-returning 返回通知、 After-throwing 異常通知、Around 環繞通知
13、織入:把切面應用到目標對象並創建新的代理對象的過程。切面在指定的連接點被織入到目標對象種。在目標對象的聲明週期中有多個點可以進行織入。 編譯期、類加載期、運行期。
14、切點開在註解上如何實現?
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Timed {
}
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect
@Component
@Slf4j
public class TimeLogAspect {
@Around("@annotation(com.nibado.example.springaop.aspects.Timed) && execution(public * *(..))")
public Object time(final ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
long start = System.currentTimeMillis();
Object value;
try {
value = proceedingJoinPoint.proceed();
} catch (Throwable throwable) {
throw throwable;
} finally {
long duration = System.currentTimeMillis() - start;
log.info(
"{}.{} took {} ms",
proceedingJoinPoint.getSignature().getDeclaringType().getSimpleName(),
proceedingJoinPoint.getSignature().getName(),
duration);
}
return value;
}
}
然後需要使用的地方直接加@Timed註解就可以織入切面了。