Spring中AOP实现;代理模式

2024-09-03 04:12

本文主要是介绍Spring中AOP实现;代理模式,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一,介绍代理模式:

        1.代理模式 设计模式的一种,解决某一类问题的产生 分为:

                             静态代理

                            动态代理【JDK动态代理、CGLIB动态代理】

        2.代理模式的角色

                 (1).抽象对象(抽象父类或接口):需要完成的功能

                 (2).被代理对象:隐藏起来的对象

                 (3).代理对象:暴露给其他人的对象,访问被代理对象通过代理对象进行访问

        3.代理模式案例:我们找中介租房 1.抽象对象: 租房 2.被代理对象:房东 3.代理对象:中介(做了额外事情,包房东保护起来) 4.调用者:我们

        4.代理模式的好处 :

                (1).被代理对象可以专注完成自己的业务(房东安心的做自己的事情即可,不用管理其他的事情)

                 (2).保护了被代理对象(房东对象比较的安全)

                 (3).增强了代码的扩展性

        5.静态代理缺点: .随着房东/被代理对像的增多,中介的压力就会越来越大,体现到代码上就是代码越来越臃肿

        6,实现静态代理的步骤

                      (1), 创建抽象对象: Zufang接口

public interface Zufang {/** 需要完成的事情*/void zufang();
}

                        (2)创建被代理对象Fangdong

public class Fangdong implements Zufang {public void zufang() {System.out.println("出租五环科技园C座c01");}
}

                          (3)创建代理对象Zhongjie

public class Zhongjie implements Zufang {//房东人:张三private Fangdong fangdong = new Fangdong();@Overridepublic void zufang() {System.out.println("收房东的中介费");fangdong.zufang();System.out.println("收我们的中介费");}
}

                        (4)创建调用者Women

public class Women {    public static void main(String[] args) {        //小王        Zhongjie zhongjie = new Zhongjie();        zhongjie.zufang();    }}

二,实现AOP:第一阶段的静态代理

1.创建接口UserService

void add() throw Exception;
void delet() throw Exception;

2.创建接口实现类UserServiceImpl

public void add() throw Exception{check();System.out.println("添加成功");log();
}
public  void  check(){System.out.println("权限检查");
}
public  void  log(){System.out.println("日志记录");
}

3.编写测试方法

 ApplicationContext app=new ClassPathXmlApplicationContext("applicationTest.xml");UserService UserService=(UserService)app.getBean("UserService");UserService.add();

4.存在问题:

第一阶段的静态代理,在UserServiceImpl实现类中业务代码严重的与横切性代码耦合在一起了。

解决办法:将横切性代码提取出来,与业务代码 分开存放。

三,实现AOP:第二阶段的静态代理

1.创建一个静态代理类:

public class UserServiceProxy implement UserService{private UserServiceImpl usi;//静态代理,指明代理的是谁。public UserServiceProxy(UserServiceImpl userServiceImpl){this.usi=userServiceImpl;   }public void add() throw Exception{check();usi.add();log();}public  void  check(){System.out.println("权限检查");}public  void  log(){System.out.println("日志记录");}
​
}

com.jr.proxy 包下创建创建UserServiceProxy类 实现跟UserServiceImpl类一样的接口

2.实现类UserServiceImpl代码改动

public void add() throw Exception{  System.out.println("添加成功");
}

3.存在问题:

        第二阶段的静态代理,将UserServiceImpl实现类中的横切性代码提取出来 放到代理类里,在代理类里做整合。

        那么一个service接口就要写一个代理类,如果Service接口特别多的话,程序中将多出非常多的代理类。

        解决办法:需要一个代理类,能代理所有实现接口的实现类。这就需要下面的动态代理

四:AOP实现:第三阶段:动态代理实现AOP

1,动态代理简介:

        (1).动态代理的分类:

                 JDK动态代理---代理拥有接口的实现类

                 cglib动态代理---代理拥有父类的子类

        (2).动态代理:

                         底层是根据反射实现的.

                         只要给代理对象传递被代理对象,直接调用被代理对象中方法

         (3).动态代理中动态含义:

