spring03:aop事物

1.导入spring框架,昨天只是选择了spring核心jar包,今天选择核心jar还要选择aop包,注意:跟hibernate一样,都要拷贝到lib路径下
2.Aop面向切面编程:是对面向对象编程的扩展(把面向对象的分散的n个功能都封装到一个类中)
3.代理对象与目标对象实现同一个接口的目的是为了保证上一层的调用对我没有变化
4.把核心功能和代理功能分开写,这是低耦合。然后运行的时候又关联起来了,这是高内聚(在配置文件中用ref关联的)
5.aop的编程思想:在某些对象中除了包含自身应该具备的核心业务功能之外,还必须包含不得不处理的增强功能(日志记录,事务控制,异常处理),
 分散到整个应用程序的各个角落,增加了程序中核心业务的逻辑和耦合度,因此我们在实际开发中要把核心功能(目标对象)和增强功能分开,
 因此就要在核心功能和增强功能之间产生一个代理对象(包含他们两的功能),在运行的时候运行的是代理对象。
6.aop编程的概念
 ---1.切面(横切逻辑):在方法之前增加了一些功能,在方法之后也增加了一些功能,此时之前和之后就把目标方法切成了一个平面,左边的平面是核心代码,右边的平面是增强代码。
       切面是我们需要在程序中给核心功能提供的增强功能,在运行的时候需要把切面和核心功能放到一起
 ---2.切入点:是程序中引入增强功能的地方(某个点),就是核心业务对象的某个方法的前或者后。切入点是个集合,因为目标对象的每个方法都有一个切入点
 ---3.连接点:是某个切入点(集合中的一个元素),当前在使用的切入点
 ---4.通知(增强):增强代码,也叫方面功能
 ---5.织入:产生代理对象
7.spring中的代理对象是通过反射实现的,他把功能(这是调用目标对象的功能)写好了,我们只需要重写他的方法,方法体里面不但调用目标方法,还在前后加了增强功能,我们重写的目的就是添加增强功能,因为调用目标方法的功能是他写好的
8.所有aop的东西都要放在<aop:config>标签里,也就是切面
 ---<aop:config>里面有子标签<aop:pointcut>,是在声明切入点,id属性是自己起的名字,expression表示表达式,就是那些方法在执行的时候会被织入增强代码
 ---<aop:config>里面有子标签<aop:advisor>,是织入增强代码,有pointcut-ref属性和advice-ref属性,都是引用的上面的,pointcut-ref属性和advice-ref属性充分体现了切面=切入点+通知
9.前置通知,后置通知,异常通知,环绕通知,这些通知都要实现接口(spring中的接口)
10.日志插件log4j:1.增加资源文件,然后运行,看控制台:创建了单例模式的对象,创建了代理类等待
   2.如果想保存在记事本的话,在增强代码所对应着类中加上一句代码,并导入一个jar包(commons-logging.jar),如果导入了hibernate的jar包的话就不用加限制这个jar包了,因为hibernate中有,此时看web-info下就有记事本了(因为在资源文件里就配置着的这个路径)
错误error,warn警告,debug调试 info信息,这是等级从高到底

11.UserBiz biz=(UserBiz) ac.getBean("userBiz");是在调用ioc容器(配置文件)中的bean对象,将产生一个代理对象(其实ioc容器就是个动态代理模式(用反射的形式获取目标对象),ioc容器里面的property属性就是用反射的形式获取对象)
-------------
注意:分散在程序各个角落的增强代码叫做横切逻辑,当把这些增强代码都放在一个类里面之后,就变成切面对象了。、

=========================================

1.工厂模式的作用:创建对象,解耦
如果目标对象中的方法中存在大量的相同代码,静态代理模式就会出现代码冗余
2.静态代理类proxy,该类位于java.lang。reflect包下(反射的包),是jvm的动态代理类的父类
3.静态代理就是在目标对象和代理对象之间加入了静态代理模式InvocationHandler,InvocationHandler是利用的反射机制
3.如果从反射的角度看,静态代理类的方法中,对目标对象的业务方法的调用也是相同的,都是method.invoke(object,args),第一个参数是调用者,第二个参数是方法的参数
注意:反射是把所有的方法都封装到method类中,因此每个方法都是一个method对象,属性都封装到filed类中,每个属性都是一个filed对象。
invoke方法里有三个参数:调用者,method对象,方法的参数

