简介
Spring是分层的 Java SE/EE应用** full-stack(全栈式) **轻量级开源框架。 <br />全栈是说整合了其它主流框架,并且支持三层开发规范(spring mvc + spring + spring jdbc template)
两大核心:以 IOC(Inverse Of Control:控制反转)和 AOP(Aspect Oriented Programming:面向 切面编程)为内核。
控制反转指容器来创建对象。AOP指面向切面编程,在不修改源码的情况下对方法进行增强,底层实现是动态代理。
Spring的优点
1)方便解耦,简化开发 Spring就是一个容器,可以将所有对象创建和关系维护交给Spring管理 什么是耦合度?对象之间的关系,通常说当一个模块(对象)更改时也需要更改其他模块(对象),这就是 耦合,耦合度过高会使代码的维护成本增加。要尽量解耦
2)AOP编程的支持 Spring提供面向切面编程,方便实现程序进行权限拦截,运行监控等功能。
3)声明式事务的支持 通过配置完成事务的管理,无需手动编程
4)方便测试,降低JavaEE API的使用 Spring对Junit4支持,可以使用注解测试
5)方便集成各种优秀框架 不排除各种优秀的开源框架,内部提供了对各种优秀框架的直接支持
其实最重要的就是第一条。解耦是最核心的。一条解耦的基本思路就是降低编译期依赖,尽可能的消除new关键字。很常见的解决手段就是使用配置文件+反射的组合来解耦。
IOC简介
把对象的创建和销毁交给容器
自己写个最最最简单的IOC容器
第一步 搞个xml存我们要托管的bean
<beans><bean id="userDao" class="com.ning.dao.impl.UserDaoImpl"></bean></beans>
第二步 做个类把这些(个)bean装载到Map里。
public class BeanFactory {private static Map<String, Object> iocMap = new HashMap<>();static {try {final InputStream resourceAsStream = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");final Document doc = new SAXReader().read(resourceAsStream);String xpath = "//bean";final List<Element> list = doc.selectNodes(xpath);for (Element element : list) {final String id = element.attributeValue("id");final String aClass = element.attributeValue("class");final Object o = Class.forName(aClass).getDeclaredConstructor().newInstance();iocMap.put(id, o);}} catch (DocumentException | ClassNotFoundException | NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) {e.printStackTrace();}}public static Object getBean(String beanId) {return iocMap.get(beanId);}}
Spring快速入门
先引入pom
<dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.1.5.RELEASE</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency></dependencies>
再写一个配置文件,约定俗成地,大家都叫它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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="userDao" class="com.ning.dao.impl.UserDaoImpl"></bean></beans>
然后写个测试类充当Service层
@Testpublic void test2() {final ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");final IUserDao userDao = (IUserDao)classPathXmlApplicationContext.getBean("userDao");userDao.save();}
可以看出,和我们写的玩具一模一样
Spring相关API

最重要的是BeanFactory和ApplicationContext
BeanFactory Vs ApplicationContext
BeanFactory是 IOC 容器的核心接口,它定义了IOC的基本功能。
特点:在第一次调用getBean()方法时,创建指定对象的实例
BeanFactory beanFactory= new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
applicationContext代表应用上下文对象,可以获得spring中IOC容器的Bean对象。 <br /> 特点:在spring容器启动时,加载并创建所有对象的实例
ApplicationContext app =new ClassPathXmlApplicationContext("applicationContext.xml");
常用实现类
1. ClassPathXmlApplicationContext它是从类的根路径下加载配置文件 推荐使用这种。2. FileSystemXmlApplicationContext它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。3. AnnotationConfigApplicationContext当使用注解配置容器对象时,需要使用此类来创建 spring 容器。它用来读取注解。
常用方法
1. Object getBean(String name);根据Bean的id从容器中获得Bean实例,返回是Object,需要强转。2. <T> T getBean(Class<T> requiredType);根据类型从容器中匹配Bean实例,当容器中相同类型的Bean有多个时,则此方法会报错。3. <T> T getBean(String name,Class<T> requiredType);根据Bean的id和类型获得Bean实例,解决容器中相同类型Bean有多个情况。
配置文件详解
其实主要就是说Bean
<bean id="" class="" scope="" init-method="" destroy-method=""></bean>* 用于配置对象交由Spring来创建。* 基本属性:id:Bean实例在Spring容器中的唯一标识class:Bean的全限定名* 默认情况下它调用的是类中的 无参构造函数,如果没有无参构造函数则不能创建成功。init-method:指定类中的初始化方法名称destroy-method:指定类中销毁方法名称
scope
默认是单例的。其它值如下所示:
1. 当scope的取值为singleton时Bean的实例化个数:1个Bean的实例化时机:当Spring核心文件被加载时,实例化配置的Bean实例Bean的生命周期:对象创建:当应用加载,创建容器时,对象就被创建了对象运行:只要容器在,对象一直活着对象销毁:当应用卸载,销毁容器时,对象就被销毁了2. 当scope的取值为prototype时Bean的实例化个数:多个Bean的实例化时机:当调用getBean()方法时实例化BeanBean的生命周期:对象创建:当使用对象时,创建新的对象实例对象运行:只要对象在使用中,就一直活着对象销毁:当对象长时间不用时,被 Java 的垃圾回收器回收了
Bean实例化三种方式
无参构造方法实例化
工厂静态方法实例化
工厂普通方法实例化
工厂静态方法实例化
应用场景
依赖的jar包中有个A类,A类中有个静态方法m1,m1方法的返回值是一个B对象。如果我们频繁使用B对象,此时我们可以将B对象的创建权交给spring的IOC容器,以后我们在使用B对象时,无需调用A类中的m1方法,直接从IOC容器获得。
public class StaticFactoryBean {public static UserDao createUserDao(){return new UserDaoImpl();}}
<bean id="userDao" class="com.lagou.factory.StaticFactoryBean"factory-method="createUserDao" />
工厂普通方法实例化
应用场景
依赖的jar包中有个A类,A类中有个普通方法m1,m1方法的返回值是一个B对象。如果我们频繁使用B象,此时我们可以将B对象的创建权交给spring的IOC容器,以后我们在使用B对象时,无需调用A类中的m1 方法,直接从IOC容器获得。
public class DynamicFactoryBean {public UserDao createUserDao(){return new UserDaoImpl();}}
<bean id="dynamicFactoryBean" class="com.lagou.factory.DynamicFactoryBean"/><bean id="userDao" factory-bean="dynamicFactoryBean" factorymethod="createUserDao"/>
依赖注入
它是 Spring 框架核心 IOC 的具体实现。 在编写程序时,通过控制反转,把对象的创建交给了 Spring,但是代码中不可能出现没有依赖的情况。IOC 解耦只是降低他们的依赖关系,但不会消除。
例如:业务层仍会调用持久层的方法。 那这种业务层和持久层的依赖关系,在使用Spring 之后,就让 Spring 来维护了。
简单的说,就是通 过框架把持久层对象传入业务层,而不用我们自己去获取。
构造函数依赖注入
public class UserServiceImpl implements UserService {private UserDao userDao;public UserServiceImpl(UserDao userDao) {this.userDao = userDao;}@Overridepublic void save() {userDao.save();}}
<bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl"/><bean id="userService" class="com.lagou.service.impl.UserServiceImpl"><!--<constructor-arg index="0" type="com.lagou.dao.UserDao" ref="userDao"/>--><constructor-arg name="userDao" ref="userDao"/></bean>
Setter依赖注入
public class UserServiceImpl implements UserService {private UserDao userDao;public void setUserDao(UserDao userDao) {this.userDao = userDao;}@Overridepublic void save() {userDao.save();}}
<bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl"/><bean id="userService" class="com.lagou.service.impl.UserServiceImpl"><property name="userDao" ref="userDao"/></bean>
如果想注入数字/字符串,把ref变成value
P命名空间注入
本质也是set方法注入
xmlns:p="http://www.springframework.org/schema/p"
先得引用p命名空间
<bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl"/><bean id="userService"class="com.lagou.service.impl.UserServiceImpl" p:userDao-ref="userDao"/>
普通数据类型注入
public class User {private String username;private String age;public void setUsername(String username) {this.username = username;}public void setAge(String age) {this.age = age;}}
<bean id="user" class="com.lagou.domain.User"><property name="username" value="jack"/><property name="age" value="18"/></bean>
集合依赖注入
List
public class UserDaoImpl implements UserDao {private List<Object> list;public void save() {System.out.println(list);System.out.println("保存成功了...");}}
<bean id="user" class="com.lagou.domain.User"><property name="username" value="jack"/><property name="age" value="18"/></bean><bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl"><property name="list"><list><value>aaa</value><ref bean="user"></ref></list></property></bean>
Set
public class UserDaoImpl implements UserDao {private Set<Object> set;public void setSet(Set<Object> set) {this.set = set;}public void save() {System.out.println(set);System.out.println("保存成功了...");}}
<bean id="user" class="com.lagou.domain.User"><property name="username" value="jack"/><property name="age" value="18"/></bean><bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl"><property name="set"><set><value>bbb</value><ref bean="user"></ref></set></property></bean>
Array
public class UserDaoImpl implements UserDao {private Object[] array;public void setArray(Object[] array) {this.array = array;}public void save() {System.out.println(Arrays.toString(array));System.out.println("保存成功了...");}}
<bean id="user" class="com.lagou.domain.User"><property name="username" value="jack"/><property name="age" value="18"/></bean><bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl"><property name="array"><array><value>ccc</value><ref bean="user"></ref></array></property></bean>
Map
public class UserDaoImpl implements UserDao {private Map<String, Object> map;public void setMap(Map<String, Object> map) {this.map = map;}public void save() {System.out.println(map);System.out.println("保存成功了...");}}
<bean id="user" class="com.lagou.domain.User"><property name="username" value="jack"/><property name="age" value="18"/></bean><bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl"><property name="map"><map><entry key="k1" value="ddd"/><entry key="k2" value-ref="user"></entry></map></property></bean>
Properties
public class UserDaoImpl implements UserDao {private Properties properties;public void setProperties(Properties properties) {this.properties = properties;}public void save() {System.out.println(properties);System.out.println("保存成功了...");}}
<bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl"><property name="properties"><props><prop key="k1">v1</prop><prop key="k2">v2</prop><prop key="k3">v3</prop></props></property></bean>
配置文件模块化
可以拆解配置文件。拆分要么按层,要么按模块。
同层就这样
ApplicationContext act =new ClassPathXmlApplicationContext("beans1.xml","beans2.xml","...");
主从就这样(更常用)
<import resource="applicationContext-xxx.xml"/>
注意:
同一个xml中不能出现相同id的bean,如果出现会报错
多个xml如果出现相同id的bean,不会报错,但是后加载的会覆盖前加载的bean
使得spring能引入jdbc.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:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><context:property-placeholder location="classpath:jdbc.properties"/><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="${jdbc.driverClassName}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/></bean><bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner"><constructor-arg name="ds" ref="dataSource"/></bean><bean id="accountDao" class="com.ning.dao.impl.AccountDaoImpl"><property name="queryRunner" ref="queryRunner"/></bean><bean id="accountService" class="com.ning.service.impl.AccountServiceImpl"><property name="accountDao" ref="accountDao"/></bean></beans>
注解开发

Java11以后移除了javax扩展,要是非得用就需要
<dependency><groupId>javax.annotation</groupId><artifactId>javax.annotation-api</artifactId><version>1.3.2</version></dependency>
如果使用注解,需要指定一个路径让spring去扫描
<!--注解的组件扫描--><context:component-scan base-package="com.lagou"></context:component-scan>
新注解
使用上面的注解还不能全部替代xml配置文件,还需要使用注解替代的配置如下:
* 非自定义的Bean的配置:<bean>* 加载properties文件的配置:<context:property-placeholder>* 组件扫描的配置:<context:component-scan>* 引入其他文件:<import>

示例
@PropertySource(value = "classpath:jdbc.properties")public class DataSourceConfig {@Value("${jdbc.driverClassName}")private String driverName;@Value("${jdbc.url}")private String url;@Value("${jdbc.username}")private String username;@Value("${jdbc.password}")private String password;@Bean(value = "dataSource")public DataSource getDataSource() {DruidDataSource dataSource = new DruidDataSource();dataSource.setDriverClassName(driverName);dataSource.setUrl(url);dataSource.setUsername(username);dataSource.setPassword(password);return dataSource;}}
@Configuration@ComponentScan(value = "com.ning")@Import(value = DataSourceConfig.class)public class SpringConfig {@Bean(value = "queryRunner")public QueryRunner getQueryRunner(@Autowired DataSource dataSource) {return new QueryRunner(dataSource);}}
Spring整合Junit
在普通的测试类中,需要开发者手动加载配置文件并创建Spring容器,然后通过Spring相关API获得 Bean实例;如果不这么做,那么无法从容器中获得对象。
ApplicationContext applicationContext =new ClassPathXmlApplicationContext("applicationContext.xml");AccountService accountService =applicationContext.getBean(AccountService.class);
我们可以让SpringJunit负责创建Spring容器来简化这个操作,开发者可以直接在测试类注入Bean实 例;但是需要将配置文件的名称告诉它。
具体步骤
- 导入spring集成Junit的坐标
- 使用@Runwith注解替换原来的运行器
- 使用@ContextConfiguration指定配置文件或配置类
- 使用@Autowired注入需要测试的对象
- 创建测试方法进行测试
<dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.1.5.RELEASE</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency>
@RunWith(SpringJUnit4ClassRunner.class)public class SpringJunitTest {}
@RunWith(SpringJUnit4ClassRunner.class)//@ContextConfiguration(value = {"classpath:applicationContext.xml"}) 加载spring核心配置文件@ContextConfiguration(classes = {SpringConfig.class}) // 加载spring核心配置类public class SpringJunitTest {}
@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(classes = {SpringConfig.class})public class SpringJunitTest {@Autowiredprivate AccountService accountService;}
@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(classes = {SpringConfig.class})public class SpringJunitTest {@Autowiredprivate AccountService accountService;//测试查询@Testpublic void testFindById() {Account account = accountService.findById(3);System.out.println(account);}}
