一、java代理模式
java代理模式是ioc的前置知识。代理模式非常简单,看代码就一目了然了。
public interface role { public void makeMoney(); }
public class Boss implements role { @Override public void makeMoney() { System.out.println("老板正在谈项目..."); } }
public class Secretary implements Role { private Role subject = new Boss(); @Override public void makeMoney() { before(); subject.makeMoney(); after(); } private void before(){ System.out.println("你可以先和秘书谈一下合作意向..."); } private void after(){ System.out.println("秘书把老板签过的合同给你..."); } }
上面是极简的java静态代理(也是接口代理模式),定义了接口、接口的实现类和代理类。或者称实现类为目标类,代理类为封装类。
一般我们先找老板的秘书,说一下合作意向(Secretary的makeMoney),可以的话(before)调用老板真正谈项目(Boss的makeMoney),谈妥了就可以签合同了(after)。可以做一下测试:
public class Test { public static void main(String[] args){ // 测试静态代理 /* 所谓静态代理,是对接口实现类的某些方法进行了一次封装 这个代理类需要实现接口的同样方法,并在内部初始化一个实现类 在该方法中调用了实现类的方法 */ Secretary proxy = new Secretary(); proxy.makeMoney(); } }
可见,与其直接访问一个目标类,更多的是访问封装类。这样做的好处在于:
1.不操作目标类,不对目标类造成破坏;
2.自定义封装类,具有充分的控制权;
3.可以增强目标类的特定方法,达到增强和扩展的目的;
但是,目标类和封装类必须实现接口定义的同一个方法。
更多地,java使用动态代理来实现功能扩展。举个例子:
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class Test { public static void main(String[] args) { final Role role = new Boss(); Role secretary = (Role) Proxy.newProxyInstance( role.getClass().getClassLoader(), // 目标类的类加载器 role.getClass().getInterfaces(), // 目标类所实现的所有接口 new InvocationHandler() { // 内部匿名类 -- InvocationHandler接口类的实现类,必须重写接口方法invoke @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // proxy: 代理对象 // method: 目标方法 // args: 目标方法的参数列表 before(); Object result = method.invoke(role, args); after(); return result; } private void before(){ System.out.println("先和秘书接洽约时间..."); } private void after(){ System.out.println("秘书送来合同书..."); } } ); secretary.makeMoney(); } }
jdk动态代理同样是接口代理模式,new InvocationHandler()实现类中的invoke方法被Proxy.newProxyInstance自动调用。secretary对象相当于上面的secretary对象,只不过不需要我们自己手动创建了。jdk动态代理帮助我们创建了一个代理对象,我们只需写接口和实现类即可。
当然,上面的new InvocarionHandler()实现类可以单独拿出来重写:
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class JdkProxy implements InvocationHandler { private Object impl; JdkProxy(Object impl){ super(); this.impl = impl; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { before(); Object result = method.invoke(impl, args); after(); return result; } private void before(){ System.out.println("先和秘书接洽约时间..."); } private void after(){ System.out.println("秘书送来合同书..."); } }
做一下测试:
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; public class JdkProxyTest { public static void main(String[] args) { Role role = new Boss(); InvocationHandler invocationHandler = new JdkProxy(role); Role boss = (Role) Proxy.newProxyInstance( role.getClass().getClassLoader(), role.getClass().getInterfaces(), invocationHandler ); boss.makeMoney(); } }
除了jdk动态代理外,还有cglib动态代理。与jdk动态代理不同的是,cglib动态代理没有接口类,它是通过子类继承目标类,在子类同名方法内部调用目标类方法来实现的。这种实现方法的性能要优于jdk动态代理。
总结一下,jdk代理帮助我们在目标类的基础上做了一次功能扩展,因此,对于日志、事务等系统级业务,可以通过动态代理交给别的组件管理,自己则专注写应用级业务,也就是上面的实现类(对应bean)和Test类(对应service)。这就是IOC控制反转。
二、applicationContext.xml和ApplicationContext
“解耦(业务逻辑代码和实体类分离)”、“功能扩展和托管(系统级业务交给框架处理,开发者只专注应用级业务处理)”是理解spring容器的两个重要思想。
动态代理解决了“功能扩展和托管”这个问题,通过xml解析,spring容器(读取applicationContext.xml生成的ApplicationContext实例)对目标类和封装类、业务代码两者进行了拆解。
我们可以将目标类注入(DI)到applicationContetx.xml文件中,由spring容器(ApplicationContext实例)读取applicationContext.xml来自动生成目标类对应的接口类和封装类。在业务代码中,只需获取ApplicationContext实例,并从中获取响应的封装类即可。这个过程实现即是IOC控制反转。
假设项目环境(IDEA)已经搭建完成(主要是JDK配置、maven配置、tomcat-->local服务器配置、项目Open Modules Settings配置、以及maven下文件添加及属性配置Mark Directory as)。
D:.
├─src
│ ├─main
│ │ ├─java // Sources Root
│ │ │ └─com
│ │ │ └─boke
│ │ ├─resources // Resources Root,可以手动添加并右键设置Mark Directory as --> Resources Root
│ │ │ ├─applicationContext.xml
│ │ │ └─log4j.properties
│ │ └─webapp // Sources Root,可以手动添加并右键设置Mark Directory as --> Sources Root
│ │ └─WEB-INF
│ └─test
│ └─java // Test Resources Root,可以手动添加并右键设置Mark Directory as --> Test Resources Root
│ └─com
│ └─node
│ ├─boke
└─target // Excluded,自动生成
备注:这些都可以在项目配置中设置。IDEA右上角"搜索"图标左侧第一个图标,或者项目文件目录上右键Open Module Settings设置。
pom.xml需要引入的依赖:junit4.11、spring-core、spring-beans、spring-context、spring-aop、spring-expression、log4j1.2.17、spring-test、commons-logging1.1.1。springframework版本为4.2.1.RELEASE。
现在跑一遍applicationContext.xml-->ApplicationContext的整个流程。
定义接口类和实现类。
// MyInterface类 public interface MyInterface { public void doSome(); } // MyImplement类 public class MyImplement implements MyInterface { public MyImplement(){ super(); } public void doSome() { System.out.println("--------执行doSome()方法...----------"); } }
将实现类注册到applicationContext.xml文件中。通常,实现类是一个POJO(plain old java object),对应applicationCOntext.xml的某个bean标签,因此也可称为bean对象。
<?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"> <!-- 注册MyImplement 它相当于:MyInterface implement = (MyInterface) new MyImplement(); --> <bean id="implement" class="com.boke.MyImplement" /> </beans>
测试。
package com.node.boke; import com.boke.MyInterface; import com.boke.MyImplement; import org.junit.Test; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext; import org.springframework.core.io.ClassPathResource; public class MyTest { /** * 示例一:传统的开发模式:接口-->实现-->调用,都绑定在了一起,耦合度比较高 */ @Test public void test01(){ MyInterface implement = new MyImplement(); implement.doSome(); } /** * 示例二:spring容器解耦 */ @Test public void test02(){ /* ClassPathXmlApplicationContext:类路径 把resources设置为source root,然后直接从其文件夹下读取applicationContext.xml即可 解耦:看不到MyInterface实现类 */ ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml"); MyInterface implement = (MyInterface)ac.getBean("implement"); implement.doSome(); } /** * 示例三:文件路径 */ @Test public void test03(){ /* FileSystemXmlApplicationContext:文件路径,默认是项目根路径下,也可以从其他路径读取applicationContext.xml文件 解耦:看不到ISomeimplementImpl实现类 */ ApplicationContext ac = new FileSystemXmlApplicationContext("applicationContext.xml"); MyInterface implement = (MyInterface)ac.getBean("implement"); implement.doSome(); } /* applicationContext与BeanFactory的区别: - applicationContext容器在进行初始化时,会将其中的所有Bean(对象)创建; 缺点:占用系统资源(内存,CPU等) 优点:响应速度快 - BeanFactory容器中的对象在容器初始化不会创建Bean对象,而是在真正获取该对象时才创建; 缺点:响应速度慢 优点:占用系统资源相对较小 */ /** * 示例四:XmlBeanFactory接口类的实现 */ @SuppressWarnings("deprecation") @Test public void test04(){ BeanFactory ac = new XmlBeanFactory(new ClassPathResource("applicationContext.xml")); MyInterface implement = (MyInterface)ac.getBean("implement"); implement.doSome(); } }
总结:
Spring的主要作用就是为代码"解耦",降低代码间的耦合度。根据功能的不同,可以将一个系统中的代码分为主业务逻辑和系统级业务逻辑两类。主业务代码逻辑联系紧密,有具体的专业业务应用场景,复用性相对较低;系统级业务逻辑相对功能独立,没有具体的专业业务应用场景,为主业务提供系统级服务,复用性强。
Spring根据代码的功能特点,将降低耦合度的方式分为两类:IOC和AOP。IOC使得主业务逻辑在相互调用的过程中不用自己创建对象和调用,而是由Spring容器统一管理,自动"注入"。AOP使得系统级服务得到了服用,由Spring容器统一完成"织入"。
Spring的特点:
- 1.非入侵:spring的api不会混入到应用业务开发中。
- 2.容器:Spring可以管理对象的生命周期以及对象间的依赖关系,并且可以通过配置文件来定义对象,以及设置与其它对象的依赖关系。
- 3.IOC: 控制反转(inversion of control),即创建调用者的实例不是由调用者完成的,而是由Spring容器完成,并注入调用者。
即不是对象从容器中查找对象,这个事情是容器在对象初始化时不等对象请求就主动将依赖传递给它。
- 4.AOP:面向切面编程(Aspect Orient Programming),是一种编程思想,是面向对象编程的OOP的补充。可以把日志、安全、事务管理等服务理解成一个"切面"。
三、ApplicationContext在bean注入时增扩了哪些功能--bean的生命始末
IOC(Inversion of Control)是一个概念,是一种思想,其实现方式多种多样。当前比较流行的实现方式有两种:依赖查找、依赖注入。依赖查找(Dependency Lookup DL):容器提供回调接口和上下文环境给组件,程序代码则需要提供具体的查找方式。 依赖注入(Dependecy Injection DI):程序代码不做定位查找,这些工作由容器自行完成。依赖注入是目前最优秀的解耦方式。依赖注入让Spring的Bean之间以配置文件的方式组织在一起,而不是以硬编码的方式耦合在一起。
通过以下代码查看bean注入和销毁的整个流程。
// 接口类 package com.boke; public interface MyInterface { public void doSome(); } // 实现类 package com.boke; import org.springframework.beans.BeansException; import org.springframework.beans.factory.*; import org.springframework.beans.factory.config.BeanPostProcessor; public class MyImplement implements MyInterface, BeanNameAware, BeanFactoryAware, InitializingBean, DisposableBean, BeanPostProcessor { private String aDao; private String bDao; public MyImplement(){ System.out.println("Step1.执行构造方法..."); } @Override public void doSome() { System.out.println("Step9:执行真正的业务处理逻辑doSome()方法..."); } public void setUp(){ System.out.println("Step7:bean生命开始..."); } public void tearDown(){ System.out.println("Step11:Bean销毁之前..."); } public String getaDao() { return aDao; } public void setaDao(String aDao) { System.out.println("Step2:执行aDao的setter方法..."); this.aDao = aDao; } public String getbDao() { return bDao; } public void setbDao(String bDao) { System.out.println("Step2:执行bDao的setter方法..."); this.bDao = bDao; } @Override public void setBeanName(String name) { System.out.println("Step3:获取bean的id = " + name); } @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { System.out.println("Step4:获取到BeanFactory容器..."); } @Override public void afterPropertiesSet() throws Exception { System.out.println("Step6:标志着bean已经初始化完毕..."); } @Override public void destroy() throws Exception { System.out.println("Step10:实现接口的销毁之前..."); } @Override public Object postProcessBeforeInitialization(Object bean, String s) throws BeansException { System.out.println("Step5:初始化完毕之前,执行before方法..."); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String s) throws BeansException { System.out.println("Step8:初始化完毕之后,执行after方法..."); return bean; } }
applicationContext.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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--测试生命bean--> <bean id="implement" class="com.boke.MyImplement" init-method="setUp" destroy-method="tearDown" > <!--初始化ISomeServiceImpl的成员变量--> <property name="aDao" value="aaa" /> <property name="bDao" value="bbb" /> </bean> </beans>
测试类。
package com.node.boke; import com.boke.MyInterface; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyTest { @Test public void test01(){ ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml"); MyInterface service = (MyInterface)ac.getBean("implement"); service.doSome(); /* 要执行tearDown,需要两个条件: - scope=singleton - 手动关闭ac */ ((ClassPathXmlApplicationContext)ac).close(); } }
执行test,打印结果如下:
INFO [AbstractApplicationContext.java:prepareRefresh:573]- Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@ba4d54: startup date [Fri Dec 14 11:41:09 CST 2018]; root of context hierarchy
INFO [XmlBeanDefinitionReader.java:loadBeanDefinitions:317]- Loading XML bean definitions from class path resource [applicationContext.xml]
Step1.执行构造方法...
Step2:执行aDao的setter方法...
Step2:执行bDao的setter方法...
Step3:获取bean的id = implement
Step4:获取到BeanFactory容器...
Step5:初始化完毕之前,执行before方法...
Step6:标志着bean已经初始化完毕...
Step7:bean生命开始...
Step8:初始化完毕之后,执行after方法...
Step9:执行真正的业务处理逻辑doSome()方法...
INFO [AbstractApplicationContext.java:doClose:951]- Closing org.springframework.context.support.ClassPathXmlApplicationContext@ba4d54: startup date [Fri Dec 14 11:41:09 CST 2018]; root of context hierarchy
Step10:实现接口的销毁之前...
Step11:Bean销毁之前...
分析:
在注入一个bean时,大致经过了11个步骤。这个流程对每一个注入的bean都适用。BeanNameAware, BeanFactoryAware, InitializingBean, DisposableBean, BeanPostProcessor是spring中自带的bean见名知意,在bean初始化、调用和销毁之际被spring自动调用。
我们可以通过实现(如上面的MyImplement)这些容器中自动调用的bean接口类中的抽象方法来实现功能的扩展,并交由spring自动处理。可见,spring在注入一个我们自写的bean时,创建了一个动态代理对象,这个对象不仅仅封装了我们自己的接口实现类,也封装了spring默认的这些接口实现类,从而完成了一个bean注入的整个生命周期。
当然,我们可以把Implement实现的这些接口单独拿出来写一个实现类,注入到spring容器中。以BeanPostProcessor为例。
package com.boke; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; public class MyBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String s) throws BeansException { System.out.println("Step5:初始化完毕之前,执行before方法..."); return bean; // 要返回这个bean,它表示当前正在初始化的bean对象 } @Override public Object postProcessAfterInitialization(Object bean, String s) throws BeansException { System.out.println("Step8:初始化完毕之后,执行after方法..."); return bean; // 要返回这个bean,它表示当前正在初始化的bean对象 } }
将其注入到applicationContext.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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--测试生命bean--> <bean id="implement" class="com.boke.MyImplement" init-method="setUp" destroy-method="tearDown" > <!--初始化ISomeServiceImpl的成员变量--> <property name="aDao" value="aaa" /> <property name="bDao" value="bbb" /> </bean> <bean class="com.node.service.beanlife.MyBeanPostProcessor" /> </beans>
效果和直接继承实现是一样的。
在这些步骤中,我们可以定制一些针对所有bean都适用的功能来进行扩展。
BeanPostProcessor说明:
Bean后处理器是一种特殊的Bean,容器中所有的Bean在初始化时均会自动执行该类的两个方法。由于该Bean是由其它Bean自动调用执行,不是程序员手工调用,故此Bean无须id属性。Bean后处理器是org.springframework.beans.factory.config.BeanPostProcessor,它会被自动加载,并执行它的实现类的两个方法:
public Object postProcessBeforeInitialization(Object bean, String beanId) throws BeanException,该方法会在目标Bean初始化完毕之前由容器自动调用。
public Object postProcessAfterInitialization(Object bean, String BeanId) throws BeansException,该方法会在目标Bean初始化完毕之后由容器自动调用。
它们的参数是:第一个参数是系统即将初始化的Bean实例,第二个参数是该Bean实例的id属性值,若Bean没有id就是name属性值。实现类需要重写这两个方法,并注册到bean中,spring容器会在每个bean调用之前(之后)自动执行者两个方法。
四、基于XML的DI
applicationContext.xml中以bean标签的方式注入属于典型的xml方式的注入。上节梳理了bean注入的生命始末,本节记录注入bean的一些方式。
1.无参构造注入
// Student类 package com.boke; public class Student { private String name; private int age; private School school; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public School getSchool() { return school; } public void setSchool(School school) { this.school = school; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + ", school=" + school + '}'; } } // School类 package com.boke; public class School { private String name; public void setName(String name) { this.name = name; } public String getName() { return name; } @Override public String toString() { return "School{" + "name='" + name + '\'' + '}'; } }
<?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并注入成员变量--> <bean id="school" class="com.boke.School"> <property name="name" value="清华大学" /> </bean> <bean id="student" class="com.boke.Student"> <!--注入成员变量的值--> <property name="name" value="Alex" /> <property name="age" value="20" /> <!--对于一个对象,称为域属性,需要用ref来注入--> <property name="school" ref="school" /> </bean> </beans>
package com.node.boke; import com.boke.School; import com.boke.Student; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyTest { @Test public void test01(){ ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml"); Student student = (Student)ac.getBean("student"); School school = (School)ac.getBean("school"); System.out.println(student); System.out.println(school); } }
2.有参构造注入
// Student类 package com.boke; public class Student { private String name; private int age; private School school; Student(){ super(); } Student(String name, int age, School school){ super(); this.name = name; this.age = age; this.school = school; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public School getSchool() { return school; } public void setSchool(School school) { this.school = school; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + ", school=" + school + '}'; } } // School类 package com.boke; public class School { private String name; School(){ super(); } School(String name){ super(); this.name = name; } public void setName(String name) { this.name = name; } public String getName() { return name; } @Override public String toString() { return "School{" + "name='" + name + '\'' + '}'; } }
<?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 不常用 --> <bean id="school" class="com.boke.School"> <property name="name" value="北京大学" /> </bean> <bean id="student" class="com.boke.Student"> <constructor-arg index="0" value="Alex" /> <constructor-arg index="1" value="24" /> <constructor-arg index="2" ref="school" /> </bean> </beans>
test测试代码和上面的一样。
3.命名空间注入:p模式
实体类不变,测试代码不变。
<?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:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--命名空间注入 1.p命名空间注入 需要在开头的bean中引入xmlns:p="http://www.springframework.org/schema/p" --> <bean id="school" class="com.boke.School" p:name="北京大学" /> <bean id="student" class="com.boke.Student" p:name="张三" p:age="28" p:school-ref="school" /> </beans>
4.命名空间注入:c模式
实体类不变,测试代码不变。
<?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"> <!--命名空间注入 1.p命名空间注入 需要在开头的bean中引入xmlns:p="http://www.springframework.org/schema/p" --> <bean id="school" class="com.boke.School" c:name="北京大学" /> <bean id="student" class="com.boke.Student" c:name="张三" c:age="28" c:school-ref="school" /> </beans>
5.域属性的自动注入
实体类不变,测试代码不变。
<?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="school" class="com.boke.School" c:name="北京大学" /> <!--autowire自动注入:会在容器中自动查找名称相同的bean注入 byName,根据bean名称匹配查找自动注入 byType,根据bean类型匹配查找自动注入,具有is-a关系的bean,即除了本类还包括子类 --> <bean id="student" class="com.boke.Student" autowire="byName"> <property name="name" value="Weiweian" /> <property name="age" value="250" /> </bean> </beans>
6.集合注入
Student类和School类不变。
package com.boke; import java.util.*; public class Some { private School[] schools; private String[] strings; private List<String> list; private Set<String> stringSet; private Map<String, Object> map; private Properties properties; public School[] getSchools() { return schools; } public void setSchools(School[] schools) { this.schools = schools; } public String[] getStrings() { return strings; } public void setStrings(String[] strings) { this.strings = strings; } public List<String> getList() { return list; } public void setList(List<String> list) { this.list = list; } public Set<String> getStringSet() { return stringSet; } public void setStringSet(Set<String> stringSet) { this.stringSet = stringSet; } public Map<String, Object> getMap() { return map; } public void setMap(Map<String, Object> map) { this.map = map; } public Properties getProperties() { return properties; } public void setProperties(Properties properties) { this.properties = properties; } @Override public String toString() { return "Some{" + "schools=" + Arrays.toString(schools) + ", strings=" + Arrays.toString(strings) + ", list=" + list + ", stringSet=" + stringSet + ", map=" + map + ", properties=" + properties + '}'; } }
<?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: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="school1" class="com.boke.School" p:name="日本大学" /> <bean id="school2" class="com.boke.School" p:name="美国大学" /> <bean id="some" class="com.boke.Some" > <!--School[]--> <property name="schools"> <array> <ref bean="school1" /> <ref bean="school2" /> </array> </property> <!--String[]--> <property name="strings"> <array> <value>日本</value> <value>美国</value> </array> </property> <!--List<String>--> <property name="list"> <array> <value>东京</value> <value>纽约</value> </array> </property> <!--Set<String>--> <property name="stringSet"> <set> <value>东京食尸鬼</value> <value>纽约时代周刊</value> </set> </property> <!--Map<String,Object>--> <property name="map"> <map> <entry key="Japan-movie" value="少妇的诱惑" /> <entry key="American-movie" value="复仇者联盟3" /> </map> </property> <!--properties就是配置文件,例如config.properties--> <property name="properties"> <props> <prop key="user">apple</prop> <prop key="pwd">123456</prop> </props> </property> </bean> </beans>
package com.node.boke; import com.boke.Some; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyTest { @Test public void test01(){ ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml"); Some some = (Some)ac.getBean("some"); System.out.println(some); } }
7.泛型注入
Student类和School类不变。
// Some类 package com.boke; import java.util.*; public class Some { private List<Map<String, School>> list; private Map<String, List<School>> map; public List<Map<String, School>> getList() { return list; } public void setList(List<Map<String, School>> list) { this.list = list; } public Map<String, List<School>> getMap() { return map; } public void setMap(Map<String, List<School>> map) { this.map = map; } public void printMap(){ for(String s: map.keySet()){ System.out.println(map.get(s)); } } @Override public String toString() { return "Some{" + "list=" + list + ", map=" + map + '}'; } }
<?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: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="school1" class="com.boke.School" p:name="日本东京大学" /> <bean id="school2" class="com.boke.School" p:name="日本野鸡大学" /> <bean id="school3" class="com.boke.School" p:name="美国哈佛大学" /> <bean id="school4" class="com.boke.School" p:name="美国野鸡大学" /> <bean id="some" class="com.boke.Some" > <!--List<Map<String, School>>--> <property name="list"> <array> <map> <entry key="Japan1" value-ref="school1" /> <entry key="Japan2" value-ref="school2" /> </map> <map> <entry key="American1" value-ref="school3" /> <entry key="American2" value-ref="school4" /> </map> </array> </property> <!--Map<String, List<School>>--> <property name="map"> <map> <entry key="Japan"> <array> <ref bean="school1" /> <ref bean="school2" /> </array> </entry> <entry key="American"> <array> <ref bean="school3" /> <ref bean="school4" /> </array> </entry> </map> </property> </bean> </beans>
package com.node.boke; import com.boke.School; import com.boke.Some; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import java.util.List; import java.util.Map; public class MyTest { @Test public void test01(){ ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml"); Some some = (Some)ac.getBean("some"); System.out.println(some); System.out.println("--------"); List<Map<String, School>> list = some.getList(); for(Map<String, School> l: list){ System.out.println(l); } some.printMap(); // 调用POJO里的方法 } }
8.SPEL注入
Stundent类、Some类、School类不变,添加Person类。
// Person类 package com.boke; public class Person { private String pname; private int page; public String getPname() { return pname; } public void setPname(String pname) { this.pname = pname; } public int getPage() { return page; } public void setPage(int page) { this.page = page; } @Override public String toString() { return "Person{" + "pname='" + pname + '\'' + ", page=" + page + '}'; } public int countAge(){ return page > 20 ? 12 : page; } }
<?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:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- SPEL,Spring Expression Language,即Spring EL 表达式语言 在Spring配置文件中为Bean的属性注入值时,可直接使用SPEL表达式计算的结果 SPEL表达式以#开头,后跟一对大括号: <bean id="abc" value="#(...)" /> --> <bean id="person" class="com.boke.Person"> <property name="pname" value="人类"/> <!--用法1:用El表达式将年龄设置成随机数--> <!--<property name="page" value="150" />--> <property name="page" value="#{T(java.lang.Math).random()*150}" /> </bean> <bean id="student" class="com.boke.Student"> <!--用法2:用EL表达式将student属性和设置成person属性--> <!--<property name="name" value="小学生" />--> <!--<property name="age" value="12" />--> <property name="name" value="#{person.pname}" /> <!--<property name="age" value="#{person.page}" />--> <!--用法3:用EL表达式给年龄做个判别--> <!--<property name="age" value="#{person.page > 20? 12: person.page}" />--> <!--或者直接在person实现类中写个判定函数引进来--> <property name="age" value="#{person.countAge()}" /> </bean> </beans>
package com.node.boke; import com.boke.Person; import com.boke.Student; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyTest { @Test public void test01(){ ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml"); Person person = (Person) ac.getBean("person"); System.out.println(person); Student student = (Student)ac.getBean("student"); System.out.println(student); } }
9.内部bean注入
POJO都不变。
<?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: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,之前的Bean后处理器和autowire都可算是内部Bean,自动注入 其实就是Bean的嵌套定义,其作用是内部Bean只让外部Bean使用,其它Bean无法使用 --> <bean id="student" class="com.boke.Student"> <property name="name" value="小学生" /> <property name="age" value="12" /> <property name="school"> <bean class="com.boke.School"> <property name="name" value="北京大学" /> </bean> </property> </bean> </beans>
package com.node.boke; import com.boke.Student; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyTest { @Test public void test01(){ ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml"); Student student = (Student)ac.getBean("student"); System.out.println(student); // School school = (School)ac.getBean("school"); // 报错,说明无法访问内部bean // System.out.println(school); } }
10.使用同类抽象bean注入
其它POJO不变,修改Student类。
package com.boke; public class Student { private String name; private int age; private String college; private String profession; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getCollege() { return college; } public void setCollege(String college) { this.college = college; } public String getProfession() { return profession; } public void setProfession(String profession) { this.profession = profession; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + ", college='" + college + '\'' + ", Role='" + profession + '\'' + '}'; } }
<?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的class相同 有着某些共同的属性 有着某些不同的属性 把这些共同的属性抽出来当做baseBean,其它bean继承它再赋值自己的属性即可 使用parent属性来继承baseBean 将baseBean通过abstract=True设置成一个抽象bean(非必须) 每个子bean都不用再写class属性了 --> <bean id="baseBean" class="com.boke.Student" abstract="true"> <property name="college" value="清华大学" /> <property name="profession" value="计算机专业" /> </bean> <bean id="student张三" parent="baseBean"> <property name="name" value="张三" /> <property name="age" value="22" /> </bean> <bean id="student李四" parent="baseBean"> <property name="name" value="李四" /> <property name="age" value="23" /> </bean> <bean id="student王五" parent="baseBean"> <property name="name" value="王五" /> <property name="age" value="24" /> </bean> <bean id="student赵四" parent="baseBean"> <property name="name" value="尼古拉斯.赵四" /> <property name="age" value="21" /> </bean> </beans>
package com.node.boke; import com.boke.Student; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyTest { @Test public void test01(){ ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml"); Student student1 = (Student)ac.getBean("student张三"); System.out.println(student1); Student student2 = (Student)ac.getBean("student李四"); System.out.println(student2); Student student3 = (Student)ac.getBean("student王五"); System.out.println(student3); Student student4 = (Student)ac.getBean("student赵四"); System.out.println(student4); } }
11.使用异类抽象bean注入
其它POJO不变,添加Teacher类。
package com.boke; public class Teacher { private String name; private int workAge; private String college; private String profession; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getWorkAge() { return workAge; } public void setWorkAge(int workAge) { this.workAge = workAge; } public String getCollege() { return college; } public void setCollege(String college) { this.college = college; } public String getProfession() { return profession; } public void setProfession(String profession) { this.profession = profession; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", workAge=" + workAge + ", college='" + college + '\'' + ", Role='" + profession + '\'' + '}'; } }
<?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的class不同 有着共同的属性,并且有着不同的属性 同样是用abstract=True属性设置,但是没有class属性 每个继承baseBean2的子bean都要写class属性指定自己的实体类 --> <bean id="baseBean" abstract="true"> <property name="college" value="辽宁民间艺术团" /> <property name="profession" value="二人转" /> </bean> <bean id="student小沈阳" class="com.boke.Student" parent="baseBean"> <property name="name" value="小沈阳" /> <property name="age" value="18" /> </bean> <bean id="teacher赵本山" class="com.boke.Teacher" parent="baseBean"> <property name="name" value="赵本山" /> <property name="workAge" value="20" /> </bean> </beans>
package com.node.boke; import com.boke.Student; import com.boke.Teacher; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyTest { @Test public void test01(){ ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml"); Student student = (Student)ac.getBean("student小沈阳"); Teacher teacher = (Teacher)ac.getBean("teacher赵本山"); System.out.println(student); System.out.println(teacher); } }
五、基于注解的DI
四个功能完全相同Component、Repository、Service和Controller,但意义不同的注解:功能相同意味着凡是可以用@Component的地方都可以用其它三个,凡是用@Controller的地方都可以用其它三个。
@Component 组件,注解到实体类上,表示当前类被Spring容器管理,在spring容器加载进来时自动初始化,其余类同
@Repository 注解在Dao实现类上
@Service 注解在Service实现类上
@Controller 注解在SpringMVC的处理器上
@Scope(value="prototype") 注解在bean上,指定该实体类是单例模式还是原型模式
@Value(java.lang.String) 注解在成员变量上,成员变量注入,初始化成员变量赋值
@Autowired 注解在域属性上,byType方式注入,对应xml注入时bean中的autowire="byType"
@Qualifier(value="xxx") 注解在域属性上,与Autowired方式联合使用,实现byName方式注入
@Resource(name="xxx") 注解在域属性上,byName方式注入 在JavaSE中,可应用到类、接口、方法、域上,用于资源注入
@Resource 注解在域属性上,byType方式注入,不写参数
@PostConstruct 注解在成员方法上,在bean刚初始化完毕后执行
@PreDestroy 注解在成员方法上,在bean即将被销毁时执行
@Configuration 注解在实体类上,将实体类充当spring容器
@Bean 注解到成员方法上,自定义bean的注入,与@Configuration配合使用
1.测试
Student类和School类。
// Student类 package com.boke; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; @Scope("singleton") // 也支持单例模式和原型模式,默认是单例模式 @Component(value = "student") public class Student { @Value("Alex") private String name; @Value("20") private int age; @Autowired // byType @Qualifier(value = "school") private School school; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public School getSchool() { return school; } public void setSchool(School school) { this.school = school; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + ", school=" + school + '}'; } @PostConstruct public void initAfter(){ System.out.println("当前bean刚初始化完毕..."); } @PreDestroy public void preDestroy(){ System.out.println("当前bean即将被销毁..."); } } // School类 package com.boke; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Component // 如果不写value,默认值是类名小写 public class School { @Value("清华大学") private String name; public void setName(String name) { this.name = name; } public String getName() { return name; } @Override public String toString() { return "School{" + "name='" + name + '\'' + '}'; } }
需要在pom.xml文件中导入spring-context,并在applicationContext.xml开头引入其配置规则。通过context标签设置组件扫描路径。
<?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:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd" > <!--注册组件扫描器--> <context:component-scan base-package="com.boke" /> </beans>
测试类。
package com.node.boke; import com.boke.School; import com.boke.Student; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyTest { @Test public void test01(){ ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml"); Student student = (Student)ac.getBean("student"); School teacher = (School)ac.getBean("school"); System.out.println(student); System.out.println(teacher); ((ClassPathXmlApplicationContext)ac).close(); } }
2.测试Configuration
Student类和School类。
// Student类 package com.boke; import org.springframework.beans.factory.annotation.Value; public class Student { @Value("李四") private String name; private int age; private School school; Student(){ super(); } public Student(String name, int age, School school){ super(); this.name = name; this.age = age; this.school = school; System.out.println(name + age + school); } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public School getSchool() { return school; } public void setSchool(School school) { this.school = school; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + ", school=" + school + '}'; } } // School类 package com.boke; public class School { private String name; School(){ super(); } public School(String name){ super(); this.name = name; } public void setName(String name) { this.name = name; } public String getName() { return name; } @Override public String toString() { return "School{" + "name='" + name + '\'' + '}'; } }
在com.boke下面新建conf文件夹。并新建What类。
package com.boke.conf; import com.boke.School; import com.boke.Student; import org.springframework.beans.factory.annotation.Autowire; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration // 表示当前类充当spring容器,所有的bean都由这个类创建(而不是spring创建) public class what { @Bean(name="mySchool", autowire = Autowire.BY_NAME) public School createSchool(){ return new School("清华大学"); } @Bean(name="myStudent", autowire = Autowire.BY_NAME) public Student createStudent(){ School school = new School("北京大学"); return new Student("张三", 20,school); } }
配置applicationContext.xml,使其只扫描com.boke.conf。
<?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:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd" > <!--注册组件扫描器--> <context:component-scan base-package="com.boke.conf" /> <!-- 当使用@Configuration时,如果Bean类上添加其它带有注解的实体类(如@Component),仍然会在容器类执行完事儿之后交由spring容器管理 @Value初始化成员变量是在@Configuration时注解之后执行,会覆盖掉其注入的bean成员属性和域属性值 --> </beans>
测试类。
package com.node.boke; import com.boke.School; import com.boke.Student; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyTest { @Test public void test01(){ ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml"); Student student1 = (Student) ac.getBean("myStudent"); School school1 = (School)ac.getBean("mySchool"); System.out.println(student1); System.out.println(school1); ((ClassPathXmlApplicationContext)ac).close(); } }
一般而言,定义spring配置类@Configuration中注入的bean优先级低于注解注入的bean,而注解注入的bean优先级要低于applicationContext.xml中直接注入的bean。因此,同一个bean对象被此三者注入时,会被逐级覆盖。
至此,IOC控制反转结束。
六、多个applicationContext.xml文件的配置
如果是多个配置文件关系平等,可以通过调用ClassPathXmlApplicationContext的重载方法来配置,也可以新建一个总配置文件,再通过import导入多个配置文件。
<?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数量也大量增加,导致配置文件变得非常庞大臃肿。 为了避免这种情况的产生,提高配置文件的可读性与可维护性,可以将Spring配置文件分解成多个配置文件。 --> <!-- 1.平等关系的配置文件 将配置文件分解为地位平等的多个配置文件,并将所有配置文件的路径定义为一个String数组 将其作为容器初始化参数出现,其将与可变参的容器构造器匹配 不用新建applicationContext,直接在测试类中用 // 多个spring的配置文件 String resource1 = "applicationContext.xmldi-SPEL注入至异类抽象Bean注入.xml"; String resource2 = "applicationContext.xmldi-无参构造注入至集合注入.xml"; // 方式一:把配置文件一个个的写进去 // ApplicationContext ac = new ClassPathXmlApplicationContext(resource1, resource2); // 方式二:把配置文件写进数组传进入 String[] resources = {resource1, resource2}; ApplicationContext ac = new ClassPathXmlApplicationContext(resources); // 方式三:通配符模糊匹配,不支持中文 // String resource = "applicationContext.xmldi-*.xml"; // ApplicationContext ac = new ClassPathXmlApplicationContext(resource); Some some = (Some)ac.getBean("some1"); System.out.println(some); Student student = (Student)ac.getBean("student小沈阳"); Teacher teacher = (Teacher)ac.getBean("teacher赵本山"); System.out.println(student); System.out.println(teacher); --> <!-- 2.包含关系的配置文件: 假设当前applicationContext.xml包含了两个xml 用import引入 <import resource = "classpath:xxx类路径"> --> <!--可以一个个xml引入--> <import resource="classpath:applicationContext.di-xml注入.xml" /> <import resource="classpath:applicationContext.di-注解注入.xml" /> <!--也可以使用通配符引入--> <!--<import resource="classpath:applicationContext.xmldi-*.xml" />--> </beans>
七、spring-junit测试
在spring容器环境下直接使用和测试bean,需要在pom.xml文件中引入spring-text包依赖。
Stundent类和School类。
// Student类 package com.boke; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import javax.annotation.Resource; @Component public class Student { @Value("尼古拉斯.赵四") private String name; @Value("18") private int age; @Resource(name = "mySchool") private School school; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public School getSchool() { return school; } public void setSchool(School school) { this.school = school; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + ", school=" + school + '}'; } public void sayHi(){ System.out.println("我的慢摇轻晃是舞蹈界的一股泥石流..."); } } // School类 package com.boke; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Component("mySchool") public class School { @Value("辽宁民间艺术团") private String name; public void setName(String name) { this.name = name; } public String getName() { return name; } @Override public String toString() { return "School{" + "name='" + name + '\'' + '}'; } public void introduce(){ System.out.println("中国二人转人才培养基地,拥有世界领先的教育水平,即使是美国总统在这儿待两天也会转手绢..."); } }
applicationContext.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:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="com.boke" /> </beans>
测试类。
package com.node.boke; import com.boke.School; import com.boke.Student; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import javax.annotation.Resource; @RunWith(value = SpringJUnit4ClassRunner.class) // 加载运行环境 @ContextConfiguration(locations = "classpath:applicationContext.xml") // 加载spring容器 public class MyTest { // 加载对应的bean @Resource // byType指定bean,byType对应is-a关系,即匹配当前类及其子类 private Student student; @Resource(name = "mySchool") // byName指定bean,如果name改成其它值直接报错 private School school; @Test public void test01() { // 直接测试 school.introduce(); student.sayHi(); } }
备注:该博文内容整理自动力节点Reyco老师课堂讲解。