Spring的AOP与动态代理[通俗易懂]

Spring的AOP与动态代理[通俗易懂]Spring的AOP与动态代理

大家好,又见面了,我是你们的朋友全栈君。

  • AOP
    在这里插入图片描述
    Aspect Oriented Programming(AOP) “面向切面编程”。
    在Spring中提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务
    (例如审计(auditing)和事务(transaction)管理)进行内聚性的开发。应用对象只实现它们应该做的——完成业务逻辑——仅此而已。
    主要功能
    日志记录,性能统计,安全控制,事务处理,异常处理等等
    通过动态代理,可以在不修改源码的前提下,实现功能的注入

AOP概念和7个专业术语

  • 1.增强(Advice)
        增强就很好理解了,AOP(切面编程)是用来给某一类特殊的连接点,添加一些特殊的功能,那么我们添加的功能也就是增强啦~
        比如:添加日志、管理事务。
        不过增强不仅仅包含需要增加的功能代码而已,它还包含了方位信息。
        那什么是方位信息呢?
        方位信息就是相对于方法的位置信息,如:方法前、方法后、方法环绕
        为什么要方位信息呢?切点不是确定了需要增强的位置了吗?
        切点定位的是“在什么类的什么方法上”,也就是说,切点只是定位到了方法本身(也叫执行点,特殊的连接点),但是我们增强的内容是放在该方法的前面呢、后面呢?还是前后都要呢?这些切点却没有告诉我们,那么我们该如何确定具体位置呢?
        所以,我们才需要用到方位信息,进一步的定位到具体的增强代码放置的位置。
        咦?增强即包含了【功能】又包含了【方位】,那我是不是不用切点就可以匹配哪些方法,并添加功能了呢?
        恩,确实如此,因为通过方位信息,虽然只是简单的描述了【功能】需要放在方法前、后、还是前后都要等信息,但是我们还是可以通过方位定位到位置。只不过,是匹配到所有类的所有方法!因为方位只是说明在方法前还是方法后,并没有要求是哪些类?哪    些方法? — So,我们可以直接使用增强来生成一个切面,而不需要切点,但这并不怎么推荐,因为它是匹配所有方法的。所以,我们才需要用切点来进一步确认位置。
  • 2.切点(Pointcut)
        一个项目中有很多的类,一个类有很多个连接点,当我们需要在某个方法前插入一段增强(advice)代码时,我们就需要使用切点信息来确定,要在哪些连接点上添加增强。
        那么切点是什么?
        如果把连接点当做数据库中的记录,那么切点就是查找该记录的查询条件。
        所以,一般我们要实现一个切点时,那么我们需要判断哪些连接点是符合我们的条件的,如:方法名是否匹配、类是否是某个类、以及子类等。
  • 3.连接点(Joinpoint)
        连接点就是程序执行的某个特定的位置,如:类开始初始化前、类初始化后、类的某个方法调用前、类的某个方法调用后、方法抛出异常后等。Spring 只支持类的方法前、后、抛出异常后的连接点。
  • 4.切面(Aspect)
        切面由切点和增强(或引介)组成,或者只由增强(或引介)实现。
  • 5.目标对象(Target)
        目标对象就是我们需要对它进行增强的业务类~
        如果没有AOP,那么该业务类就得自己实现需要的功能。
  • 6.AOP代理(AOP proxy)
        一个类被AOP织入后生成出了一个结果类,它是融合了原类和增强逻辑的代理类。
  • 7.织入(Weaving)
        织入就是将增强添加到目标类具体连接点上的过程。
         编译期织入,这要求使用特殊java编译器
        类装载期织入,这要求使用特殊的类装载器
        动态代理织入,在运行期为目标类添加增强生成子类的方式
        Spring采用的是动态代理织入,而AspectJ采用编译期织入和类装载期织入。
    在这里插入图片描述

