一、什么是AOP
aspect-oriented programming,面向切面编程,对oop的一种补充。
著名示例,aspectj,spring+aspectj
二、aop之代码重构
2.1、代理重构
示例代码
public interface Greeting { void sayHello(String name); }
实现
public class GreetingImpl implements Greeting { @Override public void sayHello(String name) { before(); System.out.println("hello " + name); after(); } private void before() { System.out.println("before"); } private void after() { System.out.println("after"); } }
以上便是标准的死代码,不宜扩展。
代码重构:
1、静态代理
2、JDK动态代理
3、CGLib动态代理
2.1.1、静态代理
实现
package com.lhx.chapter4.aop.staticproxy; import com.lhx.chapter4.aop.Greeting; import com.lhx.chapter4.aop.GreetingImpl; public class GreetingProxy implements Greeting { private GreetingImpl greetingImpl; public GreetingProxy(GreetingImpl greetingImpl){ this.greetingImpl=greetingImpl; } @Override public void sayHello(String name) { before(); greetingImpl.sayHello(name); after(); } private void before() { System.out.println("before"); } private void after() { System.out.println("after"); } }
调用
package com.lhx.chapter4.aop.staticproxy; import com.lhx.chapter4.aop.Greeting; import com.lhx.chapter4.aop.GreetingImpl; public class Client { public static void main(String[] args) { Greeting proxy = new GreetingProxy(new GreetingImpl()); proxy.sayHello("木子旭"); } }
会导致XxxProxy越来越多。
2.1.2、JDK动态代理
实现
package com.lhx.chapter4.aop.dynamicproxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class JDKDynamicProxy implements InvocationHandler { private Object target; public JDKDynamicProxy(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { before(); Object result = method.invoke(target, args); after(); return result; } public <T> T getProxy() { return (T) Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); } private void before() { System.out.println("Before"); } private void after() { System.out.println("After"); } }
调用
package com.lhx.chapter4.aop.dynamicproxy; import com.lhx.chapter4.aop.Greeting; import com.lhx.chapter4.aop.GreetingImpl; public class JDKDynamicProxyClient { public static void main(String[] args) { JDKDynamicProxy dynamicProxy = new JDKDynamicProxy(new GreetingImpl()); Greeting proxy = dynamicProxy.getProxy(); proxy.sayHello("muzixu"); } }
所有的动态代理都合并到代理类了,JDK动态代理只能代理接口,不能代理没有接口的类。
2.1.3、CGLib动态代理
实现
package com.lhx.chapter4.aop.dynamicproxy; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class CGLibDynamicProxy implements MethodInterceptor { private static CGLibDynamicProxy instance = new CGLibDynamicProxy(); private CGLibDynamicProxy(){ } public static CGLibDynamicProxy getInstance(){ return instance; } public <T> T getProxy(Class<T> cls) { return (T) Enhancer.create(cls,this); } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { before(); Object result = methodProxy.invokeSuper(o,objects); after(); return result; } private void before() { System.out.println("Before"); } private void after() { System.out.println("After"); } }
调用
package com.lhx.chapter4.aop.dynamicproxy; import com.lhx.chapter4.aop.Greeting; import com.lhx.chapter4.aop.GreetingImpl; public class CGLibDynamicProxyClient { public static void main(String[] args) { Greeting proxy = CGLibDynamicProxy.getInstance().getProxy(GreetingImpl.class); proxy.sayHello("muzixu"); } }
2.2、spring aop
1、spring aop(编程式):before advice 前置增强【通知】、after advice 后置增强【通知】、Around advice环绕增强【通知,前两种的结合】
可以理解为增强类
前置增强
package com.lhx.chapter4.aop.springaop; import org.springframework.aop.MethodBeforeAdvice; import java.lang.reflect.Method; public class GreetingBeforeAdvice implements MethodBeforeAdvice { @Override public void before(Method method, Object[] objects, Object o) throws Throwable { System.out.println("Before"); } }
后置增强
package com.lhx.chapter4.aop.springaop; import org.springframework.aop.AfterReturningAdvice; import java.lang.reflect.Method; public class GreetingAfterAdvice implements AfterReturningAdvice { @Override public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable { System.out.println("after"); } }
调用
package com.lhx.chapter4.aop.springaop; import com.lhx.chapter4.aop.GreetingImpl; import org.springframework.aop.framework.ProxyFactory; public class GreetingAdviceClient { public static void main(String[] args) { ProxyFactory proxyFactory = new ProxyFactory();//创建代理工厂 proxyFactory.setTarget(new GreetingImpl());//射入目标类对象 proxyFactory.addAdvice(new GreetingBeforeAdvice());//前置通知 proxyFactory.addAdvice(new GreetingAfterAdvice());//后置 //proxyFactory.addAdvice(new GreetingBeforeAndAfterAdvice());//前后结合 proxyFactory.addAdvice(new GreetingAroundAdvice());//环绕 GreetingImpl greeting = (GreetingImpl) proxyFactory.getProxy(); greeting.sayHello("木子旭"); } }
前置后置合并一个
package com.lhx.chapter4.aop.springaop; import org.springframework.aop.AfterReturningAdvice; import org.springframework.aop.MethodBeforeAdvice; import java.lang.reflect.Method; public class GreetingBeforeAndAfterAdvice implements MethodBeforeAdvice, AfterReturningAdvice { @Override public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable { System.out.println("After"); } @Override public void before(Method method, Object[] objects, Object o) throws Throwable { System.out.println("Before"); } }
环绕通知
package com.lhx.chapter4.aop.springaop; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; import org.springframework.stereotype.Component; @Component public class GreetingAroundAdvice implements MethodInterceptor { @Override public Object invoke(MethodInvocation methodInvocation) throws Throwable { before(); Object proceed = methodInvocation.proceed(); after(); return proceed; } private void before(){ System.out.println("Before"); } private void after(){ System.out.println("after"); } }
环绕通知不是spring提供是由org.aopalliance.intercept.MethodInterceptor提供
2、spring aop(声明式):before advice 前置增强【通知】、after advice 后置增强【通知】、Around advice环绕增强【通知,前两种的结合】
spring的application-aop.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: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/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--扫描指定包(将带有Compontent注解的类自动定义为Spring bean)--> <context:component-scan base-package="com.lhx.chapter4.aop"></context:component-scan> <!--配置一个代理--> <bean id="greetingProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <!--需要代理的接口--> <property name="interfaces" value="com.lhx.chapter4.aop.Greeting"></property> <!--接口实现类--> <property name="target" ref="greetingImpl"></property> <!--拦截器名称(也是增强类名称,Spring Bean的id)--> <property name="interceptorNames"> <list> <value>greetingAroundAdvice</value> </list> </property> </bean> </beans>
ProxyFactoryBean相当于ProxyFactory
在部分实现类增加@Component注解
客户端调用
package com.lhx.chapter4.aop.springaopdeclare; import com.lhx.chapter4.aop.Greeting; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Client { public static void main(String[] args) { //获取spring content ApplicationContext context =new ClassPathXmlApplicationContext("application-aop.xml"); //从Content中根据id获取Bean对象,其实就是一个代理 Greeting greetingProxy = (Greeting) context.getBean("greetingProxy"); greetingProxy.sayHello("muzixu "); } }
3、Spring AOP:Throws Advice【抛出增强】
程序报错,抛出异常,一般做法打印到控制台或者日志文件。一劳永逸办法使用,Throws Advice
异常接口
package com.lhx.chapter4.aop; public interface Greeting { void sayHello(String name); void sayHelloThrows(String name); }
抛出异常增强类
package com.lhx.chapter4.aop.springaopthrows; import org.springframework.aop.ThrowsAdvice; import java.lang.reflect.Method; public class GreetingThrowsAdvice implements ThrowsAdvice { //public void afterThrowing(Exception ex) //public void afterThrowing(RemoteException) //public void afterThrowing(Method method, Object[] args, Object target, Exception ex) //public void afterThrowing(Method method, Object[] args, Object target, ServletException ex) public void afterThrowing(Method method, Object[] args, Object target, Exception ex){ System.out.println("--------Throw Exception-------"); System.out.println("Target class:"+target.getClass().getName()); System.out.println("Method name:"+method.getName()); System.out.println("Exception:"+ex.getMessage()); System.out.println("------------------------------"); } }
接口中没有任何抽象方法,但是自定义又会出异常,Spirng内部是用反射来实现方法匹配的,需要实现下列接口中的其中1个
//public void afterThrowing(Exception ex) //public void afterThrowing(RemoteException) //public void afterThrowing(Method method, Object[] args, Object target, Exception ex) //public void afterThrowing(Method method, Object[] args, Object target, ServletException ex)
4、Spring AOP:Introduction Advice【引入增强】
在AOP中对方法的增强叫Weaving【织入】,对类的增强叫Introduction【引用】,Introduction advice【引用增强】就是对类功能的增强。
示例:类C实现了A接口,那么使类C可以调用B接口
新增一个接口
package com.lhx.chapter4.aop.springaopintroductionadvice; public interface Apology { void saySorry(String name); }
不想改变原有类的实现,需要使用引用增强
package com.lhx.chapter4.aop.springaopintroductionadvice; import org.aopalliance.intercept.MethodInvocation; import org.springframework.aop.support.DelegatingIntroductionInterceptor; public class GreetingIntroductionAdvice extends DelegatingIntroductionInterceptor implements Apology { @Override public void saySorry(String name) { System.out.println("sorry " + name); } @Override public Object invoke(MethodInvocation mi) throws Throwable { return super.invoke(mi); } }
引用增强类扩展了org.springframework.aop.support.DelegatingIntroductionInterceptor,同时实现了需要的接口
需要的Spring配置application-aop-intro.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: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/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--扫描指定包(将带有Compontent注解的类自动定义为Spring bean)--> <context:component-scan base-package="com.lhx.chapter4.aop"></context:component-scan> <!--配置一个代理--> <bean id="greetingProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <!--需要代理的接口--> <property name="interfaces" value="com.lhx.chapter4.aop.springaopintroductionadvice.Apology"></property> <!--接口实现类--> <property name="target" ref="greetingImpl"></property> <!--拦截器名称(也是增强类名称,Spring Bean的id)--> <property name="interceptorNames" value="greetingIntroductionAdvice"> </property> <!--代理目标 false 接口 true 类 默认是false--> <property name="proxyTargetClass" value="true"></property> </bean> </beans>
proxyTargetClass属性,默认false,代理接口,此时Spring使用JDK动态代理
如果是true,此时Spring使用CGLib动态代理。
客户端调用
package com.lhx.chapter4.aop.springaopintroductionadvice; import com.lhx.chapter4.aop.Greeting; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Client { public static void main(String[] args) { //获取spring content ApplicationContext context =new ClassPathXmlApplicationContext("application-aop-intro.xml"); //从Content中根据id获取Bean对象,其实就是一个代理 Greeting greetingProxy = (Greeting) context.getBean("greetingProxy"); greetingProxy.sayHello("muzixu "); Apology apology = (Apology) greetingProxy; //将目标类强制向上转型为Apology接口类型【引入增强的特性,即“动态接口实现”功能】 apology.saySorry("Jack"); } }
5、Spring AOP:切面
之前说的AOP框架其实可以理解为一个拦截器框架,他拦截了一个类,其实就是拦截了所有方法。在使用动态代理时,也需要在代码中对所拦截的方法名加以判断,才能过滤出需要拦截的方法。
Advisor【切面】封装了Advice【增强】与Pointcut【切面】。
增加两个以good开头的方法
package com.lhx.chapter4.aop.aoppointcut; import com.lhx.chapter4.aop.Greeting; import org.springframework.stereotype.Component; @Component public class GreetingGood implements Greeting { @Override public void sayHello(String name) { System.out.println("hello " + name); } public void goodMorning(String name) { System.out.println("goodMorning " + name); } public void goodNight(String name) { System.out.println("goodNight " + name); } @Override public void sayHelloThrows(String name) { } }
基于正则表达式的切面类
<?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: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/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--扫描指定包(将带有Compontent注解的类自动定义为Spring bean)--> <context:component-scan base-package="com.lhx.chapter4.aop"></context:component-scan> <!--配置一个切面--> <bean id="pointcutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="advice" ref="greetingAroundAdvice"></property> <property name="pattern" value="com.lhx.chapter4.aop.aoppointcut.GreetingGood.good.*"></property> </bean> <!--配置一个代理--> <bean id="greetingProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <!--接口实现类--> <property name="target" ref="greetingGood"></property> <!--拦截器名称(也是增强类名称,Spring Bean的id)--> <property name="interceptorNames" value="pointcutAdvisor"></property> <!--代理目标 false 接口 true 类 默认是false--> <property name="proxyTargetClass" value="true"></property> </bean> </beans>
interceptorNames不在是一个增强,而是一个切面。
表达式中的".*",标示匹配所有字符。
Spring AOP提供的几个切面类
RegexpMethodPointcutAdvisor
DefaultPointcutAdvisor
NameMatchMethodPointcutAdvisor
AspectJExpressionPointcutAdvisor
6、Spring AOP:自动代理【扫面Bean名称】
增加spring xml配置
<!--spring 自动代理--> <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <!--只为后缀为Impl的Bean生成代理--> <property name="beanNames" value="*Impl"></property> <!--拦截器名称(也是增强类名称,Spring Bean的id)--> <property name="interceptorNames" value="greetingAroundAdvice"></property> <!--是否对代理生成策略进行优化--> <property name="optimize" value="true"></property> </bean>
此处使用BeanNameAutoProxyCreator只为后缀为Impl的Bean生成代理类。
这里提供了optimize配置项,默认为false, true含义:对生成策略进行优化,如果该类有接口,就代理接口(JDK动态代理),如果没有接口,就代理类(CGLib动态代理)。
不像之前proxyTargetClass属性,强制代理类,而不考虑接口方式
既然有CGLib动态代理代理类,为什么还需要JDK动态代理?
CGLib创建动态代理的速度较慢,但创建代理后运行速度却非常快。而JDK动态代理正好相反。如果在运行的时候不断的用CGLib创建代理, 系统性能会降低。
所以建议一般在系统初始化的时候用CGLib去创建代理,并放入Spring的ApplicationContext中。
7、Spring AOP:自动代理【扫面切面配置】
Spring的xml配置
<!--配置一个切面--> <bean id="pointcutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="advice" ref="greetingAroundAdvice"></property> <property name="pattern" value="com.lhx.chapter4.aop.aoppointcut.GreetingGood.good.*"></property> </bean> <!--spring 自动代理 扫描切面--> <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"> <!--是否对代理生成策略进行优化--> <property name="optimize" value="true"></property> </bean>
这里无需在配置代理,因为代理将由DefaultAdvisorAutoProxyCreator自动生成,这个类可以扫描所有的切面类,并未其自动生成代理。
2.3、spring+aspectj
见下一节