Spring代理+通知
阿新 • • 發佈:2018-11-20
一.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(),可以決定該方法是否繼續執行,這裡的話我們可以做一個業務邏輯的判斷。