Spring框架是Java企业级开发中最受欢迎的框架之一,它通过简化开发流程、降低耦合度,让开发者能够更专注于业务逻辑的实现。本文将带你了解Spring框架的核心概念和基本用法。
一、Spring框架简介
Spring是一个轻量级的开源Java开发框架,由Rod Johnson在2003年创建。它的核心思想是通过控制反转(IOC)和面向切面编程(AOP)来实现松耦合的应用程序设计。
Spring框架的主要优点
解耦简化开发:Spring就像一个大工厂,管理所有对象的创建和依赖关系
AOP编程支持:方便实现权限拦截、运行监控等功能
声明式事务管理:通过配置即可完成事务管理,无需手动编程
易于测试:与Junit良好集成,支持注解测试
集成优秀框架:支持Struts2、Hibernate、MyBatis等主流框架
简化JavaEE API:对JDBC、JavaMail等复杂API进行了封装
二、IOC(控制反转)核心概念
IOC(Inversion of Control)是Spring的核心,它将对象的创建权从程序员手中反转给Spring框架管理。
IOC快速入门
添加依赖:在pom.xml中添加Spring核心依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
创建接口和实现类
package com.qcbyjy.service;
//菜单
public interface UserService {
public void hello();
}
package com.qcbyjy.service;
//具体的实现方法
public class UserServiceImpl implements UserService {
public void hello(){
System.out.println("Hello IOC");
}
}
配置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">
<!--IOC管理bean-->
<!--当有人要菜单(userService),就给他一个UserServiceImpl的实例-->
<bean id="userService" class="com.qcbyjy.service.UserServiceImpl"/>
</beans>
把log4j.properties的配置文件拷贝到resources目录下,做为log4j的日志配置 文件。
log4j.rootLogger=INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
测试代码
package com.qcbyjy.test;
import com.qcbyjy.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Demo1 {
@Test
public void run1(){
//使用Spring的工厂(加载配置文件)
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml","applicationContext2.xml");
//通过工厂获得类,告诉你需要什么
UserService userService=(UserService) applicationContext.getBean("userService");
//调用该方法
userService.hello();
}
}
ApplicationContext接口,工厂的接口,使用该接口可以获取到具体的Bean对 象。该接口下有两个具体的实现类。
springCould配置中心
ClassPathXmlApplicationContext,加载类路径下的Spring配置文件。 FileSystemXmlApplicationContext,加载本地磁盘下的Spring配置文件。
三、Spring框架的Bean管理的配置文件方式
id属性,Bean起个名字,在约束中采用ID的约束,唯一,取值要求:必须以字 母开始,可以使用字母、数字、连字符、下划线、句话、冒号id:不能出现特殊 字符。
class 属性,Bean 对象的全路径。
scope 属性,scope属性代表Bean的作用范围。
Bean的作用范围
singleton
:单例(默认),整个应用只创建一个实例prototype
:多例,每次请求都创建一个新实例request
:Web应用中,每个HTTP请求创建一个实例session
:Web应用中,同一个HTTP Session共享一个实例
Bean 对象的创建和销毁的两个属性配置
说明:Spring初始化bean或销毁bean时,有时需要作一些处理工作,因此spring 可以在创建和拆卸bean的时候调用bean的两个生命周期方法
init-method,当 bean 被载入到容器的时候调用init-method属性指定的方法
destroy-method,当 bean 从容器中删除的时候调用destroy-method属性指定 的方法
实例化Bean对象的三种方式
默认是无参数的构造方法(默认方式,基本上使用)
<bean id="us" class="com.qcbyjy.service.UserServiceImpl" />
静态工厂实例化方式
package com.qcbyjy.demo1;
import com.qcbyjy.service.UserService;
import com.qcbyjy.service.UserServiceImpl;
public class StaticFactory {
//静态工厂方式
public static UserService createUs(){
System.out.println("\"通过静态工厂的方式创建UserServiceImpl对\n" +
"象...");
//编写很多业务逻辑,权限校验
return new UserServiceImpl();
}
}
<bean id="us" class="com.qcbyjy.demo1.StaticFactory" factory-method="createUs"/>
实例工厂实例化方式
package com.qcbyjy.demo1;
import com.qcbyjy.service.UserService;
import com.qcbyjy.service.UserServiceImpl;
public class Dfactory {
public UserService createUs(){
//动态工厂方式
System.out.println("实例化工厂的方式....");
return new UserServiceImpl();
}
}
<bean id="dfactory" class="com.qcbyjy.demo1.Dfactory"/>
<bean id="us" factory-bean="dfactory" factory-method="createUs"/>
四、DI(依赖注入)
DI(Dependency Injection)是IOC的具体实现方式,Spring在创建Bean时,动态地将依赖对象注入到组件中。
三种依赖注入方式
set方法注入(最常用)
package com.qcbyjy.service;
public interface OrderService {
void saveOrder();
}
package com.qcbyjy.service;
import com.qcbyjy.dao.OrderDao;
public class OrderServiceImpl implements OrderService {
private OrderDao orderDao;
public void setOrderDao(OrderDao orderDao){
this.orderDao=orderDao;
}
private String msg;
private int age;
public OrderDao getOrderDao() {
return orderDao;
}
public void setMsg(String msg) {
this.msg = msg;
}
public void saveOrder(){
System.out.println("业务层:保存订单..."+msg+"-"+age);
//调用
orderDao.saveOrder();
}
public void setAge(int age) {
this.age = age;
}
}
package com.qcbyjy.dao;
public interface OrderDao {
void saveOrder();
}
<!--DI:依赖注入-->
<bean id="os" class="com.qcbyjy.service.OrderServiceImpl">
<property name="orderDao" ref="od"/>
<property name="msg" value="你好"/>
<property name="age" value="30"/>
</bean>
<bean id="od" class="com.qcbyjy.dao.OrderDaoImpl"></bean>
构造方法注入
package com.qcbyjy.demo2;
public class Car {
private String cname;
private Double money;
public Car(String cname,Double money){
this.cname=cname;
this.money=money;
}
@Override
public String toString() {
return "Car{" +
"cname='" + cname + '\'' +
", money=" + money +
'}';
}
}
<bean id="car" class="com.qcbyjy.demo2.Car">
<constructor-arg name="cname" value="大奔"/>
<constructor-arg name="money" value="400000"/>
</bean>
数组、集合(List,Set,Map),Properties 等的注入
package com.qcbyjy.demo3;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Properties;
public class CollectionBean {
//数组
private String[] strs;
public void setStrs(String[] strs){
this.strs=strs;
}
private List<String> list;
public void setList(List<String> list){
this.list=list;
}
private Map<String ,String> map;
public void setMap(Map<String,String> map){
this.map=map;
}
private Properties properties;
public void setProperties(Properties properties){
this.properties=properties;
}
public String toString(){
return"CollectionBean{"+
"strs="+ Arrays.toString(strs)+
",list="+list+
",map="+map+
",properties="+properties+
'}';
}
}
<bean id="collectionBean" class="com.qcbyjy.demo3.CollectionBean">
<property name="strs">
<array>
<value>美美</value>
<value>小凤</value>
</array>
</property>
<property name="list">
<list>
<value>熊大</value>
<value>熊二</value>
</list>
</property>
<property name="map">
<map>
<entry key="aaa" value="老王"/>
<entry key="bbb" value="小王"/>
</map>
</property>
<property name="properties">
<props>
<prop key="username">root</prop>
<prop key="password">123456</prop>
</props>
</property>
</bean>
五、多配置文件方式
在src的目录下又多创建了一个配置文件,现在是两个核心的配置文件,那么加 载这两个配置文件的方式有两种!
//主配置文件中包含其他的配置文件:
<import resource="applicationContext2.xml"/>
//工厂创建的时候直接加载多个配置文件:
ApplicationContext applicationContext=new ClassPathXmlApplicationContext( "applicationContext.xml","applicationContext2.xml");
六、Spring框架开发程序的方式
1.需求:编写service和dao的类,演示代码
2.技术选择:持久层使用原始的JDBC的程序,连接池选择的是Druid连接池。创 建maven工程,导入开发的jar包
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.qcbyjy</groupId>
<artifactId>springDemo22</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<!--mysql驱动包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
</dependencies>
</project>
创建数据库,创建表结构
create database spring_db;
use spring_db;
create table account(
id int primary key auto_increment,
name varchar(40),
money double
)character set utf8 collate utf8_general_ci;
insert into account(name,money) values('aaa',1000);
insert into account(name,money) values('bbb',1000);
insert into account(name,money) values('ccc',1000);
编写JavaBean的类
package com.qcbyjy.domain;
import java.io.Serializable;
public class Account implements Serializable {
private static final long serialVersionUID =7355810572012650248L;
private Integer id;
private String name;
private Double money;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getMoney() {
return money;
}
public void setMoney(Double money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
}
编写AccountDao的接口和实现类
package com.qcbyjy.dao;
import com.qcbyjy.domain.Account;
import java.util.List;
public interface AccountDao {
public List<Account> findAll();
}
package com.qcbyjy.dao;
import com.qcbyjy.domain.Account;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
public class AccountDaoImpl implements AccountDao {
//注入连接池对象
private DataSource dataSource;
public void setDataSource(DataSource dataSource){
this.dataSource=dataSource;
}
//查询所有数据
public List<Account> findAll(){
/*
DruidDataSourcedataSource=newDruidDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql:///spring_db");
dataSource.setUsername("root");
dataSource.setPassword("root");
*/
List<Account> list=new ArrayList<Account>();
Connection connection=null;
PreparedStatement stmt=null;
ResultSet rs=null;
try{
//获取连接
connection=dataSource.getConnection();
//编写sql语句
String sql="select * from account";
//预编译
stmt = connection.prepareStatement(sql);
//查询
rs=stmt.executeQuery();
//遍历,封装数据
while(rs.next()){
Account account=new Account();
account.setId(rs.getInt("id"));
account.setName(rs.getString("name"));
account.setMoney(rs.getDouble("money"));
list.add(account);
}
}catch(SQLException e){
e.printStackTrace();
}finally{
try{
connection.close();
}catch(SQLException e){
e.printStackTrace();
}
try{
stmt.close();
}catch(SQLException e){
e.printStackTrace();
}
try{
rs.close();
}catch(SQLException e){
e.printStackTrace();
}
}
return list;
}
}
package com.qcbyjy.service;
import com.qcbyjy.domain.Account;
import java.util.List;
public interface AccountService {
public List<Account> findAll();
}
package com.qcbyjy.service;
import com.qcbyjy.dao.AccountDao;
import com.qcbyjy.domain.Account;
import java.util.List;
public class AccountServiceImpl implements AccountService {
//依赖注入
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao){
this.accountDao=accountDao;
}
//查询所有数据
public List<Account> findAll() {
return accountDao.findAll();
}
}
编写配置文件
<?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="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///spring_db"/>
<property name="username" value="root"/>
<property name="password" value="12345"/>
</bean>
<!--管理bean-->
<bean id="accountService" class="com.qcbyjy.service.AccountServiceImpl">
<property name="accountDao" ref="accountDao"/>
</bean>
<bean id="accountDao" class="com.qcbyjy.dao.AccountDaoImpl">
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
编程测试程序
package com.qcbyjy.test;
import com.qcbyjy.domain.Account;
import com.qcbyjy.service.AccountService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.List;
public class Demo1 {
@Test
public void run1(){
ApplicationContext ac =new ClassPathXmlApplicationContext("applicationContext.xml");
AccountService accountService=(AccountService) ac.getBean("accountService");
//调用方法
List<Account> list=accountService.findAll();
for (Account account:list){
System.out.println(account);
}
}
}
七、注解开发
随着Spring的发展,注解方式逐渐取代了XML配置,成为主流开发方式。
快速入门
IOC注解的方式依赖没有变化
编写接口和实现类
package com.qcbyjy.demo1;
public interface UserService {
public void hello();
}
package com.qcbyjy.demo1;
import org.springframework.stereotype.Component;
/**
*<beanid="us"class="com.qcbyjy.demo2.UserServiceImpl"/>
*/
//组件,作用:把当前类使用IOC容器进行管理,如果没有指定名称,默认使用类名,
//首字母是小写。userServiceImpl。或者自己指定名称
@Component(value="us")
public class UserServiceImpl implements UserService{
public void hello(){
System.out.println("Hello IOC注解....");
}
}
在需要管理的类上添加@Component注解
@Component(value="us")
public class UserServiceImpl implements UserService{
public void hello(){
System.out.println("Hello IOC注解....");
}
}
编写配置文件,重点是开启注解扫描。
<?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/context/spring-context.xsd">
<!--开启注解扫描
<context:component-scanbase-package="com.qcbyjy.demo2"/>-->
<!--开启注解扫描com.qcbyjy.所有的包中的所有的类-->
<context:component-scan base-package="com.qcbyjy"/>
</beans>
编写测试方法
package com.qcbyjy.test;
import com.qcbyjy.demo1.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Demo1 {
/**
*IOC注解方式的入门
*/
@Test
public void run1(){
//工厂
ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService=(UserService) ac.getBean("us");
userService.hello();
}
}
常用注解
组件注解(作用相同,推荐分层使用)
@Component
:普通组件@Controller
:表现层@Service
:业务层@Repository
:持久层
依赖注入注解
@Autowired
:按类型自动装配@Qualifier
:按名称装配(需与@Autowired配合使用)@Resource
:Java原生注解,按名称装配@Value
:注入基本类型值@Autowired默认按类型进行自动装配(引用类型)
@Qualifier和@Autowired一起使用,强制使用名称注入
@ResourceJava提供的注解,也被支持。使用name属性,按名称注入 对象生命周期(作用范围)注解
作用范围注解
@Scope
:指定作用范围(singleton/prototype等)
具体的代码如下
package com.qcbyjy.demo2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
//默认当前类名就是ID名称,首字母小写
@Component(value="c")
//@Controller
//@Service(value="c")
//@Repository(value="c")
//@Scope(value="singleton") //默认值,单例的
//@Scope(value="prototype") //多例的
public class Car {
//注释注入值,属性set方法是可以省略不写的。
//只有一个属性,属性的名称是value,value是可以省略不写的
@Value("大奔2")
private String cname;
@Value(value = "400000")
private Double money;
//也不用提供set方法
//按类型自动装配的注解,和id名称没有关系
@Autowired
//按id的名称注入,Qualifier不能单独使用,需要Autowired一起使用。
//@Qualifier(value="person")
//@ResourceJava提供的注解,按名称注入对象,属性名称是name
//\@Resource(name="person")
private Person person;
/**
*Car对象创建完成后,调用init方法进行初始化操作
*/
@PostConstruct
public void init(){
System.out.println("操作...");
}
/*
publicStringgetCname(){
returncname;
}
publicvoidsetCname(Stringcname){
this.cname=cname;
}
publicDoublegetMoney(){
returnmoney;
}
publicvoidsetMoney(Doublemoney){
this.money=money;
}
*/
@Override
public String toString() {
return "Car{" +
"cname='" + cname + '\'' +
", money=" + money +
", person=" + person +
'}';
}
}
package com.qcbyjy.demo2;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component(value="person")
public class Person {
@Value("张三")
private String pname;
public String toString(){
return"Person{"+
"pname='"+pname+'\''+
'}';
}
}
package com.qcbyjy.test;
import com.qcbyjy.demo2.Car;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Demo2 {
@Test
public void run1(){
ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
Car car=(Car) ac.getBean("c");
System.out.println(car);
}
}
纯注解开发示例
纯注解的方式是微服务架构开发的主要方式,所以也是非常的重要。纯注解的目 的是替换掉所有的配置文件。但是需要编写配置类。
编写实体类
package com.qcbyjy.demo3;
import org.springframework.stereotype.Component;
@Component
public class Order {
@Value("北京")
private String address;
@Override
public String toString() {
return "Order{" +
"address='" + address + '\'' +
'}';
}
}
编写配置类,替换掉applicationContext.xml配置文件
packagecom.qcbyjy.demo4;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
*Spring的配置类,替换掉applicationContext.xml
*
*/
//声明当前类是配置类
@Configuration
//扫描指定的包结构
@ComponentScan(value="com.qcbyjy.demo4")
public class SpringConfig{
}
测试方法的编写
package com.qcbyjy.test;
import com.qcbyjy.demo3.Order;
import com.qcbyjy.demo3.SpringConfig;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Demo3 {
/**
*编写程序,需要加载配置类
*/
@Test
public void run1(){
//创建工厂,加载配置类
ApplicationContext ac=new AnnotationConfigApplicationContext(SpringConfig.class);
//获取到对象
Order order=(Order) ac.getBean("order");
System.out.println(order);
}
}
常用的注解总结
@Configuration声明是配置类
@ComponentScan扫描具体包结构的
@Import注解Spring的配置文件可以分成多个配置的,编写多个配置类。用于 导入其他配置类
package com.qcbyjy.demo3;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SpringConfig2 {
}
package com.qcbyjy.demo3;
//声明当前类是配置类
@Configuration
//扫描指定包结构
@ComponentScan(value = "com.qcbyjy.demo3")
//@ComponentScan(value={"com.qcbyjy.demo4","com.qcbyjy.demo3"})
//引入新的配置类
@Import(value={SpringConfig2.class})
public class SpringConfig {
}
@Bean注解只能写在方法上,表明使用此方法创建一个对象,对象创建完成保 存到IOC容器中
package com.qcbyjy.demo3;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import javax.sql.DataSource;
//声明当前类是配置类
@Configuration
//扫描指定包结构
@ComponentScan(value = "com.qcbyjy.demo3")
//@ComponentScan(value={"com.qcbyjy.demo4","com.qcbyjy.demo3"})
//引入新的配置类
@Import(value={SpringConfig2.class})
public class SpringConfig {
/**
*创建连接池对象,返回对象,把该方法创建后的对象存入到连接池中,使用
@Bean注解解决
<!--配置连接池对象-->
<beanid="dataSource"
class="com.alibaba.druid.pool.DruidDataSource">
<propertyname="driverClassName"
value="com.mysql.jdbc.Driver"/>
<propertyname="url"value="jdbc:mysql:///spring_db"/>
<propertyname="username"value="root"/>
<propertyname="password"value="root"/>
</bean>
*
*@return
*/
@Bean(name="dataSource")
public DataSource createDataSource(){
DruidDataSource dataSource=new DruidDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql:///spring_db");
dataSource.setUsername("root");
dataSource.setPassword("123456");
return dataSource;
}
}
总结
Spring框架通过IOC和AOP两大核心思想,极大地简化了Java企业级开发。从最初的XML配置到现在的注解驱动开发,Spring不断演进,为开发者提供了更加便捷的开发体验。掌握Spring的基本概念和使用方法,是Java开发者必备的技能。
希望通过本文的介绍,你能对Spring框架有一个清晰的认识,并能够开始在实际项目中使用它。随着实践的深入,你会发现Spring还有更多强大的特性和技巧等待你去探索。