原生的动态代理

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>groupId</groupId>
    <artifactId>dome03</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.2</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.1.5.RELEASE</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/cglib/cglib -->
        <!--基于子类的动态代理 t2的测试类使用-->
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.1</version>
        </dependency>
    </dependencies>
    
</project>

UserDao.java

package com.xbj.dao;

/** * @Author:晓宇码匠 * @Date:2019/6/15 0015 */
 public interface UserDao { 
   
    void addUser();
    void delUser();
}

UserDaoImpl.java

package com.xbj.dao.impl;

import com.xbj.dao.UserDao;

/** * @Author:晓宇码匠 * @Date:2019/6/15 0015 */
public class UserDAOImpl implements UserDao { 
   
    public void addUser() { 
   
        System.out.println("addUser");
    }

    public void delUser() { 
   
        System.out.println("delUser");
    }

    //定义初始化方法
    public void myInit(){ 
   
        System.out.println("init...");

    }
    //定义销毁方法
    public void dest(){ 
   
        System.out.println("dest...");
    }

}

MyAdvice.java

package com.xbj;

/** * @Author:晓宇码匠 * @Date:2019/6/15 0015 */
public class MyAdvice { 
   //通知
        public void before(){ 
   
            System.out.println("开启事务");
        }

        public void after(){ 
   
            System.out.println("关闭事务");
        }

}

test测试类:

import com.xbj.MyAdvice;
import com.xbj.dao.UserDao;
import com.xbj.dao.impl.UserDAOImpl;
import org.junit.Test;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/** * @Author:晓宇码匠 * @Date:2019/6/15 0015 */
public class test { 
   
    @Test
    public void  t1(){ 
   
        //目标类
        final UserDAOImpl userDAO = new UserDAOImpl();
        //通知(植入的功能)
        final MyAdvice advice = new MyAdvice();

        InvocationHandler handler = new InvocationHandler() { 
   
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
   
                if (method.equals("delUser")){ 
   
                    advice.before();
                }
                Object object=method.invoke(userDAO,args);

                if (method.getName().equals("addUser")){ 
   
                    advice.after();
                }
                return object;
            }
        };
        //Proxy.newProxyInstance(类加载器, 接口ming.,回调函数;
        UserDao proxyClass = (UserDao) Proxy.newProxyInstance(userDAO.getClass().getClassLoader(), userDAO.getClass().getInterfaces(),handler);
        proxyClass.addUser();
        System.out.println("------------------");
        proxyClass.delUser();

    }
}

控制台输出:

addUser
关闭事务
------------------
delUser

基于子类的动态代理

代码同上
test测试类添加了t2测试方法:

import com.xbj.MyAdvice;
import com.xbj.dao.UserDao;
import com.xbj.dao.impl.UserDAOImpl;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.junit.Test;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/** * @Author:晓宇码匠 * @Date:2019/6/15 0015 */
public class test { 
   
    @Test
    public void  t1(){ 
   
        //目标类
        final UserDAOImpl userDAO = new UserDAOImpl();
        //通知(植入的功能)
        final MyAdvice advice = new MyAdvice();

        InvocationHandler handler = new InvocationHandler() { 
   
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
   
                if (method.equals("delUser")){ 
   
                    advice.before();
                }
                Object object=method.invoke(userDAO,args);

                if (method.getName().equals("addUser")){ 
   
                    advice.after();
                }
                return object;
            }
        };
        //Proxy.newProxyInstance(类加载器, 接口ming.,回调函数;
        UserDao proxyClass = (UserDao) Proxy.newProxyInstance(userDAO.getClass().getClassLoader(), userDAO.getClass().getInterfaces(),handler);
        proxyClass.addUser();
        System.out.println("------------------");
        proxyClass.delUser();

    }
    @Test
    public void t2(){ 
   
        //目标类
        final UserDAOImpl userDAOimpl=new UserDAOImpl();
        //通知(植入的功能)
        final MyAdvice advice=new MyAdvice();
        Callback callback=new MethodInterceptor() { 
   
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { 
   
                advice.before();
                Object obj=method.invoke(userDAOimpl,objects);
                advice.after();
                return obj;
            }
        };
        //superclass callback
        UserDao proxyClass= (UserDao) Enhancer.create(userDAOimpl.getClass(),callback);
        proxyClass.delUser();
    }
}

