1.前置通知(BeforeAdvice):
import java.lang.reflect.Method; import org.springframework.aop.MethodBeforeAdvice; public class HelloBeforeAdvice implements MethodBeforeAdvice{ public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("do something before some method"); } }
如果出现 The hierarchy of the type HelloBeforeAdvice is inconsistent错误 , 是缺少相关的jar或者jdk环境不对。
2.后置通知(AfterAdvice)
import java.lang.reflect.Method; import org.springframework.aop.AfterReturningAdvice; public class HelloAfterAdvice implements AfterReturningAdvice { public void afterReturning(Object result, Method method, Object[] args, Object target) throws Throwable { System.out.println("do something after the method"); } }
3. Test client
import org.springframework.aop.framework.ProxyFactory; public class TestClient { public static void main(String[] args) { ProxyFactory proxyFactory = new ProxyFactory();//创建代理工厂 proxyFactory.setTarget(new HelloImpl()); proxyFactory.addAdvice(new HelloBeforeAdvice()); proxyFactory.addAdvice(new HelloAfterAdvice()); Hello helloProxy = (Hello) proxyFactory.getProxy(); System.out.println(helloProxy.sayHello("zhangsan")); } }
----------------------------
public class HelloImpl implements Hello {
public String sayHello(String str) {
System.out.println("hello>>>"+str);
return "hello>>>"+str;
}
}
4. 环绕通知(AroundAdvice)
//aop联盟提供的接口 import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; public class HelloAroundAdvice implements MethodInterceptor { public Object invoke(MethodInvocation invocation) throws Throwable { System.out.println("before method>>>"); Object result = invocation.proceed(); System.out.println("after method>>"); return result; } }
5. spring 配置文件方式
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" 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:p="http://www.springframework.org/schema/p" xmlns:util="http://www.springframework.org/schema/util" xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:cache="http://www.springframework.org/schema/cache" xsi:schemaLocation=" http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd 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/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"> <!-- 自动扫描ppms包 ,将带有注解的类 纳入spring容器管理 --> <context:component-scan base-package="com.cdv.ppms"></context:component-scan> <bean id="helloProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="interfaces" value="com.cdv.ppms.Hello"/><!-- 需要代理的接口 --> <property name="target" ref="helloImpl"/><!-- 接口实现类 , 此处需要是ref 否则object is not an instance of declaring class--> <property name="interceptorNames"><!-- 拦截器 名称--> <list> <value>helloAroundAdvice</value> </list> </property> </bean> <bean id="helloImpl" class="com.cdv.ppms.HelloImpl"></bean> </beans>
import org.springframework.stereotype.Component; @Component public class HelloImpl implements Hello { //... } @Component public class HelloAroundAdvice implements MethodInterceptor { //.... }
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestXMLClient { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Hello hello = (Hello) context.getBean("helloProxy"); hello.sayHello("test xml"); } }
6. 抛出通知(ThrowAdvice)
import java.lang.reflect.Method; import org.springframework.aop.ThrowsAdvice; import org.springframework.stereotype.Component; @Component public class HelloThrowAdvice implements ThrowsAdvice{ public void afterThrowing(Method method, Object[] args, Object target, Exception e){ System.out.println("-------------------Throw Exception------------------------"); System.out.println("target class>>"+target.getClass().getName()); System.out.println("method class>>" + method.getName()); System.out.println("exception message>>"+e.getMessage()); System.out.println("-------------------Throw Exception------------------------"); } }
<bean id="helloProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="interfaces" value="com.cdv.ppms.Hello"/><!-- 需要代理的接口 --> <property name="target" ref="helloImpl"/><!-- 接口实现类 --> <property name="interceptorNames"><!-- 拦截器 名称--> <list> <value>helloAroundAdvice</value> <value>testThrowAdvice</value> </list> </property> </bean>
import org.springframework.stereotype.Component; @Component public class HelloImpl implements Hello { public String sayHello(String str) { System.out.println("hello>>>"+str); throw new RuntimeException("error>>");//加入异常以便测试 } }
7. 引入通知(Introduction Advice)
对方法的增强叫织入(Weaving),对类的增强叫引入(Introduction)
import org.aopalliance.intercept.MethodInvocation; import org.springframework.aop.support.DelegatingIntroductionInterceptor; import org.springframework.stereotype.Component; @Component public class HelloIntroAdvice extends DelegatingIntroductionInterceptor implements Apology { /** * */ private static final long serialVersionUID = -3328637206414010549L; public Object invoke(MethodInvocation invocation) throws Throwable{ return super.invoke(invocation); } public void saySorry(String str) { System.out.println("introduction advice>>>"+str); } }
<bean id="helloProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="interfaces" value="com.cdv.ppms.Apology"/><!-- 需要代理的接口 --> <property name="target" ref="helloImpl"/><!-- 接口实现类 --> <property name="interceptorNames"><!-- 拦截器 名称--> <list> <value>helloAroundAdvice</value> <value>helloThrowAdvice</value> <value>helloIntroAdvice</value> </list> </property> <property name="proxyTargetClass" value="true"></property><!--代理目标类, 默认false代理接口--> </bean> <bean id="helloImpl" class="com.cdv.ppms.HelloImpl"></bean>
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestXMLClient {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
HelloImpl helloImpl = (HelloImpl) context.getBean("helloProxy");
helloImpl.sayHello("test xml");
Apology apology = (Apology) helloImpl;//将目标类向上强制转型为Apology接口, 这时引入通知给我们带来的特性, 也就是“接口动态实现”功能
apology.saySorry("zhangsan");
}
}
8. 切面(Advisor)
切面(Advisor)封装了通知(Advice)和切点(Pointcut)
import org.springframework.stereotype.Component; @Component public class HelloImpl implements Hello { public String sayHello(String str) { System.out.println("hello>>>"+str); return str; } public void advisorOne(String name){ System.out.println("advisor one>>>"+name); } public void advisorTwo(String name){ System.out.println("advisor two>>"+name); } }
在代理类中新增以advisor开头的方法,对其进行拦截
<context:component-scan base-package="com.cdv.ppms"></context:component-scan> <!--定义切面--> <bean id="helloAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="advice" ref="helloAroundAdvice"/> <property name="pattern" value="com.cdv.ppms.HelloImpl.advisor.*"></property><!-- 切点 (正则表达式) --> </bean> <bean id="helloProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="helloImpl"/><!-- 接口实现类 --> <property name="interceptorNames" value="helloAdvisor"/> <property name="proxyTargetClass" value="true"></property> </bean> <bean id="helloImpl" class="com.cdv.ppms.HelloImpl"></bean> <bean id="helloAroundAdvice" class="com.cdv.ppms.HelloAroundAdvice"></bean>
import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; import org.springframework.stereotype.Component; @Component public class HelloAroundAdvice implements MethodInterceptor { public Object invoke(MethodInvocation invocation) throws Throwable { System.out.println("before method>>>"); Object result = invocation.proceed(); System.out.println("after method>>"); return result; } }
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestXMLClient { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); HelloImpl helloImpl = (HelloImpl) context.getBean("helloProxy"); helloImpl.advisorOne("11111111"); helloImpl.advisorTwo("2222222222"); } } -------------around包了两层, 有问题------------------------------------------- before method>>> before method>>> advisor one>>>11111111 after method>> after method>> before method>>> before method>>> advisor two>>2222222222 after method>> after method>> ------------------------
随着项目的扩大, 上述的代理配置会越来越多。
9. 自动代理(扫描bean名称)
定义BeanNameAutoProxyCreator,该bean是个bean后处理器,无需被引用,因此没有id属性
<context:component-scan base-package="com.cdv.ppms"></context:component-scan> <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <property name="beanNames" value="*Impl"/><!--对以Impl结尾的类进行处理--> <property name="interceptorNames" value="helloAroundAdvice"/> <property name="optimize" value="true"/> </bean> <bean id="helloImpl" class="com.cdv.ppms.HelloImpl"></bean> <bean id="helloAroundAdvice" class="com.cdv.ppms.HelloAroundAdvice"></bean>
测试
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestXMLClient { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); HelloImpl helloImpl = (HelloImpl) context.getBean("helloImpl"); helloImpl.advisorOne("11111111");// helloImpl.advisorTwo("2222222222"); helloImpl.sayHello("zhangwuji"); } }
10. 自动代理(扫描切面配置)
代理将由DefaultAdvisorAutoProxyCreator自动生成,这个类可以扫描所有的切面类
<context:component-scan base-package="com.cdv.ppms"></context:component-scan> <bean id="helloAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="advice" ref="helloAroundAdvice"/> <property name="pattern" value="com.cdv.ppms.HelloImpl.advisor.*"/> </bean> <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"> <property name="optimize" value="true"/> </bean> <bean id="helloImpl" class="com.cdv.ppms.HelloImpl"></bean> <bean id="helloAroundAdvice" class="com.cdv.ppms.HelloAroundAdvice"></bean>
测试
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestXMLClient { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); HelloImpl helloImpl = (HelloImpl) context.getBean("helloImpl"); helloImpl.advisorOne("11111111"); helloImpl.advisorTwo("2222222222"); helloImpl.sayHello("zhangwuji"); } } //只有符合advisor开头的方法才会被代理
这样在spring的配置中会存在大量的切面配置(想拦截指定注解的方法,就必须拓展DefaultPointcutAdvisor类,自定义一个切面类)。
11. Spring & AspectJ
11-1 基于注解 :通过execution()表达式 拦截方法
import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.stereotype.Component; @Aspect @Component public class HelloAspect { @Around("execution(* com.cdv.ppms.HelloImpl.*(..))")//切点表达式 public Object around(ProceedingJoinPoint pjp) throws Throwable{ before(); Object result = pjp.proceed(); after(); return result; } private void after() { System.out.println("hey,, after."); } private void before() { System.out.println("hey,, before"); } }
切点表达式
execution()表示拦截的方法, 挂号中定义拦截的规则
上面第一个*表示返回任意类型
第二个*表示HelloImpl类中的任意方法
..表示任意类型参数
<context:component-scan base-package="com.cdv.ppms"></context:component-scan> <aop:aspectj-autoproxy proxy-target-class="true"/><!-- 默认值为false只能代理接口,当为true时才能代理目标类 --> <bean id="helloImpl" class="com.cdv.ppms.HelloImpl"></bean>
test
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestXMLClient { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); HelloImpl helloImpl = (HelloImpl) context.getBean("helloImpl"); helloImpl.advisorOne("11111111"); helloImpl.advisorTwo("2222222222"); helloImpl.sayHello("zhangwuji"); } }
11-2 基于注解 通过AspectJ @annotation表达式拦截方法
自定义注解
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface MyTag { }
更改aspect类的切点表达式
import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.stereotype.Component; @Aspect @Component public class HelloAspect { @Around("@annotation(com.cdv.ppms.MyTag)") public Object around(ProceedingJoinPoint pjp) throws Throwable{ before(); Object result = pjp.proceed(); after(); return result; } private void after() { System.out.println("hey,, after."); } private void before() { System.out.println("hey,, before"); } }
该注解可以标注在方法上,运行时生效
import org.springframework.stereotype.Component; @Component public class HelloImpl implements Hello { public String sayHello(String str) { System.out.println("hello>>>"+str); return str; } @MyTag public void advisorOne(String name){ System.out.println("advisor one>>>"+name); } public void advisorTwo(String name){ System.out.println("advisor two>>"+name); } }
配置
<context:component-scan base-package="com.cdv.ppms"></context:component-scan> <bean id="helloImpl" class="com.cdv.ppms.HelloImpl"></bean>
测试
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestXMLClient { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); HelloImpl helloImpl = (HelloImpl) context.getBean("helloImpl"); helloImpl.advisorOne("11111111"); helloImpl.advisorTwo("2222222222"); helloImpl.sayHello("zhangwuji"); } }
其他几个注解
@Before @After @AfterThrowing @DeclareParents引入通知 AfterReturning(返回后通知,在方法结束后执行)
11-3 引入通知
Aspect类
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.DeclareParents; import org.springframework.stereotype.Component; @Aspect @Component public class HelloAspect { //something need to do @DeclareParents(value="com.cdv.ppms.HelloImpl",defaultImpl=com.cdv.ppms.ApologyImpl.class) private Apology apology; }
接口实现类
public class ApologyImpl implements Apology { public void saySorry(String str) { System.out.println("apologyImpl>>>"+str); } }
配置
<context:component-scan base-package="com.cdv.ppms"></context:component-scan> <bean id="helloImpl" class="com.cdv.ppms.HelloImpl"></bean>
测试
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestXMLClient { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); HelloImpl helloImpl = (HelloImpl) context.getBean("helloImpl"); helloImpl.sayHello("zhangwuji"); Apology apology = (Apology) helloImpl; apology.saySorry("hahahahah"); } }