关于spring的Bean
需要建立的几个文件:
1.Bean的默认装配,通过容器的getBean()方法。
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.hnu.service.SomeService;
public class MyTest {
@Test
public void test02(){
//1.从类路径下找配置文件
ApplicationContext ac =new ClassPathXmlApplicationContext("applicationContext.xml");
SomeService service = (SomeService) ac.getBean("myService");
service.doSome();
}
}
注意:使用默认装配方式会调用类的无参构造器来创建对象,所以必须给类一个无参构造器。
2.工厂Bean
工厂类的创建交给spring容器,其他需要创建的类交给工厂类。目的是解耦合。
2.1动态工厂
创建动态工厂类:
//动态工厂类
public class ServiceFactory {
public SomeService getSomeService(){
return new SomeServiceImpl();
}
}
配置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 id="factory" class="com.hnu.ba01.ServiceFactory"/>
</beans>
测试类:
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
@Test
public void test01(){
SomeService service = new ServiceFactory().getSomeService();
service.doSome();
}
@Test
public void test02(){
//1.从类路径下找配置文件
ApplicationContext ac =new ClassPathXmlApplicationContext("com/hnu/ba02/applicationContext.xml");
ServiceFactory factory = (ServiceFactory) ac.getBean("factory");
SomeService service = factory.getSomeService();
service.doSome();
}
}
test01是我们没使用spring容器时使用工厂类的一般写法,test02是在spring配置文件中配置了动态工厂类,由spring容器来创建动态工厂类。但是这两种写法都存在测试类和工厂类耦合的问题。下面是改进写法,也是常见用法。
配置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 definitions here -->
<!-- 注册动态工厂 -->
<bean id="factory" class="com.hnu.ba02.ServiceFactory"/>
<!--注册Service 动态工厂Bean-->
<bean id="myService" factory-bean="factory" factory-method="getSomeService" />
</beans>
测试类写法:
@Test
public void test03(){
ApplicationContext ac =new ClassPathXmlApplicationContext("com/hnu/ba02/applicationContext.xml");
SomeService service = (SomeService) ac.getBean("myService");
service.doSome();
}
2.2静态工厂:
创建静态工厂类:
//静态工厂类
public class ServiceFactory {
public static SomeService getSomeService(){
return new SomeServiceImpl();
}
}
配置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 definitions here -->
<!-- 静态工厂不需要注册 -->
<!-- <bean id="factory" class="com.hnu.ba03.ServiceFactory"/> -->
<!--注册Service :动态工厂Bean-->
<bean id="myService" class="com.hnu.ba03.ServiceFactory" factory-method="getSomeService" />
</beans>
测试类:
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
@Test
public void test03(){
//1.从类路径下找配置文件
ApplicationContext ac =new ClassPathXmlApplicationContext("com/hnu/ba03/applicationContext.xml");
SomeService service = (SomeService) ac.getBean("myService");
service.doSome();
}
}
3.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">
<!--注册Service
scope="prototype" 原型模式,其对象的创建时机不是在Spring容器的初始化,而是在代码证真正访问的时候才创建(有点类似BeanFactory容器),而且用一次会创建一个。
scope="singleton" 单例模式(默认使用的模式),对象的的创建时机是在spring容器初始化的时候
-->
<bean id="myService" class="com.hnu.ba04.SomeServiceImpl" scope="singleton"/>
</beans>
除了prototype和singleton还有request,session两种模式。
request:一次请求就创建一个不同的Bean实例
session:一次会话就创建一个不同的Bean实例
4.Bean后处理器
bean后处理器是一种特殊的Bean,容器中所有的Bean在初始化时,都会自动执行该类的两个方法,由于该Bean是由其他Bean自动调用执行的,不是程序员手工调用,顾此Bean无需id属性。
需要做的是,在Bean后处理器类方法中,对Bean类和Bean类中的方法进行判断,即可实现对指定Bean的指定方法进行功能的扩展与增强。方法返回Bean的对象,即是增强后的对象。
我们需要自定义Bean后处理器类。该类实现BeanPostProcessor接口,该接口中包含两个方法,分别在目标Bean初始化之前与之后执行。它们的返回值为:功能被扩展或增强后的Bean对象。
补充:做增强就需要用到代理,学过两种代理:一种是jdk的Proxy,一种是cglib。使用Proxy需要被增强的类有实现接口,能使用Proxy就尽量不适用cglib,避免污染代码。
增强代码如下:
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;
public class MyBeanPostProcessor implements BeanPostProcessor {
/*
*bean:表示当前正在进行初始化的Bean对象
*beanName:表示当前正在进行初始化的Bean对象的id
*/
@Override
public Object postProcessAfterInitialization(final Object bean, String beanName) throws BeansException {
System.out.println("Bean初始化之前执行after");
if ("myService".equals(beanName)) {
Object obj = Proxy.newProxyInstance(bean.getClass().getClassLoader(), bean.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object invoke = method.invoke(bean, args); //内部类使用外部类成员,需要外部类成员为final
if ("doSome".equals(method.getName())) {
return ((String) invoke).toUpperCase();
}
return invoke;
}
});
return obj;
}
return bean;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("Bean初始化之后执行before");
return bean;
}
}
5.定制Bean的生命始末
可以为Bean定制初始化后的生命行为,也可以为Bean定制销毁前的生命行为。
这些方法需要在Bean类中事先定义好:是方法名随意的public void方法。
Bean的代码:
public class SomeServiceImpl implements SomeService {
public SomeServiceImpl() {
System.out.println("执行无参构造器");
}
@Override
public void doSome() {
System.out.println("执行doSome()方法!");
}
public void setUp(){
System.out.println("Bean初始化完毕后执行");
}
public void tearDown(){
System.out.println("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">
<!--注册Service-->
<bean id="myService" class="com.hnu.ba06.SomeServiceImpl"
init-method="setUp" destroy-method="tearDown"/>
</beans>
测试类MyTest:
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
//不使用spring容器的时候
@Test
public void test01(){
ApplicationContext ac = new ClassPathXmlApplicationContext("com/hnu/ba06/applicationContext.xml");
SomeService service = (SomeService) ac.getBean("myService");
service.doSome();
//对于销毁之前方法的执行,有两个条件:
//1)当前Bean需要时singleton的
//2)要手动关闭容器
((ClassPathXmlApplicationContext)ac).close();
}
}
6.Bean的生命周期(暂了解)
在spring里面Bean的生命周期中有11个可控点,我们可以捕获这些可控点,做一些其他的工作
7.Bean装配
小知识点:<bean/>标签的id属性和name属性区别
id属性的命名需要满足XML对id属性的命名规范:必须以字母开头,可以包含字母、数字、下划线、连字符、句号、冒号。
name属性的命名则可以包含各种字符。
以上是老版本的规范,现在id也可以用/开头了,id和name基本没使用区别
7.1基于XML的注入(DI)
7.1.1注入的分类
(1)设值注入(工作中使用的较多)
(2)构造注入
(3)实现特定接口的注入(侵入式编程,污染了代码,基本不用)
设值注入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">
<!-- 注册School -->
<bean id="mySchool" class="com.hnu.di01.School">
<property name="name" value="清华大学" />
</bean>
<!-- 注册Student -->
<!-- 设值注入 (比较常用)-->
<bean id="myStudent" class="com.hnu.di01.Student">
<property name="name" value="张三"/>
<property name="age" value="23" />
<property name="school" ref="mySchool" />
</bean>
</beans>
构造注入,就是通过构造函数来实现注入,这是bean可以不要无参构造器,但还是建议加上无参构造器。
public class Student {
private String name;
private int age;
private School school;
public Student() {
}
//用于构造注入
public Student(String name, int age, School school) {
super();
this.name = name;
this.age = age;
this.school = school;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public void setSchool(School school) {
this.school = school;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + ", school=" + school + "]";
}
}
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">
<!-- 注册School -->
<bean id="mySchool" class="com.hnu.di02.School">
<property name="name" value="清华大学" />
</bean>
<!-- 注册Student -->
<!--构造注入(用的较少) -->
<bean id="myStudent" class="com.hnu.di02.Student">
<!--构造注入第一种方式
<constructor-arg index="0" value="张三"/>
<constructor-arg index="1" value="23" />
<constructor-arg index="2" ref="mySchool" />
-->
<!--构造注入第二种方式,这种方式参数必须一一对应
<constructor-arg value="张三"/>
<constructor-arg value="23" />
<constructor-arg ref="mySchool" />
-->
<!--构造注入第三种方式-->
<constructor-arg name="name" value="张三"/>
<constructor-arg name="age" value="23" />
<constructor-arg name="school" ref="mySchool" />
</bean>
</beans>
7.1.2命名空间注入
使用这两种注入需要给配置文件xml添加新的约束。
(1)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">
<!-- 注册School -->
<bean id="mySchool" class="com.hnu.di03.School" p:name="清华大学" />
<!-- 注册Student -->
<bean id="myStudent" class="com.hnu.di03.Student" p:name="张三" p:age="23" p:school-ref="mySchool" />
</beans>
(2)c命名空间构造注入(注意:被注入的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"
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">
<!-- 注册School -->
<bean id="mySchool" class="com.hnu.di04.School">
<property name="name" value="清华大学" />
</bean>
<!-- 注册Student -->
<bean id="myStudent" class="com.hnu.di04.Student" c:name="张三" c:age="23" c:school-ref="mySchool" />
</beans>
7.1.3集合属性注入
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.Map;
import java.util.Properties;
public class Some {
private School[] schools;
private String[] myStrs;
private List<String> myList;
private Set<String> mySet;
private Map<String, Object> myMap;
private Properties myProperties;
public void setSchools(School[] schools) {
this.schools = schools;
}
public void setMyStrs(String[] myStrs) {
this.myStrs = myStrs;
}
public void setMyList(List<String> myList) {
this.myList = myList;
}
public void setMySet(Set<String> mySet) {
this.mySet = mySet;
}
public void setMyMap(Map<String, Object> myMap) {
this.myMap = myMap;
}
public void setMyProperties(Properties myProperties) {
this.myProperties = myProperties;
}
@Override
public String toString() {
return "Some [schools=" + Arrays.toString(schools) + ", myStrs=" + Arrays.toString(myStrs) + ", myList="
+ myList + ", mySet=" + mySet + ", myMap=" + myMap + ", myProperties=" + myProperties + "]";
}
}
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">
<!-- 注册School -->
<bean id="mySchool" class="com.hnu.di05.School">
<property name="name" value="清华大学" />
</bean>
<bean id="mySchool2" class="com.hnu.di05.School">
<property name="name" value="北京大学" />
</bean>
<!-- 注册Some -->
<bean id="mySome" class="com.hnu.di05.Some">
<property name="schools">
<array>
<ref bean="mySchool"/>
<ref bean="mySchool2"/>
</array>
</property>
<property name="myStrs">
<array>
<value>中国</value>
<value>北京</value>
</array>
</property>
<property name="myList">
<list>
<value>朝阳</value>
<value>胡同口</value>
</list>
</property>
<property name="mySet">
<set>
<value>百度大厦</value>
<value>18号楼</value>
</set>
</property>
<property name="myMap">
<map>
<entry key="mobile" value="1234567"/>
<entry key="QQ" value="7654321"/>
</map>
</property>
<!--map和properties的区别:都是键值对,但是properties得key和value都是字符串。 -->
<property name="myProperties">
<props>
<prop key="education">本科</prop>
<prop key="education">自动化</prop>
</props>
</property>
</bean>
</beans>
简化版:
<?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">
<!-- 注册School -->
<bean id="mySchool" class="com.hnu.di06.School">
<property name="name" value="清华大学" />
</bean>
<bean id="mySchool2" class="com.hnu.di06.School">
<property name="name" value="北京大学" />
</bean>
<!-- 注册Some -->
<bean id="mySome" class="com.hnu.di06.Some">
<property name="schools">
<array>
<ref bean="mySchool"/>
<ref bean="mySchool2"/>
</array>
</property>
<property name="myStrs" value="中国,北京" />
<property name="myList" value="朝阳,胡同" />
<property name="mySet" value="百度大厦,18号楼" />
<property name="myMap">
<map>
<entry key="mobile" value="1234567"/>
<entry key="QQ" value="7654321"/>
</map>
</property>
<!--map和properties的区别:都是键值对,但是properties得key和value都是字符串。 -->
<property name="myProperties">
<props>
<prop key="education">本科</prop>
<prop key="education">自动化</prop>
</props>
</property>
</bean>
</beans>
7.1.4 对于域属性的自动注入
域属性就是对象属性,域属性的自动注入有两种方式:byName和byType。
(1)byName
<?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">
<!-- 注册School -->
<bean id="school" class="com.hnu.di07.School">
<property name="name" value="清华大学" />
</bean>
<!-- 注册Student
autowrie="byName" 会从容器中查找与实体类的域属性同名的Bean的id,并将该Bean对象自动注入给该域属性。
-->
<bean id="myStudent" class="com.hnu.di07.Student" autowire="byName">
<property name="name" value="张三"/>
<property name="age" value="23" />
</bean>
</beans>
(2)byType
<?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">
<!-- 注册School -->
<bean id="mySchool" class="com.hnu.di08.School">
<property name="name" value="清华大学" />
</bean>
<!--
<bean id="mySchool2" class="com.hnu.di08.School">
<property name="name" value="北京大学" />
</bean>
-->
<!-- 注册Student
autowrie="byType" 会从容器中查找与实体类的域属性类型具有is-a关系的Bean,并将该Bean对象自动注入给该域属性。
注意:如果同时有多个具有is-a关系(视为相同类型)的Bean,会报错
-->
<bean id="myStudent" class="com.hnu.di08.Student" autowire="byType">
<property name="name" value="张三"/>
<property name="age" value="23" />
</bean>
</beans>
7.1.5 SPEL注入
定义两个类:
public class Student {
private String name;
private int age;
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;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
}
public class Person {
private String name;
private int age;
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;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
public int computeAge(){
return age > 25 ? 25 : age;
}
}
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">
<!-- 注册Person-->
<bean id="myPerson" class="com.hnu.di09.Person">
<property name="name" value="李四" />
<property name="age" value="#{T(java.lang.Math).random()*50}" />
</bean>
<!-- 注册Student-->
<bean id="myStudent" class="com.hnu.di09.Student">
<property name="name" value="#{myPerson.name}"/>
<!-- <property name="age" value="#{myPerson.age > 25 ? 25 : myPerson.age}" /> -->
<property name="age" value="#{myPerson.computeAge()}" />
</bean>
</beans>
7.1.6 使用内部Bean注入
7.1.7 使用同类抽象Bean注入
7.1.8 使用异类抽象Bean注入
7.1.9 为应用指定多个spring配置文件
(1)平等关系的配置文件(用的较多)
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
@Test
public void test01(){
/*方法一
String resource1 = "com/hnu/di09/spring-base.xml";
String resource2 = "com/hnu/di09/spring-beans.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(resource1,resource2);
*/
/*方法二:字符串数组
String resource1 = "com/hnu/di09/spring-base.xml";
String resource2 = "com/hnu/di09/spring-beans.xml";
String[] resources={resource1,resource2};
ApplicationContext ac = new ClassPathXmlApplicationContext(resources);
*/
//方法三:通配符
String resource = "com/hnu/di09/spring-*.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(resource);
Person per = (Person) ac.getBean("myPerson");
System.out.println(per);
Student stu = (Student) ac.getBean("myStudent");
System.out.println(stu);
}
}
(2)包含关系的配置文件
主配置文件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">
<!-- 被包含的两个xml文件 -->
<!--
<import resource="classpath:com/hnu/di14/spring-base.xml"/>
<import resource="classpath:com/hnu/di14/spring-beans.xml"/>
-->
<!--也可以使用通配符 ,但是使用通配符的时候要注意,主配置文件的命名格式不能和子配置文件一样,容易出现自包含的情况-->
<import resource="classpath:com/hnu/di14/spring-*.xml"/>
</beans>
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
@Test
public void test01(){
String resource = "com/hnu/di014/applicationContext.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(resource);
Person per = (Person) ac.getBean("myPerson");
System.out.println(per);
Student stu = (Student) ac.getBean("myStudent");
System.out.println(stu);
}
}