spring AOP学习笔记
Aop思想
Aop是spring几个重要的思想之一,在面试的时候也常常问到。所谓的Aop指的是面向切面编程。一个大的系统必然会分成几个模块。每个模块负责完成系统的一项功能。在每个模块中有一些通用的辅助功能模块,比如日志管理,安全管理等。然而这些通用的辅助功能模块导致系统管理起来非常负责。所以我们需要考虑是不是可以将这些通用的辅助模块从每个功能模块中抽取出来进行统一的管理。Aop帮助我们实现了这一点。无需修改功能模块的代码。
spring 为我们提供了很好的Aop的集成,我们只需要进行简单的配置就可以实现Aop。Aop内部的实现原理是动态代理。在动态代理学习模块对动态代理有详细的描述。
动态代理
代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。
按照代理的创建时期,代理类可以分为两种。
静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
动态代理:在程序运行时,运用反射机制动态创建而成。
spring中配置AOP
spring提供两种配置AOP的模式,一种是通过xml进行配置,一种是通过Annotation。下面来看看这两种配置方式
xml文档配置方式
代理类方法
package com.zhouqi.aop;
public class Lio implements Performer{
private Instrument guitar;
public Instrument getGuitar() {
return guitar;
}
public void setGuitar(Instrument guitar) {
this.guitar = guitar;
}
@Override
public void perform(){
guitar.play();
}
}
如上所示就是代理类,一个叫lio的演奏者实现了performer接口,其中performer接口中仅含有一个方法就是perform()。我们想要在perform方法调用之前或者之后调用customer这一委托类的方法。也就是在表演者表演之前观众找座位坐下,当表演结束之后观众离开。所以在调用perform()方法之前我们要织入customer的takeSeat()方法,在之后要织入leave()方法。
package com.zhouqi.aop;
public class Customer {
public void takeSeat(){
System.out.println("the customer is taking seat...");
}
public void leave(){
System.out.println("The customers are leaving");
}
}
在定义好代理类和委托类之后我们就要在xml文档中进行配置。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="guitar" class="com.zhouqi.aop.Guitar"></bean>
<bean id="lio" class="com.zhouqi.aop.Lio">
<property name="guitar" ref="guitar"></property>
</bean>
<bean id="customer" class="com.zhouqi.aop.Customer"></bean>
<aop:config>
<aop:aspect id="customerAsp" ref="customer">
<aop:pointcut id="customerPerf"
expression="execution(* com.zhouqi.aop.Performer.perform(..))"/>
<aop:before method="takeSeat" pointcut-ref="customerPerf"/>
<aop:after method="leave" pointcut-ref="customerPerf"/>
</aop:aspect>
</aop:config>
</beans>
如上所示就是在xml文档中配置spring的例子。需要注意,在使用aop是要在xml文档中加上
xmlns:aop=”http://www.springframework.org/schema/aop”
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
之后才能对aop进行配置。
使用annotation进行配置
基于annotation的配置更加地简介明了,但是对于aop来说用的较多的是使用xml文档进行配置。因为如果我们使用的是其他人所提供的方法,看不到源码,就不能在源码上加上annotation。所以xml用的较多。使用annotation需要在代理类和委托类中加入如下的注释。
package com.zhouqi.aop;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class Customer {
@Before("execution(* com.zhouqi.aop.Performer.perform(..))")
public void takeSeat(){
System.out.println("the customer is taking seat...");
}
@After("execution(* com.zhouqi.aop.Performer.perform(..))")
public void leave(){
System.out.println("The customers are leaving");
}
}
package com.zhouqi.aop;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Lio implements Performer{
@Autowired
private Instrument guitar;
public Instrument getGuitar() {
return guitar;
}
public void setGuitar(Instrument guitar) {
this.guitar = guitar;
}
@Override
public void perform(){
guitar.play();
}
}
xml文档配置如下
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.zhouqi.aop"/>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
在xml文档中加入
<context:component-scan base-package="com.zhouqi.aop"/>
表明对包com.zhouqi.aop中所有标注有@component等标注的类进行扫描并纳入到spring容器的管理之中。
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
加入上述内容表明,其将在spring上下文中自动创建一个AnnotationAwareAspectJAutoProxyCreator的类,它会自动代理一些bean。
通过上述两种在spring中配置aop我们已经完成了aop的配置,现在进行测试。
package com.zhouqi.aop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestAop {
public static void main(String[] args){
ApplicationContext ac = new ClassPathXmlApplicationContext("ApplicationContext.xml");
Performer p = (Performer)ac.getBean("lio");
p.perform();
}
}
我们可以得到如下的结果
the customer is taking seat…
The guitar is playing
The customers are leaving
传递参数给所标注的通知
有时候 需要将代理类方法中的参数传递给委托类。这是需要进行特殊的标注
package com.zhouqi.aop;
import org.springframework.stereotype.Component;
@Component("thinker")
public class Thinker {
public void thinkOfSomething(String mind){
System.out.println("the thinker is thingking");
}
}
如上所示,假如有一个代理类Thinker。它有一个方法thinkofSomething(String mind),需要将mind传递给委托类,那么Advicor应该如何配置呢?
package com.zhouqi.aop;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class Magician implements MindReader{
private String mind;
public void getMind() {
if(mind ==null){
return;
}
System.out.println(mind);
}
@Before("execution(* com.zhouqi.aop.Thinker.thinkOfSomething(String)) && args(mind)")
public void interceptMind(String mind) {
System.out.println("the mind has been intercept:"+ mind);
this.mind = mind;
}
如上所示是一个委托类Magician , 它可以读取Thinker的思想。
所需要的jar包
在进行spring配置的时候经常会报错,报错的原因很多情况下是缺少相应的jar包
下图是我在进行实验室根据提示添加的jar包,添加完之后可以正常运行。
总结
spring aop是对面向对象思想的一个重要的补充。有助于降低模块与模块之间的耦合。减少模块中与主要逻辑无关的代码,使得代码更加简洁清晰。spring的主要思想有DI、IOC和AOP。在接下来的时间需要进一步研究spring相关的源码。