Spring - 动态代理 与 AOP 理解

来源:互联网 发布:windows自带画图工具 编辑:程序博客网 时间:2024/05/25 18:09

一.动态代理模式

    (1)产生的代理对象和目标对象实现了共同的接口;(jdk动态代理)

    JDK的动态代理 :
  1. 用Jdk的API做到的;
  2. 代理对象时动态产生的;
注意:
            1. 拦截器中invoke方法体的内容就是代理对象方法体的内容;
            2. 当客户端执行代理对象,方法的时候,进入到了拦截器的invoke的方法体内;
          3. 拦截器invoke方法中的method参数是在调用的时候赋值操作;  
            具体理解: http://blog.csdn.net/lablenet/article/details/50419811

 (2)代理对象时目标对象的子类;(spring:cglib动态代理)

   cglib产生的代理类是目标类的子类;

                        springAop工具包下载: http://download.csdn.net/detail/lablenet/9382210

         (3)示例说明cglib动态代理

                  1)场景描述

                        假设你正在进行一个查询系统中薪资的判断,故需要进行日志记录,安全监测,权限判断,后输出查询结果;

                 UML图:


                  2)日志记录类

public class Logging {public void pringlnLog(){System.out.println("Log 已记录");}}

                3)安全监测类

public class SafeCheck {public void safeCheckPrint(){System.out.println("安全性检测");}}

               4)权限类

public class AdminCheck {private String access;public String getAccess() {return access;}public void setAccess(String access) {this.access = access;}public void adminCheckPrint(){System.out.println("权限检测");}}

          5)dao层

public interface SalaryManager { void selectSalary();}

            实现类:

public class SalaryManagerImp implements SalaryManager {@Overridepublic void selectSalary() {System.out.println("薪资10000");}}

            6)拦截器

public class SalaryIntercepter implements MethodInterceptor{//目标类private Object target;//日志记录private Logging logging;//安全性检测private SafeCheck safeCheck;//权限检测private AdminCheck adminCheck;public SalaryIntercepter(Object target, Logging logging,SafeCheck safeCheck, AdminCheck adminCheck) {super();this.target = target;this.logging = logging;this.safeCheck = safeCheck;this.adminCheck = adminCheck;}public Object createProxy(){    Enhancer enhancer=new Enhancer();    enhancer.setCallback(this);    enhancer.setSuperclass(this.target.getClass());    return enhancer.create();    }@Overridepublic Object intercept(Object arg0, Method arg1, Object[] arg2,MethodProxy arg3) throws Throwable {logging.pringlnLog();safeCheck.safeCheckPrint();if(adminCheck.getAccess().equals("admin")){arg1.invoke(target, arg2);}else{System.out.println("你没有权限");}return null;}}

            7)测试

@Testpublic void testSalaryIntercepter() {AdminCheck adminCheck = new AdminCheck();adminCheck.setAccess("admin");Logging logging = new Logging();SafeCheck safeCheck = new SafeCheck();Object target = new SalaryManagerImp();SalaryIntercepter intercepter = new SalaryIntercepter(target, logging,safeCheck, adminCheck);SalaryManager manager = (SalaryManager) intercepter.createProxy();manager.selectSalary();}


 

     (4)重构

            在这里如果我们想要加上某个功能来监测,故我们进行拦截器重构实现;

            基本思路是: 1)提供一个监测接口,使得日志,安全,权限,都实现该接口,使用List进行重构实现;

   UML图 :



          1)监测接口

public interface Intercepter {void intercepterCheck();}

         2)日志记录类

public class Logging implements Intercepter{@Overridepublic void intercepterCheck() {System.out.println("Log 已记录");}}
  

        3)安全监测类

public class SafeCheck implements Intercepter{@Overridepublic void intercepterCheck() {System.out.println("安全性检测");}}

      4)dao层不变

      5)拦截器实现

public class SalaryIntercepter implements InvocationHandler {// 目标类private Object target;private List<Intercepter> intercepters;// //日志记录// private Logging logging;// //安全性检测// private SafeCheck safeCheck;// //权限检测// private AdminCheck adminCheck;public SalaryIntercepter(Object target, List<Intercepter> intercepters) {super();this.target = target;this.intercepters = intercepters;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable {for (Intercepter intercepter : intercepters) {intercepter.intercepterCheck();}method.invoke(target, args);return null;}}

        6).测试

@Testpublic void testSalaryIntercepter(){AdminCheck adminCheck=new AdminCheck();adminCheck.setAccess("admin");Logging logging = new Logging();SafeCheck safeCheck = new SafeCheck();Object target = new SalaryManagerImp();List<Intercepter> intercepters=new ArrayList<Intercepter>();intercepters.add(safeCheck);intercepters.add(adminCheck);intercepters.add(logging);SalaryIntercepter intercepter = new SalaryIntercepter(target, intercepters);SalaryManager salaryManager = (SalaryManager) Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),intercepter);salaryManager.selectSalary();}


