AOP:主要为了增强方法,即动态的把一些公共处理如打印日志,监控,权限验证等操作织入到业务逻辑中。好处在于服务类可以聚焦到业务,并且公共模块一处修改,到处生效
织入:将公共处理增加到 Java 类的过程。其中织入分两种:
- 静态织入:编译器织入
- 动态织入:在运行时动态的将增强代码织入目标类中,通过动态代理实现
Java 采用动态织入的方式,其中动态织入在 Java 语言中有两种实现方式:
- JDK 动态代理:通过反射实现,被增强类必须实现接口
- cglib 动态代理:通过继承实现
JDK 动态代理原理
// 接口类
public interface ExInterface {
void execute();
}
// 具体需要加强的类
public class A implements ExInterface{
public void execute(){
System.out.println("执行A的 execute 方法...");
}
}
// 具体代理类
public class JDKProxy implements InvocationHandler{
private A target;
public JDKProxy(A target){
this.target=target;
}
public ExInterface createProxy(){
return (ExInterface) Proxy.newProxyInstance(
target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if("execute".equals(method.getName())) {
AuthCheck.authCheck();
Object result = method.invoke(target, args);
Report.recordLog();
return result;
} eles if("delete".equals(method.getName())) {
} else {
return method.invoke(target,args);
}
}
}
// 如何使用
A a = new A();
JDKProxy jdkProxy = new JDKProxy(a);
ExInterface proxy = jdkProxy.createProxy();
proxy.execute();
方法介绍:
/**
* @param loader 类加载器,一般传递目标对象(A类即被代理的对象)的类加载器
* @param interfaces 目标对象(A)的实现接口
* @param h 回调处理句柄(后面会分析到)
*/
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
/**
* @param proxy :生成的代理对象
* @param method:目标对象的方法,通过反射调用
* @param args:目标对象方法的参数
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
代理对象和接口拥有相同的方法,它的创建是通过 Proxy 类达到的,利用 newProxyInstance() 方法便可以动态生成,这个方法底层基于反射实现。Proxy 类必须重写 invoke 方法实现回调。拿到代理对象后,调用任何方法都会回调 invoke() ,此时我们可以在 invoke() 方法中增强方法,这就是 JDK 动态代理的原理。
CGLIB 动态代理原理
// 被代理类
public class A {
public void execute(){
System.out.println("执行A的execute方法...");
}
}
// 代理类
public class CGLibProxy implements MethodInterceptor {
private A target;
public CGLibProxy(A target) {
super();
this.target = target;
}
public A createProxy(){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
return (A) enhancer.create();
}
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
if("execute".equals(method.getName())) {
AuthCheck.authCheck();
Object result = method.invoke(target, args);
Report.recordLog();
return result;
} eles if("delete".equals(method.getName())) {
// xxx 代码省略
} else {
return method.invoke(target,args);
}
}
CGLIB 动态代理不需要基于接口,其中它的代理对象通过 CGLibProxy 类实现,该类需要重写 intercept 方法,该方法就类似上面提到的回调方法。这里代理对象通过 Enhancer 来设置,Enhancer 是一个用于产生代理对象的类,作用类似 JDK 动态代理中的 Proxy 类,这里传入 Class 对象就是方便使用继承
Spring AOP 默认在有接口时采用 JDK 代理,没有接口时采用 CGLIB 代理
使用示例
// 接口类
public interface UserDao {
int addUser();
}
// 接口实现类
@Repository
public class UserDaoImpl implements UserDao {
@Override
public int addUser() {
System.out.println("add user ......");
return 1;
}
}
// 切面类
@Aspect
public class MyAspect {
/**
* 使用Pointcut定义切点
*/
@Pointcut("execution(* com.zejian.spring.springAop.dao.UserDao.addUser(..))")
private void myPointcut(){}
/**
* 前置通知
*/
@Before("execution(* com.xxx.dao.UserDao.addUser(..))")
public void before(){
System.out.println("前置通知....");
}
/**
* 后置通知
* returnVal,切点方法执行后的返回值
*/
@AfterReturning(value="execution(* com.xxx.dao.UserDao.addUser(..))",returning = "returnVal")
public void AfterReturning(Object returnVal){
System.out.println("后置通知...."+returnVal);
}
/**
* 环绕通知
* @param joinPoint 可用于执行切点的类
* @return
* @throws Throwable
*/
@Around("execution(* com.xxx.dao.UserDao.addUser(..))")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕通知前....");
Object obj= (Object) joinPoint.proceed();
System.out.println("环绕通知后....");
return obj;
}
/**
* 抛出通知
* @param e
*/
@AfterThrowing(value="execution(* com.xxx.dao.UserDao.addUser(..))",throwing = "e")
public void afterThrowable(Throwable e){
System.out.println("出现异常:msg="+e.getMessage());
}
/**
* 无论什么情况下都会执行的方法,应用切入点函数
*/
@After(value="myPointcut()")
public void after(){
System.out.println("最终通知....");
}
}
配置文件:
<!-- 启动@aspectj的自动代理支持-->
<aop:aspectj-autoproxy />
<!-- 定义目标对象 -->
<bean id="userDaos" class="com.xxx.dao.impl.UserDaoImpl" />
<!-- 定义aspect类 -->
<bean name="myAspectJ" class="com.xxx.MyAspect"/>