spring基础概念AOP与 * 理解

作者:woonu 时间:2022-01-29 20:55:13 

一、代理模式

代理模式的英文叫做Proxy或Surrogate,中文都可译为”代理“,所谓代理,就是一个人或者一个机构代表另一个人或者另一个机构采取行动。在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

以简单模拟事务的执行过程说明各种代理区别

1.1 静态代理

由程序员创建或由特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。


public interface PersonDao {

void savePerson();
}

public class PersonDaoImpl implements PersonDao {

@Override
public void savePerson() {
 System.out.println("save person");
}
}


public class Transaction {

void beginTransaction(){
 System.out.println("begin Transaction");
}

void commit(){
 System.out.println("commit");
}
}

接下来编写静态代理类---实现PersonDao接口


/**
* 静态代理类
* @author qjc
*/
public class PersonDaoProxy implements PersonDao{

PersonDao personDao;
Transaction transaction;

public PersonDaoProxy(PersonDao personDao, Transaction transaction) {
 this.personDao = personDao;
 this.transaction = transaction;
}

@Override
public void savePerson() {
 this.transaction.beginTransaction();
 this.personDao.savePerson();
 this.transaction.commit();
}
}

测试


/**
* 测试静态代理
* @author qjc
*/
public class TestPersonProxy {

@Test
public void testSave(){
 PersonDao personDao = new PersonDaoImpl();
 Transaction transaction = new Transaction();
 PersonDaoProxy proxy = new PersonDaoProxy(personDao, transaction);

proxy.savePerson();
}
}

总结:

1、静态代理模式并没有做到事务的重用

2、假设dao有100个类,100个proxy,接口中有多少方法,在proxy层就得实现多少方法,有多少方法就要开启和提交多少事务

3、如果一个proxy实现了多个接口,如果其中的一个接口发生变化(添加了一个方法),那么proxy也要做相应改变

1.2 JDK *

* 类:在程序运行时,运用反射机制动态创建而成。

JDK的 * 必须具备四个条件:1、目标接口 2、目标类 3、 * 4、代理类

使用上个例子的PersonDao接口、PersonDaoImpl类及Transaction类

编写 *


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

/**
* *
*   1、目标类导入进来
*   2、事物导入进来
*   3、invoke完成:开启事务、调用目标对象的方法、事务提交
*
* @author qjc
*/
public class Interceptor implements InvocationHandler {

private Object target; // 目标类
private Transaction transaction;

public Interceptor(Object target, Transaction transaction) {
 this.target = target;
 this.transaction = transaction;
}

/**
 * @param proxy 目标对象的代理类实例
 * @param method 对应于在代理实例上调用接口方法的Method实例
 * @param args 传入到代理实例上方法参数值的对象数组
 * @return 方法的返回值,没有返回值是null
 * @throws Throwable
 */
public Object invoke(Object proxy, Method method, Object[] args)
  throws Throwable {
 String methodName = method.getName();
 if ("savePerson".equals(methodName)
   || "deletePerson".equals(methodName)
   || "updatePerson".equals(methodName)) {

this.transaction.beginTransaction(); // 开启事务
  method.invoke(target); // 调用目标方法
  this.transaction.commit(); // 提交事务

} else {
  method.invoke(target);
 }
 return null;
}
}

测试


/**
* 测试jdk *
* @author qjc
*/
public class TestJDKProxy {

@Test
public void testSave(){
 /**
  * 1、创建一个目标对象
  * 2、创建一个事务
  * 3、创建一个 *
  * 4、动态产生一个代理对象
  */
 Object target = new PersonDaoImpl();
 Transaction transaction = new Transaction();
 Interceptor interceptor = new Interceptor(target, transaction);
 /**
  * 参数一:设置代码使用的类加载器,一般采用跟目标类相同的类加载器
  * 参数二:设置代理类实现的接口,跟目标类使用相同的接口
  * 参数三:设置回调对象,当代理对象的方法被调用时,会调用该参数指定对象的invoke方法
  */
 PersonDao personDao = (PersonDao) Proxy.newProxyInstance(
   target.getClass().getClassLoader(),
   target.getClass().getInterfaces(),
   interceptor);
 personDao.savePerson();
}
}

总结

1、因为利用JDKProxy生成的代理类实现了接口,所以目标类中所有的方法在代理类中都有。

