代码结构
Text
spring01/
├── pom.xml
├── spring01.iml
└── src/
├── main/
│ ├── java/
│ │ └── com/
│ │ └── demo/
│ │ ├── bean/
│ │ │ ├── Demo.java
│ │ │ ├── Emp1.java
│ │ │ ├── Emp2.java
│ │ │ └── User.java
│ │ ├── dao/
│ │ │ ├── UserDao.java
│ │ │ └── impl/
│ │ │ ├── MysqlUserDaoImpl.java
│ │ │ └── OracleUserDaoImpl.java
│ │ ├── factory/
│ │ │ ├── Emp1Factory.java
│ │ │ ├── Emp2Factory.java
│ │ │ └── UserDaoFactory.java
│ │ ├── service/
│ │ │ ├── UserService.java
│ │ │ └── impl/
│ │ │ └── UserServiceImpl.java
│ │ └── test/
│ │ └── UserTest.java
│ └── resources/
│ ├── UserDao.properties
│ ├── applicationContext-实例工厂创建Bean.xml
│ ├── applicationContext-普通构建方法创建Bean.xml
│ ├── applicationContext-静态工厂创建Bean.xml
│ ├── applicationContext.xml
│ └── logback.xml
└── test/
└── java/
└── com/
└── demo/
├── LoggerTest.java
├── TestDemo.java
└── UserTest.java
该项目是一个Spring框架练习项目,主要围绕以下核心目标进行实践:
一、核心技术练习
-
Spring IoC容器与Bean管理
- 实现了多种Bean创建方式(普通构造方法、静态工厂、实例工厂)
- 对应配置文件:
applicationContext-*.xml
系列文件
-
设计模式应用
- 工厂模式:通过
Emp1Factory
、Emp2Factory
等类实现对象创建封装 - 接口编程:
UserDao
接口+Mysql/Oracle
多实现类
- 工厂模式:通过
-
分层架构实践
com.demo ├── bean // 数据模型层(User.java等实体类) ├── dao // 数据访问层(数据库操作接口及实现) ├── service // 业务逻辑层(服务接口及实现) └── factory // 对象工厂层(创建Bean的工厂类)
二、功能模块说明
- 数据访问 :通过
UserDao
及实现类操作数据库,支持多数据库类型(MySQL/Oracle) - 依赖注入:使用Spring容器管理对象依赖关系
- 日志系统 :集成logback日志框架(
logback.xml
配置) - 配置管理 :通过
UserDao.properties
实现配置外部化
三、测试覆盖
- 单元测试 :
LoggerTest
(日志测试)、UserTest
(用户功能测试)等测试类 - 测试规范:遵循与主代码相同的包结构,确保测试代码可维护性
四、项目特点
- 性质:通过多种配置文件和工厂类展示不同实现方式
- 结构清晰:严格遵循Maven项目规范和分层架构设计
- 可扩展性:通过接口和工厂模式预留功能扩展点
该项目适合初学者理解Spring核心概念、设计模式应用及企业级项目分层架构思想。
实体类 bean
java
package com.demo.bean;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
public class Demo {
private List<String> list;
private Set<String> set;
private Map<String,String> map;
private Properties properties;
public List<String> getList() {
return list;
}
public void setList(List<String> list) {
this.list = list;
}
public Set<String> getSet() {
return set;
}
public void setSet(Set<String> set) {
this.set = set;
}
public Map<String, String> getMap() {
return map;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
public Properties getProperties() {
return properties;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
}
package com.demo.bean;
public class Emp1 {
public void update1(){
System.out.println("Emp1的update1()方法被调用。。。 。。。");
}
}
package com.demo.bean;
public class Emp2 {
public void update2(){
System.out.println("Emp2的update2()方法被调用。。。 。。。");
}
}
package com.demo.bean;
public class User {
private Integer userId;
private String username;
private String password;
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"userId=" + userId +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
dao层
java
import com.demo.bean.User;
public interface UserDao {
public boolean updateUser(User user);
}
import com.demo.bean.User;
import com.demo.dao.UserDao;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MysqlUserDaoImpl implements UserDao {
@Override
public boolean updateUser(User user) {
Logger logger = LoggerFactory.getLogger(MysqlUserDaoImpl.class);
logger.info("Mysql正在进行修改操作:updateUser();");
return true;
}
}
import com.demo.bean.User;
import com.demo.dao.UserDao;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class OracleUserDaoImpl implements UserDao {
@Override
public boolean updateUser(User user) {
Logger logger = LoggerFactory.getLogger(OracleUserDaoImpl.class);
logger.info("Oracle正在进行修改操作:updateUser();");
return true;
}
}
简单工厂模式
java
import com.demo.bean.Emp1;
public class Emp1Factory {
public static Emp1 getInstance(){
return new Emp1();
}
}
package com.demo.factory;
import com.demo.bean.Emp2;
public class Emp2Factory {
public Emp2 getInstance() {
return new Emp2();
}
}
package com.demo.factory;
import com.demo.dao.UserDao;
import java.io.InputStream;
import java.util.Properties;
public class UserDaoFactory {
public UserDao getInstance() {
UserDao userDao = null;
try {
//读取属性文件
Properties properties = new Properties();
InputStream in = UserDaoFactory.class.getClassLoader().getResourceAsStream("UserDao.properties");
properties.load(in);
//通过key获取全名字符串
String userDaoFullName = properties.getProperty("userDao");
//通过反射获取类的实例对象
userDao = (UserDao) Class.forName(userDaoFullName).newInstance();
} catch (
Exception e) {
e.printStackTrace();
}
return userDao;
}
}
service层
java
import com.demo.bean.User;
public interface UserService {
public boolean updateUser(User user);
}
package com.demo.service.impl;
import com.demo.bean.User;
import com.demo.dao.UserDao;
import com.demo.factory.UserDaoFactory;
import com.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* 1.UserDao 的实现类不由UserServiceImpl来决定,而是由UserDaoFactory来决定<第一种>
* 2.控制权从UserServiceImpl转移到了UserDaoFactory,这就是控制反转IOC/DI
*/
@Service
public class UserServiceImpl implements UserService{
/**
<第一种>
UserDaoFactory userDaoFactory=new UserDaoFactory();
UserDao userDao=userDaoFactory.getInstance();*/
@Autowired
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
// private UserDao userDao=new UserDaoFactory.getInstance();//报错
// private UserDao userDao=new MysqlUserDaoImpl();
//private UserDao userDao=new OracleUserDaoImpl();
@Override
public boolean updateUser(User user) {
return userDao.updateUser(user);
}
}
测试
日志测试
java
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LoggerTest {
@Test
public void loggerTest() {
//System.out.println(LoggerFactory.getLogger("hello"));
Logger logger = LoggerFactory.getLogger(LoggerTest.class);
//slf4j日志的级别
logger.trace("trace");
logger.debug("debug");
logger.info("info");
logger.warn("warn");
logger.error("error");
//拼接
logger.info("Welcome to {} {} ", "www.51zxw.net", "go!");
}
}
测试 applicationContext.xml 配置文件的配置
java
package com.demo;
import com.demo.bean.Demo;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.*;
public class TestDemo {
Logger logger = LoggerFactory.getLogger(LoggerTest.class);
@Test
public void testDemo() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Demo demo = (Demo) context.getBean("demo");
List<String> list = demo.getList();
logger.info("list----------------------");
for (String s : list) {
logger.info(s);
}
logger.info("set----------------------");
Set<String> set = demo.getSet();
for (String s : set) {
logger.info(s);
}
logger.info("map----------------------");
Map<String, String> map = demo.getMap();
Set<String> keySet = map.keySet();
Iterator<String> iterator = keySet.iterator();
while (iterator.hasNext()) {
String key = iterator.next();
String value = map.get(key);
logger.info(key + " " + value);
}
logger.info("properties----------------------");
Properties properties = demo.getProperties();
String userId = properties.getProperty("userId");
String username = properties.getProperty("username");
String password = properties.getProperty("password");
logger.info(userId);
logger.info(username);
logger.info(password);
}
}
测试其他集中配置文件管理Bean
java
package com.demo;
import com.demo.bean.Emp1;
import com.demo.bean.Emp2;
import com.demo.bean.User;
import com.demo.dao.UserDao;
import com.demo.service.UserService;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* 1.从SpringIOC容器工厂中获取一个User对象
* a。获取工厂BeanFactory
* b。getBean()返回对象
* 2.ApplicationContext是BeanFactory的子接口(实际上也是工厂)
*/
public class UserTest {
Logger logger = LoggerFactory.getLogger(LoggerTest.class);
/**
* 测试普通构造方法创建的Bean
*/
@Test
public void userTest() {
//获取BeanFactory的子接口,它是用来获得配置在SpringIOC容器的对象
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//从SpringIOC容器工厂中获取一个User对象
User user = (User) context.getBean("user");
if (null != user) {
logger.info(user.toString());
}
}
/**
* 测试普通构造方法创建的Bean
*/
@Test
public void userDaoTest() {
//获取BeanFactory的子接口,它是用来获得配置在SpringIOC容器的对象
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext-实例工厂创建Bean.xml");
//从SpringIOC容器工厂中获取一个User对象
UserDao userDao = (UserDao) context.getBean("userDao");
if (null != userDao) {
userDao.updateUser(null);
}
}
/**
* 静态工厂创建Bean
*/
@Test
public void emp1Test() {
//获取BeanFactory的子接口,它是用来获得配置在SpringIOC容器的对象
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext-实例工厂创建Bean.xml");
//从SpringIOC容器工厂中获取一个User对象
Emp1 emp1 = (Emp1) context.getBean("emp1");
if (null != emp1) {
emp1.update1();
}
}
/**
* 实例工厂创建Bean
*/
@Test
public void emp2Test() {
//获取BeanFactory的子接口,它是用来获得配置在SpringIOC容器的对象
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext-实例工厂创建Bean.xml");
//从SpringIOC容器工厂中获取一个User对象
Emp2 emp2 = (Emp2) context.getBean("emp2");
if (null != emp2) {
emp2.update2();
}
}
@Test
public void userServiceTest() {
//获取BeanFactory的子接口,它是用来获得配置在SpringIOC容器的对象
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//从SpringIOC容器工厂中获取一个User对象
UserService userService = (UserService) context.getBean("userService");
if (null != userService) {
userService.updateUser(null);
}
}
}
测试第一种被UserDaoFactory来决定实现的方式
要解开注解,UserServiceImpl
代码改为
java
UserDaoFactory userDaoFactory=new UserDaoFactory();
UserDao userDao=userDaoFactory.getInstance();
// @Autowired
// private UserDao userDao;
// 其余不变
java
package com.demo.test;
import com.demo.bean.User;
import com.demo.service.UserService;
import com.demo.service.impl.UserServiceImpl;
public class UserTest {
public static void main(String[] args) {
UserService userService=new UserServiceImpl();
User user=new User();
userService.updateUser(user);
}
}
配置文件
applicationContext.xml
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="user" class="com.demo.bean.User">
<!-- 为对象注入属性值 -->
<property name="userId" value="1"></property>
<property name="username" value="张三"></property>
<property name="password" value="123456"></property>
</bean>
<!-- 1.创建属性对象MysqlUserDaoImpl(如果是Besn类型)
a.必须添加setter()方法注入属性;
b.通过构造方法注入属性
2.创建userService
-->
<bean id="userDao" class="com.demo.dao.impl.MysqlUserDaoImpl"></bean>
<bean id="userService" class="com.demo.service.impl.UserServiceImpl">
<!-- ref是通过引用userDao,然后找到实现类 -->
<property name="userDao" ref="userDao"></property>
</bean>
<!--
集合属性的注入:
list:添加list节点,然后如果集合中的数据是引用数据类型需要使用ref节点,如果是基本数据类型用value
set:添加set节点,然后如果集合中的数据是引用数据类型需要使用ref节点,如果是基本数据类型用value
map:添加map节点,由于map中储存的是key和value键值对,需要添加一个entry节点
对应key,如果数据是引用数据类型需要使用ref节点,如果是基本数据类型用value
对应value,如果数据是引用数据类型需要使用ref节点,如果是基本数据类型用value
properties:添加props节点,然后在添加prop
-->
<bean id="demo" class="com.demo.bean.Demo">
<property name="list">
<list>
<value>乔丹</value>
<value>科比</value>
<!--<bean>ref的配置</bean>-->
<!-- <ref>如果是类类型,或者引用数据类型,需要ref</ref>-->
</list>
</property>
<property name="set">
<set>
<value>姚明</value>
<value>易建联</value>
<value>王致和</value>
</set>
</property>
<property name="map">
<map>
<entry>
<key>
<value>001</value>
</key>
<value>篮球</value>
</entry>
<entry>
<key>
<value>002</value>
</key>
<value>足球</value>
</entry>
<entry>
<key>
<value>003</value>
</key>
<value>乒乓球</value>
</entry>
</map>
</property>
<property name="properties">
<props>
<prop key="userId">1</prop>
<prop key="username">test</prop>
<prop key="password">123456</prop>
</props>
</property>
</bean>
</beans>
applicationContext-实例工厂创建Bean.xml
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
1.创建Emp2
2.创建Emp2Factory静态工厂
3.编写配置文件,注意和普通工厂对比,多两个属性配置factory-method="静态方法名"
factory-bean属性的配置
总结:相比普通构造方法创建Bean而言稍微麻烦一些,所以很少用
-->
<bean id="emp2Factory" class="com.demo.factory.Emp2Factory"></bean>
<bean id="emp2" factory-bean="emp2Factory" factory-method="getInstance"></bean>
</beans>
applicationContext-普通构建方法创建Bean.xml
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">
<!-- Spring容器-->
<!--采用普通的构造方法来创建Bean-->
<bean id="userService" class="com.demo.service.impl.UserServiceImpl"></bean>
<bean id="userDao" class="com.demo.dao.impl.MysqlUserDaoImpl"></bean>
<!-- 采用普通的构建方法创建User-->
<bean id="user" class="com.demo.bean.User"></bean>
</beans>
applicationContext-静态工厂创建Bean.xml
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
1.创建Emp1
2.创建Emp1Factory静态工厂
3.编写配置文件,注意和普通工厂对比,多一个属性配置factory-method="静态方法名"
总结:相比普通构造方法创建Bean而言稍微麻烦一些,所以很少用
-->
<bean id="emp1" class="com.demo.factory.Emp1Factory" factory-method="getInstance"></bean>
</beans>
logback.xml
xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!--输出日志到控制台 appender追加-->
<appender name="consoleLog" class="ch.qos.logback.core.ConsoleAppender">
<!--负责把事件转成字符串,格式化日志信息的输出-->
<layout>
<pattern>
<!--%p是日志优先级%d是日期,%msg是日志消息%n换行-->
[%p]%d-%msg%n
</pattern>
</layout>
</appender>
<appender name="fileLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>debug</level>
<onMatch>DENY</onMatch>
</filter>
<encoder>
<pattern>
[%p]%d-%msg%n
</pattern>
</encoder>
<!-- 指定文件的输出位置-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>
</fileNamePattern>
</rollingPolicy>
</appender>
<!-- 控制台可以输出的级别 -->
<root level="info">
<appender-ref ref="consoleLog"></appender-ref>
<appender-ref ref="fileLog"></appender-ref>
</root>
</configuration>
UserDao.properties
properties
userDao=com.demo.dao.impl.OracleUserDaoImpl