龙空技术网

SpringFramework官方文档阅读(1)-下

Java架构手册 63

前言:

此时同学们对“java官方文档如何阅读”都比较关心,我们都想要了解一些“java官方文档如何阅读”的相关文章。那么小编同时在网摘上收集了一些对于“java官方文档如何阅读””的相关内容,希望兄弟们能喜欢,姐妹们一起来学习一下吧!

1.4.4. Lazy-initialized Beans

<bean id="lazy" class="com.something.ExpensiveToCreateBean" lazy-init="true"/><bean name="not.lazy" class="com.something.AnotherBean"/>

通过设定lazy-init属性来表示是否懒加载,同时,如果该bean是其他bean的dependency,依然会直接加载出来。

1.4.6. Method Injection

搞了半天终于知道这个东西是什么意思了,考虑这样一个情况,由于bean在容器中很多时候都是单例的,所以我们会一直用同一个对象。但是某些情况下,我们需要每次都拿到一个新的对象,这个时候就可以考虑重写Aware方法。

举个例子,我们有一个Command对象,现在我们每次需要一个新的Command,很明显,当我们要将这个bean注入context的时候,scope要填prototype(意思是每次取就创建一个新的,区别于Singleton)。

<bean id="command" class="edu.csu.example.demo.methodinjection.Command" scope="prototype"/><bean id="commandManager" class="edu.csu.example.demo.methodinjection.CommandManager"/>

package edu.csu.example.demo.methodinjection;public class Command {    public Object execute() {        System.out.println("命令已执行");        return "end";    }}

很自然的,我们第一个想到的方式是通过容器进行注入,将command注入到commandManager中

<bean id="commandManager" class="edu.csu.example.demo.methodinjection.CommandManager">    <property name="command" ref="command"/></bean>

由于command是prototype,理论上每次注入都应该是一个新的对象:

