利用例子来理解spring的面向切面编程
最近学习了spring的面向切面编程,在网上看到猴子偷桃的例子,觉得这种方式学习比书本上讲解有趣多了,也便于理解。现在就来基于猴子偷桃写个基本的例子。
maven工程:
1、猴子偷桃子,这里就有两个类出现,一类是猴子,一类是桃子。如果以后需要扩展,来个狼偷羊,怎么办呢?因此为了扩展,可以声明接口Stolen,表现偷的行为。
Stolen接口:
public interface Stolen {
/**
* 偷桃子
*
* @date 2014-4-1
*/
public void stolens(String name,Peaces peace);
}
猴子需要实现这个Stolen接口,表示猴子具有偷的行为,Monkey类:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import org.springframework.stereotype.Component;
import com.test.demo.stolen.Peaces;
import com.test.demo.stolen.Stolen;
/**
* 猴子偷桃子
* TODO Comment of Monkey
*
*/
@Component("monkey")
public class Monkey implements Stolen{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void stolens(String name,Peaces peace){
System.out.println("猴子 "+name+" 正在偷桃子,桃子是:"+peace.getName()+" 大小:"+peace.getType());
this.name = name;
}
}
2、猴子偷的桃子具有哪些属性呢?它的大小?品种?因此声明桃子的对象Peace(桃子的英文为peach,写错了,囧~):
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/**
* 桃子
* TODO Comment of Peaces
*
*/
public class Peaces {
/**
* 品种
*/
private String name;
/**
* 大小
*/
private String type;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
3、守护者来了,需要保护桃子不被偷,Protecter类:
import org.springframework.stereotype.Component;
import com.test.demo.stolen.Peaces;
/**
* 守护者,是一个切面
* TODO Comment of Protecter
*
*/
@Component("protecter")
public class Protecter {
public void before(String name,Peaces peace){
System.out.println("守护者在守护桃子"+peace.getName()+"...");
}
public void after(String name,Peaces peace){
System.out.println("守护者发现猴子"+name+"在偷桃子"+peace.getName());
}
}
4、现在需要在beans.xml配置了:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context = "http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"
default-autowire="byName">
<!-- 注解,自动装配bean -->
<context:annotation-config />
<!-- 自动检测Bean -->
<context:component-scan base-package="com.test"></context:component-scan>
<aop:config>
<!--切点:偷的行为 -->
<aop:pointcut expression="execution(* com.test.demo.stolen.Stolen.stolens(String,com.test.demo.stolen.Peaces)) and args(name,peace)" id="stolen"/>
<!--切面: 守护者 -->
<aop:aspect ref="protecter">
<!-- 偷之前,守护者的行为 -->
<aop:before method="before" pointcut-ref="stolen" />
<!-- 偷之后,守护者的行为 -->
<aop:after method="after" pointcut-ref="stolen"/>
</aop:aspect>
</aop:config>
</beans>
在运行时我遇到了以下两个问题:
1)、在声明切点stolen时,切点表达式中传递了参数name和peace,Peaces的类型此时需要连同包名全部声明,否则会报错:
如果参数不完整,也会报错:
5、写个测试类,来测试猴子偷桃:
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.test.demo.stolen.Peaces;
import com.test.demo.stolen.Stolen;
public class MonkeyStokenTest {
@Test
public void testStolen() {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
Stolen m = (Stolen)ctx.getBean("monkey");
Peaces p = new Peaces();
p.setName("水蜜桃");
p.setType("BIG");
m.stolens("小小",p);
}
}
控制台打印出的内容为:
至此,猴子偷桃就结束了。但是现在是有人来偷桃子,怎么处理呀?诶,之前为了方便扩展,声明了个接口Stolen,既然是人来偷桃子,那我声明个Person类,并且让Person实现Stolen接口不就行了:
import org.springframework.stereotype.Component;
import com.test.demo.stolen.Peaces;
import com.test.demo.stolen.Stolen;
@Component("person")
public class Person implements Stolen{
public void stolens(String name,Peaces peace){
System.out.println("这个人 "+name+" 正在偷桃子,桃子是:"+peace.getName()+" 大小:"+peace.getType());
}
}
写个测试类来测试下:
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.test.demo.stolen.Peaces;
import com.test.demo.stolen.Stolen;
public class PersonStolenTest {
@Test
public void testStolen() {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
Stolen m = (Stolen)ctx.getBean("person");
Peaces p = new Peaces();
p.setName("猕猴桃");
p.setType("SMALL");
m.stolens("小明",p);
}
}
打印结果:
--------------------------------------分割线-------------------------------------------
又有个疑问,既然可扩展,那偷的不一定是桃子,也许是别的,这个怎么处理?
这时就要修改代码了,可以先声明一个接口,如Fruits,提取一些共有的属性。
当偷的是桃子,则声明类Peach,实现Fruits 接口,并可以创建桃子的一些私有属性。
其他也如是。
附带pom.xml中的声明:
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>trainingTest</artifactId>
<groupId>com.test.training</groupId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<artifactId>AnnTest</artifactId>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>3.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.8</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.2</version>
</dependency>
</dependencies>
</project>
如果没有aspectjweaver-1.6.8.jar包,会报缺少这个jar包哦~