首页 > Java > java教程 > 正文

Spring框架第二篇之Bean的装配

巴扎黑
发布: 2017-06-26 11:38:32
原创
1594人浏览过

一、默认装配方式

代码通过getbean();方式从容器中获取指定的bean实例,容器首先会调用bean类的无参构造器,创建空值的实例对象。

举例:

首先我在applicationContext.xml配置文件中配置了一个bean:

1

2

3

<?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="

          

        "><!-- 注册Service 这里相当于容器做了SomeServiceImpl myService = new SomeServiceImpl(); --><bean id="myService" class="com.ietree.spring.basic.ioc.SomeServiceImpl"/></beans>

登录后复制

创建SomeServiceImpl对象,但需要注意的是该类的只具有带参构造函器,没有无参构造器:

1

2

3

4

5

6

7

8

9

10

11

package com.ietree.spring.basic.ioc;/**

 * 实现类

 *

 * @author Root */public class SomeServiceImpl implements ISomeService {private int a;    // 这里注释掉了无参构造函数,希望容器通过带参构造函数创建对象//    public SomeServiceImpl() {//        System.out.println("执行无参构造器,创建SomeServiceImpl对象");//    }public SomeServiceImpl(int a) {this.a = a;

    }

 

    @Overridepublic void doSomeThing() {

        System.out.println("执行doSomeThing()方法...");

    }

 

}

登录后复制

测试:

1

2

3

4

5

@Testpublic void testConstructor() {// 创建容器对象,加载Spring配置文件// ClassPathXmlApplicationContext会从类路径下查找配置文件ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");

 

    ISomeService service = (ISomeService) ac.getBean("myService");

    service.doSomeThing();

}

登录后复制

此时程序会报以下的错误:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'myService' defined in class path resource [applicationContext.xml]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.ietree.spring.basic.ioc.SomeServiceImpl]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.ietree.spring.basic.ioc.SomeServiceImpl.<init>()

    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1155)

    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1099)

    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:513)

    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)

    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)

    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)

    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)

    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)

    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:761)

    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867)

    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:543)

    at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139)

    at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:83)

    at com.ietree.spring.basic.test.MyTest.testConstrutor(MyTest.java:67)

    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)

    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)

    at java.lang.reflect.Method.invoke(Unknown Source)

    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)

    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)

    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)

    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)

    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)

    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)

    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)

    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)

    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)

    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)

    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)

    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)

    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)

    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)

    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)

    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)

    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678)

    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)

    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)

Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.ietree.spring.basic.ioc.SomeServiceImpl]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.ietree.spring.basic.ioc.SomeServiceImpl.<init>()at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:85)

    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1147)

    ... 36 more

Caused by: java.lang.NoSuchMethodException: com.ietree.spring.basic.ioc.SomeServiceImpl.<init>()

    at java.lang.Class.getConstructor0(Unknown Source)

    at java.lang.Class.getDeclaredConstructor(Unknown Source)

    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:80)

    ... 37 more

登录后复制

解析:这里的错误报的很明显,没有发现默认的构造器。

修改:为该类加上无参构造器:

1

2

3

4

5

6

7

8

9

10

11

12

13

package com.ietree.spring.basic.ioc;/**

 * 实现类

 *

 * @author Root */public class SomeServiceImpl implements ISomeService {private int a;    public SomeServiceImpl() {

        System.out.println("执行无参构造器,创建SomeServiceImpl对象");

    }public SomeServiceImpl(int a) {this.a = a;

    }

 

    @Overridepublic void doSomeThing() {

        System.out.println("执行doSomeThing()方法...");

    }

 

}

登录后复制

此时,再次运行测试用例,会发现运行成功。

结论:Spring容器实际上是使用了类的反射机制,会首先调用Bean类的无参构造器创建实例对象。

二、动态工厂Bean

 创建SomeServiceImpl类:

1

2

3

4

5

6

7

8

9

10

11

12

package com.ietree.spring.basic.ioc;/**

 * 实现类

 *

 * @author Root */public class SomeServiceImpl implements ISomeService {public SomeServiceImpl() {

        System.out.println("执行无参构造器,创建SomeServiceImpl对象");

    }

 

    @Overridepublic void doSomeThing() {

        System.out.println("执行doSomeThing()方法...");

    }

 

}

