我决定为你现在描述的情况编写一个新答案,而不是在你大量编辑问题后再次更新我的第一个答案。正如我所说,你的散文不构成有效的MCVE,因此我需要在这里做出一些有根据的猜测。
对于任何阅读此答案的人:请先阅读其他答案,尽管两个答案在代码和Maven配置方面存在冗余,但我不想重复自己。
根据你的描述,情况看起来像这样:
Bean标记注释:
package de.scrum_master.app;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Retention(RUNTIME)
@Target(TYPE)
public @interface Bean {}
一些POJO,其中两个是@Bean
,一个不是:
package de.scrum_master.app;
@Bean
public class Resource {
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
package de.scrum_master.app;
@Bean
public class Person {
private String firstName;
private String lastName;
private int age;
public Person(String firstName, String lastName, int age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}
@Override
public String toString() {
return "Person[firstName=" + firstName + ", lastName=" + lastName + ", age=" + age + "]";
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
package de.scrum_master.app;
public class NoBeanResource {
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
每个@Bean
类都应该实现的数据库存储接口:
这里我不得不发明一些虚假方法,因为您没有告诉我接口及其实现的真正样子。
package de.scrum_master.app;
public interface StoredOnDatabase {
void writeToDatabase();
void readFromDatabase();
}
向 Resource
类介绍方法:
这与我的第一个答案中描述的相同,没有什么需要补充的,只是重复了代码:
package de.scrum_master.aspect;
import de.scrum_master.app.Resource;
public aspect MethodIntroducer {
public Resource.new(String id) {
this();
setId(id);
}
public boolean Resource.equals(Object obj) {
if (!(obj instanceof Resource))
return false;
return getId().equals(((Resource) obj).getId());
}
public String Resource.toString() {
return "Resource[id=" + getId() + "]";
}
}
方面:拦截设置器方法调用。
package de.scrum_master.aspect;
import de.scrum_master.app.Bean;
public aspect BeanSetterInterceptor {
before(Object newValue) : @within(Bean) && execution(public void set*(*)) && args(newValue) {
System.out.println(thisJoinPoint + " -> " + newValue);
}
}
当setter方法被执行时,Aspect会打印类似于以下内容的输出:
execution(void de.scrum_master.app.Resource.setId(String)) -> dummy
execution(void de.scrum_master.app.Resource.setId(String)) -> A
execution(void de.scrum_master.app.Resource.setId(String)) -> B
execution(void de.scrum_master.app.Person.setFirstName(String)) -> Jim
execution(void de.scrum_master.app.Person.setLastName(String)) -> Nobody
execution(void de.scrum_master.app.Person.setAge(int)) -> 99
顺便提一下,你还可以通过set()
切入点直接拦截字段写访问,而不是间接地通过名称拦截setter方法。如何做取决于您想要实现什么以及是否希望保持API级别(公共方法)或跟踪在/外部setter方法中进行的内部字段赋值。
使@Bean
实现StoredOnDatabase
接口的方面:
首先,该方面为接口提供了方法实现。其次,它声明所有@Bean
类都应该实现此接口(并继承方法实现)。请注意,AspectJ可以直接在接口上声明方法实现。它甚至可以声明字段。即使在Java中有接口默认方法之前,这也起作用。无需声明实现接口并覆盖接口方法的类作为中介,它可以直接在接口上运行!
package de.scrum_master.aspect;
import de.scrum_master.app.StoredOnDatabase;
import de.scrum_master.app.Bean;
public aspect DatabaseStorageAspect {
public void StoredOnDatabase.writeToDatabase() {
System.out.println("Writing " + this + " to database");
}
public void StoredOnDatabase.readFromDatabase() {
System.out.println("Reading " + this + " from database");
}
declare parents: @Bean * implements StoredOnDatabase;
}
JUnit测试演示所有引入方面的特性:
请注意,上述类仅使用System.out.println()
,没有日志框架。因此,该测试使用System.setOut(*)
来注入Mockito模拟对象,以验证预期的日志行为。
package de.scrum_master.app;
import org.junit.*;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import java.io.PrintStream;
public class BeanAspectsTest {
private PrintStream systemOut;
@Before
public void doBefore() {
systemOut = System.out;
System.setOut(mock(PrintStream.class));
}
@After
public void doAfter() {
System.setOut(systemOut);
}
@Test
public void canCallConstructorWithArgument() {
assertNotEquals(null, new Resource("dummy"));
}
@Test
public void testToString() {
assertEquals("Resource[id=dummy]", new Resource("dummy").toString());
}
@Test
public void testEquals() {
assertEquals(new Resource("A"), new Resource("A"));
assertNotEquals(new Resource("A"), new Resource("B"));
verify(System.out, times(4)).println(anyString());
}
@Test
public void testPerson() {
Person person = new Person("John", "Doe", 30);
person.setFirstName("Jim");
person.setLastName("Nobody");
person.setAge(99);
verify(System.out, times(3)).println(anyString());
}
@Test
public void testNoBeanResource() {
NoBeanResource noBeanResource = new NoBeanResource();
noBeanResource.setId("xxx");
verify(System.out, times(0)).println(anyString());
}
@Test
public void testDatabaseStorage() {
StoredOnDatabase resource = (StoredOnDatabase) new Resource("dummy");
resource.writeToDatabase();
resource.readFromDatabase();
StoredOnDatabase person = (StoredOnDatabase) new Person("John", "Doe", 30);
person.writeToDatabase();
person.readFromDatabase();
assertFalse(new NoBeanResource() instanceof StoredOnDatabase);
verify(System.out, times(5)).println(anyString());
}
}
Maven POM:
这与第一个答案几乎相同,我只是添加了Mockito。
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>de.scrum-master.stackoverflow</groupId>
<artifactId>aspectj-itd-example-57525767</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.source-target.version>8</java.source-target.version>
<aspectj.version>1.9.4</aspectj.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>${java.source-target.version}</source>
<target>${java.source-target.version}</target>
<useIncrementalCompilation>false</useIncrementalCompilation>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.11</version>
<configuration>
<source>${java.source-target.version}</source>
<target>${java.source-target.version}</target>
<Xlint>ignore</Xlint>
<complianceLevel>${java.source-target.version}</complianceLevel>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
<executions>
<execution>
<phase>process-sources</phase>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectj.version}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.0.0</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>