IOC
传统开发方式如下:创建一个接口UserService
,使用其实现类UserServiceImpl
中的方法,这就有一个问题,当需要修改方法时(修改为其他的实现类)必须到源代码中进行修改,不符合开闭原则,耦合度高
UserService user = new UserServiceImpl();
user.hello();
工厂模式开发方式:创建一个获取该接口下的实现类的工厂UserServiceFactory
,就解决了修改方法时对源代码的修改,只需要将工厂中返回的实现类对象修改即可,但是手动管理工厂又有些麻烦,也不太符合开闭原则
class{
public static UserService getUserService(){
return new UserServiceImpl(); //当需要修改方法时,将此处返回的实现类修改即可
}
}
UserService user = new UserServiceFactory.getUserService();
user.hello();
基本使用
xml方式
现在已有userService
接口,并且也有该接口的实现类UserServiceImpl
,创建Spring-Framework全局配置文件,一般命名为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="userService" class="com.UserServiceImpl"> <!--bean标签用于声明Spring容器中管理的对象-->
<!--com.UserServiceImpl类的对象交由Spring创建,使用id可从IOC容器中获取的该对象-->
<!--id属性用于从IOC容器中获取使用该类创建的对象,若省略id属性默认是该类的首字母小写类名-->
<!--class属性指定类的全类名-->
<!--可使用name属性替代id属性,唯一区别就是:name属性可使用特殊字符命名,而id属性却不能-->
<property name="name" value="world"/> <!--property标签用于为对象的属性赋值-->
<!--name属性指定该属性的属性名,value属性指定该属性的属性值-->
</bean>
</beans>
编写程序进行使用
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
//从类路径中获取配置文件,并创建applicationContext对象,SpringBean工厂对象
UserService userService = (UserService) applicationContext.getBean("userService");
//使用getBean()方法根据容器中的ID获取对象,需要强转
userService.hello(); //hello world
//执行接口中定义的方法
}
注解方式
现在已有userService
接口,并且也有该接口的实现类UserServiceImpl
,创建Spring-Framework全局配置文件,一般命名为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
https://www.springframework.org/schema/context/spring-context.xsd">
<!--
首先添加 xmlns:context="http://www.springframework.org/schema/context" 约束文件
再在 xsi:schemaLocation属性后增加 http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd
之后使用下面标签才能有代码提示
-->
<context:component-scan base-package="com"/>
<!--开启注解扫描,base-package属性指定要扫描的包,也就是说只有开启注解扫描的包,包下的源代码中才能有效的使用注解进行管理-->
</beans>
在类中使用注解,具体如下
package com;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component("userService") //对应xml中的id属性,用于从IOC容器中获取使用该类创建的对象,省略默认是该类首字母小写类名
//@Repository()
//@Service()
//@Controller()
//上面这三个注解与Component注解作用相同,用于MVC分层,便于越多,后续Spring新版本会增强这些注解
public class UserServiceImpl implements UserService {
@Value("world") //直接属性注入方式无需setter方法,若在setter方法上使用该注解也是可以的
private String name;
@Override
public void hello() {
System.out.println("hello" + this.name);
}
}
编写程序进行使用
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) applicationContext.getBean("userService");
userService.hello();
}
Spring的Bean管理
IOC(Inversion of Control)控制反转的思想,就是将所有对象的创建和依赖关系维护容器工厂(Spring)管理,原理就是使用反射创建对象
Bean实例化的三种方式
<!--1.默认使用无参构造方式-->
<bean id="userServiceConstruction" class="com.UserServiceImpl"/>
<!--2.使用静态工厂方法实例化(简单工厂模式)-->
<bean id="userServiceSimpleFactory" class="com.UserServiceImpl" factory-method="SimpleFactory"/>
<!--factory-method属性指定工厂方法,用于返回该实例-->
<!--3.使用实例工厂方法实例化(工厂方法模式)-->
<bean id="userService" class="com.UserServiceImpl"/>
<bean id="userServiceFactoryMethod" factory-bean="userService" factory-method="userServiceFactoryMethod"/>
<!--需要先创建实例,再使用factory-bean属性指定工厂实例,再factory-method属性指定工厂方法,用于返回该实例-->
用于测试的关键代码如下
package com;
public class UserServiceImpl implements UserService { //UserService接口置空了
public UserServiceImpl() {
System.out.println("无参构造方法被执行");
}
public static UserServiceImpl SimpleFactory() {
System.out.println("静态工厂方法被执行");
return null; //用于测试就返回null了,以免构造方法多次输出
}
public UserServiceImpl userServiceFactoryMethod() {
System.out.println("实例工厂方法被执行");
return null; //用于测试就返回null了,以免构造方法多次输出
}
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
//无参构造执行两次的原因是在使用实例工厂方法时,为了不冲突,又创建了一个实例
//无参构造方法被执行
//静态工厂方法被执行
//无参构造方法被执行
//实例工厂方法被执行
}
}
Bean的作用范围
xml方式
<!--1.默认是单例模式,实例创建采用预加载策略-->
<bean id="userServiceSingle" class="com.UserServiceImpl"/>
<!--2.使用多例模式,实例创建采用懒加载策略-->
<bean id="userServiceMultiton" class="com.UserServiceImpl" scope="prototype"/>
下面是测试程序
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userServiceSingle1 = (UserService) applicationContext.getBean("userServiceSingle");
UserService userServiceSingle2 = (UserService) applicationContext.getBean("userServiceSingle");
System.out.println(userServiceSingle1 == userServiceSingle2); //true
UserService userServiceMultiton1 = (UserService) applicationContext.getBean("userServiceMultiton");
UserService userServiceMultiton2 = (UserService) applicationContext.getBean("userServiceMultiton");
System.out.println(userServiceMultiton1 == userServiceMultiton2); //false
//预加载会在读取配置文件创建applicationContext对象时就会将对象创建出来,懒加载只有在获取该对象时才会创建出来
//无参构造方法被执行
//true
//无参构造方法被执行
//无参构造方法被执行
//false
}
注解方式
package com;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component("userService")
@Scope("prototype") //指定多例模式
public class UserServiceImpl implements UserService {
}
下面是测试程序
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService1 = (UserService) applicationContext.getBean("userService");
UserService userService2 = (UserService) applicationContext.getBean("userService");
System.out.println(userService1 == userService2); //false
}
Bean的生命周期
- 实例化对象,执行构造方法
- 注入属性,执行setter方法
- 若该类实现了
BeanNameAware
接口,会执行setBeanName()
方法 - 若该类实现了
BeanFactoryAware
和ApplicationContextAware
接口,会执行setBeanFactory()
方法和setApplicationContext()
方法 - 若有一个类实现了
BeanPostProcessor
接口,会执行postProcessBeforeInitialization()
方法 - 若该类实现了
InitializingBean
接口,会执行afterPropertiesSet()
方法 - 若该类配置了
init-method
方法,会执行该初始化方法 - 若有一个类实现了
BeanPostProcessor
接口,会执行postProcessAfterInitialization()
方法 - 执行该类自身的业务方法
- 若该类实现了
DisposableBean
接口,会执行destroy()
方法 - 若该类配置
destroy-method
方法,会执行该销毁方法
xml方式
<!--只有单例模式下的对象才能被销毁-->
<bean id="userService" class="com.UserServiceImpl" init-method="initMethod" destroy-method="destroyMethod">
<property name="name" value="value"/>
</bean>
下面是整个生命周期测试代码
package com;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.*;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class UserServiceImpl implements UserService, BeanNameAware, BeanFactoryAware,
ApplicationContextAware, BeanPostProcessor, InitializingBean, DisposableBean {
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); //ClassPathXmlApplicationContext才有关闭容器方法
UserService userService = (UserService) applicationContext.getBean("userService");
userService.service();
applicationContext.close(); //关闭容器才会执行销毁方法
//1.执行构造方法
//2.执行setter()方法
//3.setBeanName()方法,对象在容器中的名字为:userService
//4.1.执行setBeanFactory()方法,当前Bean工厂为:org.springframework.beans.factory.support.DefaultListableBeanFactory@57a3af25: defining beans [userService]; root of factory hierarchy
//4.2.执行setBeanFactory()方法,当前Bean工厂为:org.springframework.context.support.ClassPathXmlApplicationContext@7e0e6aa2, started on Fri Dec 11 19:15:41 CST 2020
//6.执行afterPropertiesSet()方法
//7.执行initMethod()方法
//9.执行业务方法
//10.执行destroy()方法
//11.执行destroyMethod()方法
}
public UserServiceImpl() {
System.out.println("1.执行构造方法");
}
public void setName(String name) {
System.out.println("2.执行setter()方法");
}
@Override
public void setBeanName(String name) {
System.out.println("3.setBeanName()方法,对象在容器中的名字为:" + name);
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("4.1.执行setBeanFactory()方法,当前Bean工厂为:" + beanFactory);
//BeanFactory是顶层工厂接口
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("4.2.执行setBeanFactory()方法,当前Bean工厂为:" + applicationContext);
//ApplicationContext是BeanFactory工厂的子接口
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("5.执行postProcessBeforeInitialization()方法");
return null;
}
@Override
public void afterPropertiesSet() {
System.out.println("6.执行afterPropertiesSet()方法");
}
public void initMethod() {
System.out.println("7.执行initMethod()方法");
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("8.执行postProcessAfterInitialization()方法");
return null;
}
@Override
public void service() {
System.out.println("9.执行业务方法");
}
@Override
public void destroy() {
System.out.println("10.执行destroy()方法");
}
public void destroyMethod() {
System.out.println("11.执行destroyMethod()方法");
}
}
注解方式
初始化和销毁注解是在
javax.annotation
包中,jdk1.8以后的版本需要引入javax.annotation-api
包才能使用,注意在Web项目中依赖范围应该是provided
package com;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@Component("userService")
public class UserServiceImpl implements UserService {
@PostConstruct //初始化方法注解
public void initMethod() {
System.out.println("执行initMethod()方法");
}
@PreDestroy //销毁方法注解
public void destroyMethod() {
System.out.println("执行destroyMethod()方法");
}
}
编写程序进行测试
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); //ClassPathXmlApplicationContext才有关闭容器方法
UserService userService = (UserService) applicationContext.getBean("userService");
applicationContext.close(); //关闭容器才会执行销毁方法
}
Spring的依赖注入
DI(Dependency Injection)依赖注入就是在Spring创建对象时,将对象所依赖的属性注入进去
构造注入
<bean id="userService" class="com.UserServiceImpl">
<constructor-arg name="name" value="张三"/> <!--constructor-arg标签用于向构造方法中传递参数-->
<!--name属性指定形参,value指定参数值-->
<!--如果是复杂数据类型,则需要使用ref属性指定其他bean标签的id进行传值-->
<!--不常用的属性:index属性可指定形参的位置来传递参数值,type属性可指定参数的数据类型-->
<!--若index和name属性都省略则会按照属性顺序进行注入-->
</bean>
setter注入
<bean id="userService" class="com.UserServiceImpl">
<property name="name" value="张三"/> <!--property标签用于向setter方法中传递参数-->
<!--name属性指定形参,value指定参数值-->
<!--如果是复杂数据类型,则需要使用ref属性指定其他bean标签的id进行传值-->
</bean>
自动注入
仅针对引用类型有效
xml方式
<bean id="userDao" class="com.UserDaoImpl" />
<bean id="userService" class="com.UserServiceImpl" autowire="byType" />
<!--
使用autowire指定自动注入的规则
byName:要求变量名与要注入的Bean的id名完全一致
byType:要求当前容器中符合注入要求的数据类型:一致、或是子类、或是实现类,并且不能有多个符合条件的
-->
注解方式
@Resource注解是在
javax.annotation
包中,jdk1.8以后的版本需要引入javax.annotation-api
包才能使用,注意在Web项目中依赖范围应该是provided
package com;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component("userService")
public class UserServiceImpl implements UserService {
@Autowired
//byType自动注入,若有多个同类型的bean则根据变量的变量名进行自动注入(类的首字母小写),若有多个同类型的bean但其中有一个bean类上使用了@Primary注解会优先注入
//@Qualifier() //同时使用Qualifier注解,即可指定bean的id注入,
//@Resource() //Resource注解相当于同时使用Autowired和@Qualifier注解,即byName自动注入,若byName失败会自动byType自动注入
//也可使用name属性指定bean的id注入
private UserDao userDao;
}
P命名空间注入
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
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">
<!--将第一行的约束文件进行复制,并且增加一个p的命名空间,地址尾部改为p 就变成了 xmlns:p="http://www.springframework.org/schema/p"-->
<!--
此时就可以使用以下语法进行属性注入了
p:属性名="属性值"
p:属性名-ref="其他bean标签的id"
-->
<bean id="userService" class="com.UserServiceImpl" p:name="张三"/>
</beans>
SpEl注入
SpEL(Spring Expression Language)spring表达式语言,使用#{}
包裹表达式的方式注入属性
<bean id="userServiceOther" class="com.UserServiceImpl"/>
<bean id="userService" class="com.UserServiceImpl">
<property name="name" value="#{'张三'}"/> <!--注入字符串使用单引号-->
<property name="age" value="#{20 - 2}"/> <!--注入数字,可是进行简单的计算-->
<property name="say" value="#{userServiceOther.hello()}"/> <!--可调用其他bean的方法,若只指定其他bean的id即可注入另一个bean-->
<property name="sex" value="#{T(Math).PI}"/> <!--使用静态字段或方法-->
</bean>
集合类型属性注入
<bean id="userService" class="com.UserServiceImpl">
<property name="array">
<list>
<value>1</value>
<value>2</value>
</list>
</property>
<property name="list">
<list>
<value>1</value>
<value>2</value>
</list>
</property>
<property name="set">
<set>
<value>1</value>
<value>2</value>
</set>
</property>
<property name="map">
<map>
<entry key="key1" value="1"/> <!--key-ref和value-ref属性可指定其他Bean-->
<entry key="key2" value="2"/>
</map>
</property>
<property name="properties">
<map>
<entry key="key1" value="1"/>
<entry key="key2" value="2"/>
</map>
</property>
</bean>
该Java类中定义如下
package com;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
public class UserServiceImpl implements UserService {
private String[] array;
private List<String> list;
private Set<String> set;
private Map<String, Integer> map;
private Properties properties;
//setter省略
}
xml和注解方式整合
通常使用xml方式进行Bean管理,使用注解方式进行依赖注入,这样即便于开发,也便于维护,这时无需开启整个包的注解扫描,因为只用到了依赖注入的注解,开启注解配置即可,即在applicationContext.xml
中增加下面配置
<context:annotation-config />
若需要管理的Bean过于多时,可在在applicationContext.xml
中使用import
标签引入其他Spring配置文件,具体使用如下
<import resource="classpath:spring-config/student.xml"/> <!-- classpath:表示当前类路径,路径分隔符使用斜杠-->
<import resource="classpath:spring-config/*"/> <!--也可以使用星号通配符-->
Comments NOTHING