2、生成的代理类的所有的方法都拦截了目标类的所有的方法。而 * 中invoke方法的内容正好就是代理类的各个方法的组成体。

3、利用JDKProxy方式必须有接口的存在。

4、invoke方法中的三个参数可以访问目标类的被调用方法的API、被调用方法的参数、被调用方法的返回类型。

缺点:

1、在 * 中除了能调用目标对象的目标方法以外,功能是比较单一的,在这个例子中只能处理事务

2、 * 中的invoke方法的if判断语句在真实的开发环境下是不靠谱的,因为一旦方法很多if语句需要写很多。 

1.3 CGLIB *

使用上个例子的PersonDaoImpl类和Transaction类(不用接口)

编写 * 类


import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

/**
* CGLIB代理 *
* @author qjc
*/
public class Interceptor implements MethodInterceptor {

private Object target; // 代理的目标类
private Transaction transaction;

public Interceptor(Object target, Transaction transaction) {
 this.target = target;
 this.transaction = transaction;
}

/**
 * 创建目标对象的代理对象
 *
 * @return
 */
public Object createProxy() {
 // 代码增强
 Enhancer enhancer = new Enhancer(); // 该类用于生成代理对象
 enhancer.setCallback(this); // 参数为 *
 enhancer.setSuperclass(target.getClass());// 设置父类
 return enhancer.create(); // 创建代理对象
}

/**
 * @param obj 目标对象代理类的实例
 * @param method 代理实例上 调用父类方法的Method实例
 * @param args 传入到代理实例上方法参数值的对象数组
 * @param methodProxy 使用它调用父类的方法
 * @return
 * @throws Throwable
 */
public Object intercept(Object obj, Method method, Object[] args,
  MethodProxy methodProxy) throws Throwable {
 this.transaction.beginTransaction();
 method.invoke(target);
 this.transaction.commit();
 return null;
}
}

测试


/**
* 测试cglib *
* 通过cglib产生的代理对象,代理类是目标类的子类
* @author qjc
*/
public class TestCglibProxy {

@Test
public void testSave(){

Object target = new PersonDaoImpl();
 Transaction transaction = new Transaction();
 Interceptor interceptor = new Interceptor(target, transaction);

PersonDaoImpl personDaoImpl = (PersonDaoImpl) interceptor.createProxy();
 personDaoImpl.savePerson();
}
}

总结:

1、CGlib是一个强大的,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java接口。

2、用CGlib生成代理类是目标类的子类。

3、用CGlib生成 代理类不需要接口

4、用CGLib生成的代理类重写了父类的各个方法。

5、 * 中的intercept方法内容正好就是代理类中的方法体 CGLIB和JDK * 区别:

JDK:

目标类和代理类实现了共同的接口

* 必须实现InvocationHandler接口,而这个接口中invoke方法体的内容就是代理对象方法体的内容

CGLIB:

目标类 是代理类的父类

* 必须实现MethodInterceptor接口,而接口中的intercept方法就是代理类的方法体,使用字节码增强机制创建代理对象的.

二、面向切面编程

OOP(面向对象编程):封装、继承、多态、抽象

        封装,对代码进行基本的管理、模块化的管理。每个类可能都有自己的职能,出了问题就是论事找人就行了。从修改角度讲,直接修改代码可能有风险,这不是个长远之计,最自然的是从类型封装变化。但是新的类型和旧的体系之间怎么去融合,所以说需要在类与类之间建立一种血缘关系。那么这就是继承的需求,通过继承就可以发现这些类之间是有关联的,它们之间是有父子关系的。然后在继承基础之上多态起决定性的特征。所以说一般认为面向对象最核心的特征,其实是多态。前面几个都是在做铺垫的。多态才是它最核心的特征。子类中通过重写方法,代表了扩展这个层面的东西,而它能融入老的体系中能够正常工作,这是重用这个层面的东西,新的方法、旧的体系、扩展和重用。 

AOP(面向切面编程):

面向切面编程,是一种通过预编译方式运行期 * 实现在不修改源代码的情况下给程序动态统一添加功能的一种技术. 

OOP与AOP区别:

