Spring之AOP详解

文章目录

  • 1. AOP概念
  • 2. AOP底层原理
  • 3. AOP术语
  • 4. 基于注解的AOP使用
  • 5. 基于xml方式的AOP使用

1. AOP概念

  1. 面向切面编程,利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
  2. 通俗描述:不通过修改源代码方式,在主干功能里面添加新功能

以登录功能为例:
Spring之AOP详解_第1张图片


2. AOP底层原理

1. JDK动态代理—有接口情况

使用 JDK 动态代理,使用 Proxy 类里面的方法创建代理对象
Spring之AOP详解_第2张图片

创建接口,定义方法:

package cn.edu.xd.dao;

public interface UserDao {
    public int add(int a,int b);
    public void printMsg(String msg);
}

创建接口实现类,实现接口方法:

package cn.edu.xd.dao;

public class UserDaoImpl implements UserDao{
    @Override
    public int add(int a, int b) {
        System.out.println("add()方法执行...");
        return a+b;
    }

    @Override
    public void printMsg(String msg) {
        System.out.println("update()方法执行...");
        System.out.println("msg:"+msg);
    }
}

使用 Proxy 类创建接口代理对象:

package cn.edu.xd.proxy;

import cn.edu.xd.dao.UserDao;
import cn.edu.xd.dao.UserDaoImpl;
import cn.edu.xd.dao.UserDaoProxy;

import java.lang.reflect.Proxy;

public class JDKProxy {
    public static void main(String[] args) {
        Class[] interfaces={UserDao.class};//要实现的接口代理对象 即增强UserDao接口中的方法
        UserDaoImpl userDaoImpl=new UserDaoImpl();
        UserDao userDao= (UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(),interfaces,new UserDaoProxy(userDaoImpl));
        int result=userDao.add(1,2);
        System.out.println("result:"+result);
        userDao.printMsg("hello");

    }
}

package cn.edu.xd.dao;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Arrays;

public class UserDaoProxy implements InvocationHandler {
    private Object object;

    //为谁进行代理就把谁作为参数传递过来(有参构造函数传递)  比如这里给userDaoImpl代理 传入的参数就是userDaoImpl对象
    public UserDaoProxy(Object object) {
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("方法执行之前...."+method.getName()+" 参数:"+ Arrays.toString(args));
        Object result=method.invoke(object,args);
        System.out.println("方法执行之后....");
        return result;
    }
}

Spring之AOP详解_第3张图片

2. CGLIB动态代理—无接口情况

需要导入两个包:
在这里插入图片描述

被代理类:

package cn.edu.xd.dao;

public class UserDaoImpl{

    public int add(int a, int b) {
        System.out.println("add()方法执行...");
        return a+b;
    }

    public void printMsg(String msg) {
        System.out.println("update()方法执行...");
        System.out.println("msg:"+msg);
    }

}

代理类:

package cn.edu.xd.dao;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;
import java.util.Arrays;

public class UserDaoImplInterceptor implements MethodInterceptor {

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
       System.out.println("方法执行之前...."+method.getName()+" 参数:"+ Arrays.toString(objects));
        Object result=methodProxy.invokeSuper(o,objects);
        System.out.println("方法执行之后....");
        return result;
    }
}

使用:

package cn.edu.xd.proxy;

import cn.edu.xd.dao.UserDaoImpl;
import cn.edu.xd.dao.UserDaoImplInterceptor;
import net.sf.cglib.proxy.Enhancer;

public class CglibProxy {
    public static void main(String[] args) {
        UserDaoImpl userDao= (UserDaoImpl) Enhancer.create(UserDaoImpl.class,new UserDaoImplInterceptor());
        int result=userDao.add(1,2);
        System.out.println("result:"+result);
        userDao.printMsg("hello");
    }
}

Spring之AOP详解_第4张图片


