spring-boot 项目利用spring切面实现插件功能,对项目无侵入

栏目: Java · 发布时间: 4年前

内容简介:开发pybbs时,一直想加入插件功能,但没有比较好用的实现方法,突然有一天想起来spring里的切面了,这不正是写插件的好东西,然后就折腾了一下,总结一下最可怕的是,在拿到参数和返回值之后,还可以进行修改,修改完后,再返回回去,神不知鬼不觉!!首先创建springboot项目,最简单的就行,我这就引了一个依赖

开发pybbs时,一直想加入插件功能,但没有比较好用的实现方法,突然有一天想起来spring里的切面了,这不正是写插件的好东西,然后就折腾了一下,总结一下

最可怕的是,在拿到参数和返回值之后,还可以进行修改,修改完后,再返回回去,神不知鬼不觉!!

创建项目

首先创建springboot项目,最简单的就行,我这就引了一个依赖

<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
  </dependency>

  <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
  </dependency>

  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
  </dependency>
</dependencies>

配置

首先开启 aspect 功能

@SpringBootApplication
@EnableAspectJAutoProxy
public class PluginDemoApplication {

  public static void main(String[] args) {
    SpringApplication.run(PluginDemoApplication.class, args);
  }

}

然后编写 model , service

User.java

@Data
@ToString
public class User {

  private Integer id;
  private String username;

}

UserService.java

@Service
@Slf4j
public class UserService {

  public User save(User user) {
    log.info("save user: " + user.toString());
    return user;
  }

  public void delete(Integer id) {
    log.info("user deleted!");
  }

}

增加切入点

创建切入点,固定写法

UserServicePointcut.java

public class UserServicePointcut {

  @Pointcut("execution(public * com.example.plugindemo.service.UserService.save(..))")
  public void save() {
  }

  @Pointcut("execution(public * com.example.plugindemo.service.UserService.delete(..))")
  public void delete() {
  }

}

文原接链: https://tomoya92.github.io/2019/07/02/spring-boot-plugin/

切入

从上面提供的切入点来切入相应的服务,可以拿到 service 里方法的参数,返回值等信息,而且原service方法还不知道

切入有三种方式

  • @Before 在被切入方法执行前执行
  • @After 在被切入方法执行后执行
  • @Around 在被切入方法执行过程中执行

我这里以一个 redis 的缓存来实现两个功能

  • 当添加用户时,将用户缓存进redis里
  • 当删除用户时,再从redis里将用户删除

UserPlugin.java

@Component
@Aspect
@Slf4j
public class RedisPlugin {

  @Around("com.example.plugindemo.plugin.UserServicePointcut.save()")
  public Object save(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
    // 获取参数
    Object[] args = proceedingJoinPoint.getArgs();
    log.info(args[0].toString());
    // 修改参数内容
    ((User) args[0]).setUsername("jetty");
    // 通过 proceed 方法执行被切入方法然后再返回,如果要修改内容也可以在拿到执行完后的对象进行修改,修改完后再返回
    return proceedingJoinPoint.proceed(args);
  }

  @After("com.example.plugindemo.plugin.UserServicePointcut.save()")
  public void afterSave(JoinPoint joinPoint) {
    // 获取参数
    Object[] args = joinPoint.getArgs();
    log.info("将 user 对象缓存进 redis 里, user: {}", args[0].toString());
  }

  @Before("com.example.plugindemo.plugin.UserServicePointcut.delete()")
  public void delete(JoinPoint joinPoint) {
    // 获取参数
    Object[] args = joinPoint.getArgs();
    log.info("将 redis 里的user对象删除,根据id: {}", args[0]);
  }
}

测试

测试代码如下

PluginDemoApplicationTests.java

@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class PluginDemoApplicationTests {

  @Autowired
  private UserService userService;

  @Test
  public void save() {
    User user = new User();
    user.setUsername("tomcat");
    user.setId(1);
    user = userService.save(user);
    log.info(user.toString());
  }

  @Test
  public void delete() {
    userService.delete(1);
  }

}

接文原链: https://tomoya92.github.io/2019/07/02/spring-boot-plugin/

上面例子中对 save() 方法进行了两个切入,执行日志如下

2019-07-02 17:30:30.682  INFO 21254 --- [           main] c.example.plugindemo.plugin.RedisPlugin  : User(id=1, username=tomcat)
2019-07-02 17:30:30.689  INFO 21254 --- [           main] c.e.plugindemo.service.UserService       : save user: User(id=1, username=jetty)
2019-07-02 17:30:30.689  INFO 21254 --- [           main] c.example.plugindemo.plugin.RedisPlugin  : 将 user 对象缓存进 redis 里, user: User(id=1, username=jetty)
2019-07-02 17:30:30.691  INFO 21254 --- [           main] c.e.p.PluginDemoApplicationTests         : User(id=1, username=jetty)

在调用 delete() 方法后日志如下

2019-07-02 17:31:10.582  INFO 21256 --- [           main] c.example.plugindemo.plugin.RedisPlugin  : 将 redis 里的user对象删除,根据id: 1
2019-07-02 17:31:10.590  INFO 21256 --- [           main] c.e.plugindemo.service.UserService       : user deleted!

加载

springboot项目打包后是个jar包,可以通过 java -jar xx.jar 来执行

那有没有可能将插件分出来开发,然后打成jar包,在启动主项目时,指定一下目录就默认自动加载了呢?

答案是有的

首先修改打包插件

pom.xml

<plugin>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-maven-plugin</artifactId>
  <configuration>
    <layout>ZIP</layout>
  </configuration>
</plugin>

这样再打包的主项目通过下面脚本启动就可以加载指定目录下的插件jar包了

将插件jar包放在主项目jar包同目录下的 plugins 下,然后启动脚本里加上加载路径

`java -Dloader.path=./plugins -jar xx.jar`

原文链接:


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Bulletproof Ajax中文版

Bulletproof Ajax中文版

Jeremy Keith / 刘申、宋薇 / 人民邮电出版社 / 2007-11 / 39.00元

本书介绍了如何构建无懈可击的Ajax Web应用程序,重点讲述如何在已有Web站点使用Ajax增强网站用户体验,从而尽可能地保证网站拥有最大限度的可移植性和亲和力,这正是目前大多数网站面临的需求。书中主要介绍了JavaScript、DOM、XMLHttpRequest、数据格式等,同时还提出了一种Hijax方法,即可以让Web应用程序平稳退化的方法。 本书适合各层次Web开发和设计人员阅读......一起来看看 《Bulletproof Ajax中文版》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

URL 编码/解码
URL 编码/解码

URL 编码/解码

MD5 加密
MD5 加密

MD5 加密工具