spring (Aop) day 1024
ok了家人们,继续学习spring ,这段知识点有点绕,建议搭配b站的视频去学,passion!!!https://i-blog.csdnimg.cn/direct/69fc55ceb92c4ef0abb639db92bec81f.png
八.AOP-面向切面编程
8.1 动态署理
8.1.1 概述
什么是署理?在现实生活中,署理很常见。比如,我们必要去 某个地方,必要自己开车已往,但是公交车师傅的出现替代了 我们自己开车的这个步骤,我们只必要乘坐公交车就可以到达 我们必要到达的目标地。这就是一种署理方式。 用自己的话总结一下:署理就是原本必要自己去完成的工作可 以由一个新的替代者来完成,这就是署理的一种表现。 在 Java 中就是在不侵入原来代码的情况下完成功能的增强。 Java 中常见署理分类:静态署理,基于 JDK 的动态署理,基于 CGlib 的动态署理 8.1.2 静态署理
举个例子:现实中明星都是有经纪人的,经纪人的作用就是帮 明星来处置惩罚业务,节目组要找明星来表演,那么肯定不是和明 星直接接洽的,而是明星的经纪人出头和节目组洽谈,而且只 有当业务条件满足时,明星才会出头表演,那么这个经纪人就 是明星的署理人,他可以帮助明星来筛选演出业务,条件达不 到要求的表演就过滤了,只有达到要求了,才会被经纪人接收 然后明星就会出场表演,也就是拦截了外界对明星的直接访 } 问。这也是 JAVA 中署理对象的作用,产生一个署理对象用来 拦截外界对真实业务的访问。 可以看出,经纪人会将外界给明星的信息进行拦截、处置惩罚,这 就是我们常说的署理模式。 静态署理的实现方式
[*] 经纪人和明星在一些行为上有共同点,共同的目标,以是 定义一个共有接口: start 接口 ( 参加节目 ) 。
[*] 明星实现接口的行为,因为真正出力的是明星。
[*] 经纪人要代表明星,就必要和明星有同样的行为,同时持 有明星的引用 ( 经纪人能找到明星 ) 。
[*] 调用方要找明星之前,先找到经纪人。由经纪人出头进行 谈判,比如参加节目前片酬,比如参加节目后发的微博。
静态署理的代码实现
[*]接口
public interface FindWife {
public void findWife(String message);
}
[*] 署理类
public class BuyHouseProxy implements BuyHouse{
//定义一个 有买房需求的
private BuyHouse buyHouse;
//在创建代理对象的时候 得有委托人
public BuyHouseProxy(BuyHouse buyHouse) {
this.buyHouse = buyHouse;
}
@Override
public void buyHouse() {
//代理人 帮助 委托人
System.out.println("我是中介,帮您买房,先付款...");
buyHouse.buyHouse();//最终 委托人买方法
System.out.println("将信息偷偷滴卖给装修的人...");
}
}
[*]署理类
package com.cjx.service;
//买房的中介 代理
public class BuyHouseProxy implements BuyHouse{
//定义一个 有买房需求的
private BuyHouse buyHouse;
public BuyHouseProxy(BuyHouse buyHouse) {
this.buyHouse = buyHouse;
}
@Override
public void buyHouse() {
//代理人 帮助 委托人
System.out.println("我是中介,帮您买房,先付款...");
buyHouse.buyHouse();//最终 委托人买方法
System.out.println("将信息偷偷滴卖给装修的人...");
}
}
package com.cjx.service;
//婚介中心
public class FindWifeProxy implements FindWife{
//定义一个 有找媳妇需求的人
private FindWife findWife;
public FindWifeProxy(FindWife findWife) {
this.findWife = findWife;
}
@Override
public void findWife(String message) {
System.out.println("我们这里是百合网,交会员费,提供优质服务,按照您的条件找");
findWife.findWife(message);
System.out.println("祝你幸福...");
System.out.println("又把信息卖给了婚纱摄影");
}
}
[*]被署理类
package com.cjx.service;
//消费者 委托人
public class Customer implements BuyHouse,FindWife{
@Override
public void buyHouse() {
System.out.println("看房源...");
System.out.println("谈价格...");
System.out.println("过户...");
}
@Override
public void findWife(String message) {
System.out.println("要求是:"+message);
System.out.println("主要是暖被窝...");
}
}
[*]测试
package com.cjx.test;
import com.cjx.service.*;
import org.junit.Test;
public class DemoTest {
@Test
public void test01(){
//没有代理的时候 自己买方房
Customer customer = new Customer();
customer.buyHouse();
customer.findWife("xx");
System.out.println("-------------------");
//找中介 代理人
BuyHouseProxy buyHouseProxy = new BuyHouseProxy(customer);
buyHouseProxy.buyHouse();
System.out.println("-------------------");
//找媳妇 代理人
FindWifeProxy findWifeProxy = new FindWifeProxy(customer);
findWifeProxy.findWife("迪丽热巴..。");
System.out.println("-------------------");
// 动态代理
BuyHouse proxy = (BuyHouse) JDKProxy.getProxy(customer);
proxy.buyHouse();
}
}
从实现上可以看到 署理 的主要作用是 方法增强,它可以在不 “惊动”被署理类的情况下修改被署理类的行为。这有助于体系 解耦。我们这里署理类和被署理类都是自己亲身敲好的,即在 步伐运行前署理类的.class文件就已经存在了的形式叫做静态 署理。值得留意的是,署理类和被署理类应该共同实现一个接 口,或者是共同继承某个类。 静态署理的缺点
[*] 我们必要在运行前手动创建署理类,这意味着如果有很多 署理的话会很繁琐,一个被署理就必要一个署理;
[*] 其次署理类 和 被署理类 必须实现同样的接口,万一接口 有变更,署理、被署理类都得修改,容易出题目;
8.1.3 动态署理
动态署理 与 静态署理 最大的区别就是不消我们创建那么多 类,敲那么多代码。在步伐运行时,运用反射机制动态创建而 成。 JDK 中为我们提供了 Proxy 类来实现动态署理,其中最紧张的 方法是 Proxy.newProxyInstance :只必要我们传入相干信 息,它就可以返回我们必要的署理对象。 Object obj = Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h) 上面的方法可以实现动态的拦截被署理类的方法,也就是说我 们无需定义署理类,由该方法自动产生可以实当署理的对象。 package com.cjx.service;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JDKProxy {
//获取代理对象的方法 参数:被代理对象 委托人
//传入被代理人 返回一个代理对象
public static Object getProxy(Object obj) {
Object proxy = null;
proxy = Proxy.newProxyInstance(
obj.getClass().getClassLoader(),
//类加载器,将生成的代理类加载到内存中
obj.getClass().getInterfaces(),
// 生成的代理类需要实现的接口的字节码
//invoke:此invoke方法并非是反射的invoke方法,此方法属于动态代理,
//调用代理类的哪个方法,invoke就表示那个方法
new InvocationHandler() {
/*
proxy:代理对象
method:委托人需要增强的方法
args:要实现功能的参数
Object:被增强方法的返回值
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws
Throwable {
Object result = null;
//这个方法来自哪个接口定义的方法
Class<?> clazz =
method.getDeclaringClass();
if (clazz ==
BuyHouse.class) {
//我们应该根据委托人不同的需求,执行不同的功能增强
String methodName =
method.getName();
if
(methodName.equalsIgnoreCase("buyHouse")) {
//对买房的操作进行增强
System.out.println("我是中介,帮您买房,先付款...");
result =
method.invoke(obj, args);
System.out.println("将信息偷偷滴卖给装修的人...");
}
}
if (clazz == FindWife.class) {
//我们应该根据委托人不同的需求,执行不同的功能增强
String methodName = method.getName();
if (methodName.equalsIgnoreCase("findWife")) {
System.out.println("我们这里是百合网,交会员费,提供优质服务,按照您的条件找");
result = method.invoke(obj, args);
System.out.println("又把信息卖给了婚纱摄影");
}
}
return result;
}
}
);
return proxy;
}
}
明星与经纪人的关系先容了静态署理,欠好之处在于一个经纪 人只能署理一个明星,一旦明星有变更,或者想要署理其他明 星时,必要修改、创建经纪人,大量使用这种静态署理,会使 我们体系内的类的规模增大,并且不易维护; 而动态署理模式,大大减少类的创建、修改本钱。此外动态代 理还符合 AOP ( 面向切面编程 ) 头脑,在很多场所都有使用。 通过讲授可以得出结论 : 署理模式主要用于扩展原功能又不侵 入(修改)源代码。 ( 增强功能 ) 8.1.4 cglib署理
JDK 的动态署理已经很好的帮我们完成了署理工作,那么, CGlib 动态署理又有什么好用的特征呢? 我们可以看出,静态署理和基于 JDK 的动态署理的被署理类都 必要实现接口,那么,如果我的类没有实现接口,但是,我想 给它设置署理,那么,应该怎么实现呢? 这时候,就轮到 CGlib 出场了, CGlib 可以署理没有接口的类。 我们必要在 pom 文件中引入 CGlib 的 jar 包。 <!--https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.9</version>
</dependency> cglib 动态署理其实就是把原有对象传进去进行方法拦截,拦 截到之后进行逻辑增强。 8.1.5 署理总结
使用署理技能就是为了帮我们在不入侵原有代码的情况下增强 业务逻辑! 你完全可以使用静态署理一个一个去定义署理类,但是如许的 话太过于繁琐,而且有些情况下你不知道未来会有什么接口 (比如咱们的 Mybatis ,你现在有个 UserMapper.java ,以后 还可能有更多其他的 Mapper 接口,这些都是不确定的),所 以就是用动态署理去给他们天生署理对象吧 有接口就用 JDK 动态署理,没有接口就用 CGLIB 动态署理 8.2 AOP概述
OP 为 Aspect Oriented Programming 的缩写,意为:面向切 面编程,通过 运行期动态 实现步伐功能的统一维护的一种技 术。 AOP 是 OOP 的延续,是软件开发中的一个热门,也是 Spring 框架中的一个紧张内容。 AOP 弥补了 OOP 的不足,基于 OOP 底子上进行横向开发; OOP 步伐开发以类为主题模子,一切围绕对象进行,通过构 建模子来完成任务; AOP 步伐的开发主要关注 OOP 开发中的 共性功能,一切围绕共性功能进行。 AOP 通过接纳横向抽取机制,已经完全代替了传统纵向继承体 系重复性代码的编写方式 AOP 的底层封装了动态署理,我们可以理解为 AOP 是动态署理 的一种简单写法,进行功能的增强! 简单理解: AOP 就是实现业务代码和逻辑代码的解耦,但在运 行时又可以织入在一起。 场景 名称 场景描述 变乱控制 批量的为业务对象,增加变乱处置惩罚的功能,不消 每个对象都去办变乱处置惩罚 记录跟踪 批量的为业务对象,增加日记记录的功能,不消 在每个方法执行进行日记记录 权限控制 批量的为业务对象,增加权限查抄,不消每个业 务对象都做安全查抄 异常处置惩罚 批量的为业务对象,增加异常情况的处置惩罚功能, 不消在每个方法执行进行异常处置惩罚 参数校验 批量的为业务对象,增加各种入参的查抄,不消 在方法内部进行检 等等 ..... 8.3 AOP 相干概念
[*] Joinpoint( 连接点 ) : 所谓连接点是指那些被拦截到的点。 在 spring 中 , 这些点指的是方法 , 因为 spring 只支持方法范例 的连接点。
[*] Pointcut( 切入点 ) : 所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义。比方:我们设置 save 方法就是切入点
[*] Advice( 关照 / 增强 ) : 所谓关照是指拦截到 Joinpoint 之后所 要做的事情就是关照。 关照的范例:前置关照 , 后置关照 , 异 常关照 , 最终关照 , 环绕关照。 比方: @Before 前置关照 : 在 切入点运行前执行。
[*] Target( 目标对象 ) : 署理的目标对象。举例: UserServiceImpl ,他就是目标对象,因为它里面的功能需 要被增强。
[*] Weaving( 织入 ) : 就是代码运行的时候 业务代码和逻辑代码 织入到一起执行。比方:把 before 方法在 save 方法执行之 前执行了这就是织入。
[*] Aspect( 切面 ) : 是切入点和关照的结合。切面 = 切入点 + 方位 信息 + 增强逻辑,比方: AopAdvice 类 是一个切面类
[*] Proxy(署理): 一个类被AOP织入增强后,就产生一个结果 署理类。比方: AccountServiceImpl 它的功能在增强的时 候,现实是天生了署理对象增强的。
8.4 AOP案例
8.4.1 需求分析
需求:任意业务层接口执行前后获取当前体系时间 分析:
[*]原始步伐中将共性功能抽取出来独立制作成方法放在特定类中
[*]定义带有共性功能的方法名
[*]将抽取出来的共性功能与对应的方法名之间绑定关系
8.4.2 情况准备
[*]实体类
package com.cjx.pojo;
import org.springframework.stereotype.Component;
@Component
public class User {
private int id;
private String name;
//省略get set方法
}
[*]设置类
package com.cjx.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
//Spring配置文件
@Configuration
@ComponentScan("com.cjx")
@EnableAspectJAutoProxy
public class SpringConfig {
}
[*]关照类
public class AopAdvice {
public void before(){
System.out.println("当前方法执行前初始时间毫
秒值是:"+System.currentTimeMillis());
}
public void after(){
System.out.println("当前方法执行后初始时间毫
秒值是:"+System.currentTimeMillis());
}
}
[*]业务层接口和实现类
package com.cjx.service;
import com.cjx.pojo.User;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserServiceImpl implements UserService{
@Override
public void save(User user) {
//System.out.println("当前方法执行前初始时间毫秒值是:"+System.currentTimeMillis());
System.out.println("保存了用户数据~");
//System.out.println("当前方法执行后初始时间毫秒值是:"+System.currentTimeMillis());
}
@Override
public void update(User user) {
//System.out.println("当前方法执行前初始时间毫秒值是:"+System.currentTimeMillis());
System.out.println("更新了用户数据~");
//System.out.println("当前方法执行后初始时间毫秒值是:"+System.currentTimeMillis());
}
@Override
public void delete(String id) {
System.out.println("删除了用户数据~");
}
@Override
public List<User> findAll() {
System.out.println("查询了用户数据~");
return null;
}
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes =
{SpringConfig.class})
public class DemoTest {
@Autowired
private UserService userService;
@Test
public void test01(){
userService.save(new User());
}
} 8.4.3 AOP实现
[*]导入aspect坐标
<dependency>
<groupId>org.springframework</groupId>
<artifactId>springaspects</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
[*] 将共性功能抽取出来制作成独立的关照方法写入关照类 中。创建一个新的类,将共性功能提取出来制作成独立的 方法
/*
通知类
*/
public class AopAdvice {
public void before(){
System.out.println("当前方法执行前初始时间毫秒值是:"+System.currentTimeMillis());
}
public void after(){
System.out.println("当前方法执行后初始时间毫秒值是:"+System.currentTimeMillis());
}
}
[*]将共性功能抽取的位置定义成切入点,写入关照类中
/*
通知类
切入点:@Pointcut
你想增强哪个方法,那个方法就当做切入点,切入点在通知
类中这样配置
void
com.lzw.service.UserServiceImpl.save(com.lzw.poj
o.User)
*/
public class AopAdvice {
//配置切入点 是用来找到需要被增强的方法的
@Pointcut("execution(void
com.lzw.service.UserServiceImpl.save(com.lzw.poj
o.User))")
public void pt(){
}
}
[*]绑定原始操作与被抽取的功能之间的关系
package com.cjx.aop;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import java.util.Date;
/*
通知类
切入点:@Pointcut
你想增强哪个方法,那个方法就当做切入点,切入点在通知
类中这样配置
void
com.lzw.service.UserServiceImpl.
save(com.lzw.pojo.User)
@Component 我这个通知类交给spring管理
@Aspect 当前类为切面类 里面包含 切入点+方位信息
+增强逻辑 目的是spring底层自动完成织入
*/
@Component
@Aspect
public class AopAdvice {
/*
before after 使我们抽出来的 逻辑代码
在aop的术语中 这两个方法 叫做通知方法
在 pt 切入点 方法执行之前 执行 before功能
*/
//配置切入点 是用来找到需要被增强的方法的
@Pointcut("execution(void com.cjx.service.UserServiceImpl.save(com.cjx.pojo.User))")
public void pt(){
}
@Pointcut("execution(void com.cjx.service.UserServiceImpl.update(com.cjx.pojo.User))")
public void update(){
}
@Before("pt()")
public void before(){
System.out.println("当前方法执行前初始时间毫秒值是:"+System.currentTimeMillis());
}
@After("pt()")
public void after(){
System.out.println("当前方法执行后初始时间毫秒值是:"+System.currentTimeMillis());
}
@Before("update()")
public void before01(){
System.out.println("当前方法执行的时间是:"+new Date());
}
@After("update()")
public void after01(){
System.out.println("当前方法执行的时间是:"+new Date());
}
}
[*]在Spring设置类上声明开启AOP功能
package com.cjx.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
//Spring配置文件
@Configuration
@ComponentScan("com.cjx")
@EnableAspectJAutoProxy
public class SpringConfig {
}
8.4.4 工作流程分析
[*]Spring容器启动,加载bean
[*]启动AOP,加载所有的切入点
[*] bean 工作的过程中, AOP 内部监听机制不停监听设置的切 入点是否运行
[*] 当切入点对应的对象(目标对象)执行对应的切入点方法 时,创建目标对象的署理对象
[*]动态将关照内容织入到署理对象对应的方法中
[*]最终由署理对象完成最终工作
package com.cjx.test;
import com.cjx.config.SpringConfig;
import com.cjx.pojo.User;
import com.cjx.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.sql.PreparedStatement;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class UserTest {
@Autowired
private User user;
@Autowired
private UserService userService;
@Test
public void test01(){
userService.save(user);
userService.update(user);
userService.delete("1");
userService.findAll();
}
}
ok了家人们,see you later
https://i-blog.csdnimg.cn/direct/84082e4186d146d1a33d76ba399cd2f8.png
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页:
[1]