設計模式
java設計模式之單例模式詳解(六種)
java設計模式之工廠模式講解
java設計模式之建造者模式
java設計模式之原型模式
設計模式模版方法
介紹
- 代理模式就是通過代理類控制目標物件,外界通過代理類提供的介面完成對目標物件的訪問,通過代理類,可以在不影響目標物件的前提下,擴展一些功能,如權限校驗等,
- 代理模式主要分為兩大類:靜態代理和動態代理((JDK 代理、Cglib 代理)
- 代理模式是23種設計模式中的一種,屬于設計模式中的結構型模式,
組成
抽象角色:通過介面或抽象類宣告真實角色實作的業務方法,
代理角色:實作抽象角色,是真實角色的代理,通過真實角色的業務邏輯方法來實作抽象方法,并可以附加自己的操作,
真實角色:實作抽象角色,定義真實角色所要實作的業務邏輯,供代理角色呼叫
靜態代理
靜態代理的實作需要代理類和目標物件(被代理類)實作相同的介面
甲方公司招聘一批程式員開發非核心業務,為了降低開支,通過外包公司進行招聘,外包公司就是代理角色,外包程式員就是真實角色,程式員是抽象角色
抽象角色
/**
* @author yz
* @version 1.0
* @date 2020/12/2 16:28
* 程式員(抽象角色)
*/
public interface Programmer {
//開始編碼
public void startCoding();
}
真實角色
/**
* @author yz
* @version 1.0
* @date 2020/12/2 16:32
* 外包程式員(真實角色)
*/
public class OutsourcedProgrammer implements Programmer{
@Override
public void startCoding() {
System.out.println("程式員開始編碼");
}
}
代理角色
/**
* @author yz
* @version 1.0
* @date 2020/12/2 16:33
* 外包公司(代理角色)
*/
public class OutsourcedCompanyProxy implements Programmer{
//程式員
private Programmer programmer;
public OutsourcedCompanyProxy(Programmer programmer){
this.programmer=programmer;
}
@Override
public void startCoding() {
System.out.println("招聘程式員");
System.out.println("面試");
System.out.println("簽訂外包公司勞動合同");
programmer.startCoding();
}
}
客戶端
/**
* @author yz
* @version 1.0
* @date 2020/12/2 16:36
*/
public class Client {
public static void main(String[] args) {
Programmer programmer=new OutsourcedProgrammer();
OutsourcedCompanyProxy outsourcedCompanyProxy=new OutsourcedCompanyProxy(programmer);
outsourcedCompanyProxy.startCoding();
}
}
結果
招聘程式員
面試
簽訂外包公司勞動合同
程式員開始編碼
從代碼中可以看出,甲方公司并沒有和程式員直接接觸,而是通過外包公司,避免了和程式員耦合,降低了福利開支和管理成本
缺點
- 因為代理類和目標物件必須實作同一個介面,代理類必須和目標物件數量一致,導致代理類數量過多
- 因為采取了硬編碼的方式,如果介面增加方法,代理類和目標物件必須進行修改
JDK代理
jdk代理是動態代理的一種,是通過反射實作的,和靜態代理不同的是,代理類不再需要實作目標物件的介面
因為甲方公司需要的程式員數量過多,交給一個外包公司風險過大,而是交給了三個外包公司,這樣必須創建三個代理類,難以管理,甲方公司后續希望程式員進行測驗、運維等作業,就需要修改外包公司和程式員,
通過動態代理可以解決這個問題
抽象角色
/**
* @author yz
* @version 1.0
* @date 2020/12/2 16:28
* 程式員(抽象角色)
*/
public interface Programmer {
//開始編碼
public void startCoding();
}
真實角色
/**
* @author yz
* @version 1.0
* @date 2020/12/2 16:32
* A公司外包程式員(真實角色)
*/
public class OutsourcedAProgrammer implements Programmer {
@Override
public void startCoding() {
System.out.println("A公司外包程式員開始編碼");
}
}
/**
* @author yz
* @version 1.0
* @date 2020/12/2 16:32
* B公司外包程式員(真實角色)
*/
public class OutsourcedBProgrammer implements Programmer {
@Override
public void startCoding() {
System.out.println("B公司外包程式員開始編碼");
}
}
/**
* @author yz
* @version 1.0
* @date 2020/12/2 16:32
* C公司外包程式員(真實角色)
*/
public class OutsourcedCProgrammer implements Programmer {
@Override
public void startCoding() {
System.out.println("C公司外包程式員開始編碼");
}
}
代理角色
/**
* @author yz
* @version 1.0
* @date 2020/12/2 16:33
* 外包公司(代理角色)
*/
public class OutsourcedCompanyProxy {
//程式員
private Object target;
public OutsourcedCompanyProxy(Object target){
this.target=target;
}
//獲取代理物件
public Object getProxyInstance() {
// newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
//loader:目標類的類加載器
//interfaces 目標類所實作的介面
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("招聘程式員");
System.out.println("面試");
System.out.println("簽訂外包公司勞動合同");
return method.invoke(target, args);
}
});
}
}
客戶端
/**
* @author yz
* @version 1.0
* @date 2020/12/2 15:33
*/
public class Client {
public static void main(String[] args) {
//A外包公司程式員
Programmer aProgrammer = new OutsourcedAProgrammer();
Programmer proxyInstanceA = (Programmer) new OutsourcedCompanyProxy(aProgrammer).getProxyInstance();
proxyInstanceA.startCoding();
//B外包公司程式員
Programmer bProgrammer = new OutsourcedBProgrammer();
Programmer proxyInstanceB=(Programmer)new OutsourcedCompanyProxy(bProgrammer).getProxyInstance();
proxyInstanceB.startCoding();
//C外包公司程式員
Programmer cProgrammer = new OutsourcedCProgrammer();
Programmer proxyInstanceC=(Programmer)new OutsourcedCompanyProxy(cProgrammer).getProxyInstance();
proxyInstanceC.startCoding();
}
}
結果
招聘程式員
面試
簽訂外包公司勞動合同
A公司外包程式員開始編碼
招聘程式員
面試
簽訂外包公司勞動合同
B公司外包程式員開始編碼
招聘程式員
面試
簽訂外包公司勞動合同
C公司外包程式員開始編碼
Cglib代理
- jdk動態代理有一個很大的局限性,目標物件必須實作介面,但是有的目標物件并不需要一個介面,這就需要采用Cglib 代理
- Cglib代理-子類代理,底層是通過使用位元組碼處理框架 ASM 來轉換位元組碼并生成子類物件
- Cglib代理通過重寫intercept方法實作代理類擴展,Spring AOP就是采用Cglib實作動態代理
真實角色
/**
* @author yz
* @version 1.0
* @date 2020/12/2 16:32
* 外包程式員(真實角色)
*/
public class OutsourcedProgrammer{
public void startCoding() {
System.out.println("程式員開始編碼");
}
}
代理角色
/**
* @author yz
* @version 1.0
* @date 2020/12/2 16:33
* 外包公司(代理角色)
*/
public class OutsourcedCompanyProxy implements MethodInterceptor {
//程式員
private Object target;
public OutsourcedCompanyProxy(Object target){
this.target=target;
}
//獲取代理物件
public Object getProxyInstance() {
//cglib增強器,用來創建代理物件
Enhancer enhancer = new Enhancer();
//設定需要創建的代理物件
enhancer.setSuperclass(target.getClass());
//設定回呼,所有方法都會被intercept攔截
enhancer.setCallback(this);
//創建代理物件
return enhancer.create();
}
//重寫intercept方法
@Override
public Object intercept(Object arg0, Method method, Object[] args, MethodProxy arg3) throws Throwable {
System.out.println("招聘程式員");
System.out.println("面試");
System.out.println("簽訂外包公司勞動合同");
return method.invoke(target, args);
}
}
客戶端
/**
* @author yz
* @version 1.0
* @date 2020/12/2 15:51
*/
public class Client {
public static void main(String[] args) {
OutsourcedProgrammer programmer = new OutsourcedProgrammer();
OutsourcedProgrammer proxyInstance = (OutsourcedProgrammer)new OutsourcedCompanyProxy(programmer).getProxyInstance();
proxyInstance.startCoding();
}
}
結果
招聘程式員
面試
簽訂外包公司勞動合同
程式員開始編碼
區別
| 靜態代理 | JDK代理 | Cglib代理 | |
|---|---|---|---|
| 實作方式 | 硬編碼 | JDK API | Cglib |
| 實作介面 | 需要 | 需要 | 不需要 |
Spring AOP動態代理應用原始碼
/*
* Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.aop.framework;
import java.io.Serializable;
import java.lang.reflect.Proxy;
import org.springframework.aop.SpringProxy;
/**
* Default {@link AopProxyFactory} implementation, creating either a CGLIB proxy
* or a JDK dynamic proxy.
*
* <p>Creates a CGLIB proxy if one the following is true for a given
* {@link AdvisedSupport} instance:
* <ul>
* <li>the {@code optimize} flag is set
* <li>the {@code proxyTargetClass} flag is set
* <li>no proxy interfaces have been specified
* </ul>
*
* <p>In general, specify {@code proxyTargetClass} to enforce a CGLIB proxy,
* or specify one or more interfaces to use a JDK dynamic proxy.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @author Sebastien Deleuze
* @since 12.03.2004
* @see AdvisedSupport#setOptimize
* @see AdvisedSupport#setProxyTargetClass
* @see AdvisedSupport#setInterfaces
*/
@SuppressWarnings("serial")
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
/**
* Whether this environment lives within a native image.
* Exposed as a private static field rather than in a {@code NativeImageDetector.inNativeImage()} static method due to https://github.com/oracle/graal/issues/2594.
* @see <a href="https://github.com/oracle/graal/blob/master/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/ImageInfo.java">ImageInfo.java</a>
*/
private static final boolean IN_NATIVE_IMAGE = (System.getProperty("org.graalvm.nativeimage.imagecode") != null);
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
// config.isProxyTargetClass()默認值為false
// hasNoUserSuppliedProxyInterfaces(config)判斷bean是否有介面
if (!IN_NATIVE_IMAGE &&
(config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
//isInterface 判斷是否是一個介面
//isProxyClass 判斷是否是代理類
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}else{
return new JdkDynamicAopProxy(config);
}
}
/**
* Determine whether the supplied {@link AdvisedSupport} has only the
* {@link org.springframework.aop.SpringProxy} interface specified
* (or no proxy interfaces specified at all).
*/
private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
Class<?>[] ifcs = config.getProxiedInterfaces();
return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0])));
}
}
createAopProxy方法是創建目標物件的核心方法,可以看出AOP采用了JDK代理和Cglib代理兩種方式,如果目標物件實作了介面,將采用JDK代理,沒有實作介面,采用Cglib代理
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/231552.html
標籤:其他
上一篇:2020 辦公室帶薪養生指南