4.InvocationHandler接口是JavaSE在java.lang.reflect包下定义的一个接口,它的作用是实施调用委派。调用委派是指将任意对象的方法调用委派给接口对象的invoke方法调用,目的是为了代码合并
invoke方法的作用,以反射的方式将代理方法的调用解析为统一的形式。
------------------------
面向切面编程 (Aspect Oriented Programming——AOP)Aspect是切面,Oriented是面向,Programming是编程
1.什么是AOP?
    在传统的软件系统中存在一些象日志记录(需要添加记录时间点)、事务处理(需要添加提交和回滚)等通用性的代码块,它们通常
    散落在各个程序中,被称之为"横切逻辑"(cross-cutting concern)。
    将这些横切逻辑以面向对象的方式单独进行封装,从而形成切面(aspect),并在
    程序执行时切入进来的编程方法,被称之为面向切面编程。
注意:把事务处理和jdbc的内容混在一起就不是切面,把他分离出来的时候就是切面。横切逻辑其实就是我们常用的增强代码

2.AOP(面向切面)与OOP(面向对象)的关系
    面向切面编程(AOP)与面向对象编程(OOP)并不矛盾,是在面向对象编程基础之上
    的一种软件开发模式;
3.AOP有几种实现方式?
  (1)AspectJ
     AspectJ是一个基于Java的AOP编程语言,它通常采用ajc编译器,将横切逻辑
          切入到目标类的字节码中,所以被称为静态AOP。
  (2)SpringAOP(重点)
     Spring框架提供了一个AOP的实现机制,这个实现机制是基于动态代理模式的,因此
          被称为动态AOP。Spring框架最初(1.x版)定义了一套自己的接口和类用于AOP编程,
          但在2.x版以后,支持和引入了AspectJ类库,并建议在实际应用中使用。
  (3)JBossAOP
          基于JBoss应用服务器的一种动态AOP的实现。
 
二、SpringAOP中的常用概念
1.连接点(JoinPoint)----->目标对象的业务方法(目标方法)
  (1)什么是连接点?
          目标类在执行过程中可以插入增强代码的位置;从理论上讲有静态代码块,
          循环语句块,方法等程序执行位置;
  (2)连接点在Spring中的定义
     Spring框架只支持方法类型的连接点,不支持其它类型;因此在Spring中,
     JoinPoint专指目标对象的执行方法。
注意:连接点就是目标方法的执行。连接点是真正的客观存在的

2.切入点(Pointcut)------>连接点的表现形式(某个路径(其实就是某个包下或者类下的所有东西就是切入点。))
  (1)什么是切入点?
          切入点是连接点的表示方式,可以指定一个连接点或者多个连接点,通常
          使用AspectJ所定义的切入点表达式;
          pointcut的两种表达式:
          ①execution(表达式);
          ②within();
  (2)SpringIoC容器会根据配置,将切入点表达式封装成一个对象;
注意:切入点是连接点的一种表示方式。不是真正客观存在的,是个表达式。within表明连接点在哪

3.通知(Advice)(额的外s)
  (1)什么是通知? 
          通知代表横切逻辑,是切入连接点的增强代码,通常被定义在一个类的方法中
          或者被单独封装到一个类中,比如事务拦截器;
  (2)如何理解通知?
          通知最初是指在控制台中显示字符串的输出语句,用来说明连接点的执行情况,
          是最简单的一种横切逻辑,属于日志记录。复杂的横切逻辑还有:事务处理,
          安全检查等;使用Adive表示横切逻辑,是"一斑窥豹"的命名手法。
注意:通知就是横切逻辑,也就是指的是增强代码

4.切面(Aspect)
  (1)什么是切面?
   切面是通知和切入点(很多连接点)的组织与封装;SpringIoC容器根据切面,将Advice作用到
          相应的JoinPoint上;因此:Aspect=Advice+Pointcut;
  (2)SpringIoC容器会根据配置创建切面对象;但是切面对象通常依赖于一个普通类,
          即基于POJO的Aspect。
注意:把切入点和通知(横切逻辑)封装到一起就是个切面对象,通过切面对象可以指定我的横切逻辑应该作用到哪个连接点上。

