设计模式之代理模式与面向切面

666 阅读6分钟

「这是我参与2022首次更文挑战的第18天,活动详情查看:2022首次更文挑战

前面提到了这个观察者模式,那这里顺便说一下工厂模式和面向切面,我们使用动态代理实现面向切面,使用配置文件实现一个组件管理吧,这个也是以前写的博文。

何为AOP

用不是人话来说就是面向切面编程,什么是切面,就是和我们程序的主体功能关系不大的那一方面,例如,程序的日志功能,这个和程序的主体功能没有任何影响,但是有时候确实必要的,但有时候我又不希望在我的主体程序中出现,这就意味着这个切面必须和我的主体程序这个面保持相对独立,在我需要的时候将其装配即可。事实上我们所谓的切面可以代指所有的功能独立的那一个面,每个面之间不会相互影响,但是又可以在一个组合使用。

实例

在这个实例当中我们将使用一个动态代理的实例以AOP的模式进行编写。

Dynamic agent 的编写

我们先假设个例子以买房子找中介的例子,进行编写

Dome的流程图

为了便于博客的理解我们先查看两个流程图,一个是我们买房子(代理)的流程图,另一个是完整的图。

代理流程图

在这里插入图片描述

完整的流程

在这里插入图片描述

Dome的项目结构

在这里插入图片描述 看名字已经很清楚了,接下来要说明的就是这个项目在一个包day07下面。

完整代码

按照流程图,先给出代理部分,然后给出反射部分。

package day07;
public interface BuyHouse {
    public void buyHouse();
}

package day07;
public class PersonBuyHouse implements BuyHouse {
    public void buyHouse(){
        System.out.println("房子很满意,就它了全款!");
    }
}

package day07;
public interface HouseAgency {
    public void FindHouse();
}

package day07;

public class HouseAgent implements HouseAgency {

    @Override
    public void FindHouse() {
        // TODO Auto-generated method stub
        System.out.println("正在寻找房源");
        System.out.println("房源已找到,满足客户需求");
    }
    
}

配置文件 bean.properties

bean.PersonBuyHouse = day07.PersonBuyHouse
bean.HouseAgent = day07.HouseAgent
bean.SendAgentFindHouse = day07.SendAgentFindHouse
package day07;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

//工厂类,让中介所派一个中介来找房子
public class SendAgentFindHouse implements InvocationHandler {

    private HouseAgency houseAgency;
    private BuyHouse buyHouse;
    public Object SendAgent(){
        Object proxy = null;
        proxy = Proxy.newProxyInstance(buyHouse.getClass().getClassLoader(),buyHouse.getClass().getInterfaces(), this);
        return proxy;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 中介找房子,用户付钱
        houseAgency.FindHouse();
        buyHouse.buyHouse();
        return null;
    }
    public HouseAgency getHouseAgency() {
        return houseAgency;
    }


    public void setHouseAgency(HouseAgency houseAgency) {
        this.houseAgency = houseAgency;
    }


    public BuyHouse getBuyHouse() {
        return buyHouse;
    }


    public void setBuyHouse(BuyHouse buyHouse) {
        this.buyHouse = buyHouse;
    }

    
}

package day07;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.util.Properties;