运行t2测试方法,控制台输出:

开启事务
delUser
关闭事务

Spring 自动代理

@Component @Controller @Repository @Service 都是将bean放入Ioc容器中,只是为了代码的可读性而去区别开
@Autowired 是从Ioc容器拿对象
在这里插入图片描述
流程:

<!--1. 扫描:发现Bean  哪里的Bean你想被我管理-->
    <!--2. 在被扫描的目录去做标注,哪些具体的类要被管理-->
    <!--3.配置AOP 动态代理
     1. 哪里增强(切入点)
     2. 增强什么(advice)-->
    <!-- 4.切入点:方法 void  包.类.方法(args) -->

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">

    <context:component-scan base-package="com.xbj"/>
    
    <!--配置 自动代理-->
    <aop:config proxy-target-class="true">
        <!--<aop:pointcut id="myPointcut" expression="execution(* com.xbj.service.impl.*.*(..))" />-->
        <!--<aop:advisor advice-ref="myAspect" pointcut-ref="myPointcut" />-->
        <aop:advisor advice-ref="myAspect" pointcut="execution(* com.xbj.service.impl.*.*(..))" />
    </aop:config>
</beans>

UserController.java

package com.xbj.controller;

import com.xbj.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

/** * @Author:晓宇码匠 * @Date:2019/6/15 0015 */
/* @Controller @Repository @Component @Service 都是将bean放入Ioc容器中,只是为了代码的可读性而去区别开 * @Autowired 是从Ioc容器拿对象 * */
@Controller
public class UserController { 
   

    @Autowired
    private UserService userService;

    public void addUser(){ 
   
        userService.addUser();
    }
}

UserDao.java

package com.xbj.dao;

/** * @Author:晓宇码匠 * @Date:2019/6/15 0015 */
 public interface UserDao { 
   
    void addUser();
    void delUser();
}

UserDaoImpl.java

package com.xbj.dao.impl;

import com.xbj.dao.UserDao;
import org.junit.runner.RunWith;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;

/** * @Author:晓宇码匠 * @Date:2019/6/15 0015 */
@Repository
public class UserDaoImpl implements UserDao { 
   
    public void addUser() { 
   
        System.out.println("addUser");
    }

    public void delUser() { 
   
        System.out.println("delUser");
    }

    //定义初始化方法
    public void myInit(){ 
   
        System.out.println("init...");

    }
    //定义销毁方法
    public void dest(){ 
   
        System.out.println("dest...");
    }

}

UserService.java

package com.xbj.service;

/** * @Author:晓宇码匠 * @Date:2019/6/15 0015 */
public interface UserService { 
   
    void addUser();
    void delUser();
}

UserServiceImpl.java

package com.xbj.service.impl;

import com.xbj.dao.UserDao;
import com.xbj.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/** * @Author:晓宇码匠 * @Date:2019/6/15 0015 */
@Service
public class UserServiceImpl implements UserService { 
   
    @Autowired
    private UserDao userDao;
    public void addUser() { 
   
       userDao.addUser();
    }

    public void delUser() { 
   
        userDao.delUser();
    }
}

MyAspect.java

package com.xbj;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;

/** * @Author:晓宇码匠 * @Date:2019/6/15 0015 */
/* * 注意包名:org.aopalliance.intercept.MethodInterceptor;MyAspect * */
//切面
@Component
public class MyAspect implements MethodInterceptor { 
   
    public Object invoke(MethodInvocation invocation) throws Throwable { 
   
        System.out.println("前置增强");
        Object obj=invocation.proceed();   //invocation 调用
        System.out.println("后置增强");
        return obj;
    }

}

