| 注册
请输入搜索内容

热门搜索

Java Linux MySQL PHP JavaScript Hibernate jQuery Nginx
Mayer
8年前发布

Spring的IOC和AOP之深剖

   <p>我们首先要知道</p>    <p>用 Spring 主要是两件事:</p>    <p>1 、开发 Bean ; 2 、配置 Bean 。对于 Spring 框架来说,它要做的,就是根据配置文件来创建 bean 实例,并调用 bean 实例的方法完成 “ 依赖注入 ” 。</p>    <p>Spring框架的作用是什么?有什么优点?</p>    <p>1.降低了组件之间的耦合性 ,实现了软件各层之间的解耦 </p>    <p>2.可以使用容易提供的众多服务,如事务管理,消息服务等 </p>    <p>3.容器提供单例模式支持 </p>    <p>4.容器提供了AOP技术,利用它很容易实现如权限拦截,运行期监控等功能 </p>    <p>5.容器提供了众多的辅助类,能加快应用的开发 </p>    <p>6.spring对于主流的应用框架提供了集成支持,如hibernate,JPA,Struts等 </p>    <p>7.spring属于低侵入式设计,代码的污染极低 </p>    <p>8.独立于各种应用服务器 </p>    <p>9.spring的DI机制降低了业务对象替换的复杂性 </p>    <p>10.Spring的高度开放性,并不强制应用完全依赖于Spring,开发者可以自由选择spring的部分或全部 </p>    <h2><strong>什么是DI机制?</strong></h2>    <p>依赖注入(Dependecy Injection)和控制反转(Inversion of Control)是同一个概念,具体的讲:当某个角色 </p>    <p>需要另外一个角色协助的时候,在传统的程序设计过程中,通常由调用者来创建被调用者的实例。但在spring中 </p>    <p>创建被调用者的工作不再由调用者来完成,因此称为控制反转。创建被调用者的工作由spring来完成,然后注入调用者 </p>    <p>因此也称为依赖注入。 是面向编程中的一种设计理念,用来减低程序代码之间的耦合度。</p>    <p>spring以动态灵活的方式来管理对象 , 注入的两种方式,设置注入和构造注入。 </p>    <p>设置注入的优点:直观,自然 </p>    <p>构造注入的优点:可以在构造器中决定依赖关系的顺序。 </p>    <p>现在我们用例子来具体理解它</p>    <h2><strong>1. Spring 提供的几种依赖注入 ( 控制反转 ) 的方式</strong></h2>    <p>1)setter (设置) 注入</p>    <p>2)构造器注入</p>    <p>3)接口注入</p>    <p>但是,我们要了解什么是依赖?</p>    <p>我们所知道的依赖:</p>    <p>①依靠别人或事物而不能自立或自给;②指各个事物或现象互为条件而不可分离。</p>    <p>专业解析:依赖,在代码中一般指通过局部变量,方法参数,返回值等建立的对于其他对象的调用关系。</p>    <p>例如: 在 A 类的方法中,实例化了 B 类的对象并调用其方法以完成特定的功能,我们就说 A 类依赖于 B 类。</p>    <p>Spring设置注入和构造注入的区别</p>    <p>设置注入 是先通过调用无参构造器创建一个 bean 实例,然后调用对应的 setter 方法注入依赖关系;而构造注入则直接调用有参数的构造器,当 bean 实例创建完成后,已经完成了依赖关系的注入。另外这两种依赖注入的方式,并没有绝对的好坏,只是适应的场景有所不同。</p>    <p>setter方法注入:</p>    <p>Entity</p>    <pre>  <code class="language-java">public class Happy {  private String happyInfo;            public void happy(){      System.out.println(happyInfo);  }    public String getHappyInfo() {      return happyInfo;  }    public void setHappyInfo(String happyInfo) {      this.happyInfo = happyInfo;  }  </code></pre>    <p>applicationContext.xml:</p>    <pre>  <code class="language-java"><?xml version="1.0" encoding="UTF-8"?>  <beans xmlns="http://www.springframework.org/schema/beans"      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.xsd">   <!-- IOC 将Happy 交给Spring管理 -->     <bean id="happy" class="cn.wgy.day_01.entity.Happy">          <!-- DI 从setInfo方法得知,Happy依赖info属性,注入            赋值-->          <!--通过框架来赋值,不能用set()方法来赋值  -->          <!--happyinfo 是set()提供的  -->           <property name="happyInfo" value="中秋快乐"></property>                  <!-- <property name="happyInfo">           <value>中秋快乐</value>           </property> -->     </bean>  </beans>    </code></pre>    <p>test类</p>    <pre>  <code class="language-java">public static void main(String[] args) {      //获取上下文对象      //ApplicationContext context2=new FileSystemXmlApplicationContext("applicationContext.xml");            //BeanFactory beanf=new FileSystemXmlApplicationContext("applicationContext.xml");            ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");      //    Object getBean(String name) throws BeansException;      Happy happy = (Happy) context.getBean("happy");            happy.happy();  }  }  </code></pre>    <p>注意:ApplicationContext  ,其实还有BeauFactory也可以,那么它们两的区别是啥呢?请看下面</p>    <p><strong><img src="https://simg.open-open.com/show/e7f553991a8fb9e1c19a02b609925d59.png"> </strong></p>    <p style="text-align:center"><strong><img src="https://simg.open-open.com/show/7c26ebce76b087a23aa43b222c96db32.png"> </strong></p>    <p><img src="https://simg.open-open.com/show/87bc50bc753366a1ccda7feab5df4fec.png"></p>    <p>BeanFacotry是spring中比较原始的Factory。如XMLBeanFactory就是一种典型的BeanFactory。原始的BeanFactory无法支持spring的许多插件,如AOP功能、Web应用等。 </p>    <p>ApplicationContext接口,它由BeanFactory接口派生而来,因而提供BeanFactory所有的功能。ApplicationContext以一种更向面向框架的方式工作以及对上下文进行分层和实现继承,ApplicationContext包还提供了以下的功能: </p>    <p>01.MessageSource, 提供国际化的消息访问  </p>    <p>02. 资源访问,如URL和文件  </p>    <p>03.事件传播  </p>    <p>04. 载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层  </p>    <p>1.利用MessageSource进行国际化  </p>    <p>BeanFactory是不支持国际化功能的,因为BeanFactory没有扩展Spring中MessageResource接口。相反,由于ApplicationContext扩展了MessageResource接口,因而具有消息处理的能力(i18N)</p>    <p>2.强大的事件机制(Event)  </p>    <p>基本上牵涉到事件(Event)方面的设计,就离不开观察者模式。不明白观察者模式的朋友,最好上网了解下。因为,这种模式在java开发中是比较常用的,又是比较重要的。 </p>    <p>ApplicationContext的事件机制主要通过ApplicationEvent和ApplicationListener这两个接口来提供的,和java swing中的事件机制一样。即当ApplicationContext中发布一个事件的时,所有扩展了ApplicationListener的Bean都将会接受到这个事件,并进行相应的处理。 </p>    <p>Spring提供了部分内置事件,主要有以下几种:  </p>    <p>ContextRefreshedEvent :ApplicationContext发送该事件时,表示该容器中所有的Bean都已经被装载完成,此ApplicationContext已就绪可用 </p>    <p>ContextStartedEvent:生命周期 beans的启动信号  </p>    <p>ContextStoppedEvent: 生命周期 beans的停止信号  </p>    <p>ContextClosedEvent:ApplicationContext关闭事件,则context不能刷新和重启,从而所有的singleton bean全部销毁(因为singleton bean是存在容器缓存中的) </p>    <p>虽然,spring提供了许多内置事件,但用户也可根据自己需要来扩展spriong中的事物。注意,要扩展的事件都要实现ApplicationEvent接口。  </p>    <p>3.底层资源的访问  </p>    <p>ApplicationContext扩展了ResourceLoader(资源加载器)接口,从而可以用来加载多个Resource,而BeanFactory是没有扩展ResourceLoader </p>    <p>4.对Web应用的支持  </p>    <p>与BeanFactory通常以编程的方式被创建不同的是,ApplicationContext能以声明的方式创建,如使用ContextLoader。当然你也可以使用ApplicationContext的实现之一来以编程的方式创建ApplicationContext实例 。 </p>    <p>ContextLoader有两个实现: ContextLoaderListener 和 ContextLoaderServlet 。它们两个有着同样的功能,除了listener不能在Servlet 2.2兼容的容器中使用。自从Servelt 2.4规范,listener被要求在web应用启动后初始化。很多2.3兼容的容器已经实现了这个特性。使用哪一个取决于你自己,但是如果所有的条件都一样,你大概会更喜欢 ContextLoaderListener ;关于兼容方面的更多信息可以参照 ContextLoaderServlet 的JavaDoc。</p>    <p>这个listener需要检查 contextConfigLocation 参数。如果不存在的话,它将默认使用 /WEB-INF/applicationContext.xml 。如果它 存在,它就会用预先定义的分隔符(逗号,分号和空格)分开分割字符串,并将这些值作为应用上下文将要搜索的位置。ContextLoaderServlet可以用来替换ContextLoaderListener。这个servlet像listener那样使用contextConfigLocation参数。</p>    <p>5.其它区别  </p>    <p>1).BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化,这样,我们就不能发现一些存在的spring的配置问题。而ApplicationContext则相反,它是在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误。 </p>    <p>2).BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册</p>    <p>构造器注入:</p>    <p>其他的代码都差不多,这里我就直接写applicationContext.xml</p>    <pre>  <code class="language-java">        ">    <!--构造器的注入  -->    <bean id="user1" class="cn.wgy.day01.entity.User">    <constructor-arg  index="0" type="java.lang.String" value="Promise"/>    <constructor-arg  index="1" type="java.lang.String" value="Promise@163.com"/>    </bean>  </beans>   </code></pre>    <p>多种方式实现依赖注入</p>    <p>设值注入 :  普通属性,域属性(JavaBean属性)</p>    <p>构造注入: 普通属性,域属性(JavaBean属性)</p>    <p>命名空间p注入: 普通属性,域属性(JavaBean属性)</p>    <p>我们使用前要先要在Spring配置文件中引入p命名空间 :xmlns:p="http://www.springframework.org/schema/p"</p>    <pre>  <code class="language-java"> <bean id="user" class="cn.wgy.day01.entity.User" p:username="Promise">   <!--通过value标签注入直接量  -->  <property name="id">  <value type="java.lang.Integer">20</value>  </property>   </bean>   <!--案例二:注入引用bean  -->   <bean id="dao" class="cn.wgy.day01.dao.impl.UserDao"/>   <bean id="biz" class="cn.wgy.day01.biz.impl.UserBiz" >   <property name="dao">   <ref bean="dao"/>   </property>   </bean>  </code></pre>    <p>注入不同数据类型:</p>    <p>1.注入直接量 </p>    <p>2.引用bean组件</p>    <p>3.使用内部bean</p>    <p>4.注入集合类型的属性</p>    <p>5.注入null和空字符串</p>    <pre>  <code class="language-java">    <!--p命名空间注入属性值 -->      <!-- 案例一:普通的属性 -->      <bean id="user" class="cn.wgy.day01.entity.User" p:username="Promise"></bean>      <!-- 案例二:引用Bean的属性 -->      <bean id="dao" class="cn.wgy.day01.dao.impl.UserDao" />      <!--p命名空间注入的方式 -->      <bean id="biz" class="cn.wgy.day01.biz.impl.UserBiz" p:dao-ref="dao"></bean>      <!--案例三:注入集合类型的属性 -->      <!--01.List集合 -->      <bean id="list" class="cn.wgy.day01.entity.CollectionBean">          <property name="names">              <list>                  <value>Promise</value>                  <value>Promise2</value>              </list>          </property>      </bean>      <!--02.Set集合 配置文件 -->      <bean id="set" class="cn.wgy.day01.entity.CollectionBean">          <property name="address">              <set>                  <value>北京</value>                  <value>上海</value>              </set>          </property>      </bean>      <!--03.Map集合 -->      <bean id="map" class="cn.wgy.day01.entity.CollectionBean">          <property name="map">              <map>                  <entry key="nh">                      <key>                          <value>1</value>                      </key>                      <value>12</value>                    </entry>                  <entry>                      <key>                          <value>2</value>                      </key>                      <value>20</value>                    </entry>              </map>          </property>      </bean>        <bean id="props" class="cn.wgy.day01.entity.CollectionBean">          <property name="hobbies">              <props>                  <prop key="f">足球</prop>                  <prop key="b">篮球</prop>              </props>          </property>      </bean>      <!--注入空字符串  -->      <bean id="user2" class="cn.wgy.day01.entity.User">      <property name="email"><value></value></property>      </bean>      <!-- 注入null值 -->      <bean id="user3" class="cn.wgy.day01.entity.User">      <property name="email"><null/></property>      </bean>  </code></pre>    <h2><strong>现在我们来理解AOP</strong></h2>    <h2><strong>什么是AOP?</strong> </h2>    <p>面向切面编程(AOP)完善spring的依赖注入(DI),面向切面编程在spring中主要表现为两个方面 </p>    <p>1.面向切面编程提供声明式事务管理 </p>    <p>2.spring支持用户自定义的切面 </p>    <p>面向切面编程(aop)是对面向对象编程(oop)的补充, </p>    <p>面向对象编程将程序分解成各个层次的对象,面向切面编程将程序运行过程分解成各个切面。 </p>    <p>AOP从程序运行角度考虑程序的结构,提取业务处理过程的切面,oop是静态的抽象,aop是动态的抽象, </p>    <p>是对应用执行过程中的步骤进行抽象,,从而获得步骤之间的逻辑划分。 </p>    <p>aop框架具有的两个特征: </p>    <p>1.各个步骤之间的良好隔离性 </p>    <p>2.源代码无关性 </p>    <p>话不多说,我们直接来代码,加强理解</p>    <p>我们先看一个简单的例子:Spring AOP实现日志的输出,即后置增强和前置增强</p>    <p>我们看看代码</p>    <p>aop包下</p>    <p>我们要实现 AfterReturningAdvice 接口</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/1d95425df4092004f89258eeb14b27af.png"></p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/af5d03534f1d1e45f527aafba5f15de9.png"></p>    <pre>  <code class="language-java">public class LoggerAfter implements AfterReturningAdvice {      //save()之后执行它      public void afterReturning(Object returnValue, Method method, Object[] arguments    ,              Object target) throws Throwable {          System.out.println("===========后置增强代码==========");                }    }  </code></pre>    <p>前置,我们要实现MethodBeforeAdvice接口</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/598649b8a228fa684a64952c46e24c59.png"></p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/ff6e461effe862b5d217b8b471bb61d1.png"></p>    <pre>  <code class="language-java"> */  public class LoggerBefore implements MethodBeforeAdvice {      //获取日志对象      private static final Logger log = Logger.getLogger(LoggerBefore.class);      //save()之前执行它      public void before(Method method, Object[] arguments, Object target)              throws Throwable {          log.info("==========前置增强代码==========");                }    }  </code></pre>    <p>实现类:</p>    <p>biz:</p>    <pre>  <code class="language-java">public class UserBiz implements IUserBiz {       //实例化所依赖的UserDao对象,植入接口对象        private IDao dao;          public void save2(User user) {                      //调用UserDao的方法保存信息              dao.save(user);          }          //dao 属性的setter访问器,会被Spring调用,实现设值注入          public IDao getDao() {              return dao;          }          public void setDao(IDao dao) {              this.dao = dao;          }            </code></pre>    <p>dao:</p>    <pre>  <code class="language-java">public class UserDao implements IDao {      /**       * 保存用户信息的方法       * @param user       */      public void save(User user) {          System.out.println("save success!");      }    }  </code></pre>    <p>src下的applicationContext.xml</p>    <pre>  <code class="language-java">     <!--配置实现类  -->     <bean id="dao" class="cn.wgy.day01.dao.impl.UserDao"/>     <bean id="biz" class="cn.wgy.day01.biz.impl.UserBiz">        <property name="dao" ref="dao"></property>     </bean>     <!-- 定义前置增强组件 -->     <bean id="loggerBefore" class="cn.wgy.day01.aop.LoggerBefore"/>     <!-- 定义后置增强组件 -->     <bean id="loggerAfter" class="cn.wgy.day01.aop.LoggerAfter"/>        <!-- 代理对象 ProxyFactoryBean 代理工厂bean-->       <bean id="serviceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">       <property name="targetName" value="biz"></property>       <property name="interceptorNames" value="loggerBefore,loggerAfter"></property>       </bean>  <!-- 针对AOP的配置     <aop:config>     execution:调用那个方法         <aop:pointcut id="pointcut" expression="execution(public void save2(cn.wgy.day01.entity.User))"/>        将增强处理和切入点结合在一起,在切入点处插入增强处理,完成"织入"        <aop:advisor pointcut-ref="pointcut" advice-ref="loggerBefore"/>         <aop:advisor pointcut-ref="pointcut" advice-ref="loggerAfter"/>     </aop:config> -->                        </code></pre>    <p>test</p>    <pre>  <code class="language-java">public static void main(String[] args) {        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");      //获取到biz对象,即业务逻辑层对象            IUserBiz biz=(IUserBiz)ctx.getBean("serviceProxy");      User user=new User();      /**       * 执行这个方法时,先走前置增强,before(),然后走中间的方法,最后走后置增强       */      biz.save2(user);      System.out.println("success!");  }  </code></pre>    <p>这个简单的例子就很好的体现出了Spring AOP的力量</p>    <p>顺便我们一起来看看其他增强类型</p>    <p>环绕增强:可以把前置增强和后置增强结合起来,spring吧目标的方法的控制权全部交给了它。 <strong>我们要实现MethodInterceptor接口</strong></p>    <p style="text-align:center"><strong><img src="https://simg.open-open.com/show/79869e78598be47871fc5483b104adfe.png"> </strong></p>    <p style="text-align:center"><strong><img src="https://simg.open-open.com/show/b32caf755d242b0c127bdc17e9104d8a.png"> </strong></p>    <p>aop:</p>    <pre>  <code class="language-java">public class AroundLog implements MethodInterceptor {      private static final Logger log = Logger.getLogger(AroundLog.class);            public Object invoke(MethodInvocation arg0) throws Throwable {          /*Object target = arg0.getThis(); // 获取被代理对象          Method method = arg0.getMethod(); // 获取被代理方法          Object[] args = arg0.getArguments(); // 获取方法参数          log.info("调用 " + target + " 的 " + method.getName() + " 方法。方法入参:"               + Arrays.toString(args));          try {    Object result = arg0.proceed(); // 调用目标方法,获取目标方法返回值              log.info("调用 " + target + " 的 " + method.getName() + " 方法。 "                  + "方法返回值:" + result);              return result;          } catch (Throwable e) {              log.error(method.getName() + " 方法发生异常:" + e);        throw e;          }*/          //环绕增强                  System.out.println("===before=====");                                    //调用目标对象的方法                  Object result = arg0.proceed();                  System.out.println("===after=====");                          return result;      }  </code></pre>    <p>applicationContext.xml:</p>    <pre>  <code class="language-java"> <bean id="service" class="cn.wgy.day01.service.UserService"/>  <!--环绕增强  -->  <bean id="around" class="cn.wgy.day01.aop.AroundLog"/>  <!-- 方式一 -->  <!-- <aop:config>  切点  <aop:pointcut expression="execution(public void delete())" id="pointcut"/>   异常抛出增强  <aop:advisor advice-ref="around" pointcut-ref="pointcut"/>  </aop:config> -->    <!--方式二  -->   <!-- 代理对象 ProxyFactoryBean 代理工厂bean-->       <bean id="serviceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">       <property name="target" ref="service"></property>       <property name="interceptorNames" value="around"></property>       </bean>  </beans>  </code></pre>    <p>异常增强,我们要实现ThrowsAdvice接口</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/a3b3335b7160587c4e3d52dc95f63ce9.png"></p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/dbed68a72889678a1a9d1d8c698f24b6.png"></p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/c894b784ec2861302ae8c05c107e5697.png"></p>    <p>你注意看没有,都没有实现方法,怎么办呢?你别急,其实已经给我们规定了方法名称,而且必须是它才能。就是 void AfterThrowing()它提供了一个参数,三个参数的。</p>    <p>aop:</p>    <pre>  <code class="language-java">public class ErrorLog implements ThrowsAdvice {    private static final Logger log=Logger.getLogger(ErrorLog.class);        @SuppressWarnings("unused")  private void AfterThrowing(Method method, Object[] args, Object target,              RuntimeException e) {      log.error(method.getName()+"方法发生异常"+e);    }  </code></pre>    <p>service</p>    <pre>  <code class="language-java">public class UserService {   public void delete() {        int result=5/0;      System.out.println(result);        }  }  </code></pre>    <p>applicationContext.xml</p>    <pre>  <code class="language-java"> <bean id="service" class="cn.wgy.day01.service.UserService"/>  <bean id="error" class="cn.wgy.day01.aop.ErrorLog"/>      <!--方式二  -->   <!-- 代理对象 ProxyFactoryBean 代理工厂bean-->              <bean id="serviceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">       <property name="target" ref="service"></property>       <property name="interceptorNames" value="error"></property>       </bean>         <!-- <aop:config>  <aop:pointcut expression="execution(public void delete())" id="pointcut"/>   异常抛出增强  <aop:advisor advice-ref="error" pointcut-ref="pointcut"/>  </aop:config> -->  </code></pre>    <p>到这里我要思考一下,我们可以applicationContext.xml里面的bean组件修改修改,我们用了顾问(Advisor)要包装通知(Advice),比较灵活,可以只针对某一个方法进行处理。</p>    <p>对于环绕增强:</p>    <pre>  <code class="language-java"> <bean id="service" class="cn.wgy.day01.service.UserService"/>  <!--环绕增强  -->  <bean id="around" class="cn.wgy.day01.aop.AroundLog"/>    <!-- 方式一 -->  <!-- <aop:config>  切点  <aop:pointcut expression="execution(public void delete())" id="pointcut"/>   异常抛出增强  <aop:advisor advice-ref="around" pointcut-ref="pointcut"/>  </aop:config> -->    <!--顾问(Advisor)要包装通知(Advice),比较灵活,可以只针对某一个方法进行处理  -->  <bean id="advisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">  <property name="advice" ref="around"></property>  <property name="mappedNames" value="delete"></property>  </bean>    <!--方式二  -->   <!-- 代理对象 ProxyFactoryBean 代理工厂bean-->       <bean id="serviceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">       <property name="target" ref="service"></property>       <property name="interceptorNames" value="advisor"></property>       </bean>  </beans>   </code></pre>    <p>还有一个知识点,我们可以更方便的写bean组件,正则表达式</p>    <pre>  <code class="language-java"><bean id="service" class="cn.wgy.day01.service.UserService"/>  <!--环绕增强  -->  <bean id="around" class="cn.wgy.day01.aop.AroundLog"/>    <!-- 方式一 -->  <!-- <aop:config>  切点  <aop:pointcut expression="execution(public void delete())" id="pointcut"/>   异常抛出增强  <aop:advisor advice-ref="around" pointcut-ref="pointcut"/>  </aop:config> -->    <bean id="regex" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">  <property name="advice" ref="around"></property>  <property name="pattern" value=".*do.*"></property>    </bean>    <!--方式二  -->   <!-- 代理对象 ProxyFactoryBean 代理工厂bean-->       <!-- <bean id="serviceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">       <property name="target" ref="service"></property>       <property name="interceptorNames" value="regex"></property>       </bean> -->       <!-- 默认 -->      <!--  <bean class="org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor"></bean>-->     <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">     <property name="beanNames" value="service"></property>     <property name="interceptorNames" value="regex"></property>     </bean>  </code></pre>    <p>到这里呢,算是差不多了,但是我们缺少了分析下源代码</p>    <p>我们知道,实现aop,其实源代码里面通过了动态代理来实现</p>    <p>我们来了解一下 代理 模式的定义:</p>    <p>为其他对象提供一种代理 以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。</p>    <p>代理模式使用代理对象完成用户请求,屏蔽用户对真实对象的访问。</p>    <p>在软件设计中,使用代理模式的意图也很多,比如因为安全原因需要屏蔽客户端直接访问真实对象,或者在远程调用中需要使用代理类处理远程方法调用的技术细节 ( 如 RMI) ,也可能为了提升系统性能,对真实对象进行封装,从而达到延迟加载的目的。</p>    <p>代理模式角色分为 4 种:</p>    <p>主题接口:定义代理类和真实主题的公共对外方法,也是代理类代理真实主题的方法;</p>    <p>真实主题:真正实现业务逻辑的类;</p>    <p>代理类:用来代理和封装真实主题;</p>    <p>Main :客户端,使用代理类和主题接口完成一些工作。</p>    <p><strong>1. 什么是动态代理 ?</strong></p>    <p>答:动态代理可以提供对另一个对象的访问,同时隐藏实际对象的具体事实。代理一般会实现它所表示的实际对象的接口。代理可以访问实际对象,但是延迟实现实际对象的部分功能,实际对象实现系统的实际功能,代理对象对客户隐藏了实际对象。客户不知道它是与代理打交道还是与实际对象打交道。</p>    <p><strong>2. 为什么使用动态代理</strong></p>    <p>答:因为动态代理可以对请求进行任何处理</p>    <p><strong>3. 哪些地方需要动态代理 ?</strong></p>    <p>答:不允许直接访问某些类;对访问要做特殊处理等</p>    <p><strong>JAVA 动态代理的作用是什么?</strong></p>    <p>主要用来做方法的增强,让你可以在不修改源码的情况下,增强一些方法,在方法执行前后做任何你想做的事情(甚至根本不去执行这个方法),因为在 <strong> InvocationHandler </strong> 的 invoke方法中,你可以直接获取正在调用方法对应的 <strong> Method </strong> 对象,具体应用的话,比如可以添加调用日志,做事务控制等。</p>    <p>还有一个有趣的作用是可以用作远程调用,比如现在有 Java接口,这个接口的实现部署在其它服务器上,在编写客户端代码的时候,没办法直接调用接口方法,因为接口是不能直接生成对象的,这个时候就可以考虑代理模式(动态代理)了,通过 <strong> Proxy.newProxyInstance </strong> 代理一个该接口对应的 <strong>InvocationHandler</strong> 对象,然后在 <strong>InvocationHandler</strong> 的 invoke方法内封装通讯细节就可以了。具体的应用,最经典的当然是Java标准库的RMI,其它比如hessian,各种webservice框架中的远程调用,大致都是这么实现的。</p>    <p>动态代理我们分之为jdk动态代理和cglib动态代理</p>    <p>JDK 实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,如何实现动态代理呢,这就需要 CGLib 了。 CGLib 采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。 JDK 动态代理与 CGLib 动态代理均是实现 Spring AOP 的基础。</p>    <p>CGLib 创建的动态代理对象性能比 JDK 创建的动态代理对象的性能高不少,但是 CGLib 在创建代理对象时所花费的时间却比 JDK 多得多,所以对于单例的对象,因为无需频繁创建对象,用 CGLib 合适,反之,使用 JDK 方式要更为合适一些。同时,由于 CGLib 由于是采用动态创建子类的方法,对于 final 方法,无法进行代理。</p>    <p>1.JDK动态代理</p>    <p>此时代理对象和目标对象实现了相同的接口,目标对象作为代理对象的一个属性,具体接口实现中,可以在调用目标对象相应方法前后加上其他业务处理逻辑。</p>    <p>代理模式在实际使用时需要指定具体的目标对象,如果为每个类都添加一个代理类的话,会导致类很多,同时如果不知道具体类的话,怎样实现代理模式 呢?这就引出动态代理。</p>    <p>JDK动态代理只能针对实现了接口的类生成代理。</p>    <p>看看代码:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/1fb93d13879d06af6540f72283052606.png"></p>    <p>dao实现类</p>    <pre>  <code class="language-java">public class UserDaoImpl implements UserDao {        public void add() {          System.out.println("add success");      }              }  </code></pre>    <pre>  <code class="language-java">public class ProxySubject implements Subject {   private Subject realSubject;      public String request() {          System.out.println("代理增强");          return realSubject.request();      }      public Subject getRealSubject() {          return realSubject;      }      public void setRealSubject(Subject realSubject) {          this.realSubject = realSubject;      }  </code></pre>    <pre>  <code class="language-java">public class RealSubject implements Subject {        public String request() {          // TODO Auto-generated method stub          return "真实主题";      }    }  </code></pre>    <pre>  <code class="language-java"> */  public interface Subject {  public String request();  }  </code></pre>    <p>test</p>    <pre>  <code class="language-java">    public static void main(String[] args) {      /**       * 静态代理       */          /*Subject sub=new RealSubject();//被代理对象          System.out.println(sub.toString());          ProxySubject ps=new ProxySubject();//代理对象          System.out.println(ps.toString());          ps.setRealSubject(sub);          String request = ps.request();//走真实代理对象  RealSubject          System.out.println(request);*/                    /**           * 动态代理           */      final UserDao dao=new UserDaoImpl();      //代理对象      //第一个参数:获取和dao一样的类加载器,通过反射机制获取类加载器      //new InvocationHandler()叫匿名内部类,拿到了接口的实现类      UserDao newProxyInstance = (UserDao) Proxy.newProxyInstance(dao.getClass().getClassLoader(), dao.getClass().getInterfaces(), new InvocationHandler() {          //newProxyInstance 被代理对象          public Object invoke(Object newProxyInstance, Method method, Object[] args)                  throws Throwable {              System.out.println("增强");              //原始对象  dao     真正的dao                  Object invoke = method.invoke(dao, args);              System.out.println("记录日志");              return invoke;          }                });      //增强代理对象,方法      newProxyInstance.add();      }  </code></pre>    <p>applicationContext.xml</p>    <pre>  <code class="language-java"> <bean id="service" class="cn.wgy.day01.service.UserService"></bean>  <bean id="error" class="cn.wgy.day01.aop.ErrorLog"/>  <aop:config>  <aop:pointcut expression="execution(public void delete())" id="pointcut"/>   <!-- 异常抛出增强 -->  <aop:advisor advice-ref="error" pointcut-ref="pointcut"/>  </aop:config>  </beans>   </code></pre>    <p>2.CGLIB代理</p>    <p>CGLIB(CODE GENERLIZE LIBRARY)代理是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的所有方法,所以该类或方法不能声明称final的。</p>    <p>如果目标对象没有实现接口,则默认会采用 CGLIB代理;</p>    <p>如果目标对象实现了接口,可以强制使用 CGLIB实现代理(添加CGLIB库,并在spring配置中加入<aop:aspectj-autoproxy proxy-target-class="true"/>)</p>    <pre>  <code class="language-java">public class CglibProxy implements MethodInterceptor {      private Enhancer enhancer = new Enhancer();         public Object getProxy(Class clazz){          //设置需要创建子类的类          enhancer.setSuperclass(clazz);          enhancer.setCallback(this);          //通过字节码技术动态创建子类实例          return enhancer.create();         }        public Object intercept(Object obj, Method method, Object[] args,              MethodProxy proxy) throws Throwable {          System.out.println("前置代理");              //通过代理类调用父类中的方法              Object result = proxy.invokeSuper(obj, args);              System.out.println("后置代理");              return result;        }  </code></pre>    <p>test</p>    <pre>  <code class="language-java">public class Test {      public static void main(String[] args) {          final UserService service=new UserService();          //1.创建          Enhancer enhancer=new Enhancer();          //2.设置根据哪个类生成子类          enhancer.setSuperclass(service.getClass());          //3.指定回调函数          enhancer.setCallback(new MethodInterceptor() {               //实现MethodInterceptor接口方法                public Object intercept(Object proxy, Method method, Object[] object,                      MethodProxy methodproxy) throws Throwable {               //System.out.println("代码增强");               System.out.println("前置代理");               //通过代理类调用父类中的方法                Object invoke = method.invoke(service, object);              System.out.println("后置代理");                return invoke;              }          });          //通过字节码技术动态创建子类实例            UserService proxy = (UserService) enhancer.create();          proxy.delete();      }  }  </code></pre>    <p>applicationContext.xml</p>    <pre>  <code class="language-java"> <bean id="service" class="cn.wgy.day01.service.UserService"></bean>  <bean id="error" class="cn.wgy.day01.aop.ErrorLog"/>  <aop:config>  <aop:pointcut expression="execution(public void delete())" id="pointcut"/>   <!-- 异常抛出增强 -->  <aop:advisor advice-ref="error" pointcut-ref="pointcut"/>  </aop:config>  <aop:aspectj-autoproxy proxy-target-class="true"/>  </code></pre>    <p> </p>    <p>来自:http://www.cnblogs.com/whyhappy/p/5955899.html</p>    <p> </p>    
 本文由用户 Mayer 自行上传分享,仅供网友学习交流。所有权归原作者,若您的权利被侵害,请联系管理员。
 转载本站原创文章,请注明出处,并保留原始链接、图片水印。
 本站是一个以用户分享为主的开源技术平台,欢迎各类分享!
 本文地址:https://www.open-open.com/lib/view/open1476340951596.html
AOP Spring JEE框架 IoC