深入浅出设计模式---3、代理模式和工厂模式
代理模式
定义: 给某对象提供一个代理对象,通过代理对象可以访问该对象的功能。主要解决通过代理去访问[不能直接访问的对象,例如 租房中介,你可以直接通过中介去了解房东的房源信息,此时中介就可以称为代理。
优点: 1、职责清晰。 2、高扩展性。 3、智能化。
缺点: 1、由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。 2、实现代理模式需要额外的工作,有些代理模式的实现非常复杂。
代理实现方式:
基于接口的动态代理
- 提供者:JDK官方的Proxy类。
- 要求:被代理类最少实现一个接口。
基于子类的动态代理 - 提供者:第三方的CGLib,如果报asmxxxx异常,需要导入asm.jar。
- 要求:被代理类不能用final修饰的类(最终类)。
JDK动态代理
JDK动态代理要点:
- 被代理的类必须实现一个接口
- 创建代理对象的时候,用JDK代理需要实现InvocationHandler
- 代理过程在invoke中实现
我们以王五租房为例,王五通过中介直接租用户主房屋,中介在这里充当代理角色,户主充当被代理角色。
创建房东接口对象: LandlordService
public interface LandlordService {
void rentingPay(String name);
}
创建房东对象: Landlord
public class Landlord implements LandlordService{
/****
* @param name
*/
@Override
public void rentingPay(String name){ System.out.println(name+" 来交租!");
}
}
创建代理处理过程对象: QFangProxy
public class QFangProxy implements InvocationHandler{
private Object instance;
public QFangProxy(Object instance) {
this.instance = instance;
}
/****
* 代理过程
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
args[0] = "中介QFang带领租户"+args[0];
Object result = method.invoke(instance, args);
return result;
}
}
创建代理,并通过代理调用房东方法: JdkProxyTest
public class JdkProxyTest {
public static void main(String[] args) {
//给QFang产生代理
LandlordService landlordService = new Landlord();
QFangProxy proxy = new QFangProxy(landlordService);
LandlordService landlordServiceProxy = (LandlordService)Proxy.newProxyInstance(LandlordService.class.getClassLoader(), new Class[] {LandlordService.class}, proxy);
//通过代理对象调用Landlord对象的方法
landlordServiceProxy.rentingPay("王五");
}
}
运行结果如下:
中介QFang带领客户 王五 来交租!
CGLib动态代理
CGLib动态代理要点:
- 代理过程可以实现MethodInterceptor(Callback)接口中的invoke来实现
- 通过Enhancer来创建代理对象
在上面的案例基础上,把 QFangProxy 换成 SFangProxy ,代码如下:
public class SFangProxy implements MethodInterceptor {
private Object instance;
public SFangProxy(Object instance) {
this.instance = instance;
}
/***
* 代理过程
* @throws Throwable
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
args[0]="S房网带租户"+args[0];
return method.invoke(instance,args);
}
}
创建测试类: CGLibProxyTest,代码如下:
public class CGLibProxyTest {
public static void main(String[] args) {
//给QFang产生代理
LandlordService landlordService = new Landlord();
SFangProxy proxy = new SFangProxy(landlordService);
LandlordService landlordServiceProxy = (LandlordService)Enhancer.create(LandlordService.class,proxy);
//通过代理对象调用Landlord对象的方法
landlordServiceProxy.rentingPay("王五");
}
}
Spring AOP-动态代理
基于SpringAOP可以实现非常强大的功能,例如声明式事务、基于AOP的日志管理、基于AOP的权限管理等功能,利用AOP可以将重复的代码抽取,重复利用,节省开发时间,提升开发效率。Spring的AOP其实底层就是基于动态代理而来,并且支持JDK动态代理和CGLib动态代理,动态代理的集中体现在 DefaultAopProxyFactory类中,我们来解析下 DefaultAopProxyFactory类。

如果我们在spring的配置文件中不配置 <aop:config proxy-target-class=“true”>,此时默认使用的将是JDK动态 代理,如果配置了,则会使用CGLib动态代理。
JDK动态代理的创建 JdkDynamicAopProxy如下:
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
//创建代理对象
@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
logger.debug("Creating JDK dynamic proxy: target source is " +
this.advised.getTargetSource());
}
Class<?>[] proxiedInterfaces =
AopProxyUtils.completeProxiedInterfaces(this.advised, true);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
@Override
@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//JDK动态代理过程
}
}
CGLib动态代理的创建 ObjenesisCglibAopProxy如下:
protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
Class<?> proxyClass = enhancer.createClass();
Object proxyInstance = null;
if (objenesis.isWorthTrying()) {
try {
proxyInstance = objenesis.newInstance(proxyClass, enhancer.getUseCache());
} catch (Throwable var7) {
logger.debug("Unable to instantiate proxy using Objenesis, falling back to regular proxy construction", var7);
}
}
if (proxyInstance == null) {
try {
Constructor<?> ctor = this.constructorArgs != null ? proxyClass.getDeclaredConstructor(this.constructorArgTypes) : proxyClass.getDeclaredConstructor();
ReflectionUtils.makeAccessible(ctor);
proxyInstance = this.constructorArgs != null ? ctor.newInstance(this.constructorArgs) : ctor.newInstance();
} catch (Throwable var6) {
throw new AopConfigException("Unable to instantiate proxy using Objenesis, and regular proxy instantiation via default constructor fails as well", var6);
}
}
((Factory)proxyInstance).setCallbacks(callbacks);
return proxyInstance;
}
工厂设计模式
定义: 工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种 创建对象的最佳方式。它负责实现创建所有实例的内部逻辑。工厂类的创建产品类的方法可以被外界直接调用,创建所需的 产品对象。
优点:
- 一个调用者想创建一个对象,只要知道其名称就可以了。
- 屏蔽产品的具体实现,调用者只关心产品的接口。
- 降低了耦合度
工厂模式案例

我们来做这么一个案例,创建一个接口Product和Product的实现类Mobile以及Car,再定义一个具体的工厂对ProcutFactory,并通过ProductFactory来获取指定的Product。
Product接口:
public interface Product {
void show();
}
创建接口实现类Mobile:
public class Mobile implements Product {
@Override
public void show() {
System.out.println("HUWEI Mate 30 Pro"); }
}
创建接口实现类Car:
public class Car implements Product {
@Override
public void show() {
System.out.println("国产车 BYD");
}
}
创建工厂ProductFactory,根据参数创建指定产品对象:
public class ProductFactory {
/***
* 调用info方法,传入需要创建的对象的名字来创建具体对象 * @param name
* @return
*/
public Product info(String name){
if(name.equals("mobile")){
return new Mobile();
}else if(name.equals("car")){
return new Car();
}else{
return null;
}
}
}
使用工厂ProductFactory创建指定对象:
public class ProductFactoryTest {
public static void main(String[] args) {
//创建ProductFactory
ProductFactory factory = new ProductFactory();
//获取Mobile对象
Product mobile = factory.info("mobile");
mobile.show();
//获取Car
Product car = factory.info("car");
car.show();
}
}
运行结果如下:
HUWEI Mate 30 Pro
国产车 BYD
BeanFactory工厂模式
Spring内部源码也有工厂模式的实现,并且解决了上面我们提到的工厂模式的缺陷问题。
Spring中的BeanFactory就是简单工厂模式的体现,根据传入一个唯一的标识来获得Bean对象,但是否是在传入参 数后创建还是传入参数前创建这个要根据具体情况来定。
BeanFactory源码:
public interface BeanFactory {
//根据参数获取指定对象的实例
Object getBean(String name) throws BeansException;
<T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException; Object getBean(String name, Object... args) throws BeansException;
<T> T getBean(Class<T> requiredType) throws BeansException;
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
}
在BeanFactory接口中,有多个getBean方法,该方法其实就是典型的工厂设计模式特征,在接口中定义了创建对象 的方法,而对象如何创建其实在接口的实现类中实现 DefaultListableBeanFactory。
我们用Spring的工厂对象BeanFactory来解决上面工厂模式案例所带来的问题。
案例:
<bean id="car" class="com.factory.Car"/>
<bean id="mobile" class="com.factory.Mobile"/>
测试:
public void testBeanFactory(){
//加载核心配置文件 beas.xml
Resource resource = new ClassPathResource("beans.xml");
//创建容器对象BeanFactory
BeanFactory beanFactory = new XmlBeanFactory(resource);
//使用Car对象
Product car = (Product) beanFactory.getBean("car");
//使用Mobile对象
Product mobile = (Product) beanFactory.getBean("mobile");
car.show();
mobile.show();
}
结果:
国产车 BYD
HUWEI Mate 30 Pro
使用Spring的BeanFactory,以后要新增一个产品,只需要创建产品对应的xml配置即可,而不需要像 ProductFactory那样硬编码存在。
更多推荐
所有评论(0)