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--><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 {@Testpublic 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;}@Overridepublic 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;}@Overridepublic 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 {@Testpublic 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注解方式的入門*/@Testpublic 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方法進行初始化操作*/@PostConstructpublic void init(){System.out.println("操作...");}/*publicStringgetCname(){returncname;}?publicvoidsetCname(Stringcname){this.cname=cname;}?publicDoublegetMoney(){returnmoney;}?publicvoidsetMoney(Doublemoney){this.money=money;}
*/@Overridepublic 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 {@Testpublic 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;@Overridepublic 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 {/***編寫程序,需要加載配置類*/@Testpublic 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還有更多強大的特性和技巧等待你去探索。