Spring提供了两种方式来生成代理对象: JDKProxy和Cglib,具体使用哪种方式生成由AopProxyFactory根据AdvisedSupport对象的配置来决定。默认的策略是如果目标类是接口,则使用JDK动态代理技术,否则使用Cglib来生成代理
因为Spring 基于动态代理,所以Spring 只支持 方法连接点。其他如 AspectJ 和 JBoss 可以提供更细粒度的连接点
而且对于static方法和final方法都无法支持aop(因为此类方法无法生成代理类)
首先看下 XML 方式来使用 Spring AOP 切面
先定义一个接口 (切点)
public interface Performance { public void perform(); }
一个实现类
public class PerformanceImpl implements Performance { @Override public void perform() { System.out.println("正在演出..."); } }
增强类 (切面 )
public class Audience { public void performance() {} public void closePhones() { System.out.println("关闭手机"); } public void look() { System.out.println("看表演"); } public void openPhones() { System.out.println("打开手机"); } public void goHome() { System.out.println("回家"); } public void doSomething() { System.out.println("鼓励"); }
//这个是环绕通知,带有ProceedingJoinPoint参数 public void watchPerformance(ProceedingJoinPoint jp) { try { System.out.println("观看前"); jp.proceed();//该方法调用被代理对象的目标方法 System.out.println("观看后"); } catch (Throwable e) { e.printStackTrace(); } } }
XML文件中配置
<bean id="performance" class="springAop.PerformanceImpl"/> <bean id="audience" class="springAop.Audience"/> <aop:config> <!-- 切面:提供方法的类 --> <aop:aspect id="watch" ref="audience"> <!-- 切点: 将要被增强的类的方法--> <aop:pointcut id="addAllMethod" expression="execution(* springAop.Performance.perform(..))" /> <aop:before method="closePhones" pointcut-ref="addAllMethod" /> <aop:after method="openPhones" pointcut-ref="addAllMethod" />
<!-- 声明环绕通知 -->
<aop:after method="watchPerformance" pointcut-ref="addAllMethod" />
</aop:aspect> </aop:config>
测试
public class XmlTest { public static void main(String[] args) { @SuppressWarnings("resource") ApplicationContext ctx = new ClassPathXmlApplicationContext("aop.xml"); Performance performance = ctx.getBean("performance", Performance.class); performance.perform(); } }
注解方式
增强类的注解方式
import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; @Aspect public class Audience { @Pointcut("execution(** springAop.Performance.perform(..))") public void performance() {} //通知方法会在目标方法调用前执行 @Before("execution(** springAop.Performance.perform(..))") public void closePhones() { System.out.println("关闭手机"); } //@Before("performance()") public void look() { System.out.println("看表演"); } //通知方法会在目标方法返回后调用 @AfterReturning("execution(** springAop.Performance.perform(..))") public void openPhones() { System.out.println("打开手机"); } //通知方法会在目标方法返回或抛出异常后调用 @After("execution(** springAop.Performance.perform(..))") public void goHome() { System.out.println("回家"); } //通知方法会在目标方法抛出异常后调用 @AfterThrowing("execution(** springAop.Performance.perform(..))") public void doSomething() { System.out.println("鼓励"); } //环绕通知 @Around("execution(** springAop.Performance.perform(..))") public void watchPerformance(ProceedingJoinPoint jp) { try { System.out.println("观看前"); jp.proceed();//该方法调用被代理对象的目标方法 System.out.println("观看后"); } catch (Throwable e) { e.printStackTrace(); } } }
添加一个配置类
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 //启用AspectJ自动代理 @ComponentScan //启用组件扫描 /** * 默认情况下@ComponentScan会扫描与配置类相同的包 * 可以@ComponentScan("包名")这样指定要扫描的路径 * 也可以指定多个扫描包名@ComponentScan(basePackages={"packagesName1","packagesName2"}) * * XML配置方式 <context:component-scan> */ public class ConcertConfig { @Bean //声明 Audience bean public Audience audience() { return new Audience(); } @Bean public Performance performance() { return new PerformanceImpl(); } }
测试代码
import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes=ConcertConfig.class) public class JavaConfigTest { @Autowired private Performance performance; @Test public void testAop() { performance.perform(); } }
处理带参数的通知
一下代码为在注解方式基础上改动而来
接口类
public interface Performance { public void perform(); public void haveParam(int i); }
Audience 类中
@Pointcut("execution(** springAop.Performance.haveParam(int)) && args(param)") public void performance(int param) {} @Before("performance(param)") public void look(int param) { System.out.println("测试参数" + param); }
测试代码
@Test public void testAop() { performance.haveParam(6); }
通过注解引入新功能
首先我们定义一个新接口
public interface Encoreable { void performEncore(); }
该接口实现类
public class EncoreableImpl implements Encoreable { @Override public void performEncore() { System.out.println("Encoreable引入了新功能....."); } }
将 performEncore 功能引入 Performance 的实现类中
配置类
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.DeclareParents; @Aspect public class EncoreableAspect { /** * value 属性指定了那种类型的Bean要引入该接口,(这里既所有实现了Performance的类型)。标记后面的加号表示是 Performance的所有子类,而不是Performance本身 * defaultImpl 属性指定了为引入功能提供具体实现的类。 * @DeclareParents 注解所标注的静态属性指明了要引入的接口 (这里是 Encoreable 接口) */ @DeclareParents(value = "springAop.Performance+", defaultImpl = EncoreableImpl.class) public static Encoreable encoreable; }
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:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd"> <!-- 启用AspectJ自动代理 --> <aop:aspectj-autoproxy /> <bean id="performance" class="springAop.PerformanceImpl"/> <bean id="audience" class="springAop.Audience"/> <bean id="encoreableAspect" class="springAop.EncoreableAspect"/> <bean id="encoreable" class="springAop.EncoreableImpl"/> </beans>
测试类
public class XmlTest { public static void main(String[] args) { @SuppressWarnings("resource") ApplicationContext ctx = new ClassPathXmlApplicationContext("aop.xml"); Performance performance = ctx.getBean("performance", Performance.class); performance.perform(); Encoreable performance1 = ctx.getBean("performance", Encoreable.class); performance1.performEncore(); } }
输出结果:
正在演出...
Encoreable引入了新功能.....