public class CommandManager implements ApplicationContextAware {    private ApplicationContext applicationContext;    private Command command;    // inject    public void setCommand(Command command) {        this.command = command;    }    @Override    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {        this.applicationContext = applicationContext;    }    public Object process(Map commandState) {        // grab a new instance of the appropriate Command        System.out.println(command);        // set the state on the (hopefully brand new) Command instance        return command.execute();    }}

进行测试:

System.out.println("------------------Method Injection------------------------");        CommandManager cm = context.getBean("commandManager", CommandManager.class);        Map<String, String> commandState = new HashMap<>();        cm.process(commandState);        cm.process(commandState);

结果:

可以看到,还是同一个元素,这与我们一开始写的prototype是相违背的。

现在我们用method injection的方式,方法很简单,虽然通过setter注入进去的对象是同一个,但是通过context.get出来的一定不同,我们通过实现ApplicationContextAware接口来直接从容器中获取对象:

public class CommandManager implements ApplicationContextAware {    private ApplicationContext applicationContext;    @Override    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {        this.applicationContext = applicationContext;    }    public Object process(Map commandState) {        // grab a new instance of the appropriate Command        Command command = createCommand();        System.out.println(command);        // set the state on the (hopefully brand new) Command instance        return command.execute();    }    protected Command createCommand() {        // notice the Spring API dependency!        return this.applicationContext.getBean("command", Command.class);    }}

注意删除property

结果:

当然,这种方法看上去好像直接对context进行了操作,不是很好,所以spring提供了另外一个方式来做这个事情:

我们通过将commandManager设为抽象类,且createCommand设为抽象方法,同时我们在注入bean的时候加入lookup-method标签来指定当我们调用method时,返回那个bean:

<bean id="command" class="edu.csu.example.demo.methodinjection.Command" scope="prototype"/><bean id="commandManager" class="edu.csu.example.demo.methodinjection.CommandManager">    <lookup-method name="createCommand" bean="command"/></bean>

public abstract class CommandManager implements ApplicationContextAware {    private ApplicationContext applicationContext;    @Override    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {        this.applicationContext = applicationContext;    }    public Object process(Map commandState) {        // grab a new instance of the appropriate Command        Command command = createCommand();        System.out.println(command);        // set the state on the (hopefully brand new) Command instance        return command.execute();    }    protected abstract Command createCommand();}

结果:

值得注意的是,我们没有在任何地方实现createCommand这个抽象方法。

Arbitrary Method Replacement

我们甚至能够替换类中的某个函数的实现,xml中通过replaced-method这个tag来进行实现:

package edu.csu.example.demo.methodinjection;public class MyValueCalculator {    public String computeValue(String input) {        System.out.println("original: this input is:" + input);        return "this is original method";    }}

新建一个类,重写这个方法,但是并不是继承:

package edu.csu.example.demo.methodinjection;import java.lang.reflect.Method;public class ReplacementComputeValue {    public Object reimplement(Object o, Method m, Object[] args) throws Throwable {        // get the input value, work with it, and return a computed result        String input = (String) args[0];        System.out.println("replaced: this input is:" + input);        return "this is replaced method";    }}

然后我们在xml中进行替换:

<bean id="myValueCalculator" class="edu.csu.example.demo.methodinjection.MyValueCalculator">    <!-- arbitrary method replacement -->    <replaced-method name="computeValue" replacer="replacementComputeValue">        <arg-type>String</arg-type>    </replaced-method></bean><bean id="replacementComputeValue" class="edu.csu.example.demo.methodinjection.ReplacementComputeValue"/>

执行结果:

1.5. Bean Scopes

spring中一切bean都是通过容器控制的,所以容器同样可以控制其生命周期,常见的生命周期有:singleton、prototype、request、session、application、websocket

The Singleton Scope:每次用到该对象时,容器都会返回同一个对象

The Prototype Scope:每次用到该对象时,容器都会返回一个新的对象

注意,如果singleton的bean A中引用了prototype的bean B,并且要求每次创建A时都新建一个B,这种情况是不能直接进行引用,要想实现这样的效果,参考上面的method-injection

Scoped Beans as Dependencies

对于有scope的bean作为注入对象,如果需要延长bean的生命周期,需要进行proxy来进行代理调用(这里有点不理解,哪怕是代理,bean销毁了应该也调用不了了,感到奇怪)

1.5.5. Custom Scopes:见文档,感觉很 很复杂,用得到再说吧

Scope (Spring Framework 5.2.1.RELEASE API)​docs.spring.io

1.6. Customizing the Nature of a Bean

重要,bean的生命周期调用

1.6.1. Lifecycle Callbacks

官方不建议使用InitializingBean(会增加code和spring之间不必要的耦合),而更加提倡:init-method和@PostConstruct

<bean id="exampleInitBean" class="edu.csu.example.demo.lifecycle.ExampleBean" init-method="init"/>

package edu.csu.example.demo.lifecycle;public class ExampleBean {    public void init() {        System.out.println("this is initial func");    }}

输出结果:

可以知道调用了init func

Startup and Shutdown Callbacks

在管理bean的过程中,管理bean的类实现了三个生命周期function

public interface Lifecycle {    void start();    void stop();    boolean isRunning();}

当applicationcontext收到相应信号时就会调用相应的func

public interface LifecycleProcessor extends Lifecycle {    void onRefresh();    void onClose();}

LifecycleProcessor新增了两个生命周期

在某些情况下,我们的某个bean是需要依赖其他的bean的(例如depends-on),所以对bean的初始化顺序也有一定的要求,在这里,我们可以通过smartLifecycle这个接口来对初始化顺序进行管理:

public interface Phased {    int getPhase();}

public interface SmartLifecycle extends Lifecycle, Phased {    boolean isAutoStartup();    void stop(Runnable callback);}

可以看到,SmartLifecycle接口有两个父接口,其中一个是Lifecycle,第二个是phased,对于第二个接口来说,可以通过具体的实现来控制bean的初始化顺序,大体来说phase越小初始化得越早。

1.6.2. ApplicationContextAware and BeanNameAware

有的时候我们需要通过applicationContext来进行一些必要的操作,这时我们需要拿到applicationContext这个对象,实现方式有两种

1:实现ApplicationContextAware这个接口

public interface ApplicationContextAware {    void setApplicationContext(ApplicationContext applicationContext) throws BeansException;}

实现了这个接口之后,spring容器在初始化时就会将上下文注入进去,这样就能拿到上下文对象了。

2:通过autowire

autowire可以将applicationContext以参数的形式给到构造函数和setter函数中的参数当中,这样就能够拿到上下文对象了。

public interface BeanNameAware {    void setBeanName(String name) throws BeansException;}

这个接口会在set完properties后调用,嗯。

1.6.3. Other Aware Interfaces

忒多了,截个图吧,有需要再看:

本文出自知乎

更多相关内容,Java架构师,软件开发等学习资料,电子书及视频还有高级讲师公开课免费资源

需要的可以私聊小编发送【学习】二字

标签: #java官方文档如何阅读