Spring中的AOP

简单来说就是:在方法中增加新的业务逻辑,但是不修改方法中的代码。

AOP采取横向抽取机制,将分散在各个方法中的重复代码提取出来,然后在程序编译或运行时,再将这些提取出来的代码应用到需要执行的地方。

AOP的基本概念

  1. 连接点:方法的调用
  2. 切入点:实际被增强的方法
  3. 通知/增强处理:AOP框架在特定的切入点执行的增强处理,即在定义好的切入点处要执行的程序代码。可以理解为切面类中的方法,它是切面的具体表现
  4. 切面:把通知/增强应用到切入点的过程
  5. 目标对象:所有被通知的对象,也称为被增强对象。如果AOP框架采用的是动态的AOP实现,那么该对象就是一个被代理对象
  6. 代理:将通知应用到目标对象之后,被动态创建的对象
  7. 织入:将切面代码插入到目标对象上,从而生成代理对象的过程

AOP的实现原理

  • 有接口情况下,使用JDK动态代理(需要目标对象实现业务接口,代理类实现InvocationHandler 接口)

  • 没有接口的情况下,使用CGLIB代理

JDK动态代理代码演示

public interface UserDao {
    void addUser();
    void deleteUser();
}
//被代理类(目标对象)
public class UserDaoImpl implements UserDao {
    @Override
    public void addUser() {
        System.out.println("添加用户");
    }

    @Override
    public void deleteUser() {
        System.out.println("删除用户");
    }
}
//切面类:可以存在多个通知 Advice(即增强的方法)
public class MyAspect {

    //方法表示切面中的通知
    public void Permissions() {
        System.out.println("模拟检查权限. . . ");
    }

    public void log() {
        System.out.println("模拟记录日志. . . ");
    }
}
//代理类  需要实现 InvocationHandler 接口
public class JdkProxy implements InvocationHandler {

    private UserDao userDao;

    //创建代理方法
    public Object createProxy(UserDao userDao) {
        this.userDao = userDao;
        //1.类加载器
        ClassLoader classLoader = JdkProxy.class.getClassLoader();

        //被代理对象实现的所有接口
        Class<?>[] clazz = userDao.getClass().getInterfaces();

        //使用代理类 进行增强 返回的是代理后的对象 (创建代理对象) this指的是代理类 JdkProxy 本身
        return Proxy.newProxyInstance(classLoader,clazz,this);

    }

    /**
     * 所有动态代理类的的方法调用,都会交由 invoke() 方法来处理
     * @param proxy  被代理后的对象
     * @param method 将要被执行的方法信息(反射)
     * @param args   执行方法时需要的参数
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //声明切面
        MyAspect myAspect = new MyAspect();
        //前增强
        myAspect.Permissions();
        Object invoke = method.invoke(userDao, args);
        //后增强
        myAspect.log();
        return invoke;
    }
}
public class JdkTest {
    public static void main(String[] args) {

        //创建代理对象
        JdkProxy jdkProxy = new JdkProxy();

        //创建目标对象
        UserDao userDao = new UserDaoImpl();

        //从代理对象中获取增强后的目标对象
        UserDao userDao1 = (UserDao) jdkProxy.createProxy(userDao);

        //执行方法
        userDao1.addUser();
        userDao1.deleteUser();
    }
}

 目标对象UserDaoImpl实现了业务接口UserDao 

 代理类 JdkProxy 实现了 InvocationHandler 

 在程序运行时运用反射机制动态创建而成

SpringAOP,如果不强制使用CGLIB包,默认情况下使用的是JDK的动态代理来代理接口

CGLIB代理代码演示

如果目标对象没有实现接口的类进行处理,那么就可以使用CGLIB代理(子类代理)。

CGLIB是一个高性能开源的代码生成包,它采用非常底层的字节码技术对指定的目标类生成一个子类,并对子类进行增强。Spring核心包已经集成了CGLIB所需要的包。

底层是通过使用字节码处理框架ASM来转换字节码并生成新的类

//目标对象
public class UserDao {

    public void addUser() {
        System.out.println("添加用户");
    }

    public void deleteUser() {
        System.out.println("删除用户");
    }
}
//切面类:可以存在多个通知 Advice(即增强的方法)
public class MyAspect {

    //方法表示切面中的通知
    public void Permissions() {
        System.out.println("模拟检查权限. . . ");
    }

    public void log() {
        System.out.println("模拟记录日志. . . ");
    }
}
/**
 * 代理类
 * 创建动态对象 Enhancer(核心库)
 * 然后调用了Enhancer类的setSuperclass()方法来确定目标对象
 * 接下来调用了setCallback()方法添加回调函数 this代表的是代理类 Cglibproxy
 * 最后通过return语句将创建的代理类对象返回
 * intercept()方法会在程序执行目标方法时被调用,方法运行时将会执行切面类中的增强方法
 */
public class Cglibproxy implements MethodInterceptor {