                        代理类可以代理任意类型对象.(被代理对象必须实现了指定接口)

2,JDK动态代理实现AOP

创建动态代理类:

//使用动态代理时需要实现InvocationHandler,因为在调用者中通过接口调用方法时知道需要执行哪个方法
public class JDKProxy implements InvocationHandler {
​private Object targetObject;//代理的目标对象       //产生代理对象:public Object createProxyInstance(Object targetObject){this.targetObject=targetObject;
/*** 参数一     ClassLoader   类加载器* 参数二:   Interfaces  接口类型* 参数三:   InvocationHandler  代理对象* */return Proxy.newProxyInstance(this.targetObject.getclass().getClassLoader(),this.targetObject.getclass().getInterfaces(),this);}/** 三个参数*      1.proxy:就是代理对象本身*      2.method:代理对象调用的方法,被封装为的对象。简单说 谁在运行调用,这个method就是谁。*      3.args:代理对象调用方法时,传递的实际参数*/public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {check();Object invoke = method.invoke(this.targetObject, args);log();return invoke;}
​public  void  check(){System.out.println("权限检查");}public  void  log(){System.out.println("日志记录");}
​
}

 底层代码实现

public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) throws IllegalArgumentException{
​
// 浅克隆一份接口的class文件数组,数组是另一块内存空间;但数组里面的class还是同一个class,都是指向同一块内存地址
final Class<?>[] intfs = interfaces.clone();
​
// 查找或生成指定的代理类class文件,下面在说这个方法
Class<?> cl = getProxyClass0(loader, intfs);
​
// 获取代理class的有参构造方法(参数就是InvocationHandler接口)
final Constructor<?> cons = cl.getConstructor(constructorParams);
​
final InvocationHandler ih = h;
// 此处判断构造方法是否是公开权限,如果不是则设置访问权限
if (!Modifier.isPublic(cl.getModifiers())) {AccessController.doPrivileged(new PrivilegedAction<Void>() {public Void run() {cons.setAccessible(true);return null;}});
}
​
// 反射实例化代理对象
return cons.newInstance(new Object[]{h});
}

创建测试类:​

    @Testpublic void show2(){//1.加载Spring的核心配置文件ApplicationContext applicationContext=new ClassPathXmlApplicationContext("ApplicationContext.xml");JDKProxy jdkProxy=applicationContext.getBean("proxy",JDKProxy.class);//给UserBiz接口创建临时代理对象userBizUserBiz userBiz=(UserBiz) jdkProxy.getTargetObj(new UserBizImpl());//----invoke方法是核心代理逻辑思想,代理对象调用的所有方法都会触发该方法执行---userBiz.add();}

存在问题:

        JDK动态代理的产生必须要实现对应的接口的,如果没有对应的接口,这个时候代理对象就没有办法产生。

 3,CGLIB动态代理实现AOP

创建父类

public abstract  class UserSuper {
​public  abstract   void add();
}

创建子类

public class UserSub extends UserSuper{public void add(){System.out.println("添加成功!");    }
}

创建动态代理类

        使用Enhancer类中的create方法 2.create方法的参数: Class:字节码 :它是用于指定被代理对象的字节码。 Callback:用于提供增强的代码, 它是让我们写如何代理。我们一般都是些一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的。 此接口的实现类都是谁用谁写。我们一般写的都是该接口的子接口实现类:MethodInterceptor

public class CGLibProxy implements MethodInterceptor{ //拦截器
​private Object targetObject;//代理的目标对象   //产生代理对象:public Object createProxyInstance(Object targetObject){this.targetObject=targetObject;Enhancer  en=new Enhancer();//该类用于生成子类的代理对象en.setSuperclass(this.targetObject.class);//被代理对象的字节码。en.setCallback(this); //指定回收对象return en.create();}
​public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {check(); Object o1 = methodProxy.invokeSuper(o, objects);log();return o1;}public  void  check(){System.out.println("权限检查");}public  void  log(){System.out.println("日志记录");}
}

创建测试类

 CGLBProxy cglbProxy=new CGLBProxy();UserBiz userBiz=(UserBiz) cglbProxy.createProxyInstance(new UserBizImpl());userBiz.upd();

存在问题:

        横切行代码最好还是存放在一个单独的类里面去,然后借助动态代理,在作用在代理类对象上。

        Spring提供了两种切面声明方式,实际工作中我们可以选择其中一种: ​ A) 基于XML配置方式声明切面 ​ B) 基于注解方式声明切面

五:第四阶段:基于XML配置方式声明切面

1.添加依赖

  <dependencies><!--进行junit单元测试 --><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.11</version><scope>test</scope></dependency>
