一.动态代理模式

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


    JDK的动态代理 :

  1. 用Jdk的API做到的;

  2. 代理对象时动态产生的;

注意:

            1. 拦截器中invoke方法体的内容就是代理对象方法体的内容;

            2. 当客户端执行代理对象,方法的时候,进入到了拦截器的invoke的方法体内;

          3. 拦截器invoke方法中的method参数是在调用的时候赋值操作;   


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

                        springAop工具包下载

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

                  1)场景描述

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

                 UML图:

Spring - 动态代理 与 AOP 理解_示例


                  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图 :


Spring - 动态代理 与 AOP 理解_spring_02


          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 图 :

Spring - 动态代理 与 AOP 理解_aop_03



        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>