最近在学习面向切面编程,把一个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注解就可以织入切面了。