1. 程式人生 > >Spring代理+通知

Spring代理+通知

一.23設計模式之代理模式

組成:

抽象角色:通過介面或抽象類宣告真實角色的業務方法。

代理角色:實現抽象角色,是真實角色的代理,通過真實角色業務邏輯來實現抽象的方法,也可以附加自己的操作。

真實角色:實現抽象角色,定義真實角色所要實現的業務邏輯,供代理角色呼叫。

首先來看看Java的代理模式:

1.靜態代理

package com.zking.spring02.staticproxy;

/**
 * 
* @ClassName: IPerson
* @Description: 抽象角色
* @author 夏
* @date 2018年9月18日 下午2:35:33
*
 */
public interface IPerson {
	/**
	 * 
	* @Title: dance
	* @Description: 跳舞的方法
	* @return void
	 */
	public void dance();
	
}
package com.zking.spring02.staticproxy;

/**
 * 
* @ClassName: Proxy
* @Description: 抽象代理
* @author 夏
* @date 2018年9月18日 下午2:35:53
*
 */
public class Proxy  implements IPerson{

	//定義介面物件
	private IPerson p;
	
	public Proxy(IPerson p) {
		super();
		this.p = p;
	}

/**
 * 實現介面方法
 */
	public void dance() {
    p.dance();	
	}
	
	
	
	
	
	
	

}
package com.zking.spring02.staticproxy;

/**
 * 
* @ClassName: Person
* @Description: 真實角色
* @author 夏
* @date 2018年9月18日 下午2:35:21
*
 */
public class Person implements IPerson {

/**
 * 實現跳舞的方法
 */
	public void dance() {
	System.out.println("dance");
	}

}
package com.zking.spring02.staticproxy;

/**
 * 
* @ClassName: Test
* @Description: 測試類
* @author 夏
* @date 2018年9月18日 下午2:41:35
*
 */
public class Test {
	public static void main(String[] args) {
		//定義真實角色物件
		Person p=new Person();
		IPerson ip=new Proxy(p);
	    ip.dance();
		
	}
	


	
	
	
}

可以這樣理解,抽象角色宣告我真實角色的業務行為,然後代理去實現抽象角色,也就是去接這個這個業務,接到這個業務後,然後由真實角色去實現這個行為。

2.java動態代理

package com.zking.spring02.dynamicproxy;

/**
 * 
* @ClassName: IPerson
* @Description: 抽象角色
* @author 夏
* @date 2018年9月18日 下午2:35:33
*
 */
public interface IPerson {
	
	/**
	 * 
	* @Title: dance
	* @Description: 業務行為介面方法
	* @return void
	 */
	public void dance();
	
}
package com.zking.spring02.dynamicproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;



/**
 * 
* @ClassName: Proxy
* @Description: 抽象代理
* @author 夏
* @date 2018年9月18日 下午2:35:53
*
 */
public class Proxy  implements InvocationHandler{   

	//定義介面物件
	private IPerson ip;

	public Proxy(IPerson ip) {
		super();
		this.ip = ip;
	}

/**
 * 實現方法
 */
	public Object invoke(Object arg0, Method arg1, Object[] arg2) throws Throwable {
		return arg1.invoke(ip, arg2);
	}


	
	
	
	
	
	
	

}
package com.zking.spring02.dynamicproxy;

/**
 * 
* @ClassName: Person
* @Description: 真實角色
* @author 夏
* @date 2018年9月18日 下午2:35:21
*
 */
public class Person implements IPerson {
/**
 * 實現介面方法
 */
	public void dance() {
	System.out.println("dance");
	}

}
package com.zking.spring02.dynamicproxy;


import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;



/**
 * 
* @ClassName: Test
* @Description: 測試類
* @author 夏
* @date 2018年9月18日 下午2:41:35
*
 */
public class Test {
	public static void main(String[] args) {
        //定義真實角色
		Person p=new Person();
		//代理真實角色
		InvocationHandler  invocationHandler=new com.zking.spring02.dynamicproxy.Proxy(p);
		//介面物件
		IPerson ip=(IPerson) Proxy.newProxyInstance(Person.class.getClassLoader(), Person.class.getInterfaces(), invocationHandler);
     
		ip.dance();
		
	}
	


	
	
	
}

java靜態代理和動態代理的區別:

靜態代理實現的是抽象角色的介面,每次介面類多增加一個方法,代理類就要去實現,如果程式規模大的話那麼程式碼的複雜程度也就大,重複的程式碼也就多了。

動態代理實現的介面是InvocationHandler,實現的方法相當於接口裡面的方法被集中處理,靈活性高,復讀性強。

 

3.Spring代理

package com.zking.spring02.springproxy;

/**
 * 
* @ClassName: IPerson
* @Description: 抽象角色
* @author 夏
* @date 2018年9月18日 下午2:35:33
*
 */
public interface IPerson {
	
	public void dance();
	
}
package com.zking.spring02.springproxy;

/**
 * 
* @ClassName: Person
* @Description: 真實角色
* @author 夏
* @date 2018年9月18日 下午2:35:21
*
 */
public class Person implements IPerson {