    //创建一个动态类对象
    public Object createProxy(Object target) {

        //创建一个动态类对象(CGLIB的核心库)
        Enhancer enhancer = new Enhancer();
        // 确定需要增强的类,设置其父类
        enhancer.setSuperclass(target.getClass());
        //添加回调函数
        enhancer.setCallback(this);
        //返回创建的代理类
        return enhancer.create();

    }
    
    /**
     * @param proxy       CGlib根据指定父类生成的代理对象
     * @param method      拦截的方法
     * @param args        拦截方法的参数数组
     * @param methodProxy 方法的代理对象,用于执行父类的方法
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {

        //创建切面类对象
        MyAspect myAspect = new MyAspect();
        //前增强
        myAspect.Permissions();
        Object obj = methodProxy.invokeSuper(proxy, args);
        //后增强
        myAspect.log();
        return obj;

    }
}
public class CglibTest {
    public static void main(String[] args) {

        //创建代理对象
        Cglibproxy cglibproxy = new Cglibproxy();

        //创建目标对象
        UserDao userDao = new UserDao();

        //获取增强后的目标对象
        UserDao userDao1 = (UserDao) cglibproxy.createProxy(userDao);

        //执行方法
        userDao1.addUser();
        userDao1.deleteUser();
        
    }
}

注意:目标对象不能为final,否则报错 java.lang.IllegalArgumentException

基于代理类的AOP实现

在Spring中,使用ProxyFactoryBean是创建AOP代理的最基本方式

Spring的通知类型

Spring中的通知按照在目标类方法的连接点位置,可以分为以下5种类型:

  • 前置通知:在目标方法执行前执行,可以应用于权限管理等功能
  • 后置通知:在目标方法执行完执行,准备执行return的代码时通知,通常用作执行结果日志输出、结果加密等
  • 环绕通知:在目标方法执行前和目标方法执行后都要执行,通常用作方法性能统计、接口耗时、统一加密、解密等
  • 异常通知:程序抛出异常时执行,通常用作告警处理、事务回滚等
  • 最终通知:相当于finally中执行的代码,通常用在关闭资源、清理缓存等业务逻辑中

ProxyFactoryBean

ProxyFactoryBean是FactoryBean接口的实现类,FactoryBean负责实例化一个Bean,而ProxyFactoryBean负责为其他Bean创建代理实例。

ProxyFactoryBean类中的常用可配置属性:

属性名称描述
target 代理的目标对象
proxyInterfaces代理要实现的接口
proxyTargetClass是否对类代理而不是接口,设置为true时,使用CGLIB代理
interceptorNames需要织入目标的Advice
singleton返回的代理是否为单例,默认为true
optimize当设置为true时,强制使用CGLIB

代码演示

//被代理类(目标对象)
public class UserDaoImpl implements UserDao {
    @Override
    public void addUser() {
        System.out.println("添加用户");
    }

    @Override
    public void deleteUser() {
        System.out.println("删除用户");
    }
}
//切面类
public class MyAspect implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {

        permissions();
        //执行目标方法
        Object proceed = invocation.proceed();
        log();
        return proceed;
    }

    //方法表示切面中的通知
    public void permissions() {
        System.out.println("模拟检查权限. . . ");
    }

    public void log() {
        System.out.println("模拟记录日志. . . ");
    }
}

注意:切面类要实现的包全路径是: import org.aopalliance.intercept.MethodInterceptor;

配置文件如下:

  <!--1.目标类 -->
    <bean id="userDao" class="com.dfbz.dao.Impl.UserDaoImpl"></bean>

    <!--2.切面类 -->
    <bean id="myAspect" class="com.dfbz.factorybean.MyAspect"></bean>

    <!--3.使用Spring代理工厂定义一个名称为 userDaoProxy的代理对象-->
    <bean id="userDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!--3.1指定代理要实现的接口-->
        <property name="proxyInterfaces" value="com.dfbz.dao.UserDao"></property>
        <!--3.2指定目标对象-->
        <property name="target" ref="userDao"></property>
        <!--3.3指定切面,植入环绕通知-->
        <property name="interceptorNames" value="myAspect"></property>
        <!--3.4指定代理方式 true: CGLIB代理; false:JDK动态代理-->
        <property name="proxyTargetClass" value="true"></property>
    </bean>

首先通过<bean>元素定义了目标类和切面,然后使用ProxyFactoryBean类定义了代理对象。

在定义的代理对象中,分别通过<property>子元素指定了代理实现的接口代理的目标对象、需要织入目标类的通知以及代理方式
 

public class ProxyFactoryBeanTest {
    public static void main(String[] args) {

        //获取核心容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        //从spring容器中获取内容
        UserDao userDao = (UserDao) ac.getBean("userDaoProxy");

        //执行方法
        userDao.addUser();
        userDao.deleteUser();
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值