手写IOC和AOP(二)----new关键字强耦合改造

在转账案例中,TransferServlet和TransferServiceImpl代码中,直接用的new关键字去创建对象,属于一个强耦合代码关系。当需要切换Dao层实现类的时候必须修改service层代码,不符合面向接口与开发的最优原则。假如在项目迭代中,dao层不再采用JDBC技术实现,而是采用mybatis实现,我们在写完MybatisAccountDaoImpl后,需要在之前所有new accountDaoImpl的地方都得进行更改。

private TransferService transferService = new TransferServiceImpl();
private AccountDao accountDao = new AccountDaoImpl();

问题解决思路:
(1)采用反射技术来实现对象的实例化,Class.forName(“全限定名”),我们可以将要创建对象的类的全限定名配置到xml中。
(2)使用工厂模式来进行解耦合。工厂对象读取配置文件信息,通过反射生成对应的对象,当service层需要dao层对象的时候直接通过工厂类获取即可,解决了service层和dao层的强耦合关系。
代码改造(一):引入工厂类
xml配置文件:

<?xml version="1.0" encoding="UTF-8" ?>
<beans>
    <bean id="accountDao" class="com.nanmao.dao.impl.AccountDaoImpl"></bean>
    <bean id="transferService" class="com.nanmao.service.impl.TransferServiceImpl"></bean>
</beans>

添加的maven依赖:

 <!--dom4j依赖-->
        <dependency>
            <groupId>dom4j</groupId>
            <artifactId>dom4j</artifactId>
            <version>1.6.1</version>
        </dependency>
        <!--xpath表达式依赖-->
        <dependency>
            <groupId>jaxen</groupId>
            <artifactId>jaxen</artifactId>
            <version>1.1.6</version>
        </dependency>

在BeanFactory中,获取xml配置信息,通过反射生成对象,存储到map集合中,第三方可以通过getBean方法从map中获取对应的对象。

/**
 * 使用反射技术来创建对象
 */
public class BeanFactory {
    //存储创建的对象(采用map存储)
    private static Map<String,Object> map = new HashMap<>();

    //用静态代码块来实现在类加载的时候就完成bean对象的创建
    static {
        //1.解析xml对象,通过id获取到类的全限定名
        InputStream resourceAsStream = BeanFactory.class.getClassLoader().getResourceAsStream("bean.xml");
        SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read(resourceAsStream);
            //获取跟标签,即beans标签
            Element rootElement = document.getRootElement();
            List<Element> beanElements = rootElement.selectNodes("//bean");
            for (Element beanElement : beanElements) {
                String id = beanElement.attributeValue("id");
                String clazz = beanElement.attributeValue("class");
                //2.通过反射生成对应的对象
                Class<?> aClass = Class.forName(clazz);
                Object o = aClass.newInstance();
                //3.将生成的对象存储到容器中
                map.put(id,o);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 对外提供获取对象的方法
     * @param id bean标签id
     * @return 创建的对象
     */
    public static Object getBean(String id){
        return map.get(id);
    }
}

TransferServiceImpl代码改造:

    //    private AccountDao accountDao = new AccountDaoImpl();
    //通过工厂获取对象
    private AccountDao accountDao = (AccountDaoImpl)BeanFactory.getBean("accountDao");

TransferServlet代码改造:

//    private TransferService transferService = new TransferServiceImpl();
    private TransferService transferService = (TransferServiceImpl)BeanFactory.getBean("transferService");

代码改造(二):工厂类升级
引入了工厂类后,我们发现获取对应的对象还需要我们在代码中传入对应的id值,还是比较麻烦,能不能把BeanFactory.getBean也去掉呢?可以。
最佳的形式应该是只写一个属性定义:

private AccountDao accountDao;

如果我们单纯的只定义一个属性,在调用方法时肯定会报空指针异常的,我们需要给属性赋值,可以在构造方法和set方法中进行赋值。这里我们采用set方法的形式。
xml改造:在transferService标签下添加属性标签,根据属性名通过反射调用对应的set方法完成属性的赋值

<?xml version="1.0" encoding="UTF-8" ?>
<beans>
    <bean id="accountDao" class="com.nanmao.dao.impl.AccountDaoImpl"></bean>
    <bean id="transferService" class="com.nanmao.service.impl.TransferServiceImpl">
        <!-- set + name 找到对应的set方法,将ref中对应的对象set进去-->
        <property name="AccountDao" ref="accountDao"></property>
    </bean>
</beans>

TransferServiceImpl改造:

    private AccountDao accountDao;

    //set方法进行传值
    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }

BeanFactory改造:添加了第4步和第5步操作,利用反射调用set方法完成属性的赋值

/**
 * 使用反射技术来创建对对象
 */
public class BeanFactory {
    //存储创建的对象(采用map存储)
    private static Map<String,Object> map = new HashMap<>();

    //用静态代码块来实现在类加载的时候就完成bean对象的创建
    static {
        //1.解析xml对象,通过id获取到类的全限定名
        InputStream resourceAsStream = BeanFactory.class.getClassLoader().getResourceAsStream("bean.xml");
        SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read(resourceAsStream);
            //获取跟标签,即beans标签
            Element rootElement = document.getRootElement();
            List<Element> beanElements = rootElement.selectNodes("//bean");
            for (Element beanElement : beanElements) {
                String id = beanElement.attributeValue("id");
                String clazz = beanElement.attributeValue("class");
                //2.通过反射生成对应的对象
                Class<?> aClass = Class.forName(clazz);
                Object o = aClass.newInstance();
                //3.将生成的对象存储到容器中
                map.put(id,o);
            }
            ***//4.判断bean标签下是否有需要设置的属性信息,如有,则通过反射调用进行设置
            List<Element> propElements = rootElement.selectNodes("//property");
            for (Element propElement : propElements) {
                String name = propElement.attributeValue("name");
                String ref = propElement.attributeValue("ref");
                //找到当前需要处理依赖关系的bean
                Element parent = propElement.getParent();
                String parentId = parent.attributeValue("id");
                Object parentBean = map.get(parentId);
                //获取该对象中所有的方法,遍历找到对应的setAccountDao方法
                Method[] methods = parentBean.getClass().getMethods();
                for (Method method : methods) {
                    if(method.getName().equalsIgnoreCase("set" + name)){
                        method.invoke(parentBean, map.get(ref));
                    }
                }
                //5.将处理完的对象重新放到容器中
                map.put(parentId,parentBean);
            }***
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 对外提供获取对象的方法
     * @param id bean标签id
     * @return 创建的对象
     */
    public static Object getBean(String id){
        return map.get(id);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值