​<!--依赖于commons-logging日志管理 --><dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.2</version></dependency>
​<!--提供了框架的基本组成部分,包括IOC 和 DI--><dependency><groupId>org.springframework</groupId><artifactId>spring-core</artifactId><version>4.1.6.RELEASE</version></dependency>
​<!-- 提供了BeanFactory--><dependency><groupId>org.springframework</groupId><artifactId>spring-beans</artifactId><version>4.1.6.RELEASE</version></dependency>
​<!--上下文配置对象,提供一个框架式的对象访问方式--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>4.1.6.RELEASE</version></dependency>
​<!--提供了强大的表达式语言--><dependency><groupId>org.springframework</groupId><artifactId>spring-expression</artifactId><version>4.1.6.RELEASE</version></dependency>
​
<!-- ====注解式声明切面  --><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.1</version></dependency></dependencies>

2.定义切面类:

public class Advice {public void check(){System.out.println("权限检查");}public void log(){System.out.println("日志记录");}
}

3.编写业务代码:

public interface UserService {void add() throws Exception;void delete() throws Exception;
}
public class UserServiceImpl implements UserService {public void add() throws Exception {System.out.println("添加User成功");}public void delete() throws Exception {System.out.println("删除User成功");}
}

4.编写applicationContext.xml配置文件

===引入一个命名空间: aop

  xmlns:aop="http://www.springframework.org/schema/aop"      http://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-4.3.xsd<!--创建业务实现类对象 --><bean id="usi" class="com.jr.aop.aspectj.UserServiceImpl"/><!--创建切面类对象 --><bean id="advice" class="com.jr.aop.aspectj.Advice"/><!-- <aop:config proxy-target-class="true"> 如果这样配置则是强制使用CGLIB方式进行代理不写或者设置为false默认使用 : jdk方式进行代理  不过即使proxy-target-class设置为false,如果目标类没有声明接口,则spring将自动使用CGLib动态代理。-->
<aop:config><aop:aspect ref="advice"><!--定义切点: --><aop:pointcut id="addpointcut" expression="execution(* com.jr.aop.aspectj.UserServiceImpl.add*(..))"/><!--定义通知 --><aop:before method="check" pointcut-ref="addpointcut"/><aop:after method="log" pointcut="execution(* com.jr.aop.aspectj.UserServiceImpl.add*(..))"/></aop:aspect></aop:config>

5.编写测试代码:

    @Testpublic void test5() throws Exception {ApplicationContext app=new ClassPathXmlApplicationContext("applicationAOP.xml");UserService userService=(UserService)app.getBean("usi");userService.add();userService.delete();}

运行结果: 权限检查 添加User成功 日志记录 删除User成功

六:第四阶段:基于注解方式声明切面

1.添加依赖

  <dependencies><!--进行junit单元测试 --><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.11</version><scope>test</scope></dependency>
​<!--依赖于commons-logging日志管理 --><dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.2</version></dependency>
​<!--提供了框架的基本组成部分,包括IOC 和 DI--><dependency><groupId>org.springframework</groupId><artifactId>spring-core</artifactId><version>4.1.6.RELEASE</version></dependency>
​<!-- 提供了BeanFactory--><dependency><groupId>org.springframework</groupId><artifactId>spring-beans</artifactId><version>4.1.6.RELEASE</version></dependency>
​<!--上下文配置对象,提供一个框架式的对象访问方式--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>4.1.6.RELEASE</version></dependency>
​<!--提供了强大的表达式语言--><dependency><groupId>org.springframework</groupId><artifactId>spring-expression</artifactId><version>4.1.6.RELEASE</version></dependency>
​
<!-- ====注解式声明切面  --><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.1</version></dependency></dependencies>   

2.定义切面类:

@Aspect
@Component
public class Advice {
​@Before("execution(* com.jr.aop.aspectj.UserServiceImpl.add*(..))")public void check(){System.out.println("权限检查");}
​@After("execution(* com.jr.aop.aspectj.UserServiceImpl.add*(..))")public void log(){System.out.println("日志记录");}
​
}

3.编写业务代码:

public interface UserService {void add() throws Exception;void delete() throws Exception;
}
@Service
public class UserServiceImpl implements UserService {public void add() throws Exception {System.out.println("添加User成功");}
​public void delete() throws Exception {System.out.println("删除User成功");}
}

4.编写applicationContext.xml配置文件

===引入两个命名空间:context 和 aop

    xmlns:aop="http://www.springframework.org/schema/aop"xmlns:context="http://www.springframework.org/schema/context"http://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-4.3.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-4.3.xsd<!-- 扫描指定路径,自动注入注解--><context:component-scan base-package="com.jr"/><!--自动为spring容器中那些配置@aspectJ切面的bean创建代理,有一个proxy-target-class属性,默认为false 那么标准的JDK 基于接口的代理将起作用。不过即使proxy-target-class设置为false,如果目标类没有声明接口,则spring将自动使用CGLib动态代理。--><aop:aspectj-autoproxy/>

