先看一个例子:
声明一个接口:
// + - * / 运算的标准接口!
public interface Calculator {int add(int i, int j);int sub(int i, int j);int mul(int i, int j);int div(int i, int j);
}
实现该接口:
package com.sunsplanter.proxy;/*** 在每个方法中,输出传入的参数和计算后的返回结果!*/
public class CalculatorLogImpl implements Calculator {@Overridepublic int add(int i, int j) {System.out.println("参数是:" + i + "," + j);int result = i + j;System.out.println("方法内部 result = " + result);return result;}@Overridepublic int sub(int i, int j) {System.out.println("参数是:" + i + "," + j);int result = i - j;System.out.println("方法内部 result = " + result);return result;}@Overridepublic int mul(int i, int j) {System.out.println("参数是:" + i + "," + j);int result = i * j;System.out.println("方法内部 result = " + result);return result;}@Overridepublic int div(int i, int j) {System.out.println("参数是:" + i + "," + j);int result = i / j;System.out.println("方法内部 result = " + result);return result;}
}
存在的问题:
目标: 将重复的代码统一提取,并且[[动态插入]]到每个业务方法!
用代理模式解决:
相关术语:
解决问题的思维:AOP
解决问题技术:代理技术
代理技术太麻烦,因此使用框架
Spring AOP框架(底层是代理技术:jdk动态daili,cglib)
代理在开发中实现的方式具体有两种:静态代理,[动态代理技术]
缺点:静态代理确实实现了解耦,但是由于代码都写死了,完全不具备任何的灵活性。就拿sout来说,将来其他地方也需要sout,那还得再声明更多个静态代理类,那就产生了大量重复的代码,sout还是分散的,没有统一管理。
项目结构:
原有代码不变,
使用静态代理即主动创建一个代理类CalculatorStaticProxy实现接口,:
public class CalculatorStaticProxy implements Calculator {// 将被代理的目标对象声明为成员变量(令代理类与被代理对象建立关系)private Calculator target;//将代理对象传入构造方法,“注入”public CalculatorStaticProxy(Calculator target) {this.target = target;}@Overridepublic int add(int i, int j) {// 附加功能由代理类中的代理方法来实现System.out.println("参数是:" + i + "," + j);// 通过目标对象来实现核心业务逻辑int addResult = target.add(i, j);System.out.println("方法内部 result = " + result);return addResult;}@Overridepublic int sub(int i, int j) {// 附加功能由代理类中的代理方法来实现System.out.println("参数是:" + i + "," + j);// 通过目标对象来实现核心业务逻辑int subResult = target.sub(i, j);System.out.println("方法内部 result = " + result);return subResult;}……
提出进一步的需求:将sout集中到一个代理类中,将来有任何日志需求,都通过这一个代理类来实现。这就需要使用动态代理技术了。
动态代理技术分类
JDK动态代理技术实现(了解)
代理工程:基于jdk代理技术,生成代理对象
在这里我们不给出代理的具体实现代码,后续会学习Spring AOP框架,该框架封装动态代理技术,们只需写少量的配置,指定生效范围即可,即可完成面向切面思维编程的实现!
OOP引入封装、继承、多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合。OOP允许开发者定义纵向的关系。但在OOP设计中,它导致了大量公共代码的重复,而不利于各个模块的重用。
AOP技术恰恰相反,它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。
使用AOP,可以在不修改原来代码的基础上添加新功能。
AOP思想主要的应用场景
AOP术语名词介绍
1-横切关注点
从每个方法中抽取出来的同一类非核心业务。在同一个项目中,我们可以使用多个横切关注点对相关方法进行多个不同方面的增强。
有十个附加功能,就有十个横切关注点。
AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处基本相似,比如权限认证、日志、事务、异常等。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。
2-通知(增强)
每一个横切关注点上要做的事情都需要写一个方法来实现,这样的方法就叫通知方法。就是具体你要织入的代码。
3-切入点 pointcut
定位连接点的方式,或者可以理解成被选中的连接点!
是一个表达式,比如execution(* com.spring.service.impl..(…))。符合条件的每个方法都是一个具体的连接点。
4-切面 aspect
切入点和通知的结合。是一个类。
3-连接点 joinpoint
这也是一个纯逻辑概念,不是语法定义的。
指那些被拦截到的点。在 Spring 中,可以被动态代理拦截目标类的方法
6-目标 target
被代理的目标对象。
7-代理 proxy
向目标对象应用通知之后创建的代理对象。
8-织入 weave
指把通知应用到目标上,生成代理对象的过程。可以在编译期织入,也可以在运行期织入,Spring采用后者。
切入点表达式语法格式:
execution([访问控制权限修饰符] 返回值类型 [全限定类名]方法名(形式参数列表) [异常])
访问控制权限修饰符:
返回值类型:
全限定类名:
方法名:
必填项。
*表示所有方法。
set*表示所有的set方法。
r
形式参数列表:
必填项
() 表示没有参数的方法
(…) 参数类型和个数随意的方法
(*) 只有一个参数的方法
(*, String) 第一个参数类型随意,第二个参数是String的。
异常:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns=""xmlns:xsi=""xmlns:context=""xmlns:aop=""xsi:schemaLocation=" .xsd .xsd .xsd"><!--开启组件扫描--><context:component-scan base-package="com.powernode.spring6.service"/><!--开启自动代理后,凡是带有@Aspect注解的bean都会生成代理对象。proxy-target-class是可选属性,默认为false时采用JDK动态代理,但即便是最坏的情况,即采用JDK动态代理,而类又没有实现接口,Spring仍会自动转向cglib动态代理--><aop:aspectj-autoproxy proxy-target-class="true"/>
</beans>
</beans>
package com.sunsplanter.spring6.service;// 目标类
@Component
public class OrderService {// 目标方法public void generate(){System.out.println("订单已生成!");}
}
第二步:定义切面类, 纳入Spring管理
package com.sunsplanter.spring6.service;import org.aspectj.lang.annotation.Aspect;// 切面类的注解
@Aspect
@Component
public class MyAspect {
}
第三步:在Spring配置文件中添加组建扫描
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns=""xmlns:xsi=""xmlns:context=""xmlns:aop=""xsi:schemaLocation=" .xsd .xsd .xsd"><!--开启组件扫描--><context:component-scan base-package="com.powernode.spring6.service"/>
</beans>
第四步: 在切面类中定义一个切面
package com.sunsplanter.spring6.service;import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import org.aspectj.lang.annotation.Aspect;// 切面类
@Aspect
@Component
public class MyAspect {//通知+切点=切面// 切点表达式,@Before即前置通知@Before("execution(* com.powernode.spring6.service.OrderService.*(..))")// 这就是需要增强的代码(通知)public void advice(){System.out.println("我是一个通知");}
}
第五步:测试程序:
package com.st;import com.sunsplanter.spring6.service.OrderService;
import org.junit.Test;
import t.ApplicationContext;
import t.support.ClassPathXmlApplicationContext;public class AOPTest {@Testpublic void testAOP(){ApplicationContext applicationContext = new ClassPathXmlApplicationContext(l");OrderService orderService = Bean("orderService", OrderService.class);ate();}
}
目标:横向插入增强代码(日志输出/)
本文发布于:2024-01-27 20:56:01,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/17063601592588.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |