Key Points
AOP的基本概念
Advice的类型
AOP的实现方式---Proxy
AOP的实现方式---CGLib
XML配置AOP
注解配置AOP
前面已经介绍了关于Spring中的IOC,接下来介绍下AOP。
首先,在OO设计中,由类组成了模块化,而AOP的出现,弥补了OO中的不足点。原因是:在AOP中模块化的单元是切面。切面能对关注点进行模块化,例如:横切多个类型和对对象的事务管理,而每个关注点被称作为横切关注点。
一 AOP的基本概念
1. 切面(Aspect):
一个关注点的模块化,可以横切多个对象,例如:事务管理。在Spring AOP中,切面可以使用基于模式或者基于Aspect注解的方式来实现。
2. 连接点(Jointpoint)
在程序执行过程中某个特定的点。比如某个方法调用的时候或者处理异常的时候。在Spring AOP中,一个连接点总表示一个方法的执行。
3. 通知(advice)
许多AOP框架都是以拦截器做通知模型,并维护一个以连接点为中心的拦截器链。
在切面的某个特定连接点上执行的动作。
4. 切入点(Pointcut)
匹配连接点的断言。通知和一个切入点表达式关联,并在满足这个切入点的连接点上运行(例如:当执行某个特定名称的方法时)。切入点表达式如何和连接点匹配是AOP的核心。Spring缺省使用AspectJ切入点语法。
5. 引入(Introduction)
用来给一个类型声明额外的方法或属性(也被称为连接类型声明)。Spring允许引入一个新的接口以及对应的实现到任何被代理的对象。例如:你可以使用引入来使一个bean实现IsModified接口,以便简化缓存机制。
6. 目标对象(Target Object)
被一个或多个切面通知的对象,也被称为被通知对象。而在Spring AOP中,目标对象是通过运行时代理实现的,所以这个对象永远是被代理(Proxied)对象。
7. AOP代理(AOP Proxy)
AOP框架创建的对象,用来实现某个契约。在AOP中,AOP代理可以是JDK的Proxy或者CGLib。
8. 织入(Weaving
把切面连接到其它应用程序类型或对象上,并创建一个被通知的对象。这些可以在编译时(如使用AspectJ编译器),类加载时或运行时完成。Spring和其它的AOP都是在运行时织入。
二 通知(Advice)的类型
主要包含以下五种通知:
1) Before Advice
在连接点执行的通知。
2) After returning Advice
在连接点之后执行的通知,如果没有任何异常,那就返回。
3) After throwing Advice
执行连接点时抛出异常退出时执行的通知。
4) After Advice
在连接点退出的时候执行的通知,不管任何时候,都会被执行。
5) Around Advice
包围一个连接点的通知,如方法调用,这是最强大的通知类型。可以在连接点前后完成自定义的行为,也可以选择是否继续执行连接点或者直接返回值或抛出异常来终止执行。
下面有一个Sample:
PersonServiceBean bean = (PersonServiceBean)this.targetObject;
Object result = null;
if(bean.getUser() != null){
//before Advise():前置通知
try {
result = methodProxy.invoke(proxy, args);
//after advise():后置通知
} catch (Exception e) {
e.printStackTrace();
//exception Advise():例外通知
}finally{
//finally Advise:最终通知
}
}
三 AOP的实现方式---Proxy
Spring默认使用J2SE动态代理来作为AOP的代理,故对于代理接口而言用Proxy就可以执行。
1. Beans.xml
2. JDKProxyFactory.java
public class JdkProxyFactory implements InvocationHandler{
private Object targetObject;
/**
* 创建代理对象
*/
public Object CreateProxyInstance(Object targetObject){
this.targetObject = targetObject;
return Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(),
this.targetObject.getClass().getInterfaces(), this);
}
/**
* 拦截方法
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
PersonServiceBean bean = (PersonServiceBean)this.targetObject;
Object result = null;
if(bean.getUser() != null){
result = method.invoke(proxy, args);
}
return result;
}
}
3. PersonService.java
public interface PersonService {
public void save(String user);
public void update(String user);
}
4. PersonServiceBean.java
public class PersonServiceBean implements PersonService{
private String user = null;
/**
* @param user
*/
public PersonServiceBean(String user) {
this.user = user;
}
public PersonServiceBean() {}
/**
* @return the user
*/
public String getUser() {
return user;
}
/**
* @param user the user to set
*/
public void setUser(String user) {
this.user = user;
}
public void save(String user){
System.out.println("PersonServiceBean.save() is running");
}
public void update(String user){
System.out.println("PersonServiceBean.update() is running");
}
}
5. AopTest.java
JdkProxyFactory factory = new JdkProxyFactory();
PersonService personService = (PersonService)factory.CreateProxyInstance(new PersonServiceBean("Jamson"));
personService.save("888");
四 AOP的实现方式---CGLib
如果是一个业务对象没有实现接口,在这种情况下,我们实现的解决方案------使用CGLib来代理。
1. Beans.xml
2. CGlibProxyFactory.java
public class CGlibProxyFactory implements MethodInterceptor {
private Object targetObject;
/**
* 创建代理对象
* @param targetObject
* @return
*/
public Object CreateObjectInstance(Object targetObject){
this.targetObject = targetObject;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.targetObject.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
/**
* 拦截方法
*/
public Object intercept(Object proxy, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
PersonServiceBean bean = (PersonServiceBean)this.targetObject;
Object result = null;
if(bean.getUser() != null){
//before Advise():前置通知
try {
result = methodProxy.invoke(proxy, args);
//after advise():后置通知
} catch (Exception e) {
e.printStackTrace();
//exception Advise():例外通知
}finally{
//finally Advise:最终通知
}
}
return result;
}
}
3. PersonServiceBean.java:同JDK
4. AopTest.java
CGlibProxyFactory factory = new CGlibProxyFactory();
PersonServiceBean personServiceBean = (PersonServiceBean)factory.CreateProxyInstance(new PersonServiceBean("Jamson"));
personServiceBean.save("888");
五 XML配置AOP
由于Spring框架的支持,我们在实际的开发中使用XML配置或注解来实现AOP。
XML配置开发AOP,分为三步:
1. Service层的开发:
PersonService.java/PersonServiceBean.java同注解方式。
2. 切面的开发
public class InteceptorClass {
public void doAccessCheck(){
System.out.println("before advice");
}
public void doWriteLog(String result){
System.out.println("after advice"+":"+result);
}
public void doMemoryClear(){
System.out.println("finally advice");
}
public void doWriteErrorLog(Exception e){
System.out.println("Exception advice");
}
public Object doAroundMethod(ProceedingJoinPoint pjp)throws Throwable{
System.out.println("enter around advice method.");
Object obj = pjp.proceed();
System.out.println("exit around advice method.");
return obj;
}
}
3. XML的配置
<aop:aspectj-autoproxy/>
<bean id="inteceptorClass" class="com.Aop.inteceptor.InteceptorClass" />
<aop:config>
<aop:aspect id="aspectd" ref="inteceptorClass">
<aop:pointcut id="myPointCutMethod" expression="execution(* com.Aop.service.impl.PersonServiceBean.*(..))" />
<aop:before pointcut-ref="myPointCutMethod" method="doAccessCheck"/>
<aop:after-returning pointcut-ref="myPointCutMethod" method="doWriteLog"/>
<aop:after pointcut-ref="myPointCutMethod" method="doMemoryClear"/>
<aop:after-throwing pointcut-ref="myPointCutMethod" method="doWriteErrorLog"/>
<aop:around pointcut-ref="myPointCutMethod" method="doAroundMethod"/>
</aop:aspect>
</aop:config>
<bean id="personService" class="com.Aop.service.impl.PersonServiceBean"
scope="singleton">
</bean>
4. AopTest.java:同注解方式。
六 注解配置AOP
注解配置AOP,大致分为三步:
1. 使用注解@Aspect来定义一个切面,在切面中定义切入点(@Pointcut),通知类型(@Before, @AfterReturning,@After,@AfterThrowing,@Around).
2. 开发需要被拦截的类。
3. 将切面配置到xml中,当然,我们也可以使用自动扫描Bean的方式。这样的话,那就交由Spring AoP容器管理。
下面是一个Sample:
1. Beans.xml
<aop:aspectj-autoproxy/>
<bean id="inteceptorClass" class="com.Aop.inteceptor.InteceptorClass" />
<bean id="personService" class="com.Aop.service.impl.PersonServiceBean"
scope="singleton">
</bean>
2. PersonService.java
public interface PersonService {
public String save(String user);
public void update(String user);
}
3. PersonServiceBean.java
public class PersonServiceBean implements PersonService{
private String user = null;
/**
* @param user
*/
public PersonServiceBean(String user) {
this.user = user;
}
public PersonServiceBean() {}
/**
* @return the user
*/
public String getUser() {
return user;
}
/**
* @param user the user to set
*/
public void setUser(String user) {
this.user = user;
}
public String save(String user){
System.out.println("PersonServiceBean.save() is running");
return "Jamson";
}
public void update(String user){
System.out.println("PersonServiceBean.update() is running");
}
}
4. 切面的定义
@Aspect
public class InteceptorClass {
//这里的定义步骤:返回类型 package.class.method(parameter)
@Pointcut("execution (* com.Aop.service.impl.PersonServiceBean.*(..))")
private void myPointCutMethod(){};
@Before("myPointCutMethod() & args(name)")
public void doAccessCheck(){
System.out.println("before advice");
}
@AfterReturning(pointcut="myPointCutMethod()", returning="result")
public void doWriteLog(String result){
System.out.println("after advice"+":"+result);
}
@After("myPointCutMethod()")
public void doMemoryClear(){
System.out.println("finally advice");
}
@AfterThrowing(pointcut="myPointCutMethod()", throwing="e")
public void doWriteErrorLog(Exception e){
System.out.println("Exception advice");
}
@Around("myPointCutMethod()")
public Object doAroundMethod(ProceedingJoinPoint pjp)throws Throwable{
System.out.println("enter around advice method.");
Object obj = pjp.proceed();
System.out.println("exit around advice method.");
return obj;
}
}
5. 测试类(AopTest.java)
public class AOPTest {
/**
* @throws java.lang.Exception
*/
@BeforeClass
public static void setUpBeforeClass() throws Exception {
}
@Test public void instanceSpring(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
PersonService personService = (PersonService)context.getBean("personService");
personService.save("Jamson");
}
}
在Console的log:
before advice
enter around advice method.
PersonServiceBean.save() is running
after advice:Jamson
finally advice
exit around advice method.
关于advice的更多的设置属性,可以参考: http://www.springframework.org/schema/aop/spring-aop-2.5.xsd。