在Spring_Demo项目中创建名为“ClassifyAOP”的模块module,并将如下jar包加入到lib文件夹中,并全部将其Add as Library。具体如下所示:
Sprig早期提供的AOP实现,在现在几乎已经被废弃了,不过具有一定的讨论价值,它需要通过XML的方式去配置。例如要完成printRole方法的切面前置通知的功能,这时可以把printRole称为AOP的连接点。首先在com.ccff.spring.classify.aop.aspect包下创建名为“ProxyFactoryBeanAspect”的前置通知类,并实现MethodBeforeAdvice接口的before方法,具体代码如下所示:
package com.ccff.spring.classify.aop.aspect;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class ProxyFactoryBeanAspect implements MethodBeforeAdvice {
/**
* 前置通知
* @param method:被拦截的方法
* @param objects:参数 数组[role]
* @param o:被拦截的对象
* @throws Throwable
*/
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("前置通知!!!");
}
}
有了它还需要对Spring IoC容器描述对应的信息,这个时候需要一个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-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
<bean id="proxyFactoryBeanAspect" class="com.ccff.spring.classify.aop.aspect.ProxyFactoryBeanAspect" />
<bean id="roleService" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>com.ccff.spring.classify.aop.service.RoleServicevalue>
property>
<property name="target">
<bean class="com.ccff.spring.classify.aop.service.RoleServiceImpl" />
property>
<property name="interceptorNames">
<list>
<value>proxyFactoryBeanAspectvalue>
list>
property>
bean>
beans>
然后在com.ccff.spring.classify.aop.test包下创建名为“Test”的测试方法,具体代码如下所示:
package com.ccff.spring.classify.aop.test;
import com.ccff.spring.classify.aop.pojo.Role;
import com.ccff.spring.classify.aop.service.RoleService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
RoleService roleService = (RoleService) context.getBean("roleService");
Role role = new Role();
role.setRoleId(1L);
role.setRoleName("role-name-1");
role.setRoleNote("role-note-1");
roleService.printRole(role);
}
}
最后,运行上述测试方法,查看输出到控制台的日志信息如下:
这样Spring AOP就被用起来了,它虽然很经典,但是已经不是主流方式了,所以至此不再赘述。
在之前我们的讨论中,一个方法只有一个切面,而事实是Spring也能支持多个切面,当有多个切面时,在测试过程中发现它不会存在任何顺序,这些顺序代码会随机生成,但是有时候我们希望它按照指定的顺序运行。
第一步,在com.ccff.spring.classify.aop.multi包下创建名为“MultiBean”的接口,并提供接口方法,具体代码如下所示:
package com.ccff.spring.classify.aop.multi;
public interface MultiBean {
void testMulti();
}
然后在该包下继续创建名为“MultiBeanImpl”的实现类,实现MultiBean接口,具体代码如下所示:
package com.ccff.spring.classify.aop.multi;
public class MultiBeanImpl implements MultiBean {
@Override
public void testMulti() {
System.out.println("test multi aspects!!!");
}
}
至此,已经定义好了连接点,那么现在需要切面,因此在com.ccff.spring.classify.aop.aspect包下分别创建名为“Aspect1”、“Aspect2”和“Aspect3”的三个切面,具体代码如下:
package com.ccff.spring.classify.aop.aspect;
import org.aspectj.lang.annotation.*;
@Aspect
public class Aspect1 {
@Pointcut("execution(* com.ccff.spring.classify.aop.multi.MultiBeanImpl.testMulti(..))")
public void print(){}
@Before("print()")
public void before(){
System.out.println("before 1 ......");
}
@After("print()")
public void after(){
System.out.println("after 1 ......");
}
@AfterThrowing("print()")
public void afterThrowing(){
System.out.println("afterThrowing 1 ......");
}
@AfterReturning("print()")
public void afterReturning(){
System.out.println("afterReturning 1 ......");
}
}
package com.ccff.spring.classify.aop.aspect;
import org.aspectj.lang.annotation.*;
@Aspect
public class Aspect2 {
@Pointcut("execution(* com.ccff.spring.classify.aop.multi.MultiBeanImpl.testMulti(..))")
public void print(){}
@Before("print()")
public void before(){
System.out.println("before 2 ......");
}
@After("print()")
public void after(){
System.out.println("after 2 ......");
}
@AfterThrowing("print()")
public void afterThrowing(){
System.out.println("afterThrowing 2 ......");
}
@AfterReturning("print()")
public void afterReturning(){
System.out.println("afterReturning 2 ......");
}
}
package com.ccff.spring.classify.aop.aspect;
import org.aspectj.lang.annotation.*;
@Aspect
public class Aspect3 {
@Pointcut("execution(* com.ccff.spring.classify.aop.multi.MultiBeanImpl.testMulti(..))")
public void print(){}
@Before("print()")
public void before(){
System.out.println("before 3 ......");
}
@After("print()")
public void after(){
System.out.println("after 3 ......");
}
@AfterThrowing("print()")
public void afterThrowing(){
System.out.println("afterThrowing 3 ......");
}
@AfterReturning("print()")
public void afterReturning(){
System.out.println("afterReturning 3 ......");
}
}
至此,以上3个切面就拦截了连接点。
在com.ccff.spring.classify.aop.config包下创建名为“MultiConfig”的Java配置类用于管理Bean,具体代码如下所示:
package com.ccff.spring.classify.aop.config;
import com.ccff.spring.classify.aop.aspect.Aspect1;
import com.ccff.spring.classify.aop.aspect.Aspect2;
import com.ccff.spring.classify.aop.aspect.Aspect3;
import com.ccff.spring.classify.aop.multi.MultiBean;
import com.ccff.spring.classify.aop.multi.MultiBeanImpl;
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("com.ccff.spring.classify.aop")
public class MultiConfig {
@Bean
public MultiBean multiBean(){
return new MultiBeanImpl();
}
@Bean
public Aspect1 aspect1(){
return new Aspect1();
}
@Bean
public Aspect2 aspect2(){
return new Aspect2();
}
@Bean
public Aspect3 aspect3(){
return new Aspect3();
}
}
在com.ccff.spring.classify.aop.test包下创建名为“TestMultiBean”的测试类,具体代码如下所示:
package com.ccff.spring.classify.aop.test;
import com.ccff.spring.classify.aop.config.MultiConfig;
import com.ccff.spring.classify.aop.multi.MultiBean;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class TestMultiBean {
@Test
public void test(){
ApplicationContext context = new AnnotationConfigApplicationContext(MultiConfig.class);
MultiBean multiBean = (MultiBean) context.getBean("multiBean");
multiBean.testMulti();
}
}
至此,定义的连接点和3个切面已经可以执行了,但是此时的3个切面的执行顺序是无序的。
如果使用注解的切面,为了保证多个切面之前执行是有序的,可以给切面加入注解@Order。例如修改上面定义的三个切面如下:
package com.ccff.spring.classify.aop.aspect;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
@Aspect
@Order(1)
public class Aspect1 {
@Pointcut("execution(* com.ccff.spring.classify.aop.multi.MultiBeanImpl.testMulti(..))")
public void print(){}
@Before("print()")
public void before(){
System.out.println("before 1 ......");
}
@After("print()")
public void after(){
System.out.println("after 1 ......");
}
@AfterThrowing("print()")
public void afterThrowing(){
System.out.println("afterThrowing 1 ......");
}
@AfterReturning("print()")
public void afterReturning(){
System.out.println("afterReturning 1 ......");
}
}
package com.ccff.spring.classify.aop.aspect;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
@Aspect
@Order(3)
public class Aspect2 {
@Pointcut("execution(* com.ccff.spring.classify.aop.multi.MultiBeanImpl.testMulti(..))")
public void print(){}
@Before("print()")
public void before(){
System.out.println("before 2 ......");
}
@After("print()")
public void after(){
System.out.println("after 2 ......");
}
@AfterThrowing("print()")
public void afterThrowing(){
System.out.println("afterThrowing 2 ......");
}
@AfterReturning("print()")
public void afterReturning(){
System.out.println("afterReturning 2 ......");
}
}
package com.ccff.spring.classify.aop.aspect;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
@Aspect
@Order(2)
public class Aspect3 {
@Pointcut("execution(* com.ccff.spring.classify.aop.multi.MultiBeanImpl.testMulti(..))")
public void print(){}
@Before("print()")
public void before(){
System.out.println("before 3 ......");
}
@After("print()")
public void after(){
System.out.println("after 3 ......");
}
@AfterThrowing("print()")
public void afterThrowing(){
System.out.println("afterThrowing 3 ......");
}
@AfterReturning("print()")
public void afterReturning(){
System.out.println("afterReturning 3 ......");
}
}
此时,运行上面的测试方法后,查看输出在控制台上的日志信息如下:
除了使用注解@Order外,还可以让切面实现Ordered(org.springframework.core.Ordered)接口,它定义了一个getOrder方法。例如修改切面Aspect2如下:
package com.ccff.spring.classify.aop.aspect;
import org.aspectj.lang.annotation.*;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
@Aspect
//@Order(3)
public class Aspect2 implements Ordered {
@Pointcut("execution(* com.ccff.spring.classify.aop.multi.MultiBeanImpl.testMulti(..))")
public void print(){}
@Before("print()")
public void before(){
System.out.println("before 2 ......");
}
@After("print()")
public void after(){
System.out.println("after 2 ......");
}
@AfterThrowing("print()")
public void afterThrowing(){
System.out.println("afterThrowing 2 ......");
}
@AfterReturning("print()")
public void afterReturning(){
System.out.println("afterReturning 2 ......");
}
@Override
public int getOrder() {
return 3;
}
}
再次运行上面的测试方法后,查看输出在控制台上的日志信息如下:
众所周知,Spring AOP的实现方法是动态代理,在多个代理的情况下,涉及到了责任链模式,为了更好的理解,给出多个切面的执行顺序如下:
上图展示了一条责任链,换句话说,Spring底层也是通过责任链模式来处理多个切面的。