5.织入(weave)
  (1)什么是织入?
      在不侵入目标对象的业务方法的情况下,切入横切逻辑,即将附加的增强代码和
          目标对象的业务方法整合起来。
  (2)如何织入呢? (原理)
          通过代理对象;代理对象在其所依赖的InvocationHandler对象的
     invoke方法中将增强代码与业务代码实施整合。
  (3)如何创建代理对象呢?
     Spring的IoC容器通过ProxyFactory,动态产生代理对象。
          由于ProxyFactory封装了复杂的织入过程,因此也被称为织入器(weaver)。
  (4)ProxyFactory产生代理对象的两种方式:
          ①基于JDK(Proxy)的动态代理机制(默认方式)
             特点:目标类实现接口
          ②基于CGLIB(Code Generation Library)的动态字节码生成库
             特点:目标类没有实现接口;(hibernate中的load方法也是这样)
                      通过子类继承,重写目标类的业务方法完成横切逻辑的织入;
------------------------------------------------
一、AOP编程案例:
1.基于POJO的通知封装
  (1)什么是POJO?
     POJO(Plain Old Object)即普通Java对象
  (2)将通知(Advice)按照不同的类型封装为POJO中的方法
  (3)环绕通知的独特之处
          ①环绕通知又称为拦截器;
          ②环绕通知所使用的方法必须含有ProceedingJoinPoint类型的参数
       (a)ProceedingJoinPoint位于aspectjrt.jar包中;
       (b)ProceedingJoinPoint继承于JoinPoint,扩展了连接点的
                    概念,包含了多个环绕通知和连接点,并将它们组成了一个链式结构,
                    其中连接点是这个链式结构的最后一个元素。
       (c)ProceedingJoinPoint#proceed()方法,表示下一个链式
                    元素的执行。下一个链式元素可能是另外一个环绕通知或者是
          JoinPoint。
2.基于POJO的切面配置
  (1)使用AOP名称空间
  (2)注册POJO
  (3)配置切面
          ①注册Pointcut对象;
          ②注册切面对象;
         
二、Adivce类型:
根据切入连接点的时机和方式,划分为5种类型:
(1)前置通知 (在目标方法(切入点)执行之前需要织入的横切逻辑(增强代码))
           配置标签:<aop:before>
           切入时机 :在目标对象方法执行之前切入
(2)后置通知 (在目标方法执行之后织入的横切逻辑(增强代码),后置通知里面的参数是目标方法的返回值,在配置文件中也要指明方法名和参数名)
    配置标签: <aop:after-returning>
    切入时机 :在目标对象方法正常执行完最后一条语句(return语句)时切入
(3)异常通知 (在执行目标方法时出现异常时候织入的横切逻辑(增强代码))
           配置标签:<aop:after-throwing>
           切入时机:在目标对象方法执行出现异常时切入
(4)最终通知(在执行finally语句块的时候织入横切逻辑(增强代码))
           配置标签:<aop:after>
    切入时机:在目标对象方法执行完finally语句块之后切入
------------------------------------------------------------
(5)环绕通知(也称为拦截器Interceptor):
           配置标签:<aop:around>
           切入时机:在目标对象方法的执行之前和执行之后切入
       
三、切入点表达式(Pointcut Expression)
(Ⅰ)execution表达式
 ① execution(public * *(..))
    说明: (a)(..)表示方法中的零个或多个参数;
        (b)后面紧跟括号的*,代表方法的名称;
        (c)第一个*,表示方法的返回值类型;
 ② execution(* hello*(..))  // void helloSave()
     符合任何以hello开头的方法 
     说明:(a)后面紧跟括号的*,代表方法的部分名称;
        (b)第一个*,表示方法的返回值类型;
 ③ execution(* com.company.IHello.*(..))
     符合IHello接口/类中声明的任何方法
     说明:(a)后面紧跟括号的*,代表方法的名称;
        (b)第一个*,表示方法的返回值类型;
 ④ execution(* com.company.service.*.*(..))
    符合com.company.service包下的所有方法
    说明:(a)后面紧跟括号的*,代表方法的名称;
       (b)倒数第二颗*,代表包下的任意类或者是接口;
       (c)第一个*,表示方法的返回值类型;
 ⑤ execution(* com.company.service..*.*(..))
    符合com.company.service包或者其子包下的任何方法
    说明:(a)后面紧跟括号的*,代表方法的名称;
       (b)倒数第二颗*,代表包或者其子包下的任意类或者是接口;
       (c)第一个*,表示方法的返回值类型;
