您的位置:首页 > 编程语言 > Java开发

Spring - 动态代理 与 AOP 理解

2015-12-29 15:46 549 查看

一.动态代理模式

    (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 {

@Override
public 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();
}

@Override
public 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)测试
@Test
public 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{

@Override
public void intercepterCheck() {
System.out.println("Log 已记录");
}

}  
        3)安全监测类

public class SafeCheck implements Intercepter{

@Override
public 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;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {

for (Intercepter intercepter : intercepters) {
intercepter.intercepterCheck();
}
method.invoke(target, args);
return null;
}

}

        6).测试
@Test
public 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 {

@Override
public 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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息