美文网首页
第三章 在spring中引入IoC和DI

第三章 在spring中引入IoC和DI

作者: 仙境源地 | 来源:发表于2019-07-25 09:20 被阅读0次

    概念关系 控制反转(IoC) VS 依赖注入(DI)

    控制反转可以分为两种子类型:依赖注入(DI)和依赖查找

    1.依赖查找

    依赖拉取(dependency pull,DL)

    例如:老版的EJB里面 通过JNDI API查找EJB组件
    spring基于依赖拉取的典型场景

    applicationContext.getBean("renderer");
    

    上下文依赖查找(contextualized dependecy lookup,CDL)

    2.依赖注入

    构造函数

    setter依赖注入

    字段注入

    BeanFactory接口

    涉及到的核心类有
    DefaultListableBeanFactory
    BeanDefinitionReader
    BeanDefinition

    BeanDefinitionReader

    DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
    XmlBeanDefinitionReader rdr = new XmlBeanDefinitionReader(factory);
    rdr.loadBeanDefinitions(new ClassPathResource("spring/xml-bean-factory-config.xml"));
    Oracle oracle = (Oracle) factory.getBean("oracle");
    System.out.println(oracle.defineMeaningOfLife());
    

    3.5 配置ApplicationContext

    ApplicationContext的嵌套

    pro-spring-5 chapter03/nesting demo源码

    文件结构.png
    package com.apress.prospring5.ch3;
    
    import org.springframework.context.support.GenericXmlApplicationContext;
    
    public class HierarchicalAppContextUsage {
    
        public static void main(String... args) {
            GenericXmlApplicationContext parent = new GenericXmlApplicationContext();
            parent.load("classpath:spring/parent-context.xml");
            parent.refresh();
    
            GenericXmlApplicationContext child = new GenericXmlApplicationContext();
            child.load("classpath:spring/child-context.xml");
            child.setParent(parent);
            child.refresh();
    
            Song song1 = (Song) child.getBean("song1");
            Song song2 = (Song) child.getBean("song2");
            Song song3 = (Song) child.getBean("song3");
    
            System.out.println("from parent ctx: " + song1.getTitle());
            System.out.println("from child ctx: " + song2.getTitle());
            System.out.println("from parent ctx: " + song3.getTitle());
    
            child.close();
            parent.close();
        }
    }
    
    package com.apress.prospring5.ch3;
    
    public class Song {
        private String title;
        
        public void setTitle(String title) {
            this.title = title;
        }
        
        public String getTitle() {
            return title;
        }
    }
    

    child-context.xml文件内容

    <?xml version="1.0" encoding="UTF-8"?>
    
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:c="http://www.springframework.org/schema/c"
        xmlns:p="http://www.springframework.org/schema/p"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="song1" class="com.apress.prospring5.ch3.Song"
              p:title-ref="parentTitle"/>
    
        <bean id="song2" class="com.apress.prospring5.ch3.Song"
              p:title-ref="childTitle"/>
    
        <bean id="song3" class="com.apress.prospring5.ch3.Song">
            <property name="title">
                <ref parent="childTitle"/>
            </property> 
        </bean>
    
        <bean id="childTitle" class="java.lang.String" c:_0="No Such Thing"/>
    </beans>
    

    parent-context.xml文件内容

    <?xml version="1.0" encoding="UTF-8"?>
    
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:c="http://www.springframework.org/schema/c"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="childTitle" class="java.lang.String" c:_0="Daughters"/>
    
        <bean id="parentTitle" class="java.lang.String" c:_0="Gravity"/>
    </beans>
    

    xml配置
    注解配置
    java配置

    @ComponentScan(basePackages = {"com.apress.prospring5.ch3.annotated"})
    @Configuration
    public class HelloWorldConfiguration {
        @Bean
        public MessageProvider provider() {
            return new HelloWorldMessageProvider();
        }
    
        @Bean
        public MessageRenderer renderer(){
            MessageRenderer renderer = new StandardOutMessageRenderer();
            renderer.setMessageProvider(provider());
            return renderer;
        }
    }
    
    @ImportResource(locations = {"classpath:spring/app-context-xml.xml"})
    @Configuration
    public class HelloWorldConfiguration {
    
    }
    

    3.5.2 方法注入

    除了构造函数注入,setter注入,field注入,spring还有一种不常用的方法注入,spring的方法注入功能有两种形式
    查找方法注入(Lookup Method Injection)
    方法替换(Method Replacement)

    查找方法注入

    解决场景:解决当bean依赖于另一个具有不同生命周期bean的需求
    例如,单例依赖于非单例场景
    @Lookup注解 (<lookup-method>标签)
    工作原理
    spring使用cglib动态生成查找方法注入类的子类,将动态重写该方法

    <beanid = ” abstractLookupBean ”
    class=” com .apress.prospring5 .ch3 .AbstractLookupDemoBean”>
    <lookup-method name="getMySinger" bean=” sinqer"/> </bean>

    对于 abstractLookupBean ,需要使用 <lookup-method>标记来配置查找方法。<lookup-method>标记的name属性告诉Spring应该覆盖的bean方法的名称 。该 方法不接收任何参数,并且所返回的类型应该是希望从该方法返回的bean的类型。此时,该方法应返回一个类型为 Singer的类或其子类。 bean属性告诉Spring查找方法应该返回哪个bean。

    pro-spring-5 method-injection demo source

    image.png
    package com.apress.prospring5.ch3.annotated;
    
    public interface DemoBean {
        Singer getMySinger();
        void doSomething();
    }
    
    package com.apress.prospring5.ch3.annotated;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.stereotype.Component;
    
    @Component("standardLookupBean")
    public class StandardLookupDemoBean implements DemoBean {
    
        private Singer mySinger;
    
        @Autowired
        @Qualifier("singer")
        public void setMySinger(Singer mySinger) {
            this.mySinger = mySinger;
        }
    
        @Override
        public Singer getMySinger() {
            return this.mySinger;
        }
    
        @Override
        public void doSomething() {
            mySinger.sing();
        }
    }
    

    原型singerbean

    package com.apress.prospring5.ch3.annotated;
    
    import org.springframework.context.annotation.Scope;
    import org.springframework.stereotype.Component;
    
    /**
     * Created by iuliana.cosmina on 2/20/17.
     */
    @Component("singer")
    @Scope("prototype")
    public class Singer {
        private String lyric = "I played a quick game of chess with the salt and pepper shaker";
    
        public void sing() {
            // commented to avoid console pollution
            //System.out.println(lyric);
        }
    }
    
    
    package com.apress.prospring5.ch3.annotated;
    
    import org.springframework.beans.factory.annotation.Lookup;
    import org.springframework.stereotype.Component;
    
    @Component("abstractLookupBean")
    public class AbstractLookupDemoBean implements DemoBean {
        @Lookup("singer")
        public Singer getMySinger() {
            return null; // This implementation will be overridden by dynamically generated subclass
        }
    
        @Override
        public void doSomething() {
            getMySinger().sing();
        }
    }
    
    package com.apress.prospring5.ch3.config;
    
    import com.apress.prospring5.ch3.annotated.DemoBean;
    import com.apress.prospring5.ch3.annotated.Singer;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.support.GenericApplicationContext;
    import org.springframework.util.StopWatch;
    
    /**
     * Created by iuliana.cosmina on 2/19/17.
     */
    public class LookupConfigDemo {
    
        @Configuration
        @ComponentScan(basePackages = {"com.apress.prospring5.ch3.annotated"})
        public static class LookupConfig {}
    
        public static void main(String... args) {
            GenericApplicationContext ctx = new AnnotationConfigApplicationContext(LookupConfig.class);
            //Arrays.stream(ctx.getBeanDefinitionNames()).forEach(s-> System.out.println(s));
    
            DemoBean abstractBean = ctx.getBean("abstractLookupBean", DemoBean.class);
            DemoBean standardBean = ctx.getBean("standardLookupBean", DemoBean.class);
    
    
            displayInfo("abstractLookupBean", abstractBean);
            displayInfo("standardLookupBean", standardBean);
    
            ctx.close();
        }
    
        public static void displayInfo(String beanName, DemoBean bean) {
            Singer singer1 = bean.getMySinger();
            Singer singer2 = bean.getMySinger();
    
            System.out.println("[" + beanName + "]: Singer Instances the Same?  "
                    + (singer1 == singer2));
    
            StopWatch stopWatch = new StopWatch();
            stopWatch.start("lookupDemo");
    
            for (int x = 0; x < 100000; x++) {
                Singer singer = bean.getMySinger();
                singer.sing();
            }
    
            stopWatch.stop();
            System.out.println("100000 gets took " + stopWatch.getTotalTimeMillis() + " ms");
        }
        /**
         * execute result:
         * [abstractLookupBean]: Singer Instances the Same?  false
         * 100000 gets took 1004 ms
         * [standardLookupBean]: Singer Instances the Same?  true
         * 100000 gets took 1 ms
         *
         * Process finished with exit code 0
         */
    }
    
    

    执行结果:
    1.单例bean的比较
    2.执行效率的比较

    方法替换

    场景:使用方法替换,可以任务替换任何bean的方法实现,而不需更改所修改bean的源代码
    例如:想替换第三方库某个类的方法实现
    实现原理:动态创建bean类的子类,将想要替换的方法的调用重定向为另一个实现了MethodReplacer接口的bean

    使用
    <replaced-method>

    <bean id="replacementTarget" class="com.apress.prospring5.ch3.ReplacementTarget">
    <replaced-method name="formatMessage" replacer="methodReplacer">

    <arg-type>String</arg-type>
    </replaced-method>
    </bean>

    样例

    chapter03/method-replacement

    package com.apress.prospring5.ch3;
    
    public class ReplacementTarget {
        public String formatMessage(String msg) {
            return "<h1>" + msg + "</h1>"; 
        }
    
        public String formatMessage(Object msg) {
            return "<h1>" + msg + "</h1>"; 
        }
    }
    
    package com.apress.prospring5.ch3;
    
    import org.springframework.beans.factory.support.MethodReplacer;
    
    import java.lang.reflect.Method;
    
    public class FormatMessageReplacer implements MethodReplacer {
    
        @Override
        public Object reimplement(Object arg0, Method method, Object[] args)
                throws Throwable {
            if (isFormatMessageMethod(method)) {
                String msg = (String) args[0];
                return "<h2>" + msg + "</h2>";
            } else {
                throw new IllegalArgumentException("Unable to reimplement method "
                        + method.getName());
            }
        }
    
        private boolean isFormatMessageMethod(Method method) {
            if (method.getParameterTypes().length != 1) {
                return false;
            }
            if (!("formatMessage".equals(method.getName()))) {
                return false;
            }
            if (method.getReturnType() != String.class) {
                return false;
            }
            if (method.getParameterTypes()[0] != String.class) {
                return false;
            }
            return true;
        }
    }
    
    
    <?xml version="1.0" encoding="UTF-8"?>
    
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="methodReplacer" class="com.apress.prospring5.ch3.FormatMessageReplacer"/>
    
    
        <bean id="replacementTarget" class="com.apress.prospring5.ch3.ReplacementTarget">
            <replaced-method name="formatMessage" replacer="methodReplacer">
                <!--bean存在重载方法时,可指定参数-->
                <arg-type>String</arg-type>
            </replaced-method>
        </bean>
        
        <bean id="standardTarget" class="com.apress.prospring5.ch3.ReplacementTarget"/>
    </beans>
    
    package com.apress.prospring5.ch3;
    
    import org.springframework.context.support.GenericXmlApplicationContext;
    import org.springframework.util.StopWatch;
    
    public class MethodReplacementDemo {
        public static void main(String... args) {
            GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
            ctx.load("classpath:spring/app-context-xml.xml");
            ctx.refresh();
    
            ReplacementTarget replacementTarget = (ReplacementTarget) ctx
                    .getBean("replacementTarget");
            ReplacementTarget standardTarget = (ReplacementTarget) ctx
                    .getBean("standardTarget");
    
            displayInfo(replacementTarget);
            displayInfo(standardTarget);
    
            ctx.close();
            /*
            execute result:
            <h2>Thanks for playing, try again!</h2>
            1000000 invocations took: 426 ms
            <h1>Thanks for playing, try again!</h1>
            1000000 invocations took: 43 ms
             */
        }
    
        private static void displayInfo(ReplacementTarget target) {
            System.out.println(target.formatMessage("Thanks for playing, try again!"));
    
            StopWatch stopWatch = new StopWatch();
            stopWatch.start("perfTest");
    
            for (int x = 0; x < 1000000; x++) {
                String out = target.formatMessage("No filter in my head");
                //commented to not pollute the console
                //System.out.println(out);
            }
    
            stopWatch.stop();
    
            System.out.println("1000000 invocations took: "
                    + stopWatch.getTotalTimeMillis() + " ms");
        }
    }
    
    

    相关文章

      网友评论

          本文标题:第三章 在spring中引入IoC和DI

          本文链接:https://www.haomeiwen.com/subject/hfcqrctx.html