Spring AOP

本文详细介绍了Java中的动态代理机制,包括JDK动态代理和CGLIB动态代理的实现方式。JDK动态代理基于接口实现,而CGLIB则是通过继承目标类。SpringAOP在有接口时使用JDK代理,无接口时使用CGLIB。文章还展示了SpringAOP中的切面编程,包括前置通知、后置通知、环绕通知等应用场景,并给出了具体的配置和代码示例。
摘要由CSDN通过智能技术生成

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"/>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值