动态代理-AOP

1 什么是AOP?

  • Aspect Oriented Programming的缩写,面向切面编程,切面指定就是动态代理的方法,作用是在不改变业务层方法源代码的基础上对方法进行增强,底层使用的是动态代理技术,面向切面编程也可以理解成面向动态代理编程。

2 AOP相关概念

在这里插入图片描述

  • Target(目标对象):被代理的对象就是目标对象
  • Proxy(代理对象):被增强后的对象就是代理对象
  • Joinpoint(连接点):就是目标对象中所有被拦截到的方法
  • Pointcut(切入点):就是目标对象中被增强的方法
  • Advice(通知):执行目标方法之前或者之后调用的方法就是通知
  • Aspect(切面):通知方法和切入点方法结合所在的位置叫做切面
  • Weaving(织入):通知方法和切入点方法结合的过程,织入之后的结果就是切面

总结一下:
连接点是所有被拦截到的方法,切入点是所有被增强的方法,连接点不一定是切入点,但是切入点一定是连接点。在执行目标对象方法之前或者之后要做的事叫做通知,通知中有增强的业务。将切入点和通知组织到一起叫织入,织入形成的结果就是切面。

3 AOP配置实现步骤

<1>【第一步】导入相关依赖:spring-context、aspectjweaver

<!--spring核心依赖-->
<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.1.9.RELEASE</version>
</dependency>
<!--切入点表达式依赖,作用:通过表达式找到哪些方法需要增强,也就是找到切入点-->
<dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.6</version>
</dependency>

<2>【第二步】定义通知类和目标对象

  • AOP目标接口:
public interface StudentService {//查询全部public abstract List<Student> findAll() throws IOException;public void transfer(Integer outId, Integer inId, double money);
}
  • AOP目标实现类:
@Service("studentService")
public class StudentServiceImpl implements StudentService {@Overridepublic List<Student> findAll() throws IOException {System.out.println("查询所有学生信息findAll...");return null;}@Overridepublic void transfer(Integer outId,Integer inId,double money){//1 张三的账户-1000元System.out.println("调用dao:张三("+outId+")的账户"+(-money)+"元");//2 李四的账户+1000元System.out.println("调用dao:李四("+inId+")的账户"+money+"元");}//接口中没有该方法,不会被拦截public void show(){System.out.println("----------------");}
}

注:代理的为接口对象,接口中没有的方法,实现类自己的方法不会被增强

  • 通知类:
import org.aspectj.lang.ProceedingJoinPoint;//通知类,告诉spring在增强的前后需要做什么事
public class Advice {public void before(){//前置通知:开启事务System.out.println("前置通知:开启事务");}public void afterReturn(){//后置通知:提交事务System.out.println("后置通知:提交事务");}public void afterThrowable(){//异常通知:回滚事务System.out.println("异常通知:回滚事务");}public void after(){//最终通知:释放资源System.out.println("最终通知:释放资源");}// 环绕通知:是Spring给我们提供的一种手动调用目标对象方法或者其他通知方法的方式// spring在调用环绕通知方法时会传递一个封装了目标方法的对象,叫做ProceedingJoinPointpublic Object around(ProceedingJoinPoint pjp){Object result =null;try {//前置通知before();//执行目标方法,相当于动态代理中的 result=method.invoke(...)result = pjp.proceed();//后置通知afterReturn();} catch (Throwable throwable) {//异常通知afterThrowable();throwable.printStackTrace();} finally {//最终通知after();}return result;}
}

<3>xml文件配置AOP