二 .Aop

    (1).切面 

             事务,日志,安全性框架,权限等都是切面,非目标类的都是切面;

    (2).通知

        切面中的方法就是通知;

    (3).目标类

    (4).切入点

    只有符合切入点,才能让通知和目标方法结合起来;
     (5).织入

          形成代理对象的方法的过程; 


      好处:事务,日志,安全性框架,权限,目标方法之间完全是松耦合的;
      使用:找目标类及其找目标类的切面;
   

       (6) 具体价值步骤

          1)当spring容器启动的时候,加载了spring的配置文件;
  2)未配置文件中所有的类创建对象;
  3)spring容器解析aop:config的配置:解析切入点表达式,用切入点表达式纳入spring容器中的bean匹配;
     如果成功,则会位该bean创建代理对象,代理对象的方法=目标方法+通知
  4)在客户端利用context.getbean获取对象的时候,如果有代理对象,则返回代理对象;
  5)如果目标类没有实现接口,则spring容器采用cglib的方式产生代理对象,否则采用jdk的代理对象;
<aop:config>                                      <aop:pointcut expression="execution(* cn.labelnet.salary.SalaryManagerImp.*())" id="pointsalary"/>                                    <aop:aspect ref="logging">                       <aop:before method="pringlnLog" pointcut-ref="pointsalary"/>                   </aop:aspect>                                      <aop:aspect ref="adminCheck">                       <aop:around method="isAdmin" arg-names="point" pointcut-ref="pointsalary"/>                   </aop:aspect>                   <aop:aspect ref="safeCheck">                        <aop:before method="safeCheckPrint"  pointcut-ref="pointsalary"/>                   </aop:aspect>                              </aop:config>

 
   

       (7)各种通知

           1)前置通知(aop:before)
      在目标方法执行之前执行,无论目标方法是否抛出异常,都执行,因为在执行前置通知的时候,目标方法还没有执行,还没有遇到异常;
  
   <aop:before method="pringlnLog" pointcut-ref="pointsalary"/>


           2)后置通知(aop:after-returning)
      在目标方法之后执行,当目标方法遇到异常,后置通知不执行;后置通知可以接受目标方法的参数var,但是需要注意,后置通知的参数名称和配置文件中的returning="var"的值是一致的;
  
3)最终通知(aop:after)
      在目标方法执行之后执行,无论目标方法是否跑出异常,都执行,因为相当于finally;
  
 4)异常通知(aop:after-throwing)
                接收的目标方法和抛出的异常信息,异常方法中的参数th和配置文件中的throwing="th"一致;
  
 5)环绕通知(aop:)
       如果不在环绕通知中调用ProceedingJoinoPoint的proceed,目标方法不会执行;可以控制目标方法的执行;

     (8)示例:

             场景还是上面的查询薪资;

UML 图 :



        1)权限检查

public class AdminCheck {private String access;public String getAccess() {return access;}public void setAccess(String access) {this.access = access;}
        //重要 对于配置文件中 aop:round 环绕通知public void isAdmin(ProceedingJoinPoint point) throws Throwable{if(this.access.equals("admin")){point.proceed();}else{System.out.println("sorry,you no 权限");}}}

     2)日志记录

public class Logging {public void pringlnLog(){System.out.println("Log 已记录");}}

    3)安全监测

public class SafeCheck {public void safeCheckPrint(){System.out.println("安全性检测");}}


     4)dao层实现

          接口:

public interface SalaryManager { void selectSalary();}

        接口实现:

public class SalaryManagerImp implements SalaryManager {@Overridepublic void selectSalary() {System.out.println("薪资10000");}}


      5)配置实现

          在上面我们知道使用 Intercepter 拦截器实现,动态代理,在这里我们使用spring配置文件实现:

<?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-2.5.xsd">           <bean id="salaryTarget" class="cn.labelnet.salary.SalaryManagerImp"></bean>                      <bean id="adminCheck" class="cn.labelnet.salary.AdminCheck">              <property name="access" value="admin"></property>           </bean>                      <bean id="logging" class="cn.labelnet.salary.Logging"></bean>                      <bean id="safeCheck" class="cn.labelnet.salary.SafeCheck"></bean>                      <aop:config>                                      <aop:pointcut expression="execution(* cn.labelnet.salary.SalaryManagerImp.*())" id="pointsalary"/>                                    <aop:aspect ref="logging">                       <aop:before method="pringlnLog" pointcut-ref="pointsalary"/>                   </aop:aspect>                                      <aop:aspect ref="adminCheck">                       <aop:around method="isAdmin" arg-names="point" pointcut-ref="pointsalary"/>                   </aop:aspect>                   <aop:aspect ref="safeCheck">                        <aop:before method="safeCheckPrint"  pointcut-ref="pointsalary"/>                   </aop:aspect>                              </aop:config>  </beans>


3.Demo免积分下载

    http://download.csdn.net/detail/lablenet/9382126


0 0