test.java


import com.xbj.controller.UserController;
import com.xbj.dao.UserDao;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;


/** * @Author:晓宇码匠 * @Date:2019/6/15 0015 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { 
   "classpath:applicationContext.xml"})
public class test { 
   
    @Autowired
    private UserController controller;
    @Test
    public void t1() { 
   
        controller.addUser();
    }


}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/106638.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • hi3516a与hi3516e_led player6.0怎么使用

    hi3516a与hi3516e_led player6.0怎么使用背景公司新做了一块3516Dv300的开发板,其中有MIPITx接口,刚好公司库房还有好几百块的LCD屏,LCD屏是800×480的,还是原装屏,不用掉怪可惜的了,所以就让硬件的同事化了个转接板,使用的芯片是ICN6211,这货最大分辨率可以支持到1920×1200,感兴趣的小伙伴自己下个手册看看。调试过程MIPI屏一般都有一组寄存器需要初始化,这个可以根据使用的芯片资料来初始化,大部分厂家会提供初始化寄存器,使用的MIPICommandMode,至于怎么使用,大家自己去Google。我们

    2025年11月21日
    4
  • perl正则表达式中文问题

    perl正则表达式中文问题
     
    在使用perl从地址中提取街道的时候遇到了个很诡异的问题
    同样一个地址,连续进行两次匹配出来的结果居然不一样
    一次是正常的,一次是乱码,搞了半天没弄明白是怎么回事
    看来perl的中文处理能力还是有待加强
    后来在进行正则匹配之前尝试用了useencoding”gbk”; 
    还算运气不错,居然搞定了
    useencoding”gbk”; 
    $address=~/^(.*(市|区))?(.*?(街|路|道)).*

    2022年5月31日
    23
  • WPF TextBox使用密码样式

    WPF TextBox使用密码样式WPF的Textbox并没有password样式,可以手动更改<TextBoxWidth=”200″Margin=”10,0,0,0″Text=”{BindingappData.CurrentUser.LoginPwd}”Background=”Transparent”><TextBo

    2022年7月25日
    13
  • rabbitmq集群部署详解_搭建hadoop集群的步骤

    rabbitmq集群部署详解_搭建hadoop集群的步骤集群搭建参考文档RabbitMQ集群搭建了解原理参考文档RabbitMQ集群原理(非常简洁优秀的总结)1.保证各个节点erlang版本一致。RabbitMQ基于erlang语言,必须保证各个节点erlang版本完全一致。2.可以跨操作系统建立集群。因RabbitMQ基于erlang运行,因此只要erlang版本一致即可,与操作系统无关。3.如果节点加入集群失败,请根据给出的错误原因对症解决。一般有以下几点:a.连接磁盘节点失败。…

    2025年10月23日
    5
  • bwapp 06

    bwapp 06文章目录Base64Encoding(Secret)ClearTextHTTP(Credentials)HostHeaderAttack(ResetPoisoning)HTML5WebStorage(Secret)SSL2.0DeprecatedProtocolTextFiles(Accounts)Base64Encoding(Secret)抓包,找到sercret,进行base64编码以上级别,40位16进制数,推测使用了sha1编码注意先解码,再base

    2022年9月23日
    5
  • 超详细的数据分析职业规划

    超详细的数据分析职业规划最近有不少同学向大讲台老师咨询有关数据分析职业发展的问题,由此可见,随着大数据的飞速发展,数据分析职业也成为很多同学关注的目标。不要急,大讲台老师这就给大家介绍数据分析的职业发展。入门和职业规划应该从两个角度考虑:领域和路线。领域是不少新人常忽略的要素,其实数据分析不会脱离业务存在。你进入哪个行业,很大程度会决定你初期的技能树和技能点。譬如金融领域的风控模型、营销领域的生命周期、广告

    2022年5月10日
    47

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号