最近在看《Spring实战》,在这儿使用注解完整的实现一个切面的例子,也实现通过注解引入新功能;
实现切面
关于切面相关概念这篇不提,可以大致理解成,只要调用某个特定的方法,这个调用信息会被切面拦截,然后执行切面定义的逻辑,之后才能顺利的调用该方法。
我这个是一个maven项目所有代码写在同一个包下面,测试类除外。关于切面这个部分可能需要导入一些包。
pom.xml1
2
3
4
5
6
7
8
9
10<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.4</version>
</dependency>
定义特定的方法
首先定一个接口
1
2
3
4
5package aopJavaConfig;
public interface Performance {
public void perform();
}实现这个接口,定义一个叫演出的方法
1
2
3
4
5
6
7
8
9
10package aopJavaConfig;
import org.springframework.stereotype.Component;
@Component
public class PerformanceImpl implements Performance {
@Override
public void perform(){
System.out.println("演出~~~");
}
}
定义一个切面,同时定义了切点和通知
1 | package aopJavaConfig; |
- @Aspect修饰的这个类是切面,我们可以在切面中定义切点和通知;
- @Pointcut修饰的是切点,可以看到切点就是我们之前定义的那个特定的方法;
- @Before修饰的方法会在定义的特定方法之前被调用;
- @AfterReturning修饰的方法会在特定方法被成功调用后执行;
- @AfterThrowing修饰的方法会在特定方法执行异常时被执行;
定义一个配置类
这个配置类负责开启自动代理,并且将我们定义好的切面声明成Bean;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17package aopJavaConfig;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@EnableAspectJAutoProxy
@ComponentScan
public class ConcertConfig {
@Bean
public Audience audience(){
return new Audience();
}
}
至此,我们有两个bean,一个是那个特定的方法,一个是声明好的切面,那么我们就有一个很朴素的冲动,这样定义是否成功了呢,所以写一个测试类;
测试切面定义是否成功
预期的结果:调用演出方法,在控制台打印出来演出之前要干嘛,演出成功后要干嘛;
具体的输出请查看代码本身;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19package aopJavaConfig;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = ConcertConfig.class)
public class PerformanceTest {
@Autowired
private Performance performance;
@Test
public void test() {
performance.perform();
}
}
至此,使用java注解方式定义一个切面结束;
引入新功能(DeclareParents)
接上文代码,当我们接受老代码,期望在原来的特定方法的基础上加一个功能,很朴素的想法就是修改原来的方法,加一段功能代码,但是假设现在老代码很复杂不得修改;那么为了增加这个功能,Spring提供了一个另一个实现方式。
新功能代码
定义新接口
1
2
3
4
5package aopJavaConfig;
public interface Encoreable {
void performEncore();
}实现这个接口
1
2
3
4
5
6
7
8
9
10package aopJavaConfig;
import org.springframework.stereotype.Component;
@Component
public class EncoreableImpl implements Encoreable{
@Override
public void performEncore(){
System.out.println("返场表演~~~");
}
}
将新功能引入原来特定方法形成的bean中
可以将bean看作一个对象,这个特定的这个对象中添加一个新的方法,也就是让这个对象可以直接调用新加的方法;1
2
3
4
5
6
7
8
9
10
11
12package aopJavaConfig;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.DeclareParents;
@Aspect
public class EncoreableIntroducer {
/**
* 此注解可以将新加的接口引入到原来调用目标函数的bean中
*/
@DeclareParents(value = "aopJavaConfig.Performance+", defaultImpl = EncoreableImpl.class)
public static Encoreable encoreable;
}
- @DeclareParents 这个注解的value制定了这个新方法加给谁,defaultImpl这个参数指定了新加方法的具体实现。
实际上新加方法是加给了value制定方法的父类。新加方法要告知Spring容器
修改配置类如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22package aopJavaConfig;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@EnableAspectJAutoProxy
@ComponentScan
public class ConcertConfig {
@Bean
public Audience audience(){
return new Audience();
}
@Bean
public EncoreableIntroducer encoreableIntroducer(){
return new EncoreableIntroducer();
}
}
测试类
预期结果:能够使用特定类的对象成功调用新加的方法;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22package aopJavaConfig;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = ConcertConfig.class)
public class PerformanceTest {
@Autowired
private Performance performance;
@Test
public void test() {
performance.perform();
System.out.println("+++++++++++++++++++++++++++++++++++");
Encoreable encoreable = (Encoreable)performance;
encoreable.performEncore();
}
}
测试结果如下:1
2
3
4
5
6手机静音!
请坐!
演出~~~
表演结束,鼓掌!!!
+++++++++++++++++++++++++++++++++++
返场表演~~~
至此使用注解方式开发切面,切点,通知,新加方法都已经完成。代码经过了验证;
如有错误,还请指正。欢迎交流。
后记
- 整个工程代码在下面的链接中,如果需要可以下载看一看。
- 点击跳转到我的GitHub