AOP原理
AOP是使用代理模式,在指定方法执行前、执行后等各个阶段进行自定义处理
下面所讲到的代理模式中,一共有两种类型,一种是基于接口的代理,一种是基于继承的代理
- 基于接口的代理模式,需要指定目标类的接口,java类加载器将根据给定的接口生成一个动态代理类,并且在执行目标方法时,执行指定方法(如invoke方法)。因为动态代理类是由接口生成,因此如果通过代理类执行接口不存在但目标类实现了的方法,将会编译不通过,报错
- 基于继承的代理模式,目标类不需要有接口,类加载器将动态生成继承自目标类的代理类,因此代理类拥有目标类的所有方法,cglib就是基于这种模式
AOP术语
- target目标类:需要被代理的类。例如:UserService
- Joinpoint连接点:所谓连接点是指那些可能被拦截到的方法。例如:所有的方法
- PointCut切入点:已经被增强的连接点。例如:addUser()
- advice通知/增强,增强代码。例如:after、before
- Weaving织入:是指把增强advice应用到目标对象target来创建新的代理对象proxy的过程.
- proxy代理类
- Aspect切面:是切入点pointcut和通知advice的结合
实现AOP模式
手动代理模式
使用Proxy.newProxyInstance()方法进行代理
Target target = new Target()
TargetInterface proxy = (TargetInterface)Proxy.newProxyInstance(Target.class.getClassLoader(), new Class[]{TargetInterface.class}, new InvocationHandler() {
public Object before(Object proxy, Method method, Object[] args){
System.out.println("执行" + method.getName() + "\r\n");
return proxy;
}
public Object after(Object proxy, Method method, Object[] args, Object result){
System.out.println("执行结果" + result.toString() + "\r\n");
return result;
}
public Object exceptionHandler(Object proxy, Method method, Object[] args){
return null;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try{
proxy = this.before(proxy, method, args);
Object result = method.invoke(target, args);
result = this.after(proxy, method, args, result);
return result;
}catch (Throwable throwable){
return this.exceptionHandler(proxy, method, args)
}
}
});
cglib
cglib通过动态创建目标类的子类作为代理类来实现代理模式,cglib代理的好处是,目标类不需要实现任何接口,因为代理类是继承自目标类,因此有目标类的所有方法
先定义一个切面类
/** MyAspect.java **/
package com.sinbxeunha.josechan.aspect;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class MyAspect {
public Object before(Object proxy, Method method, Object[] args, MethodProxy methodProxy){
System.out.println("执行" + method.getName() + "\r\n");
return null;
}
public Object after(Object proxy, Method method, Object[] args, MethodProxy methodProxy, Object result){
assert result != null && result.toString() != null;
System.out.println("执行结果:" + result.toString() + "\r\n");
return result;
}
public Object exceptionHandler(Object proxy, Method method, Object[] args, MethodProxy methodProxy, Throwable throwable){
System.out.println("结果异常:" + throwable.getMessage() + "\r\n");
return null;
}
}
使用cglib进行代理
//cglib代理
Target target = new Target();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
MyAspect aspect = new MyAspect();
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
try{
//前方法
proxy = aspect.before(proxy, method, args, methodProxy);
//下面两句是一样的
Object result = method.invoke(target, args);
// Object result = Proxy.invokeSuper(target, args);
//后方法
return result = aspect.after(proxy, method, args, methodProxy, result);
}catch (Throwable throwable){
return aspect.exceptionHandler(proxy, method, args, methodProxy, throwable);
}
}
});
aopalliance
aop联盟定义了一系列规范,实现这些接口就能实现切面编程
同样的我们实现切面类
/*** MyAspect.java ***/
package com.sinbxeunha.josechan.aspect;
import org.aopalliance.intercept.MethodInvocation;
public class MyAspect {
public Object before(MethodInvocation invocation){
System.out.println("执行" + invocation.getMethod().getName() + "\r\n");
return null;
}
public Object after(Object result){
System.out.println("执行结果:" + result.toString() + "\r\n");
return result;
}
public Object exceptionHandler(Throwable throwable){
System.out.println("结果异常:" + throwable.getMessage() + "\r\n");
return null;
}
}
定义通知,实现Advice接口,这里我们实现MethodInterceptor接口,该接口底层继承自Advice接口
/**MyInterceptor.java**/
package com.sinbxeunha.josechan.aspect;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.stereotype.Component;
public class MyInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
MyAspect aspect = new MyAspect();
try{
aspect.before(invocation);
Object result = invocation.proceed();
assert result != null;
return aspect.after(result);
}catch (Throwable throwable){
return aspect.exceptionHandler(throwable);
}
}
}
创建代理对象
//工厂bean
ProxyFactoryBean bean = new ProxyFactoryBean();
//配置
AdvisedSupport config = new AdvisedSupport();
//目标类
Target target = new Target();
//通知
MyInterceptor interception = new MyInterceptor();
//设置目标类的接口
config.setInterfaces(new Class[]{TargetInterface.class});
//设置目标
config.setTarget(target);
//设置通知
config.addAdvice(interception);
//获得代理对象,链式调用 bean.获得工厂.配置工厂.获得代理对象
TargetInterface proxy = (TargetInterface) bean.getAopProxyFactory().createAopProxy(config).getProxy();
AspectJ
-
切入点表达式
execution(<方法的返回类型> <完整类名>.<方法名>(...<参数类型>)) -
aspectJ定义了一系列通知,并通过注解的方式指定通知的类型
-
通知类型如下:
- Before 前置通知,在目标方法执行前执行
- AfterReturning 后置通知,在目标方法执行后执行
- AfterThrowing 抛出异常通知,在目标方法抛出异常时执行
- After 最终通知,所有流程走完后执行该通知
- Around 环绕通知,类似于前面MyInterceptor类的invoke方法,需要手动写整个增强过程
-
当指定了目标类的接口时,aspectj会通过接口实现代理类,否则将采用cglib的方式,继承目标类实现代理,当然,你也可以通过配置,让aspectj强制使用cglib的模式
首先先定义一个切面类
/** AspectJAspect.java **/
package com.sinbxeunha.josechan.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.weaver.tools.JoinPointMatch;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
//@Component
//@EnableAspectJAutoProxy
@Aspect
public class AspectJAspect {
@Pointcut("execution(* com.sinbxeunha.josechan.service.*.*(..))")
private void pointCut() {
}
//注解前置通知
@Before("pointCut()")
public Object before(JoinPoint joinPoint) {
System.out.println("执行" + joinPoint.getSignature().toString() + "\r\n");
return null;
}
//注解后置通知
@AfterReturning(value = "pointCut()", returning = "result")
public Object after(JoinPoint joinPoint, Object result) {
assert result != null && result.toString() != null;
System.out.println("执行结果:" + result.toString() + "\r\n");
return result;
}
//注解抛出异常通知
@AfterThrowing(value = "pointCut()", throwing = "throwable")
public Object exceptionHandler(JoinPoint joinPoint, Throwable throwable) {
System.out.println("结果异常:" + throwable.getMessage() + "\r\n");
return null;
}
//注解最终通知
@After("pointCut()")
public Object afterAll(JoinPoint joinPoint) {
return null;
}
//注解环绕通知,这个通知的增强内容相当于上面所有通知的并集
// @Around("pointCut()")
public Object around(ProceedingJoinPoint joinPoint) {
try {
before(joinPoint);
Object result = joinPoint.proceed();
assert result != null;
return after(joinPoint, result);
} catch (Throwable throwable) {
return exceptionHandler(joinPoint, throwable);
} finally {
afterAll(joinPoint);
}
}
}
接下来通过工厂构造代理
// AspectJ
//目标类
Target target = new Target();
//切面类
AspectJAspect aspect = new AspectJAspect();
//代理工厂
AspectJProxyFactory factory = new AspectJProxyFactory();
factory.setTarget(target);
factory.setInterfaces(new Class[]{TargetInterface.class});
factory.addAspect(aspect);
// //强制使用cglib
// factory.setProxyTargetClass(true);
TargetInterface proxy = factory.getProxy();
通过spring容器实现自动AspectJ代理
要通过spring容器实现自动代理,需要将切面类解析道容器中,并且开启自动代理选项
只需要在切面类上增加两行注解:
/** AspectJAspect.java **/
//...省略
@Component
@EnableAspectJAutoProxy
////强制使用cglib
//@EnableAspectJAutoProxy(proxyTargetClass = true)
@Aspect
public class AspectJAspect {
//...省略
}
并且通过容器去获取目标类
//将目标类解析到容器中
//...省略
@Service
public class Target implements TargetInterface {
//...省略
//通过容器获取目标类
/** @var ApplicationContext applicationContext 容器实例 **/
Target proxy = applicationContext.getBean(Target.class);
网友评论