文章目錄
- 什麼是代理
- 代理模式
-
- 靜态代理
-
-
- 實作步驟
-
- 動态代理
-
- 基本JDK動态代理
-
- 實作步驟:
- Spring 手動代理---JDK動态代理
-
- 實作步驟
- cglib動态代理
-
- 實作步驟
- Spring 手動代理 cglib位元組碼增強
-
- 實作步驟
- Spring 工廠Bean代理 ----半自動化
-
- 實作步驟
- Spring aop 實作代理
-
- 實作步驟
什麼是代理
我們大家都知道微商代理,簡單地說就是代替廠家賣商品,廠家“委托”代理為其銷售商品。關于微商代理,首先我們從他們那裡買東西時通常不知道背後的廠家究竟是誰,也就是說,“委托者”對我們來說是不可見的;其次,微商代理主要以朋友圈的人為目标客戶,這就相當于為廠家做了一次對客戶群體的“過濾”。我們把微商代理和廠家進一步抽象,前者可抽象為代理類,後者可抽象為目标類(被代理類)。通過使用代理,通常有兩個優點,并且能夠分别與我們提到的微商代理的兩個特點對應起來:
優點一:可以隐藏目标類的實作;
優點二:可以實作客戶與委托類間的解耦,在不修改委托類代碼的情況下能夠做一些額外的處理
參考連結:https://juejin.im/post/5ad3e6b36fb9a028ba1fee6a
代理模式
靜态代理
一般将代理類在程式運作之前就存在的代理模式稱為靜态代理。通常情況下,代理類與目标類都實作了同一接口。
實作步驟
1.目标類:
public class StuServiceImpl implements StuService{
@Override
public void addStu() {
System.out.println("add Stu!!!");
}
@Override
public void deleteStu() {
System.out.println("delete Stu!!!");
}
@Override
public void modifyStu() {
System.out.println("modify Stu!!!");
}
}
2.代理類:
public class AgencyStu implements StuService{
private StuService stuService;
//接收目标類執行個體
public AgencyStu(StuService stuService) {
this.stuService = stuService;
}
//實作代理
@Override
public void addStu() {
System.out.println("before add");
stuService.addStu();
System.out.println("end add");
}
@Override
public void deleteStu() {
System.out.println("before delete");
stuService.deleteStu();
System.out.println("end delete");
}
@Override
public void modifyStu() {
System.out.println("before modify");
stuService.modifyStu();
System.out.println("end modify");
}
}
可以看到上面代理類實作了對目标類的代理,但是由于代理類是在程式運作開始前就存在了,是以當存在不同的代理需求時,還需要再次建立一個新的代理類去實作代理需求,這就造成了代碼的備援,是以就出現了動态代理。
動态代理
基本JDK動态代理
代理類在運作時被建立的代理方式交動态代理。也就是說代理類不是在java代碼中定義的,而是在運作過程中在我們java代碼的訓示下動态生成的。相比于靜态代理,它可以實作對代理方法的統一處理,而不用像靜态代理一樣,挨個去編寫重複代碼。
實作步驟:
需要建立一個類似于"中介"的類,中介類需要實作InvocationHandler接口,并重寫method.invoke方法,然後調用Proxy.newProxyInstance建立代理類,實作動态代理。
例如:
目标類:
public class ShopServiceImpl implements ShopService{
@Override
public void addShop() {
System.out.println("add Shop!!!");
}
@Override
public void deleteShop() {
System.out.println("delete Shop!!!");
}
@Override
public void queryShop() {
System.out.println("query Shop!!!");
}
}
中介類:
public class Dynamicagency implements InvocationHandler {
private Object object;
//調用目标類執行個體
public Dynamicagency(Object object) {
this.object = object;
}
@Override
//代理類每執行方法時,都會調用此類的invoke方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before");
Object result = method.invoke(object,args);
System.out.println("after");
return result;
}
}
動态建立代理類
//生成中介類執行個體
Dynamicagency dynamicagency = new Dynamicagency(new ShopServiceImpl());
//生成代理類
ShopService shopService = (ShopService) Proxy.newProxyInstance(ShopService.class.getClassLoader(),
new Class[]{ShopService.class},dynamicagency);
shopService.addShop();
在這裡解釋一下Proxy.newProxyInstance的參數:
參數1 : loader,類加載器,動态代理類,運作時建立,任何類都需要加載器将其加載到記憶體。
參數2: Class[] interfaces 代理類需要實作的所有接口
1. 目标類執行個體.getClass().getInterfaces(); 注意隻能獲得自己接口,不能獲得父元素接口
2. new Class[]{interface.class}
參數3:InvocationHandler 處理類,接口,必須進行實作類,一般采取匿名内部類
提供invoke方法,代理類的每一個方法執行時,都将調用一次invoke。
參數31:Object proxy : 代理對象
參數32:Method method: 代理對象目前執行的方法的描述對象(反射)
參數33:Object[] args:方法實際參數
是以,代理類執行代理方法,其實就是對實作InvocationHandler 中介類的調用,然後中介類通過反射原理實作對原目标類方法的調用。
Spring 手動代理—JDK動态代理
在這裡說一下aop術語:
AOP 術語:
- target : 目标類,需要被代理的類,例如: UserService
- Joinpoint: 連接配接點,所謂連接配接點是指那些可能被攔截到的方法。例如所有方法
- PonitCut :切入點,指已經被增強的連接配接點。例如addUser()
- advice: 通知/增強,增強代碼。例如after,before
- Weaving : 織入,是指把增強advice應用到目标對象target來建立的代理對象proxy的過程。
- proxy 代理類
-
Aspect 切面 是把pointcut和通知advice的結合。
一個是一個特殊的面
一個切入點和一個通知,組合成一個特殊的面
圖示:
Java 實作代理模式以及通過Spring AOP 實作代理模式什麼是代理代理模式
實作步驟
- 目标(委托)類:接口+實作類
public class UserServiceImpl implements Userservice {
@Override
public void addUser() {
System.out.println("add User!!!");
}
@Override
public void deleteUser() {
System.out.println("delete User!!!");
}
@Override
public void updateUser() {
System.out.println("update User!!!");
}
}
- 切面類:用于通知
public class MyAspect {
public void before()
{
System.out.println("running before");
}
public void after()
{
System.out.println("running after");
}
}
- 工廠類: 編寫工廠生産代理
public class MyBeanfactory {
public static Userservice createUserSerive()
{
final Userservice userservice = new UserServiceImpl();
final MyAspect myAspect = new MyAspect();
Userservice result = (Userservice) Proxy.newProxyInstance(Userservice.class.getClassLoader()
,userservice.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
myAspect.before();
Object object = method.invoke(userservice,args);
myAspect.after();
return object;
}
});
return result;
}
}
cglib動态代理
Cglib是一個強大的、高性能的代碼生成包,它廣泛被許多AOP架構使用,為它們提供方法的攔截。
cglib動态代理的特點是目标類可以不用實作特定的方法,cglib在運作時建立目标類的增強類。
實作步驟
- 建立目标類(不用實作接口)
public class ShopServiceImpl {
public void addShop() {
System.out.println("add Shop!!!");
}
public void deleteShop() {
System.out.println("delete Shop!!!");
}
public void queryShop() {
System.out.println("query Shop!!!");
}
}
- 編寫攔截器(實作MethodInterceptor)
public class MyMethodIntecetor implements MethodInterceptor {
private ShopServiceImpl shopService;
public MyMethodIntecetor(ShopServiceImpl shopService) {
this.shopService = shopService;
}
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("before");
//兩種方法都可以
//Object result = methodProxy.invokeSuper(proxy,args);
Object result = method.invoke(shopService,args);
System.out.println("after");
return result;
}
}
其中MethodInterceptor中intercept方法的參數如下:
proxy 表示的是代理類
method:代理對象目前執行的方法的描述對象(反射)
args:方法實際參數
methodProxy:方法的代理
3. 建立代理類
Enhancer enhancer = new Enhancer();
//添加父類
enhancer.setSuperclass(ShopServiceImpl.class);
//調用回調函數
enhancer.setCallback(new MyMethodIntecetor(new ShopServiceImpl()));
//建立代理對象
ShopServiceImpl shopService = (ShopServiceImpl) enhancer.create();
shopService.addShop();
Spring 手動代理 cglib位元組碼增強
這種方式主要是通過BeanFactory生産代理對象,與上面不同的就是多個工廠還有含有通知的類。
實作步驟
- 目标類
public class ShopServiceImpl {
public void addShop() {
System.out.println("add Shop!!!");
}
public void deleteShop() {
System.out.println("delete Shop!!!");
}
public void queryShop() {
System.out.println("query Shop!!!");
}
}
- 含有通知的類
public class AdviceClass {
public void before()
{
System.out.println("before");
}
public void after()
{
System.out.println("after");
}
}
- 建立工廠
public class MyBeanFactory {
public static ShopServiceImpl createShopServiceImpl()
{
ShopServiceImpl shopService = new ShopServiceImpl();
AdviceClass adviceClass = new AdviceClass();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(ShopServiceImpl.class);
enhancer.setCallback(new MethodInterceptor() {
@Override
//建立匿名内部類
public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
adviceClass.before();
// 兩種寫法都可以
//Object result = methodProxy.invokeSuper(object,args);
Object result = method.invoke(shopService,args);
adviceClass.after();
return result;
}
});
ShopServiceImpl proxyShop = (ShopServiceImpl) enhancer.create();
return proxyShop;
}
}
Spring 工廠Bean代理 ----半自動化
上面的工廠是我們手動建立的,其實我們可以通過使用Spring中的ProxyFactoryBean代替手動建立工廠。下面是步驟。
實作步驟
- 目标類
public class UserServiceImpl implements UserService{
@Override
public void addUser() {
System.out.println("addUser!!!");
}
@Override
public void deleteUser() {
System.out.println("deleteUser!!!");
}
@Override
public void updateUser() {
System.out.println("updateUser!!!");
}
}
- 切面類
public class MyAspect implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("before");
//實作目标類的相應方法
Object object =methodInvocation.proceed();
System.out.println("after");
return object;
}
}
- 接下來是配置xml檔案
<!--建立目标類-->
<bean id="userServiceImplId" class="com.zamao.FactoryAgencyBeanXml.UserServiceImpl"></bean>
<!--建立切面類-->
<bean id="myAspect" class="com.zamao.FactoryAgencyBeanXml.MyAspect"></bean>
<!--建立代理類-->
<!--
使用工廠bean FactoryBean,底層調用getObject() 傳回特殊bean
ProxyFactoryBean 用于建立代理工廠bean,生成特殊代理對象
interfaces : 确定接口們 通過<array> 可以設定多個值 隻有一個值時,value=""
target: 目标類
interceptorNames : 通知切面類的名稱,類型String[],如果設定一個值value=""
-->
<bean id="proxyUserService" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="interfaces" value="com.zamao.FactoryAgencyBeanXml.UserService"> </property>
<property name="target" ref="userServiceImplId"></property>
<property name="interceptorNames" value="myAspect"></property>
</bean>
Spring aop 實作代理
與上面不同的是将配置工廠替換成了aop配置,相比于工廠配置來說更加簡便,也可以對攔截條件進行設定。
實作步驟
1.導入aspectjweaver-1.9.4.jar。
2. 引入aop命名空間
在xml中添加如下代碼:
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
3.目标類:
public class UserServiceImpl implements UserService {
@Override
public void addUser() {
System.out.println("addUser!!! springAopProgramm");
}
@Override
public void deleteUser() {
System.out.println("deleteUser!!! springAopProgramm");
}
@Override
public void updateUser() {
System.out.println("updateUser!!! springAopProgramm");
}
}
4.切面類:
public class MyAspect implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("before");
Object object =methodInvocation.proceed();
System.out.println("after");
return object;
}
}
5.配置xml
<!--建立目标類-->
<bean id="userServiceImplId" class="com.zamao.springAopProgramme.UserServiceImpl"></bean>
<!--建立切面類-->
<bean id="myAspect" class="com.zamao.springAopProgramme.MyAspect"></bean>
<!--aop程式設計
1. 引入命名空間
2. 使用<aop:config> 進行配置
<aop:pointcut> 切入點,從目标對象中獲得具體方法
<aop:advisor> 特殊的切面,隻有一個通知和一個切入點
advice-ref 通知引用
point-ref 切入點引用
3. 切入點表達式:
execution(傳回值 包 類名 (方法名) 參數名)
-->
<aop:config>
<aop:pointcut id="MyPointCut" expression="execution(* com.zamao.springAopProgramme.*.*(..))"/>
<aop:advisor advice-ref="myAspect" pointcut-ref="MyPointCut"></aop:advisor>
</aop:config>