登录后复制

创建工厂类ServiceFactory:

1

2

3

4

5

6

7

package com.ietree.spring.basic.ioc;/**

 * 工厂类

 *

 * @author Root */public class ServiceFactory {public ISomeService getSomeService() {return new SomeServiceImpl();

    }

 

}

登录后复制

使用动态工厂方式获取Bean对象,配置如下:

1

2

3

<?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="

          

        "><!-- 注册动态工厂 --><bean id="factory" class="com.ietree.spring.basic.ioc.ServiceFactory"/><!-- 注册Service:动态工厂Bean --><bean id="myService" factory-bean="factory" factory-method="getSomeService"/></beans>

登录后复制

在这里并没有注册SomeServiceImpl类,而是通过ServiceFactory工厂的getSomeService方法获取的。

测试:

1

2

3

4

5

6

@Testpublic void testFactory1() {

         

    ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");

    ISomeService service = (ISomeService) ac.getBean("myService");

    service.doSomeThing();

}

登录后复制
登录后复制

运行成功。

三、静态工厂Bean

 静态工厂和动态工厂不同的是,静态工厂中使用的是静态方法创建对象,如:

1

2

3

4

5

6

7

package com.ietree.spring.basic.ioc;/**

 * 工厂类

 *

 * @author Root */public class ServiceFactory {    // 使用静态方法创建对象public static ISomeService getSomeService() {return new SomeServiceImpl();

    }

 

}

登录后复制

对应的配置文件修改如下:

1

2

3

<?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="

          

        "><!-- 注册Service:静态工厂Bean --><bean id="myService" class="com.ietree.spring.basic.ioc.ServiceFactory" factory-method="getSomeService"/></beans>

登录后复制

测试:

1

2

3

4

5

6

@Testpublic void testFactory1() {

         

    ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");

    ISomeService service = (ISomeService) ac.getBean("myService");

    service.doSomeThing();

}

登录后复制
登录后复制

成功创建SomeServiceImpl对象。

四、容器中的Bean的作用域

 Bean的作用域(scope)分为四种,分别是singleton、prototype、request、session。

scope:
  singleton(默认):单例模式,其对象的创建时机是在Spring容器初始化时创建,是默认值
  prototype:原型模式,其对象的创建时机不是在Spring容器初始化时创建,而是在代码中真正访问时才创建,每次使用getBean方法获取的同一个的实例都是一个新的实例
  request:对于每次HTTP请求,都将会产生一个不同的Bean实例
  session:对于每个不同的HTTP session,都将会产生一个不同的Bean实例

验证:

首先配置作用域为singleton:

1

<bean id="myService" class="com.ietree.spring.basic.ioc.SomeServiceImpl" scope="singleton"/>

登录后复制

测试:

1

2

3

4

5

6

7

8

@Testpublic void test05() {

         

    ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");

         

    ISomeService service1 = (ISomeService) ac.getBean("myService");

    ISomeService service2 = (ISomeService) ac.getBean("myService");

    System.out.println("service1 = service2吗?" + (service1 == service2));

}

登录后复制

程序输出:

1

2

调用无参构造器

service1 = service2吗?true

登录后复制

结论:当作用域为singleton单例模式时,只会创建一个对象实例,并且对象是在Spring容器初始化时创建。

同样,当配置为prototype原型模式时:

1

<bean id="myService" class="com.ietree.spring.basic.ioc.SomeServiceImpl" scope="prototype"/>

登录后复制

程序输出:

1

2

3

调用无参构造器

调用无参构造器

service1 = service2吗?false

登录后复制

结论:构造器被调用了两次,说明创建的service1和service2不是同一个对象,并且对象是在被使用到时才创建的。

五、Bean后处理器

 Bean后处理器是一种特殊的Bean,容器中所有的Bean在初始化时,均会自动执行该类的两个方法。由于该Bean是由其它Bean自动调用执行,不是程序员手工调用,故此Bean无须id属性。

需要做的是,在Bean后处理器类方法中,只要对Bean类与Bean类中的方法进行判断,就可实现对指定的Bean的指定的方法进行功能扩展与增强。方法返回的Bean对象,即是增强过的对象。

代码中需要自定义Bean后处理器类,该类就是实现了接口BeanPostProcessor的类。该接口中包含两个方法,分别在目标Bean初始化完毕之前与之后执行,它的返回值为功能被扩展或增强后的Bean对象。

