Spring AOP 是基于jdk的动态代理和CGLIB代理实现的。广泛应用于处理一些具有横切性质的系统级服务,如日志、事务处理、缓存、性能统计、权限控制,异常处理等
jdk动态代理:代理对象必须是某个接口的实现,它是通过运行期间创建接口的实现类来完成对目标对象的代理。
CGLIB代理:它是在运行期间生成的代理对象是对目标对象扩展的子类。
AOP在目标对象有接口时才有jdk动态代理实现,在没有接口时使用CGLIB代理实现。
JDK动态代理实例:Subject 为接口,RealSubject 为实现类
// 定义真实项目 class RealSubject implements Subject { @Override public String say(String name, int age) { return name + " " + age; } } class MyInvocationHandler implements InvocationHandler { private Object obj = null; public Object bind(Object obj) { this.obj = obj; return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj .getClass().getInterfaces(), this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { system.out.println("调用方法前执行操作…………"); Object temp = method.invoke(this.obj, args); system.out.println("调用方法后执行操作"); return temp; } } class hello { public static void main(String[] args) { MyInvocationHandler demo = new MyInvocationHandler(); Subject sub = (Subject) demo.bind(new RealSubject()); String info = sub.say("Rollen", 20); System.out.println(info); } }
CGLIB代理举例:没有Subject 接口
public class CGlibProxyFactory implements MethodInterceptor{ private Object targetObject; public Object createProxyInstance(Object targetObject) { this.targetObject=targetObject; Enhancer enhancer=new Enhancer(); enhancer.setSuperclass(this.targetObject.getClass());//设置目标类的子类,该子类会覆盖所有父类中的非final方法 enhancer.setCallback(this);//设置回调 return enhancer.create(); } public Object intercept(Object proxy, Method method, Object[] args,MethodProxy methodProxy) throws Throwable { RealSubject person=(RealSubject )this.targetObject; Object result=null; result = methodProxy.invoke(targetObject, args); return null; } }
几个概念:
连接点--joinpoint : 表示在哪干;
切入点--pointcut:表示在哪干的集合;
通知-- advice :表示干什么;包括前置通知(before advice)、后置通知(after advice)、环绕通知(around advice)
方面/切面 -- aspect : 表示在哪干和干什么的集合;
引入 -- inter-type declaration :为已有类添加额外的字段和方法;
目标对象 --Target object :表示对谁干;
织入 -- weaving:是一个过程,表示将切面引入到目标对象,并创建AOP代理对象的过程。织入可以在编译期、类加载期、运行时。
AOP实现方式:
1、基于schema的AOP
<aop:config> -------------------------------->AOP定义开始
<aop:pointcut/> ----------------------->切入点定义
<aop:advisor/> ----------------------->表示只有一个通知和一个切入点的切面,不推荐使用,因为有侵入性,必须实现通知API
<aop:aspect> ------------------------>定义切面
<aop:pointcut/> ----------------->切入点定义
<aop:before/> ------------------->前置通知
<aop:after-returning/> ---------->后置返回通知
<aop:after-throwing/> ---------->后置异常通知
<aop:after/> ------------------->后置最终通知
<aop:around/> ------------------->环绕通知
<aop:declare-parents/> ---------->引入定义
</aop:aspect>
</aop:config>
实例:
1)定义目标接口
package cn.javass.spring.chapter6.service; public interface IHelloWorldService { public void sayHello(); }
2)定义目标接口实现:
package cn.javass.spring.chapter6.service.impl; import cn.javass.spring.chapter6.service.IHelloWorldService; public class HelloWorldService implements IHelloWorldService { @Override public void sayHello() { System.out.println("============Hello World!"); } }
3)定义切面支持类
package cn.javass.spring.chapter6.aop; public class HelloWorldAspect { //前置通知 public void beforeAdvice() { System.out.println("===========before advice"); } //后置最终通知 public void afterFinallyAdvice() { System.out.println("===========after finally advice"); } }
4)在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" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <bean id="helloWorldService" class="cn.javass.spring.chapter6.service.impl.HelloWorldService"/> <bean id="aspect" class="cn.javass.spring.chapter6.aop.HelloWorldAspect"/> <aop:config> <aop:pointcut id="pointcut" expression="execution(* cn.javass..*.*(..))"/> <aop:aspect ref="aspect"> <aop:before pointcut-ref="pointcut" method="beforeAdvice"/> <aop:after pointcut="execution(* cn.javass..*.*(..))" method="afterFinallyAdvice"/> </aop:aspect> </aop:config> </beans>
2、基于注解@AspectJ的AOP
Spring默认不支持@AspectJ风格的切面声明,为了支持需要使用如下配置:<aop:aspectj-autoproxy/>
实例:
1)2)同上
3)定义切面支持类
package cn.javass.spring.chapter6.aop; import org.aspectj.lang.annotation.Aspect; @Aspect public class HelloWorldAspect2 { //定义切入点 @Pointcut(value="execution(* cn.javass..*.sayAdvisorBefore(..)) && args(param)", argNames = "param") public void beforePointcut(String param) {} //定义通知 @Before(value = "beforePointcut(param)", argNames = "param") public void beforeAdvice(String param) { System.out.println("===========before advice param:" + param); } }
4)在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" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <aop:aspectj-autoproxy/> <bean id="helloWorldService" class="cn.javass.spring.chapter6.service.impl.HelloWorldService"/> <bean id="aspect" class="cn.javass.spring.chapter6.aop.HelloWorldAspect2"/> </beans>