背景
- 用注解改造前面Spring博客集里(指 Spring 02)基于xml的Spring接管下的三层项目架构
- 对前面Spring博客集里(指 Spring 04)@Controller + @Service + @Repository 3个注解的用法进行演示
实现类
数据访问层
- 数据访问层的实现类:添加@Repository注解,专门用来创建数据访问层对象
package com.example.dao;
import com.example.pojo.User;
import org.springframework.stereotype.Repository;
/**
* 数据访问层的实现类
*/
@Repository
public class UserMapperImpl implements UserMapper{
//模拟用户信息导入
@Override
public int insertUser(User user) {
System.out.println("用户: " + user.getName() + ", 导入成功!");
return 1;
}
}
业务逻辑层
- 业务逻辑层的实现类:添加@Service注解,专门用来创建业务逻辑层对象
- 同时,由于持有数据访问层的接口类型的成员变量,需要添加@Autowired注解
- 注意:对于@Autowired,这里是同源类型注入的情况3:存在接口和实现类关系的注入。上面注册了数据访问层的实现类对象,这里注入接口变量中,面向接口编程
package com.example.Service.impl;
import com.example.Service.UserService;
import com.example.dao.UserMapper;
import com.example.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* 业务逻辑层实现类
*/
@Service
public class UserServiceImpl implements UserService {
//数据访问层接口指向数据访问层实现类
@Autowired
UserMapper userMapper;
@Override
public int insertUser(User user) {
return userMapper.insertUser(user);
}
}
界面层
-
界面层:添加@Controller注解,专门用来创建界面层对象
-
同时,由于持有业务逻辑层的接口类型的成员变量,需要添加@Autowired注解,也是同源类型注入的情况3:存在接口和实现类关系的注入
package com.example.controller;
import com.example.Service.UserService;
import com.example.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
/**
* 界面层
*/
@Controller
public class UserController {
//业务逻辑层接口指向业务逻辑层实现类
@Autowired
UserService userService;
public int insertUser(User user){
return userService.insertUser(user);
}
}
applicationContext.xml
- 项目结构
- 添加包扫描:由上面的项目结构可知,这里的包扫描可行但不是最优做法,因为pojo包根本不会交给容器创建对象但也要扫描,而且要进入impl包才能扫描到UserServiceImpl类。扫描做法以后会改进
<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 https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.example"/>
beans>
包扫描的方式
- 分单个包进行扫描(推荐):对需要将实体类交给Spring容器进行对象创建的包分别进行扫描
<context:component-scan base-package="com.example.controller"/>
<context:component-scan base-package="com.example.Service"/>
<context:component-scan base-package="com.example.dao"/>
- 一次扫描多个包,各包路径之间可用空格或逗号隔开(不直观)
<context:component-scan base-package="com.example.dao com.example.Service com.example.controller"/>
- 从根包开始扫描(不推荐):可能需要扫描许多根本不用交给容器创建对象的包,使得Spring容器的创建速度变慢,做很多无用功
<context:component-scan base-package="com"/>
测试
package com.example.test;
import com.example.controller.UserController;
import com.example.pojo.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestInsert {
//测试:注解改造后的Spring接管下的简单三层架构
@Test
public void testInsertUser(){
//创建Spring容器
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
//从容器中获取UserController对象
UserController uc = (UserController) ac.getBean("userController");
//完成用户数据的导入
uc.insertUser(new User("荷包蛋", 20, "黑河"));
}
}
测试输出
用户: 荷包蛋, 导入成功!
Process finished with exit code 0
补充了解
- 我们在前面Spring博客集中简单分析了基于xml的IOC(setter注入 + 构造方法注入)以及基于注解的IOC(DI依赖注入),其实基于xml的IOC也可以完成引用类型的自动注入,可以简单了解一下,不常用
xml引用类型自动注入
手动注入
- 当Student实体类持有School实体类对象的引用时,原先applicationContext.xml这样注册
<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="school" class="com.example.pojo02.School">
<property name="name" value="nefu"/>
<property name="address" value="哈尔滨"/>
bean>
<bean id="stu" class="com.example.pojo02.Student">
<property name="name" value="荷包蛋"/>
<property name="age" value="20"/>
<property name="school" ref="school"/>
bean>
beans>
标签自动注入
其实,还可以使用标签属性,实现基于xml的引用类型的自动注入。
可以使用autowire="byType"根据已经注册的引用类型自动注入。
也可以使用autowire="byName"根据已经注册的引用类型的名称进行自动注入。
- autowire="byType",不用再手动指定Student实体类里的引用类型的属性,Spring会将Bean工厂中已经注册的引用类型的对象,自动注入给对应类型的Student实体类对象中的属性
<bean id="school" class="com.example.pojo02.School">
<property name="name" value="nefu"/>
<property name="address" value="哈尔滨"/>
bean>
<bean id="stu" class="com.example.pojo02.Student" autowire="byType">
<property name="name" value="荷包蛋"/>
<property name="age" value="20"/>
bean>
- autowire="byName",不用再手动指定Student实体类里的引用类型的属性,Spring会根据Bean工厂中已经注册的引用类型的对象的名称,自动注入给Student实体类中名称相同的属性
<bean id="school" class="com.example.pojo02.School">
<property name="name" value="nefu"/>
<property name="address" value="哈尔滨"/>
bean>
<bean id="stu" class="com.example.pojo02.Student" autowire="byName">
<property name="name" value="荷包蛋"/>
<property name="age" value="20"/>
bean>
测试
package com.example.test;
import com.example.pojo02.Student;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestStudent02 {
//测试:xml引用类型自动注入
@Test
public void testStudent(){
//创建Spring容器,同时生成bean工厂中注册的对象
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("source02/applicationContext.xml");
//获取对象
Student stu = (Student) applicationContext.getBean("stu");
System.out.println(stu);
}
}
测试输出
School类的构造方法被执行,实体对象被创建.....
Student类的构造方法执行,实体对象被创建....
Student{name='荷包蛋', age=20, school=School{name='nefu', address='哈尔滨'}}
Process finished with exit code 0