3. AOP术语

  1. 连接点
    类里面哪些方法可以被增强,这些可以被增强的方法称为连接点

  2. 切入点
    实际被真正增强的方法称为切入点

  3. 通知
    实际增强的逻辑部分称为通知
    通知分为5种:前置通知,后置通知,环绕通知,异常通知,最终通知

  4. 切面
    是一个动作,把通知应用到切入点的过程

  5. 切入点表达式
    确定哪个切入点,切入点表达式作用:知道对哪个类里面的哪个方法进行增强
    语法结构: execution([权限修饰符] [返回类型] [类全路径] [[方法名称]([参数列表])
    权限修饰符可以省略,其他的不能省略

eg1: 对 cn.edu.xd.dao.UserDao 类里面的 add 进行增强(…表示参数列表)
execution(* cn.edu.xd.dao.UserDao.add(..))

eg2: 对 cn.edu.xd.dao.UserDao类里面的所有的方法进行增强
execution(* cn.edu.xd.dao.UserDao.*(..))

eg3:对 cn.edu.xd.dao包里面所有类,类里面所有方法进行增强
execution(* cn.edu.xd.dao.*.*(..))


4. 基于注解的AOP使用

导入依赖包:
Spring之AOP详解_第5张图片

bean.xml配置:
Spring之AOP详解_第6张图片

创建被代理类(需要被增强的类),加上注解@Component

package cn.edu.xd.bean;

import org.springframework.stereotype.Component;

@Component
public class User {
    public void add(){
        System.out.println("add()...");
    }
}

创建代理类(增强类),加上注解@Component和@Aspect

package cn.edu.xd.proxy;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class UserProxy {
    //前置通知
    @Before(value = "execution(* cn.edu.xd.bean.User.add(..))")
    public void before(){
        System.out.println("before()...");
    }
    //最终通知 即使出现异常也会执行
    @After(value = "execution(* cn.edu.xd.bean.User.add(..))")
    public void after(){
        System.out.println("after()...");
    }
    //后置通知(返回通知) 出现异常不会执行
    @AfterReturning(value = "execution(* cn.edu.xd.bean.User.add(..))")
    public void afterReturning(){
        System.out.println("afterReturning()...");
    }
    //异常通知  出现异常时会执行
    @AfterThrowing(value = "execution(* cn.edu.xd.bean.User.add(..))")
    public void afterThrowing(){
        System.out.println("afterThrowing()...");
    }
    //环绕通知
    @Around(value = "execution(* cn.edu.xd.bean.User.add(..))")
    public void around(ProceedingJoinPoint point) throws Throwable {

        System.out.println("环绕之前");
        point.proceed();
        System.out.println("环绕之后");
    }
}

Spring之AOP详解_第7张图片

为了使异常通知出现,修改代码如下:

 public void add(){
        int a=10/0;//设置除零错误
        System.out.println("add()...");
    }

Spring之AOP详解_第8张图片

相同的切入点抽取
上面代码中的value = "execution(* cn.edu.xd.bean.User.add(..))"出现了多次,可以进行如下提取:

 @Pointcut(value = "execution(* cn.edu.xd.bean.User.add(..))")
    public void pointCommon(){

    }
    //前置通知
    @Before(value = "pointCommon()")
    public void before(){
        System.out.println("before()...");
    }

设置多个增强类的增强顺序

与前面的增强类UserProxy类似,现在又加入一个增强类UserProxy2,代码如下:

package cn.edu.xd.proxy;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Component
@Aspect
@Order(1)
public class UserProxy {
    @Pointcut(value = "execution(* cn.edu.xd.bean.User.add(..))")
    public void pointCommon(){

    }
    //前置通知
    @Before(value = "pointCommon()")
    public void before(){
        System.out.println("before()...");
    }
    //最终通知 即使出现异常也会执行
    @After(value = "execution(* cn.edu.xd.bean.User.add(..))")
    public void after(){
        System.out.println("after()...");
    }
    //后置通知(返回通知) 出现异常不会执行
    @AfterReturning(value = "execution(* cn.edu.xd.bean.User.add(..))")
    public void afterReturning(){
        System.out.println("afterReturning()...");
    }
    //异常通知  出现异常时会执行
    @AfterThrowing(value = "execution(* cn.edu.xd.bean.User.add(..))")
    public void afterThrowing(){
        System.out.println("afterThrowing()...");
    }
    //环绕通知
    @Around(value = "execution(* cn.edu.xd.bean.User.add(..))")
    public void around(ProceedingJoinPoint point) throws Throwable {

        System.out.println("环绕之前");
        point.proceed();
        System.out.println("环绕之后");
    }
}

可以使用注解@Order(数字)的方式指定增强顺序,数字从0开始,数字越小,优先级越高

完全注解开发

package cn.edu.xd.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration //表示是配置类 相当于bean.xml
@ComponentScan(basePackages = {"cn.edu.xd"})  //表示对哪些包进行扫描
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class SpringConfig {
}

 @Test
    public void test1(){
        ApplicationContext context
                = new AnnotationConfigApplicationContext(SpringConfig.class);
        User user = context.getBean("user", User.class);
        user.add();
    }

5. 基于xml方式的AOP使用

  1. 创建增强类(UserProxy)和被增强类(User)
  2. 在bean.xml中给这两个类创建对象
 <bean id="user" class="cn.edu.xd.bean.User"/>
    <bean id="userProxy" class="cn.edu.xd.proxy.UserProxy"/>
  1. 在 spring 配置文件中配置切入点
<aop:config>
        <aop:pointcut id="p" expression="execution(* cn.edu.xd.bean.User.add(..))"/>
        <aop:aspect ref="userProxy">
            <aop:before method="before" pointcut-ref="p"/>
            <aop:after method="after" pointcut-ref="p"/>
            <aop:after-returning method="afterReturning" pointcut-ref="p"/>
            <aop:around method="around" pointcut-ref="p"/>
            <aop:after-throwing method="afterThrowing" pointcut-ref="p"/>
        aop:aspect>
    aop:config>

你可能感兴趣的:(#,Spring,spring,java,后端)