OOP:针对业务处理过程的实体及其属性和行为进行抽象封装,以获得更加清楚的逻辑单元划分。

 AOP:针对业务处理过程中的横切逻辑 进行提取,它所面对的是处理过程中的某个步骤或者阶段,以获得逻辑过程中各部分之间低耦合的隔离效果。这两种设计思想在目标上有着本质的差异。AOP做到了代码块的重用。 

spring AOP代理机制:

1、若目标对象实现了若干接口,spring使用JDK的java.lang.reflect.Proxy类代理。

      优点:因为有接口,所以使系统更加松耦合

      缺点:为每一个目标类创建接口

2、若目标对象没有实现任何接口,spring使用CGLIB库生成目标对象的子类。

      优点:因为代理类与目标类是继承关系,所以不需要有接口的存在。

      缺点:因为没有使用接口,所以系统的耦合性没有使用JDK的 * 好。

使用第一个例子的 PersonDao接口、PersonDaoImpl类和Transaction类

编写spring配置


<bean id="personDao" class="cn.qjc.aop.xml.PersonDaoImpl"></bean>
<bean id="transaction" class="cn.qjc.aop.xml.Transaction"></bean>

<aop:config>
 <!-- 切入点表达式 确定目标类 -->
 <aop:pointcut expression="execution(* cn.qjc.aop.xml.PersonDaoImpl.*(..))" id="perform"/>

<!-- ref指向对象就是切面 -->
 <aop:aspect ref="transaction">
  <aop:before method="beginTransaction" pointcut-ref="perform"/>
  <aop:after-returning method="commit" pointcut-ref="perform"/>
 </aop:aspect>
</aop:config>

</beans>

测试


/**
* 测试spring *
* @author qjc
*/
public class TransactionTest {

@Test
public void testSave(){
 ApplicationContext context = new ClassPathXmlApplicationContext("cn/qjc/aop/xml/applicationContext.xml");
 PersonDao personDao = (PersonDao) context.getBean("personDao");
 personDao.savePerson();
}
}

spring AOP原理

1、当spring容器启动的时候,加载两个bean,对像个bean进行实例化
2、当spring容器对配置文件解析到<aop:config>的时候,把切入点表达式解析出来,按照切入点表达式匹配spring容器内容的bean
3、如果匹配成功,则为该bean创建代理对象
4、当客户端利用context.getBean获取一个对象时,如果该对象有代理对象,则返回代理对象,如果没有代理对象,则返回对象本身

标签:spring,AOP, ,
0
投稿

猜你喜欢

  • Android实现注册界面

    2023-06-20 15:04:45
  • C#值类型、引用类型、泛型、集合、调用函数的表达式树实践

    2022-03-09 20:55:27
  • Android手机屏幕同步工具asm.jar

    2023-10-31 20:01:26
  • Android中的常用尺寸单位(dp、sp)快速入门教程

    2023-09-19 08:59:08
  • 用命令行编译java并生成可执行的jar包方法

    2023-01-15 23:21:35
  • Android IPC机制绑定Service实现本地通信

    2023-10-08 15:25:40
  • SpringBoot文件分片上传教程

    2023-07-21 21:08:40
  • java面试try-with-resources问题解答

    2023-09-03 15:08:01
  • c# DevExpress gridcontrol日期行的显示格式设置

    2022-07-11 05:28:52
  • 基于Ok+Rxjava+retrofit实现断点续传下载

    2021-08-27 02:21:49
  • Android自动编辑文本框(AutoCompleteTextView)使用方法详解

    2023-02-05 18:17:53
  • Android开发使用Handler的PostDelayed方法实现图片轮播功能

    2023-10-20 04:37:42
  • android通过自定义toast实现悬浮通知效果的示例代码

    2022-08-11 03:23:54
  • 详解Android中Drawable方法

    2021-06-12 12:40:05
  • C#实现对AES加密和解密的方法

    2021-07-27 16:34:24
  • spring入门教程之bean的继承与自动装配详解

    2023-11-10 14:46:23
  • Android学习笔记之蓝牙功能

    2022-05-19 05:07:44
  • IDEA集成MyBatis Generator插件的使用

    2023-08-12 00:28:47
  • C#创建windows系统用户的方法

    2022-07-16 20:30:12
  • springboot无法从静态上下文中引用非静态变量的解决方法

    2022-03-05 16:50:05
  • asp之家 软件编程 m.aspxhome.com