Spring 分层架构
MVC 设计模式:将程序按照不同的业务功能分成不同的层,多层组件构建起来项目的整体,能做到明确分工且模块之间的低耦合度。
将 Web 应用程序分为 3 层:Controller 层、Service 层、DAO 层,这三层之间的关系是:Controller层调用Service 层,Service 层调用DAO 层,并且 Service 层和 DAO 层设计为接口,这是一个典型的 MVC 模式后台代码分层结构。
在实际项目开发中,经常会通过 IOC 容器来架构程序的分层,实现有两种方式:基于 XML 配置文件、基于注解的方式。
1. 基于 XML 配置方式
1、创建 Student 实体类
package com.trainingl.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.stereotype.Component;
import java.util.List;
public class Student {
private Integer id;
private String name;
private Double score;
public Student(){
}
public Student(Integer id, String name, Double score) {
this.id = id;
this.name = name;
this.score = score;
}
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 getScore() {
return score;
}
public void setScore(Double score) {
this.score = score;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", score=" + score +
'}';
}
}
2、创建 StudentController 类
package com.trainingl.controller;
import com.trainingl.entity.Student;
import com.trainingl.service.StudentService;
public class StudentController {
private StudentService studentService;
public Student getUserById(int id){
return studentService.getStuById(id);
}
}
3、创建 StudentService 接口以及实现类 StudentServiceImpl
package com.trainingl.service;
import com.trainingl.entity.Student;
public interface StudentService {
public Student getStuById(Integer id);
}
package com.trainingl.service;
import com.trainingl.dao.StudentDAO;
import com.trainingl.entity.Student;
public class StudentServiceImpl implements StudentService{
private StudentDAO studentDAO;
public Student getStuById(Integer id) {
return studentDAO.getStuById(id);
}
}
4、创建 StudentDAO 接口以及实现类 StudentDAOImpl
package com.trainingl.dao;
import com.trainingl.entity.Student;
public interface StudentDAO {
public Student getStuById(Integer id);
}
package com.trainingl.dao;
import com.trainingl.entity.Student;
import java.util.HashMap;
import java.util.Map;
public class StudentDAOImpl implements StudentDAO {
private static Map<Integer,Student> students;
static {
students = new HashMap<Integer, Student>();
students.put(1,new Student(1,"张三",85.5));
students.put(2,new Student(2,"李四",90.0));
students.put(3,new Student(3,"王五",95.0));
}
public Student getStuById(Integer id) {
return students.get(id);
}
}
5、在 spring.xml 配置 studentController、studentService、studentDAO,并完成依赖注入
<?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"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd
">
<!--配置StudentController-->
<bean id="studentController" class="com.trainingl.controller.StudentController">
<property name="studentService" ref="studentService"></property>
</bean>
<!--配置StudentService-->
<bean id="studentService" class="com.trainingl.service.StudentServiceImpl">
<property name="studentDAO" ref="studentDAO"></property>
</bean>
<!--配置StudentDAO-->
<bean id="studentDAO" class="com.trainingl.dao.StudentDAOImpl">
</bean>
</beans>
6、在测试类中获取 studentController 对象,调用方法获取 student 对象
package com.trainingl.test;
import com.trainingl.Annotation.StudentConfiguration;
import com.trainingl.controller.StudentController;
import com.trainingl.entity.Student;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
//1.加载spring容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
//2.获取对象
StudentController stuController = (StudentController)applicationContext.getBean("studentController");
System.out.println(stuController.getUserById(1));
}
}
2. 基于注解的方式
第一步:将 StudentController、StudentService、StudentDAO 类扫描到 IOC 容器里;
第二步:在类中设置注解完成依赖注入;
1、修改 spring.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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd
">
<!--将指定包下的类扫描到IOC容器中-->
<context:component-scan base-package="com.trainingl"></context:component-scan>
</beans>
base-package="com.trainingl"
表示将 com.trainingl 包下所有子包下的类全部扫描到 IOC 容器中,一步即可将所有参与项目的类完成扫描注入。
2、修改 StudentController,添加注解
//@Component
@Controller
public class StudentController {
@Autowired
private StudentService studentService;
public Student getUserById(int id){
return studentService.getStuById(id);
}
}
对比之前的代码,有两处改动:
- 在类名处添加
@Controller
注解,其实添加@Component
也是完全一样的,因为 @Controller 是 @Component 的子类,但是通常为了程序的可读性,开发过程中都添加的是@Controller
,表示作为该类的一个控制器; - studentService 属性处添加
@Autowired
注解,表示 IOC 容器自动完成装载,默认是以 byType 的方式;
3、修改 StudentServiceImpl
//@Component
@Service
public class StudentServiceImpl implements StudentService{
@Autowired
private StudentDAO studentDAO;
public Student getStuById(Integer id) {
return studentDAO.getStuById(id);
}
}
注:在类名处添加 @Service
注解,表示该类是业务层。
4、修改 StudentDAOImpl
//@Component
@Repository
public class StudentDAOImpl implements StudentDAO {
private static Map<Integer,Student> students;
//模拟初始化数据库
static {
students = new HashMap<Integer, Student>();
students.put(1,new Student(1,"张三",85.5));
students.put(2,new Student(2,"李四",90.0));
students.put(3,new Student(3,"王五",95.0));
}
public Student getStuById(Integer id) {
return students.get(id);
}
}
注:在类名处添加 @Repository
注解,表示该类是数据接口层。
5、运行测试代码
public class Main {
public static void main(String[] args) {
//1.加载spring容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
//2.获取对象
StudentController stuController = (StudentController)applicationContext.getBean("studentController");
System.out.println(stuController.getUserById(1));
}
}
控制台的打印结果和上面相同:
通过代码可以看出,使用注解的方式可以简化代码,所以实际开发中,推荐使用基于注解的方式来架构分层。
在上面的测试程序中,我们分别给 StudentController、StudentService、StudentDAO 添加了 @Controller
、@Service
、@Repository
注解,分别表示控制器、业务层和数据接口层,并且这三个注解与 @Component
没有区别,开发时可以使用任意一个注解,但是为了具有明确的语义性和可读性,我们在开发时,在哪个层用到哪个注解。
前面也提到过类中属性自动装载,默认是通过 byType 的方式,还可以使用 byName 的方式,使用 byName 的方式需要结合 @Qualifier
注解一起使用。
注意:我们在类中添加注解时,其实设置了默认的 id,即类名首字母小写之后的值就是 id 的默认值。
@Service
public class StudentServiceImpl implements StudentService
此时,IOC容器中默认赋值,StudentService 构建 bean 的 id=studentService
,与 StudentController 中的属性名正好一致,所以可以完成自动装载。
当然,也可以手动赋值,只需要在类名处将 @Service
修改成 @Service("myStudentService")
,这时 IOC 容器构建 bean 时的 id = "myStudentService"
。所以在自动装载的对象前,也要设置 @Qualifier("myStudentService")
,做到严格一致,否则就会报错。
@Controller
public class StudentController {
@Autowired
@Qualifier("myStudentController")
private StudentService studentService;
public Student getUserById(int id){
return studentService.getStuById(id);
}
}
@Service("myStudentController")
public class StudentServiceImpl implements StudentService{
@Autowired
private StudentDAO studentDAO;
public Student getStuById(Integer id) {
return studentDAO.getStuById(id);
}
}
因此,@Qualifier()
中的值必须与 @Service()
中的值一致,才能完成自动装载。