	public void dance() {
	System.out.println("dance");
	}

}
<?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:context="http://www.springframework.org/schema/context"
	xmlns:util="http://www.springframework.org/schema/util"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
		http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.3.xsd">

<!-- 配置目標 -->
<bean id="p" class="com.zking.spring02.springproxy.Person"></bean>

<!-- 配置混合代理 -->
<bean id="myProxy" class="org.springframework.aop.framework.ProxyFactoryBean">

<!-- 引入目標 -->
<property name="target" ref="p"></property>

<!-- 代理介面 -->
<property name="proxyInterfaces">
<list>
<!-- 介面 -->
<value>com.zking.spring02.springproxy.IPerson</value>
</list>
</property>



</bean>




</beans>

 

package com.zking.spring02.springproxy;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * 
* @ClassName: Test
* @Description: 測試類
* @author 夏
* @date 2018年9月18日 下午2:41:35
*
 */

public class Test {
	
	@org.junit.Test
public void SpringProxy() {
	ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
IPerson ip=	(IPerson) applicationContext.getBean("myProxy");
	ip.dance();
	
	}


	
	
	
}

這裡我們可以清楚的看到,java類中已經沒有了代理類了,這裡的代理類是以一種xml配置的形式建立bean物件,代理的所有操作,在混合代理中已經實現了混合代理的bean物件中就包含了目標--真實角色,以及代理介面--抽象角色,實現原理大體一致,只是形式不同而已

二.Spring通知

Spring通知可分為:前置通知,後置通知,環繞通知。

Java類定義前置通知

package com.zking.spring02.advice;

import java.lang.reflect.Method;

import org.springframework.aop.MethodBeforeAdvice;

/**
 * 
* @ClassName: BeforeAdvice
* @Description:前置通知類
* @author 夏
* @date 2018年9月18日 下午3:51:48
*
 */
public class BeforeAdvice implements MethodBeforeAdvice{

	public void before(Method method, Object[] args, Object target) throws Throwable {
	System.out.println("前置通知");
	}

}

Java類定義後置通知

package com.zking.spring02.advice;

import java.lang.reflect.Method;

import org.springframework.aop.AfterReturningAdvice;

/**
 * 
* @ClassName: AfterAdvice
* @Description: 後置通知類
* @author 夏
* @date 2018年9月18日 下午10:46:07
*
 */
public class AfterAdvice implements AfterReturningAdvice{

	public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
	System.out.println("後置通知");
		
	}

}

Java類定義環繞通知

package com.zking.spring02.advice;


import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;


/**
 * 
* @ClassName: Interceptor
* @Description: 環繞通知類
* @author 夏
* @date 2018年9月18日 下午10:46:23
*
 */
public class Interceptor implements MethodInterceptor{

	public Object invoke(MethodInvocation invocation) throws Throwable {
		System.out.println("環繞通知");
		return invocation.proceed();
	}


}

在xml代理中使用各通知

<?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:context="http://www.springframework.org/schema/context"
	xmlns:util="http://www.springframework.org/schema/util"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
		http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.3.xsd">

<!-- 配置目標 -->
<bean id="p" class="com.zking.spring02.springproxy.Person"></bean>
<!-- 配置混合代理 -->
<bean id="myProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 引入目標 -->
<property name="target" ref="p"></property>
<!-- 代理介面 -->
<property name="proxyInterfaces">
<list>
<value>com.zking.spring02.springproxy.IPerson</value>
</list>
</property>

<!-- 引用通知 -->
<property name="interceptorNames">
<list>
<!-- 通知過濾 -->
<idref bean="myAdvisor"/>
<!-- 前置通知 -->
<idref bean="BeforeAdvice"/>
<!-- 後置通知 -->
<idref bean="AfterAdvice"/>
<!-- 環繞通知 -->
<idref bean="Interceptor"/>
</list>
</property>


</bean>

<!-- 配置前置通知 -->
<bean id="BeforeAdvice" class="com.zking.spring02.advice.BeforeAdvice"></bean>
<!-- 配置後置通知 -->
<bean id="AfterAdvice" class="com.zking.spring02.advice.AfterAdvice"></bean>
<!-- 配置環繞通知 -->
<bean id="Interceptor" class="com.zking.spring02.advice.Interceptor"></bean>

<!-- 配置通知過濾 -->
<bean id="myAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice" ref="BeforeAdvice"></property>

<!-- 指定特有方法設定通知 -->
<property name="pattern" value=".*dance*."></property>
</bean>




</beans>

可以看到,每個通知的實現的介面都是不一樣的,所以帶來的效果也是不同的,使用通知的前提條件是,必須要在xml檔案中配置相應的通知,然後代理再去引用這些通知來達到想要的通知效果。

前置通知的位置在實現方法之前,那麼一般我們可以用這個通知做編碼格式轉換,以及資訊的加密,解密等。

後置通知的位置是在實現方法之後,那麼這個通知我們可以做一個方法操作日誌等。

那麼環繞通知是在前置通知之後,它的return invocation.proceed(),可以決定該方法是否繼續執行,這裡的話我們可以做一個業務邏輯的判斷。