需要建立的几个文件:
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();
}
}
注意:使用默认装配方式会调用类的无参构造器来创建对象,所以必须给类一个无参构造器。
工厂类的创建交给spring容器,其他需要创建的类交给工厂类。目的是解耦合。
创建动态工厂类:
//动态工厂类
public class ServiceFactory {
public SomeService getSomeService(){
return new SomeServiceImpl();
}
}
配置applicationContext.xml:
测试类:
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:
测试类写法:
@Test
public void test03(){
ApplicationContext ac =new ClassPathXmlApplicationContext("com/hnu/ba02/applicationContext.xml");
SomeService service = (SomeService) ac.getBean("myService");
service.doSome();
}
创建静态工厂类:
//静态工厂类
public class ServiceFactory {
public static SomeService getSomeService(){
return new SomeServiceImpl();
}
}
配置applicationContext.xml:
测试类:
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();
}
}
除了prototype和singleton还有request,session两种模式。
request:一次请求就创建一个不同的Bean实例
session:一次会话就创建一个不同的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;
}
}
可以为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配置:
测试类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();
}
}
在spring里面Bean的生命周期中有11个可控点,我们可以捕获这些可控点,做一些其他的工作
小知识点:
id属性的命名需要满足XML对id属性的命名规范:必须以字母开头,可以包含字母、数字、下划线、连字符、句号、冒号。
name属性的命名则可以包含各种字符。
以上是老版本的规范,现在id也可以用/开头了,id和name基本没使用区别
7.1.1注入的分类
(1)设值注入(工作中使用的较多)
(2)构造注入
(3)实现特定接口的注入(侵入式编程,污染了代码,基本不用)
设值注入xml配置如下:
构造注入,就是通过构造函数来实现注入,spring底层会直接调用Bean的带参构造器来创建对象,这时可以不要无参构造器,但还是建议加上无参构造器。(设值注入是通过 类名.forName.getInstance() 使用无参构造器创建对象,再通过set方法实现注入,所以必须要无参构造器)
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配置:
7.1.2命名空间注入
使用这两种注入需要给配置文件xml添加新的约束。
(1)p命名空间设值注入
(2)c命名空间构造注入(注意:被注入的Bean需要带参构造器)
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 myList;
private Set mySet;
private Map myMap;
private Properties myProperties;
public void setSchools(School[] schools) {
this.schools = schools;
}
public void setMyStrs(String[] myStrs) {
this.myStrs = myStrs;
}
public void setMyList(List myList) {
this.myList = myList;
}
public void setMySet(Set mySet) {
this.mySet = mySet;
}
public void setMyMap(Map 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配置:
中国
北京
朝阳
胡同口
百度大厦
18号楼
本科
自动化
简化版:
本科
自动化
7.1.4 对于域属性的自动注入
域属性就是对象属性,域属性的自动注入有两种方式:byName和byType。
(1)byName
(2)byType
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配置:
SpEL:
1.可以使用静态方法
2.可以访问其他Bean的成员变量
3.可以直接计算表达式
4.可以调用其他Bean的方法
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配置如下:
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);
}
}