举例:利用Bean后处理器实现大小写字符串转换

接口类ISomeService:

1

2

3

4

5

6

7

8

/**

 * 接口类

 *

 * @author Root */public interface ISomeService {

     

    String doSomeThing();

     

}

登录后复制

实现类SomeServiceImpl:

1

2

3

4

5

6

7

8

/**

 * 实现类

 *

 * @author Root */public class SomeServiceImpl implements ISomeService {public SomeServiceImpl() {

        System.out.println("调用无参构造器");

    }    // 返回小写字母“abcde”    @Overridepublic String doSomeThing() {return "abcde";

    }

}

登录后复制

定义Bean处理器MyBeanPostProcessor:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import org.springframework.beans.BeansException;import org.springframework.beans.factory.config.BeanPostProcessor;/**

 * Bean后处理器

 *

 * @author Root */public class MyBeanPostProcessor implements BeanPostProcessor {// bean:表示当前正在进行初始化的Bean对象// beanName:表示当前正在进行初始化的Bean对象的id    @Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {

        System.out.println("执行----before()方法---");return bean;

    }

 

    @Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

        System.out.println("执行----after()方法---");

 

        Object obj = Proxy.newProxyInstance(

                bean.getClass().getClassLoader(),

                bean.getClass().getInterfaces(),new InvocationHandler() {

 

                    @Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

 

                        Object invoke = method.invoke(bean, args);return ((String) invoke).toUpperCase();

                    }

                });return obj;

    }

 

}

登录后复制

使用JDK动态代理实现大小写转换的功能。

配置文件:

1

<bean id="myService" class="com.ietree.spring.basic.ioc.method2.SomeServiceImpl"/><!-- 注册Bean后处理器,由于该Bean是由其它Bean自动调用执行,不是程序员手工调用,故此Bean无须id属性 --><bean class="com.ietree.spring.basic.ioc.method2.MyBeanPostProcessor"></bean>

登录后复制

注意:Bean后处理器不需要配置id的,因为它是随着对象的创建自动调用的。

测试:

1

2

3

4

5

6

7

8

@Testpublic void test05() {

 

    ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");

 

    ISomeService service = (ISomeService) ac.getBean("myService");

    String result = service.doSomeThing();

    System.out.println(result);

}

登录后复制

程序输出:

1

2

调用无参构造器

执行----before()方法---执行----after()方法---ABCDE

登录后复制

增强成功。可以判断代理类的类型,进行对单个或单独一类对象做增强。

六、定制Bean的生命周期

 Bean实例从创建到最后销毁,需要经过很多过程,执行很多生命周期方法。

Step1:调用无参构造器,创建实例对象。

Step2:调用参数的setter,为属性注入值。

Step3:若Bean实现了BeanNameAware接口,则会执行接口方法setBeanName(String beanId),使Bean类可以获取其在容器中的id名称。

Step4:若Bean实现了BeanFactoryAware接口,则执行接口方法setBeanFactory(BeanFactory factory),使Bean类可以获取到BeanFactory对象。

Step5:若定义并注册了Bean后处理器BeanPostProcessor,则执行接口方法postProcessBeforeInitialization()。

Step6:若Bean实现了InitializingBean接口,则执行接口方法afterPropertiesSet()方法。该方法在Bean的所有属性的set方法执行完毕后执行,是Bean初始化结束的标志,即Bean实例化结束。

Step7:若设置了init-method方法,则执行。

Step8:若定义并注册了Bean后处理器BeanPostProcessor,则执行接口方法postProcessAfterInitialization().

Step9:执行业务方法。

Step10:若Bean实现了DisposableBean接口,则执行接口方法destroy()。

Step11:若设置了destroy-method方法,则执行。

举例:

创建接口类ISomeService:

1

2

3

4

5

6

/**

 * 接口类

 *

 * @author Root */public interface ISomeService {    void doSomeThing();

     

}

登录后复制

创建接口实现类SomeServiceImpl:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