    1 配置目标对象,添加到spring容器中2 配置通知对象,添加到spring容器中3 配置切入点方法和通知方法织入过程,也就配置切面
  • 纯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:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop.xsd"><!--1.配置service--><bean id="studentService" class="com.itheima.service.impl.StudentServiceImpl"/><!--2.配置通知对象--><bean id="myAdvice" class="com.itheima.aop.Advice"/><!--3.配置AOP--><aop:config><!--3.1配置AOP切入点表达式[可放在任意位置]--><!--*空格代表void 方法名.*:.StudentServiceImpl.*:.方法名(..)方法参数, ..代表参数任意--><aop:pointcut id="pt" expression="execution(* com.itheima.service.impl.*.*(..))"/><!--<aop:pointcut id="pt" expression="execution(* com.itheima.service.impl.StudentServiceImpl.*(..))"/>--><!--3.2配置切面--><aop:aspect ref="myAdvice"><!--前置通知--><aop:before method="before" pointcut-ref="pt"/><!--后置通知--><aop:after-returning method="afterReturn" pointcut-ref="pt"/><!--异常通知--><aop:after-throwing method="afterThrowable" pointcut-ref="pt"/><!--最终通知--><aop:after method="after" pointcut-ref="pt"/><!--使用环绕通知--><!--<aop:around method="around" pointcut-ref="pt"/>--></aop:aspect></aop:config>
</beans>
  • 注解配置:
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;//通知类,告诉spring在增强的前后需要做什么事
@Component("advice")
@Aspect//告知是一个切面类,扫描时会扫描它的注解//代替:<aop:aspect ref="advice">
public class Advice {@Pointcut("execution(* com.itheima.service.impl.*.*(..))")//id为方法名[首字母小写]public void pt() {}//==注意:使用注解配置AOP,后置通知和异常通知会在最终通知之后调用,
// 在spring-context的5.1.9版本中是这样的,在更高的版本中可能得到了解决,
// (5.2.6及以上版本解决了)。
// 但是我们可以使用环绕通知解决这个问题,推荐使用环绕通知。==**/*  @Before("pt()")public void before(JoinPoint joinPoint) {Object[] args = joinPoint.getArgs();//前置通知:开启事务System.out.println("前置通知:开启事务"+args[0]);}*/
//两种得到传递参数的方法 如果目标方法没有传参,则不执行@Before("execution(* com.itheima.service.impl.*.*(..))&&args(x)")public void before(int x) {//前置通知:开启事务System.out.println("前置通知:开启事务" + x);}@AfterReturning("pt()")public void afterReturn() {//后置通知:提交事务System.out.println("后置通知:提交事务");}@AfterThrowing("pt()")public void afterThrowable() {//异常通知:回滚事务System.out.println("异常通知:回滚事务");}@After("pt()")public void after() {//最终通知:释放资源System.out.println("最终通知:释放资源");}// 环绕通知:是Spring给我们提供的一种手动调用目标对象方法或者其他通知方法的方式// spring在调用环绕通知方法时会传递一个封装了目标方法的对象,叫做ProceedingJoinPoint//@Around("pt()")public Object around(ProceedingJoinPoint pjp) {Object result = null;try {//前置通知//Object[] args = pjp.getArgs();//before();//执行目标方法,相当于动态代理中的 result=method.invoke(...)result = pjp.proceed();//后置通知afterReturn();} catch (Throwable throwable) {//异常通知afterThrowable();throwable.printStackTrace();} finally {//最终通知after();}return result;}
}

注:
使用注解配置AOP,后置通知和异常通知会在最终通知之后调用,
在spring-context的5.1.9版本中是这样的,在更高的版本中可能得到了解决,
(5.2.6及以上版本解决了)。
但是我们可以使用环绕通知解决这个问题,推荐使用环绕通知

