设计模式
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代理