(Ⅱ)within表达式
   within表达式只接受类型声明,表明了符合要求的类或接口中的所有方法
     ① within(com.company.service.*)
     说明:符合com.company.service包下的类和接口所定义的方法
     ② within(com.company.service..*)
     说明:符合com.company.service包下或其子包下类和接口所定义的方法
    
--------------------------------------
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation=
       "http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
       ">
  
 <bean id="userservice"  //这是目标对象
       class="aop.service.UserServiceImpl" />
      
 <!--注册切面对象所依赖的对象pojo,这里是切面,切面是所有的增强代码(横切逻辑) -->
 <bean id="aopbean"
       class="aop.aspect.AopBean" />
 
 <!--向Ioc容器提供面向切面编程的配置  -->    
 <aop:config>
  <!-- 定义切入点 ,为了让连接点引用他-->
  <aop:pointcut id="servicepointcut"
   expression="within(aop.service.*)"/>
  
  <!-- aop是切面对象(横切逻辑+切入点), --> ,其实就是把某个横切逻辑切入到连接点(是切入点的子集)。此时横切逻辑切入连接点
  <aop:aspect id="aspectbean" ref="aopbean">
   <!-- 横切逻辑,前置通知  -->
   <aop:before method="mybefore"
        pointcut-ref="servicepointcut"/>
   
   <!-- 正常后置通知 -->
   <aop:after-returning method="myafterReturning"
        returning="returnVal"
        pointcut-ref="servicepointcut"/>
       
   <!-- 异常通知 -->
    <aop:after-throwing method="myafterException"
        throwing="ex"
        pointcut-ref="servicepointcut" />
   
    <!-- 最终通知 -->   
    <aop:after method="myafter"
      pointcut-ref="servicepointcut" />
   
    <!-- 环绕通知 -->
   <!--  <aop:around method="myaround"
     pointcut-ref="servicepointcut" /> -->
  </aop:aspect>
 </aop:config>   
</beans>

-------------------------
spring的IoC容器生成动态代理对象proxy,为我们织入横切逻辑(也就是帮我们把目标方法和增强方法进行整合)。在代理对象所依赖的InvocationHandler对象的invoke方法中整合的。
public class Test {
    /* 当前路径是:工程目录下的src*/
 private static final String CONFIG="aop.xml";
 public static void main(String[] args) {
  //创建Spring的IoC容器对象
  ApplicationContext ac=
    new ClassPathXmlApplicationContext(CONFIG);
  /* 通过IOC容器,实际上获得的是目标对象的动态代理对象;其原理可以
   * 参考《动态代理模式的编程方法》之DynamicProxyFactory.java*/该代理对象与目标对象有相同的接口。该代理类是proxy类的子类
  UserService us=(UserService)ac.getBean("userservice");
  //表明通过IoC容器获得的是代理对象
  System.out.println(us.getClass().getName());
  
  us.save();
  System.out.println();
  us.update();
  System.out.println();
  us.delete();
 }

--------------------------------------
1.目标对象的业务方法的执行
public void oneMethod(){

      statement1;
      statement2;
      ...
      statementn;
}
2.基于四大通知的代理对象的代理方法的执行
<aop:before>
try{
  target.oneMethod();
  <aop:after-returning>
}catch(Exception ex){
  <aop:after-throwing>
}finally{
  <aop:after>
}

3.基于事务的代理对象的执行
try{
  Session sess=factory.openSession();
  Transaction tx=sess.beginTransaction();
  target.method();
  tx.commit();
}catch(Exception ex){
  tx.rollback();
}finally{
  sess.close();
}

百度:
由于本人只了解Java,所以姑且认为代理模式有两种实现方式,一种是静态代理、另一种是动态代理。他们的区别在于编译时知不知道代理的对象是谁。在模块比较多的系统中,静态代理是不合适也非常低效的,
因为静态代理需要专门为每一个接口设计一个代理类,系统比较大成百上千的接口是很正常的,静态代理模式太消耗人力了。动态代理是JDK所支持的代理模式,它可以非常好的实现横切关注点

1.

<?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"
 xmlns:p="http://www.springframework.org/schema/p"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
 http://www.springframework.org/schema/aop
 http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
 ">
   <!-- 实例化dao对象 -->
   <bean id="userDao" class="com.model.dao.impl.UserDaoImpl"></bean>
   <!-- 实例Dao的代理对象 -->
 <bean id="proxyDao" class="com.model.dao.impl.ProxyUserDao">
  <property name="dao" ref="userDao"></property>
 </bean>
 <!-- 实例Biz -->
 <bean id="userBiz" class="com.biz.UserBizImpl">
  <property name="dao" ref="proxyDao"></property>
 </bean>
 
 
 