import org.springframework.beans.BeansException;import org.springframework.beans.factory.BeanFactory;import org.springframework.beans.factory.BeanFactoryAware;import org.springframework.beans.factory.BeanNameAware;import org.springframework.beans.factory.DisposableBean;import org.springframework.beans.factory.InitializingBean;/**

 * 实现类

 *

 * @author Root */public class SomeServiceImpl implements ISomeService, BeanNameAware, BeanFactoryAware, InitializingBean, DisposableBean {    // 两个属性private String adao;private String bdao;    public void setAdao(String adao) {this.adao = adao;

        System.out.println("Step2:执行settter");

    }public void setBdao(String bdao) {this.bdao = bdao;

        System.out.println("Step2:执行settter");

    }public SomeServiceImpl() {

        System.out.println("Step1:调用无参构造器");

    }

     

    @Overridepublic void doSomeThing() {

        System.out.println("Step9:执行doSomeThing()");

    }    public void setUp(){

        System.out.println("Step7:初始化完毕之后 ");

    }    public void tearDown(){

        System.out.println("Step11:销毁之前");

    }

 

    @Overridepublic void setBeanName(String name) {

        System.out.println("Step3:获取到bean的id = " + name);

    }

 

    @Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {

        System.out.println("Step4:获取到BeanFactory容器 ");

    }

 

    @Overridepublic void afterPropertiesSet() throws Exception {

        System.out.println("Step6:Bean初始化完毕了 ");

    }

 

    @Overridepublic void destroy() throws Exception {

        System.out.println("Step10:实现的接口销毁之前 ");

    }

}

登录后复制

创建BeanPostProcessor接口的实现类MyBeanPostProcessor:

1

2

3

4

5

6

7

8

9

10

11

12

import org.springframework.beans.BeansException;import org.springframework.beans.factory.config.BeanPostProcessor;/**

 * Bean后处理器

 *

 * @author Root */public class MyBeanPostProcessor implements BeanPostProcessor {// bean:表示当前正在进行初始化的Bean对象// beanName:表示当前正在进行初始化的Bean对象的id    @Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {

        System.out.println("Step5:执行----before()方法---");return bean;

    }

 

    @Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

        System.out.println("Step8:执行----after()方法---");return bean;

    }

 

}

登录后复制

配置applicationContext.xml文件:

1

2

3

<?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="

          

        "><!-- 注册Service --><bean id="myService" class="com.ietree.spring.basic.ioc.method3.SomeServiceImpl" init-method="setUp" destroy-method="tearDown"><property name="adao" value="aaa"></property><property name="bdao" value="bbb"></property></bean><!-- 注册Bean后处理器,由于该Bean是由其它Bean自动调用执行,不是程序员手工调用,故此Bean无须id属性 --><bean class="com.ietree.spring.basic.ioc.method3.MyBeanPostProcessor"></bean></beans>

登录后复制

测试类:

1

2

3

4

5

6

7

8

@Testpublic void test05() {

 

    String resource = "com/ietree/spring/basic/ioc/method3/applicationContext.xml";

    ApplicationContext ac = new ClassPathXmlApplicationContext(resource);

 

    ISomeService service = (ISomeService) ac.getBean("myService");

    service.doSomeThing();// 对于销毁方法的执行,有两个条件:// 1)当前的Bean需要是singleton的// 2)要手工关闭容器    ((ClassPathXmlApplicationContext) ac).close();

}

登录后复制

程序输出:

1

2

3

4

5

6

7

8

9

10

Step1:调用无参构造器

Step2:执行settter

Step2:执行settter

Step3:获取到bean的id = myService

Step4:获取到BeanFactory容器

Step5:执行----before()方法---Step6:Bean初始化完毕了

Step7:初始化完毕之后

Step8:执行----after()方法---Step9:执行doSomeThing()

Step10:实现的接口销毁之前

Step11:销毁之前

登录后复制

正如程序输出的序列一样,此顺序即是对象创建的调用顺序,在编程中可以在某一个过程对其进行增强操作。

七、标签的id属性与name属性

 一般情况下,命名使用id属性,而不是用name属性,在没有id属性的情况下,name属性与id属性作用是相同的。但,当中含有一些特殊字符时,就需要使用name属性了。

id的命名需要满足XML对ID属性命名规范:必须以字母开头,可以包含字母、数字、下划线、连字符、句号、冒号。

name属性值可以包含各种字符。

以上就是Spring框架第二篇之Bean的装配的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习
PHP中文网抖音号
发现有趣的

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号