AOP相关的概念
1)Aspect:切面,切入系统的一个切面。比如事务管理是一个切面,权限管理也是一个切面。
2)Join point:连接点,也就是可以进行横向切入的位置。
3)Advice:通知,切面在某个连接点执行的操作(分为:Before advice,After returning advice,After throwing advice,After (finally) advice,Around advice)。
4)Pointcut:切点,符合切点表达式的连接点,也就是真正被切入的地方。
1) JDK动态代理
主要使用到 InvocationHandler 接口和 Proxy.newProxyInstance() 方法。JDK动态代理要求被代理实现一个接口,只有接口中的方法才能够被代理。其方法是将被代理对象注入到一个中间对象,而中间对象实现InvocationHandler接口,在实现该接口时,可以在 被代理对象调用它的方法时,在调用的前后插入一些代码。而 Proxy.newProxyInstance() 能够利用中间对象来生产代理对象。插入的代码就是切面代码。所以使用JDK动态代理可以实现AOP。我们看个例子:
被代理对象实现的接口,只有接口中的方法才能够被代理:
public interface UserService { public void addUser(User user); public User getUser(int id); }
被代理对象:
public class UserServiceImpl implements UserService { public void addUser(User user) { System.out.println("add user"); } public User getUser(int id) { User user = new User(); user.setId(id); System.out.println("getUser"); return user; } }
代理中间类:
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class ProxyUtil implements InvocationHandler {
//被代理的对象 private Object target; public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("do method before"); Object result = method.invoke(target, args); System.out.println("do method after"); return result; } ProxyUtil(Object target){ this.target = target; } public Object getTarget() { return target; } public void setTarget(Object target) { this.target = target; } }
测试:
import java.lang.reflect.Proxy; import User; public class ProxyTest { public static void main(String[] args){
// 被代理的对象 Object proxyedObject = new UserServiceImpl(); ProxyUtil proxyUtils = new ProxyUtil(proxyedObject); //生成代理对象,对被代理对象的这些接口进行代理:UserServiceImpl.class.getInterfaces() UserService proxyObject = (UserService) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), UserServiceImpl.class.getInterfaces(), proxyUtils); proxyObject.getUser(1); proxyObject.addUser(new User()); } }
结果:
do method before
getUser
do method after
do method before
add user
do method after
该方式有一个要求,被代理的对象必须实现接口,而且只有接口中的方法才能被代理。
2)CGLIB(code generate libary)
字节码生成技术实现AOP,其实就是继承被代理对象,然后override需要被代理的方法,在覆盖该方法时,自然是可以插入我们自己的代码的。因为需要override被代理对象的方法,所以CGLIB技术实现AOP时,就必须要求需要被代理的方法不能是final方法,因为final方法不能被子类覆盖。我们使用CGLIB实现上面的例子:package aop; import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; public class CGProxy implements MethodInterceptor{ private Object target; // 被代理对象 public CGProxy(Object target){ this.target = target; } public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy proxy) throws Throwable { System.out.println("do method before"); Object result = proxy.invokeSuper(arg0, arg2); System.out.println("do method after"); return result; } public Object getProxyObject() { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(this.target.getClass()); // 设置父类 // 设置回调
// 在调用父类方法时,回调 this.intercept() enhancer.setCallback(this); // 创建代理对象 return enhancer.create(); } }
测试:
public class CGProxyTest { public static void main(String[] args){ Object proxyedObject = new UserServiceImpl(); // 被代理的对象 CGProxy cgProxy = new CGProxy(proxyedObject); UserService proxyObject = (UserService) cgProxy.getProxyObject(); proxyObject.getUser(1); proxyObject.addUser(new User()); } }
结果:
它的原理是生成一个父类enhancer.setSuperclass(this.target.getClass())的子类enhancer.create(),然后对父类的方法进行拦截enhancer.setCallback(this). 对父类的方法进行覆盖,所以父类方法不能是final的。do method before
getUser
do method after
do method before
add user
do method after
spring实现AOP的相关源码:
@SuppressWarnings("serial") public class DefaultAopProxyFactory implements AopProxyFactory, Serializable { @Override public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) { Class<?> targetClass = config.getTargetClass(); if (targetClass == null) { throw new AopConfigException("TargetSource cannot determine target class: " + "Either an interface or a target is required for proxy creation."); } if (targetClass.isInterface()) { return new JdkDynamicAopProxy(config); } return new ObjenesisCglibAopProxy(config); } else { return new JdkDynamicAopProxy(config); } }
}
如果被代理对象实现了接口,那么就使用JDK的动态代理技术,反之则使用CGLIB来实现AOP,所以Spring默认是使用JDK的动态代理技术实现AOP的。
JdkDynamicAopProxy:final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable { @Override public Object getProxy(ClassLoader classLoader) { if (logger.isDebugEnabled()) { logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource()); } Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised); findDefinedEqualsAndHashCodeMethods(proxiedInterfaces); return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this); }
}
Spring AOP的配置
Spring中AOP的配置一般有两种方法,一种是使用 aop:config 标签在xml中进行配置,一种是使用注解以及@Aspect风格的配置。
1)基于aop:config的AOP配置
下面是一个典型的事务AOP的配置:
<tx:advice id="transactionAdvice" transaction-manager="transactionManager"?> <tx:attributes > <tx:method name="add*" propagation="REQUIRED" /> <tx:method name="*" propagation="SUPPORTS" /> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut id="transactionPointcut" expression="execution(* com.zby.service..*Impl.*(..))" /> <aop:advisor pointcut-ref="transactionPointcut" advice-ref="transactionAdvice" /> </aop:config>
2) 基于注解和@Aspect风格的AOP配置
<!-- 使用annotation定义事务 --> <tx:annotation-driven transaction-manager="transactionManager" />
扫描切面:
<context:component-scan base-package="com.zby.aop" />
启用@AspectJ风格的注解:
<aop:aspectj-autoproxy proxy-target-class="true"/>
proxy-target-class="true" 这个最好不要随便使用,它是指定只能使用CGLIB代理。
切面失效
因为Spring AOP是基于动态代理对象的,那么如果target中的方法不是被代理对象调用的,那么就不会织入切面代码。
失效示例:
切面:
@Aspect
@Order(100)
public class MethodMonitorUMPAspect {
Logger logger = LoggerFactory.getLogger(UMPAspect.class);
/**
* 环绕增强切面
*
* @param pjp
* @return
*/
@Around(value = "@annotation(methodMonitor)")
public Object intercept(ProceedingJoinPoint pjp, MethodMonitor methodMonitor) throws Throwable {
logger.info("do method before");
obj = pjp.proceed();
logger.info("do method after");
return obj;
}
}
@Service("userService") public class UserServiceImpl implements UserService{
public void a() { b(); }
@methodMonitor public void b() {
System.out.println("do b"); } }
虽然b()方法被调用了,但是因为不是代理对象调用的,所以切面并没有执行。这就是Spring aop的缺陷。解决方法如下:
首先:将 <aop:aspectj-autoproxy /> 改为:
<aop:aspectj-autoproxy expose-proxy="true"/>
修改失效示例:
@Service("userService") public class UserServiceImpl implements UserService{ public void a() { ((UserService)AopContext.currentProxy()).b(); } @methodMonitor public void b() {
System.out.println("do b"); } }
结果:
do method before
do b
do method after
((UserService)AopContext.currentProxy())先获得当前的代理对象,然后在调用b() 方法就可以了。expose-proxy="true" 表示将当前代理对象暴露出去,不然 AopContext.currentProxy()返回的是null。
标签:do,target,Object,代理,method,Aop,原理,public From: https://www.cnblogs.com/zhengbiyu/p/17263302.html