 <!-- 以下是面向切面编程 -->
 <!-- 四个事务(增强功能),也叫横切逻辑 -->
 <bean id="befer" class="com.comm.befer"></bean>
 <bean id="after" class="com.comm.after"></bean>
 <bean id="around" class="com.comm.around"></bean>
 <bean id="execptionn" class="com.comm.execptionn"></bean>
 
 <!-- Aop的配置标签 -->
 <aop:config>
     <!--声明切入点 ,切入点是个表达式(路径,路径的最底层是各个方法),也就是说切入点是方法的集合-->
     <aop:pointcut expression="execution(* com.model.dao.impl.*.*(..))" id="mypoint"/>
    <!-- 织入过程,把增强代码(横切逻辑)织入到连接点上(一个方法)。例如第一个表示befer这个增强代码的作用域是mypoint(在这个作用域的所有方法都会加入befer这个类里面的增强代码) -->
    <aop:advisor advice-ref="befer" pointcut-ref="mypoint"/><!-- 织入前置通知 -->
    <aop:advisor advice-ref="after" pointcut-ref="mypoint"/><!-- 织入后置通知 -->
    <aop:advisor advice-ref="around" pointcut-ref="mypoint"/><!-- 织入环绕通知 -->
    <aop:advisor advice-ref="execptionn" pointcut-ref="mypoint"/><!-- 织入异常通知 -->
 </aop:config>
</beans>

2.

<?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"
 xmlns:p="http://www.springframework.org/schema/p"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
 http://www.springframework.org/schema/aop
 http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
 ">
   <!-- 实例化dao对象 -->
   <bean id="userDao" class="com.model.dao.impl.UserDaoImpl"></bean>
   <!-- 实例Dao的代理对象 -->
 <bean id="proxyDao" class="com.model.dao.impl.ProxyUserDao">
  <property name="dao" ref="userDao"></property>
 </bean>
 <!-- 实例Biz -->
 <bean id="userBiz" class="com.biz.UserBizImpl">
  <property name="dao" ref="proxyDao"></property>
 </bean>
 
 
 
 <!-- 以下是面向切面编程 -->
 <!-- 四个事务(增强功能),也叫横切逻辑 -->
 <bean id="befer" class="com.comm.befer"></bean>
 <bean id="after" class="com.comm.after"></bean>
 <bean id="around" class="com.comm.around"></bean>
 <bean id="execptionn" class="com.comm.execptionn"></bean>
 
 <!-- Aop的配置标签 -->
 <aop:config>
     <!--声明切入点 ,切入点是个表达式(路径,路径的最底层是各个方法),也就是说切入点是方法的集合-->
     <aop:pointcut expression="execution(* com.model.dao.impl.*.*(..))" id="mypoint"/>
    <!-- 织入过程,把增强代码(横切逻辑)织入到连接点上(一个方法)。例如第一个表示befer这个增强代码的作用域是mypoint(在这个作用域的所有方法都会加入befer这个类里面的增强代码) -->
    <aop:advisor advice-ref="befer" pointcut-ref="mypoint"/><!-- 织入前置通知 -->
    <!--  <aop:advisor advice-ref="after" pointcut-ref="mypoint"/>
    <aop:advisor advice-ref="around" pointcut-ref="mypoint"/>
    <aop:advisor advice-ref="execptionn" pointcut-ref="mypoint"/>-->
 </aop:config>
</beans>

猜你喜欢

转载自1601844782.iteye.com/blog/2278136