Spring框架是一个开源的Java平台,用于构建企业级应用程序和Java EE应用程序。它提供了许多功能和模块,使开发者能够更轻松地构建和管理复杂的应用程序。整个Spring框架的学习分成四个模块:IOC基础容器、AOP面向切面过程、Spring整合web环境、SpringMVC web层解决方案。
Spring Framework的技术栈,共分为五块,Testjunit测试类、Core容器、AOP层、数据接入、Web接入层。
先解释一下Spring的基础术语:
1、IoC(Inversion of Control,控制反转):
IoC是Spring框架的两大核心之一。它通过将对象的创建和依赖注入的控制权交给Spring容器来实现。开发者只需要定义依赖关系和配置,而不需要显式地管理对象的创建和销毁。那么,通俗的讲,什么是控制反转呐?就是中介。
假如你拥有一家公司,你需要雇佣一名保洁人员给你打扫卫生,那么你需要去管理这个保洁人员,负责给他开工资、处理他的五险一金、处理他的节假日福利等等,各种杂事,但如果你把它交给一个物业公司,那么,你只需要向物业公司提出你的需求,其余的事情都交给他去处理就可以了,IoC就是这个作用。之所以叫控制反转,是因为正常是你去雇佣保洁,主动去做这个事。但是现在是让物业公司去做,再把保洁安置到你的公司,对于保洁的控制权从你的手里,转移到物业公司,所以叫控制反转。那么保洁人员安置到你公司的这个动作,就叫做依赖注入,所以有IoC的地方,就会提到依赖注入。
2、DI(Dependency Injection,依赖注入):
DI是IoC的一种具体实现方式,它是Spring框架的另一个核心概念。通过依赖注入,Spring容器可以在对象创建时自动将其依赖的其他对象注入到该对象中,降低了对象之间的耦合性,使得代码更加灵活和可维护。
盗一张别人的图,觉得这个齿轮图,很好的展现了IOC解耦的一个作用。
3、BeanFactory:
bean工厂,去生成、管理Bean对象的,是IoC的核心接口,就是通过这个接口,去实现反转、注入的。以下是BeanFactory的开发步骤:
<!--pom文件导入spring核心包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.7</version>
</dependency>
/**
* 新建一个service、一个实现类
**/
public interface UserService {}
public class UserServiceImpl implements UserService {}
<!--创建beans.xml配置文件,放到resources目录下-->
<bean id="userService" class="com.dj.service.impl.UserServiceImpl"></bean>
import com.dj.dao.UserDao;
import com.dj.service.UserService;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
/**
* 创建测试类,获取beanFactory创建的bean对象
**/
public class BeanFactoryTest {
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
xmlBeanDefinitionReader.loadBeanDefinitions("beans.xml");
UserService userService = (UserService) beanFactory.getBean("userService");
System.out.println(userService);
}
}
4、ApplicationContext:
ApplicationContext由BeanFactory派生而来,除了生成bean对象,也包含事件发布、国际化消息资源包等,父类共6个接口。在IDEA中(使用windows的快捷键),选中要查看的对象,右键show diagram,可以查看当前对象有哪些父类接口。
在IDEA中(使用windows的快捷键),选中要查看的对象,使用Ctrl+H的快捷键,可以查看类的继承关系。
import com.dj.dao.UserDao;
import com.dj.service.UserService;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
/**
* 创建测试类,获取beanFactory创建的bean对象
**/
import com.dj.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ApplicationContextTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserService userService = (UserService) context.getBean("userService");
System.out.println(userService);
}
}
BeanFactory和ApplicationContext都能生成bean对象,但是生成的时机是不一样的。BeanFactory是在获取bean对象时才能获取,而ApplicaitonContext是在类加载的时候就已经生成了。
5、关于bean的常用属性
id、class、name、scope、lazy-init、init-method、autowire、factory-bean、factory-method
1)关于id,通过配置bean的id名称,修改beanName,重要
<bean id="userService" class="com.dj.service.impl.UserServiceImpl"></bean>
//如果不配置id,beanName为com.dj.service.impl.UserServiceImpl
UserService userService = (UserService) context.getBean("com.dj.service.impl.UserServiceImpl");
2)关于name,配置别名,可以配置多个别名aa、bb、cc,几乎不用,不重要
<bean id="userService" name="aa,bb,cc" class="com.dj.service.impl.UserServiceImpl"></bean>
UserService aa= (UserService) context.getBean("aa");
UserService bb= (UserService) context.getBean("bb");
debug模式,查看context,context->beanFactory->aliasMap
3)关于scope,如果是单纯的spring环境,属性有singleton、prototype,默认是singleton,即使实例化多个对象,对象地址都是同一个,一般只用默认的。
当scope是prototype,每次实例化,每次都是新的对象。
如果添加spring-webmvc、spring-web环境时,scope属性还有request、session。
不重要。
<bean id="userService" class="com.dj.service.impl.UserServiceImpl" scope="prototype"></bean>
UserService userService1= (UserService) context.getBean("userService");
UserService userService2= (UserService) context.getBean("userService");
4)关于lazy-init,先不创建,谁用谁创建,对于beanFactory无效,ApplicationContext有效
<bean id="userService" class="com.dj.service.impl.UserServiceImpl" lazy-init="true"></bean>
5)关于init-method、destroy-method(用不到),初始化方法是指定对象构造完毕后,默认执行的、销毁方法是指容器关闭后,需要显示的调用容器关闭方法,才会默认执行该方法。
<!--在service实现类中添加方法名为init、destroy的方法,此处省略-->
<bean id="userService" class="com.dj.service.impl.UserServiceImpl" init-method="init" destroy-method="destroy"></bean>
此处,还可以提到InitializingBean接口,利用它的afterPropertiesSet方法执行赋值
package com.dj.service.impl;
import com.dj.service.UserService;
import org.springframework.beans.factory.InitializingBean;
public class UserServiceImpl implements UserService, InitializingBean {
public UserServiceImpl(){
System.out.println("构造方法:UserServiceImpl ...");
}
public void init(){
System.out.println("对象初始化后执行的方法:init ...");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("对象初始化完成后的属性操作:afterPropertiesSet ...");
}
}
import com.dj.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ApplicationContextTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserService userService = (UserService) context.getBean("userService");
System.out.println(userService);
}
}
/** 控制台打印的结果如下,由此可看出各方法执行的顺序:
* 构造方法:UserServiceImpl ...
* 对象初始化完成后的属性操作:afterPropertiesSet ...
* 对象初始化后执行的方法:init ...
* com.dj.service.impl.UserServiceImpl@478190fc
**/
6)关于spring实例化bean的两种方式:构造方法、工厂方法。spring的实例化底层都是通过BeanFactory来实现的,所以工厂方法就是两层工厂方法的嵌套,默认就是无参构造器。
package com.dj.dao;
public interface UserDao {
}
package com.dj.dao.impl;
import com.dj.dao.UserDao;
public class UserDaoImpl implements UserDao {
}
package com.dj.service.impl;
import com.dj.dao.UserDao;
import com.dj.service.UserService;
public class UserServiceImpl implements UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public UserServiceImpl(){
System.out.println("无参构造方法:UserServiceImpl ...");
}
public UserServiceImpl(String name){
System.out.println("有参构造方法:UserServiceImpl ...");
}
}
<!--配置constructor-arg参数时,就调用有参构造方法,不配置,就调用无参构造方法-->
<bean id="userService" class="com.dj.service.impl.UserServiceImpl">
<constructor-arg name="name" value="dj"></constructor-arg>
<property name="userDao" ref="userDao"/>
</bean>
工厂方法又分为三种:静态工厂方法、实例工厂方法、实现FactoryBean规范延迟实例化bean。
静态工厂方法,直接用类名调用静态方法即可:
/**
* 新建工厂类
*/
package com.dj.service.impl;
public class UserDaoFactory1 {
public static UserDaoImpl getUserDaoBean() {
//通过此方法实例化对象,方便进行其它的逻辑处理
return new UserDaoImpl();
}
}
<bean id="userDao1" class="com.dj.service.impl.UserDaoFactory1" factory-method="getUserDaoBean"></bean>
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ApplicationContextTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Object object = context.getBean("userDao1");
System.out.println(object);
}
}
实例工厂方法,先获取工厂bean,再调用实例化方法:
package com.dj.service.impl;
import com.dj.dao.impl.UserDaoImpl;
/**
* 新建工厂类
*/
public class UserDaoFactory2 {
public UserDaoImpl getUserDaoBean() {
return new UserDaoImpl();
}
}
<bean id="userDaoFactory2" class="com.dj.service.impl.UserDaoFactory2"></bean>
<bean id="userDao2" factory-bean="userDaoFactory2" factory-method="getUserDaoBean"></bean>
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ApplicationContextTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Object object = context.getBean("userDao2");
System.out.println(object);
}
}
实现FactoryBean规范延迟实例化bean,通过实现FactoryBean接口来实现,:
package com.dj.factory;
import com.dj.dao.UserDao;
import com.dj.dao.impl.UserDaoImpl;
import org.springframework.beans.factory.FactoryBean;
/**
* 新建工厂类
*/
public class UserDaoFactory3 implements FactoryBean<UserDao> {
@Override
public UserDao getObject() throws Exception {
return new UserDaoImpl();
}
@Override
public Class<?> getObjectType() {
return UserDao.class;
}
}
<bean id="userDao3" class="com.dj.factory.UserDaoFactory3"></bean>
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ApplicationContextTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Object object = context.getBean("userDao3");
System.out.println(object);
}
}
7)bean的依赖注入配置:通过set方法,构造器的方法。其中,ref是reference,参考、引用的意思。
- set方法:
<property name="name" value="dj">
<property name="userDao" ref="userDao">
- 构造器方法
<constructor-arg name="name" value="dj"></constructor-arg>
<constructor-arg name="userDao" ref="userDao"></constructor-arg>
依赖注入的类型共三类:普通型、引用型、集合数据类型。此处仅演示集合类型,List、Set、Properties。
package com.dj.service.impl;
import com.dj.dao.UserDao;
import com.dj.service.UserService;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
public class UserServiceImpl implements UserService {
private List<String> stringList;
private List<UserDao> userDaoList;
private Set<String> stringSet;
private Map<String,String> stringMap;
private Properties properties;
public void setStringList(List<String> stringList) {
this.stringList = stringList;
}
public void setUserDaoList(List<UserDao> userDaoList) {
this.userDaoList = userDaoList;
}
public void setStringSet(Set<String> stringSet) {
this.stringSet = stringSet;
}
public void setStringMap(Map<String,String> stringMap) {
this.stringMap = stringMap;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
public void show(){
System.out.println(stringList);
System.out.println(userDaoList);
System.out.println(stringSet);
System.out.println(stringMap);
System.out.println(properties);
}
}
<bean id="userService" class="com.dj.service.impl.UserServiceImpl">
<property name="stringList">
<list>
<value>aaa</value>
<value>bbb</value>
<value>ccc</value>
</list>
</property>
<property name="userDaoList">
<list>
<ref bean="userDao"></ref>
<ref bean="userDao1"></ref>
<ref bean="userDao2"></ref>
</list>
</property>
<property name="stringSet">
<set>
<value>111</value>
<value>222</value>
<value>333</value>
</set>
</property>
<property name="stringMap">
<map>
<entry key="a" value="1"></entry>
<entry key="b" value="2"></entry>
<entry key="c" value="3"></entry>
</map>
</property>
<property name="properties">
<props>
<prop key="1">a</prop>
<prop key="2">b</prop>
<prop key="3">c</prop>
</props>
</property>
</bean>
import com.dj.service.UserService;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
public class BeanFactoryTest {
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
xmlBeanDefinitionReader.loadBeanDefinitions("beans.xml");
UserService userService = (UserService) beanFactory.getBean("userService");
userService.show();
}
}
关于自动装配,用byName居多,一般不会重复。name是根据set方法的名字来匹配的,setUserDao()方法名中,去set,小写首字母,得到userDao,要与xml文件中的bean的id相匹配,才可实现自动装配。
<bean id="userService" class="com.dj.service.impl.UserServiceImpl" autowire="byName"/>
<bean id="userDao" class="com.dj.dao.impl.UserDaoImpl"></bean>
package com.dj.service.impl;
import com.dj.dao.UserDao;
import com.dj.service.UserService;
public class UserServiceImpl implements UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
8)spring的xml标签,大体上分为两类:默认标签、自定义标签。默认标签是可以直接使用的,不需要额外导入的,如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"
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/beans/spring-context.xsd">
<!--通过前缀,来引用命名空间-->
<context:property-placeholder></context:property-placeholder>
</beans>
spring默认的命名空间下,默认的标签,共4种:
- beans:xml的根标签,一个xml也可以有多套beans,但是不会用到。也可以用profile属性来切换不同的环境。
beans是默认生成的根标签,多套beans标签也是放在根标签里面的。如果新增<beans>标签,它是要加在最下面的,不然会让之前添加的<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: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/beans/spring-context.xsd">
<bean id="userDao2" factory-bean="userDaoFactory2" factory-method="getUserDaoBean"></bean>
<bean id="userDao3" class="com.dj.factory.UserDaoFactory3"></bean>
<beans profile="test">
<bean id="userService" class="com.dj.service.impl.UserServiceImpl"/>
</beans>
</beans>
那么如何告诉程序,我现在需要用test环境,而不是默认的环境呐?在主程序中设置环境变量、或者在vm启动参数添加也可以。
//放在main方法中
System.setProperty("spring.profiles.active","test");
//vm options添加此环境变量
-Dspring.profiles.active=test
指定test环境时,除test标签内的环境变量会生效,根默认的配置也会生效。
- bean:beans的子标签,这个就不详细说了,一直在用。
- import:外部配置导入的标签。项目比较大的时候,可能会根据业务拆分成模块的配置文件,此时就需要import来将其它配置添加到主配置文件中。在resources资源目录下添加xml文件,userBeans.xml。
<!--在beans根目录下,添加import标签-->
<import resource="userBeans.xml"></import>
- alias:指定bean的别名,使用较少,算了,不写了,就这样吧。
<!--在beans根目录下,添加alias标签-->
<alias name="userDao" alias="xxxx"></alias>
9)spring获取bean的方法:
- public Object getBean(String name);常用
- public <T> T getBean(String name, Class<T> requiredType);
- public <T> T getBean(Class<T> requiredType);常用,且<bean>的返回类型只能有一种
UserService userService1 = (UserService) beanFactory.getBean("userService");
UserService userService2 = beanFactory.getBean("userService", UserService.class);
UserService userService3 = beanFactory.getBean(UserService.class);
10)spring引入非自定义bean(druid、mysql),二者没有什么强相关性,可独立使用,具体druid和mysql的关系,百度吧。
<!--pom文件中导入坐标-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.23</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
<!--beans.xml文件中添加Druid的实例化信息-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
<!--beans.xml文件中添加mysql的实例化信息,具体查询一下mysql的实例化方法就行-->
<bean id="driver" class="java.lang.Class" factory-method="forName">
<constructor-arg name="className" value="com.mysql.jdbc.Driver"></constructor-arg>
</bean>
<bean id="connection" class="java.sql.DriverManager" factory-method="getConnection" scope="prototype">
<constructor-arg name="url" value="jdbc:mysql://localhost:3306"></constructor-arg>
<constructor-arg name="user" value="root"></constructor-arg>
<constructor-arg name="password" value="root"></constructor-arg>
</bean>
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ApplicationContextTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
System.out.println(context.getBean("dataSource"));
System.out.println(context.getBean("connection"));
}
}