public class BeanFactory {
    private Properties prop = new Properties();
    public BeanFactory(InputStream in){
        try {
            prop.load(in);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    public BeanFactory(InputStreamReader in){
        try {
            prop.load(in);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    public Object getBean(String name){
        Object bean=null;
        try {
            Class<?> beanClass = Class.forName(prop.getProperty(name));
            bean = beanClass.getConstructor().newInstance();
            Object buyhouse = Class.forName(prop.getProperty("bean.PersonBuyHouse")).getConstructor().newInstance();
            Object houseagent = Class.forName(prop.getProperty("bean.HouseAgent")).getConstructor().newInstance();
            BeanInfo beanInfo = Introspector.getBeanInfo(beanClass);
            PropertyDescriptor[] propertyDescriptor = beanInfo.getPropertyDescriptors();
            for(PropertyDescriptor pd:propertyDescriptor){
                if(pd.getName().equals("houseAgency")){
                    pd.getWriteMethod().invoke(bean, houseagent);
                }else if(pd.getName().equals("buyHouse")){
                    pd.getWriteMethod().invoke(bean, buyhouse);
                }
            }
            

        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SecurityException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IntrospectionException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }



        return bean;


    }
    
}

package day07;

import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;

public class AOPDomeTest {
    public static void main(String[] args) {

        InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream("day07/bean.properties");
        try {
            BeanFactory beanFactory = new BeanFactory(new InputStreamReader(in,"utf8"));
            SendAgentFindHouse sendAgentFindHouse = (SendAgentFindHouse) beanFactory.getBean("bean.SendAgentFindHouse");
            BuyHouse buyHouse = (BuyHouse) sendAgentFindHouse.SendAgent();
            buyHouse.buyHouse();




        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    
}

总结

按照上面的例子可能还不太形象,但是,通过编码,和流程图可以发现,买房子和中介在代码上毫无直接关系,但是在我们有需求的时候可以很快的组合在一起,并且每一个模块都是相对独立的,拓展性,通过配置文件可以具有很高的拓展性,我们只需要修改配置文件即可,甚至不需要进行代码的重新编写。那么这种模式就是AOP模式,目前大量的框架都在使用这种模式,在未来的研读Spring框架当中会大量见到。

补充

Java代理模式

此代理非彼代理(玩爬虫,玩渗透,玩翻墙)的小伙伴请保持镇定。这个呢其实是一种设计模式。其作用呢其实就是通过另一个代理方法来实现对另一个类方法的实现。在现实生活中举个例子就是,你想购买一件东西然后去找代购一样。流程图大概就是这个样子的。 (下面是个例子) 在这里插入图片描述

代理

代理又分为两种一种是静态代理,一种是动态代理,这玩意主要是通过反射来实现的。

静态代理

这里直接举个海外代购衣服的例子。

public class interface Buy{
	//想要做的那件事,公共的接口
	public void buy();
}

public class Person implement Buy{

	public void buy(){
	Sysout.out.println("货很满意,付款");
	//对于Person来说买衣服付钱就好了,其他的事情会给代购去做
	}
}

public class BuyProxy implement Buy{
	//专门代购的人,先确定代购的对象,然后帮他代购
	private Buy buyer;
	public void BuyProxy(Buy buyer){
		this.buyer = buyer
	}
	public void buy(){
	 	System.out.println("已找到合适的货物");
	 	buyer.buy();
	}

}

class Test{

	public static void main(String[] args){
		Buy you = new Person();
		BuyProxy buyproxy = new BuyProxy(you);
		buyproxy.buy();
	}
}

在上面这个流程上看起来和我们python的装器有点像。但是区别是有的,python做了一个覆盖的操作,当然你不用@这个语法糖豆然后将我们的被装饰方法和原来的方法区别开来即可(python毕竟不是一个纯面对对象的一款计算机语言)。

动态代理

这里就不得不提到到我们的反射这个机制了。这个动态的好处就是,不用像静态那样写一个接口就要写一个新的动态方法。这个完全没有必要。 最主要的就是通过Proxy.newProxyInstance()这个方法来帮助我们生成一个代理类 下面是实例:

public interface Subject {
	//购物的目的
    public void shopping(int mon);
}

////////////////////////////////////////////////////////////////////////////

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class CreateProxy implements InvocationHandler {

    private Object target;//需要被代理的对象,也就是买衣服的人的对象

    //创建代理对象
    public Object create(Object target){
        this.target = target;
        Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);

        return proxy;
        
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // TODO Auto-generated method stub
        //代理对象要实现的方法

        System.out.println("从海外寻找产品");
        System.out.println("已找到");
        method.invoke(target, args);//有参数的话args会传进去
        return null;
    }
    
}

////////////////////////////////////////////////////////////////////////////
public class Person implements Subject {

    @Override
    public void shopping(int mon) {
        // TODO Auto-generated method stub
        System.out.println("买件衣服,满意付款");
        System.out.println("给了"+mon+"的钱款");
        
    }
    
    
}

////////////////////////////////////////////////////////////////////////////
public class BuyDoingTest {
    
    public static void main(String[] args) {
        CreateProxy cp = new CreateProxy();
        Subject person = new Person();
        Subject proxy = (Subject)cp.create(person);
        proxy.shopping(30);//调用的是invoke
    }
    
}