5.编写测试代码:

   @Testpublic void test5() throws Exception {
​ApplicationContext app=new ClassPathXmlApplicationContext("applicationAOP.xml");UserService userService=(UserService)app.getBean("userServiceImpl");userService.add();userService.delete();}
 运行结果:权限检查添加User成功日志记录删除User成功

这篇关于Spring中AOP实现;代理模式的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1131987

相关文章

Java 正则表达式URL 匹配与源码全解析

《Java正则表达式URL匹配与源码全解析》在Web应用开发中,我们经常需要对URL进行格式验证,今天我们结合Java的Pattern和Matcher类,深入理解正则表达式在实际应用中... 目录1.正则表达式分解:2. 添加域名匹配 (2)3. 添加路径和查询参数匹配 (3) 4. 最终优化版本5.设计思

C#实现将Excel表格转换为图片(JPG/ PNG)

《C#实现将Excel表格转换为图片(JPG/PNG)》Excel表格可能会因为不同设备或字体缺失等问题,导致格式错乱或数据显示异常,转换为图片后,能确保数据的排版等保持一致,下面我们看看如何使用C... 目录通过C# 转换Excel工作表到图片通过C# 转换指定单元格区域到图片知识扩展C# 将 Excel

Java使用ANTLR4对Lua脚本语法校验详解

《Java使用ANTLR4对Lua脚本语法校验详解》ANTLR是一个强大的解析器生成器,用于读取、处理、执行或翻译结构化文本或二进制文件,下面就跟随小编一起看看Java如何使用ANTLR4对Lua脚本... 目录什么是ANTLR?第一个例子ANTLR4 的工作流程Lua脚本语法校验准备一个Lua Gramm

Java字符串操作技巧之语法、示例与应用场景分析

《Java字符串操作技巧之语法、示例与应用场景分析》在Java算法题和日常开发中,字符串处理是必备的核心技能,本文全面梳理Java中字符串的常用操作语法,结合代码示例、应用场景和避坑指南,可快速掌握字... 目录引言1. 基础操作1.1 创建字符串1.2 获取长度1.3 访问字符2. 字符串处理2.1 子字

Java Optional的使用技巧与最佳实践

《JavaOptional的使用技巧与最佳实践》在Java中,Optional是用于优雅处理null的容器类,其核心目标是显式提醒开发者处理空值场景,避免NullPointerExce... 目录一、Optional 的核心用途二、使用技巧与最佳实践三、常见误区与反模式四、替代方案与扩展五、总结在 Java

基于Java实现回调监听工具类

《基于Java实现回调监听工具类》这篇文章主要为大家详细介绍了如何基于Java实现一个回调监听工具类,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录监听接口类 Listenable实际用法打印结果首先,会用到 函数式接口 Consumer, 通过这个可以解耦回调方法,下面先写一个

使用Java将DOCX文档解析为Markdown文档的代码实现

《使用Java将DOCX文档解析为Markdown文档的代码实现》在现代文档处理中,Markdown(MD)因其简洁的语法和良好的可读性,逐渐成为开发者、技术写作者和内容创作者的首选格式,然而,许多文... 目录引言1. 工具和库介绍2. 安装依赖库3. 使用Apache POI解析DOCX文档4. 将解析

Qt中QGroupBox控件的实现

《Qt中QGroupBox控件的实现》QGroupBox是Qt框架中一个非常有用的控件,它主要用于组织和管理一组相关的控件,本文主要介绍了Qt中QGroupBox控件的实现,具有一定的参考价值,感兴趣... 目录引言一、基本属性二、常用方法2.1 构造函数 2.2 设置标题2.3 设置复选框模式2.4 是否

Java字符串处理全解析(String、StringBuilder与StringBuffer)

《Java字符串处理全解析(String、StringBuilder与StringBuffer)》:本文主要介绍Java字符串处理全解析(String、StringBuilder与StringBu... 目录Java字符串处理全解析:String、StringBuilder与StringBuffer一、St

C++使用printf语句实现进制转换的示例代码

《C++使用printf语句实现进制转换的示例代码》在C语言中,printf函数可以直接实现部分进制转换功能,通过格式说明符(formatspecifier)快速输出不同进制的数值,下面给大家分享C+... 目录一、printf 原生支持的进制转换1. 十进制、八进制、十六进制转换2. 显示进制前缀3. 指