  • 核心配置类代替XML
import org.springframework.context.annotation.*;@Configuration//表示代表替换applicationContext.xml的标识[可以不写]
@ComponentScan("com.itheima")//开启Spring注解扫描
@EnableAspectJAutoProxy//开启Spring的AOP注解支持
public class SpringConfig {
}

4.底层动态代理类似原理[studentService动态代理工厂]

package com.itheima.proxy;
import com.itheima.aop.Advice;
import com.itheima.service.StudentService;
import com.itheima.service.impl.StudentServiceImpl;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;public class StudentServiceProxyFactory {public static StudentService createStudentServiceProxy() {Advice advice = new Advice();//1.创建真实对象StudentService studentService = new StudentServiceImpl();//可采用set注入//2.创建代理对象/**ClassLoader loader,创建代理对象的class对象Class<?>[] interfaces,告诉代理对象要和目标对象实现相同的接口,就具有相同的功能。InvocationHandler h,处理增强的逻辑*/ClassLoader classLoader = studentService.getClass().getClassLoader();Class<?>[] interfaces = studentService.getClass().getInterfaces();//获取所有的直接实现的接口StudentService service = (StudentService) Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() {/*** @param proxy 代理对象* @param method 调用代理对象的方法,findAll、findById、transfer、update。。。* @param args 调用代理对象方法传递进来的参数们* @return 此处的返回值将返回给调用处* @throws Throwable*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object result = null;if (method.getName().equals("transfer") || method.getName().equals("delete")) {try {//1.开启事务advice.before();//2.执行操作,调用目标方法result = method.invoke(studentService, args);//3.提交事务advice.afterReturn();} catch (Exception e) {e.printStackTrace();//4.如果有异常则回滚事务advice.afterThrowable();} finally {//5.释放资源advice.after();}} else {//执行操作,调用目标方法result = method.invoke(studentService, args);}return result;}});return service;}
}

注:只可对单一实现类对象进行增强

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/330643.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux-basic(12)正则表达式与文件格式化处理

【12.1.1】什么是正则表达式&#xff1f; 1&#xff09;简单说&#xff1a;正则表示法就是处理字串的方法&#xff0c;他是以行为单位来进行字串的处理行为&#xff0c; 正则表达式透过一些特殊符号的辅助&#xff0c;可以让使用者轻易的达到查找、删除、替换某特定字串的处理程…

阿里巴巴对Java编程【OOP规约】的规约

转载自 阿里巴巴对Java编程【OOP规约】的规约 OOP规约 1. 【强制】避免通过一个类的对象引用访问此类的静态变量或静态方法&#xff0c;无谓增加编译器解析成本&#xff0c;直接用类名来访问即可。 2. 【强制】所有的覆写方法&#xff0c;必须加 Override 注解。 说明&#xff…

AOP切点表达式及通知类参数传递方式

1.切入点表达式的写法 execution( * com.itheima.service.impl.StudentServiceImpl.findAll(…)) //较少 execution( * com.itheima.service.impl.StudentServiceImpl.(…)) //较少 execution( * com.itheima.service.StudentService.(…)) //StudentService中的所有方法会被代…

linux-basic(13)学习shell script

【13.1】什么是shell script&#xff1f;1&#xff09;shell script 是利用 shell 的功能所写的一个『程序 (program)』&#xff0c;这个程序是使用纯文字档&#xff0c;将一些 shell 的语法与命令(含外部命令)写在里面&#xff0c; 搭配正规表示法、管线命令与数据流重导向等功…

阿里巴巴对Java编程【集合处理】的规约

转载自 阿里巴巴对Java编程【集合处理】的规约集合处理1. 【强制】关于 hashCode 和 equals 的处理&#xff0c;遵循如下规则&#xff1a; 1&#xff09; 只要重写 equals &#xff0c;就必须重写 hashCode 。 2&#xff09; 因为 Set 存储的是不重复的对象&#xff0c;依据 ha…

http请求状态码400的原因总结

会出现这个HTTP请求状态码400&#xff0c;说明这个请求是无效的&#xff0c;并没有进入后台服务器&#xff08;控制器&#xff09;里。 通常的原因&#xff1a; 前端提交的字段名称或者字段类型和后台的实体类不一样&#xff0c;或者前端提交的参数跟后台需要的参数个数不一致…

做一个完整的Java Web项目需要掌握的技能

转自&#xff1a; https://blog.csdn.net/JasonLiuLJX/article/details/51494048--------------------------------------------------------------------------------最近自己做了几个Java Web项目&#xff0c;有公司的商业项目&#xff0c;也有个人做着玩的小项目&#xff0…

阿里巴巴对Java编程【并发处理】的规约

转载自 阿里巴巴对Java编程【并发处理】的规约并发处理1. 【强制】获取单例对象需要保证线程安全&#xff0c;其中的方法也要保证线程安全。 说明&#xff1a;资源驱动类、工具类、单例工厂类都需要注意。2. 【强制】创建线程或线程池时请指定有意义的线程名称&#xff0c;方便…

查询sql打印日志配置

mybatis-plus:mapper-locations: classpath*:mapper/*.xml# 设置别名包扫描路径&#xff0c;通过该属性可以给包中的类注册别名type-aliases-package: com.heima.model.user.pojosconfiguration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

js语法+dom+js图片库+最佳实践+图片库改进版

【2】js语法 【2.2.4】数据类型类型1&#xff09;字符串 var mood happy; var moood "happy"; 类型2&#xff09;数值&#xff1b; var age 33.24; 类型3&#xff09;布尔值&#xff1b;var married true; 【2.2.5】数组1&#xff09;填充方式 填充方式1&#xf…

RabbitMQ--topic

Topic类型的Exchange与Direct相比&#xff0c;都是可以根据RoutingKey把消息路由到不同的队列。只不过Topic类型Exchange可以让队列在绑定Routing key 的时候使用通配符&#xff01; Routingkey 一般都是有一个或多个单词组成&#xff0c;多个单词之间以”.”分割&#xff0c;…

阿里巴巴对Java编程【控制语句】的规约

转载自 阿里巴巴对Java编程【控制语句】的规约控制语句1. 【强制】在一个 switch 块内&#xff0c;每个 case 要么通过 break / return 等来终止&#xff0c;要么注释说明程序将继续执行到哪一个 case 为止 &#xff1b; 在一个 switch 块内&#xff0c;都必须包含一个 default…

RabbitMQ消息

如何确保RabbitMQ消息的可靠性&#xff1f; 开启生产者确认机制&#xff0c;确保生产者的消息能到达队列开启持久化功能&#xff0c;确保消息未消费前在队列中不会丢失开启消费者确认机制为auto&#xff0c;由spring确认消息处理成功后完成ack开启消费者失败重试机制&#xff…

阿里巴巴对Java编程【注释规约】的规约

转载自 阿里巴巴对Java编程【注释规约】的规约注释规约1. 【强制】类、类属性、类方法的注释必须使用 Javadoc 规范&#xff0c;使用/**内容*/格式&#xff0c;不得使用// xxx 方式。 说明&#xff1a;在 IDE 编辑窗口中&#xff0c; Javadoc 方式会提示相关注释&#xff0c;生…

动态创建标记+css_dom+js动态效果

【7】动态创建标记【7.1】一些传统方法【7.1.1】document.write方法&#xff0c;不推荐使用 &#xff08;1&#xff09;<!DOCTYPE html> <html lang"en"> <head><meta http-equiv"content-type" content"text/html; charsetutf-…

orcle安装及用户初始化

1.orcle资源 orlce安装包点击下载 2.首次安装 参照: https://jingyan.baidu.com/article/f79b7cb32095f79144023eae.html 3.卸载后安装 先卸载清除本地的orcle服务 参照: https://jingyan.baidu.com/article/6b18230943e9d7fb59e1590f.html再重新下载资源解压安装’’ 注意…

JDK8新特性之Lambda表达式

转载自 JDK8新特性之Lambda表达式 什么是Lambda表达式 Java 8的一个大亮点是引入Lambda表达式&#xff0c;使用它设计的代码会更加简洁。当开发者在编写Lambda表达式时&#xff0c;也会随之被编译成一个函数式接口。 Lambda语法 一行执行语句的写法&#xff1a; (paramete…

eclipse发布web项目到tomcat服务器

README: 使用eclipse发布web项目到tomcat有很多坑儿的。下面依依道来。 step1&#xff09;eclipse建立web 项目&#xff1a;step2&#xff09;在tomcat服务器上为该web项目配置的虚拟目录&#xff0c;即把该web项目发布到tomcat&#xff1a; tomcat的server.xml 增加如下语句&…

springboot设置默认端口访问界面

1.项目结构 2.配置方法 <1>配置类默认加载 Configuration public class WebConfigurer implements WebMvcConfigurer {Overridepublic void addViewControllers(ViewControllerRegistry registry) {//默认地址&#xff08;可以是页面或后台请求接口&#xff09;registr…

Java BigDecimal和double区别

转自&#xff1a; https://www.cnblogs.com/mingforyou/p/3344489.htmlBigDecimal类 对于不需要任何准确计算精度的数字可以直接使用float或double&#xff0c;但是如果需要精确计算的结果&#xff0c;则必须使用BigDecimal类&#xff0c;而且使用BigDecimal类也可以进行大数的…