Spring - IoC的简单配置示例
通过上一篇的文章可以初步了解了spring框架的相关概念。
现在我们完成一个简单的登录注册案例。
项目结构如下
pom.xml配置
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.20</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.48</version>
</dependency>
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.7</version>
</dependency>
</dependencies>
database.properties配置
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/user
jdbc.username=root
jdbc.password=123456
版本一 xml配置版
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">
<!--读取配置文件-->
<bean id="configurer" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
<property name="location" value="database.properties"/>
</bean>
<!--德鲁伊连接池配置-->
<bean id="datasource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClass}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!--queryRunner配置-->
<bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
<!--构造方法:public QueryRunner(DataSource ds) {super(ds);}-->
<constructor-arg name="ds" ref="datasource"/>
</bean>
<bean id="menuController" class="com.alter.spring.controller.MenuController">
<property name="loginController" ref="loginController"/>
<property name="registerController" ref="registerController"/>
</bean>
<bean id="loginController" class="com.alter.spring.controller.LoginController">
<property name="userService" ref="userService"/>
</bean>
<bean id="userService" class="com.alter.spring.service.impl.UserServiceImpl">
<property name="userDAO" ref="userDAO"/>
</bean>
<bean id="userDAO" class="com.alter.spring.dao.impl.UserDAOImpl">
<property name="queryRunner" ref="queryRunner"/>
</bean>
<bean id="registerController" class="com.alter.spring.controller.RegisterController">
<property name="userService" ref="userService"/>
</bean>
</beans>
MenuController
package com.alter.spring.controller;
import java.util.Scanner;
public class MenuController {
private LoginController loginController;
private RegisterController registerController;
public void mainUI(){
Scanner sc = new Scanner(System.in);
System.out.println("请输入选项");
System.out.println("1.登录;2.注册;3.退出");
String option = sc.next();
if(option.equals("1")) {
//登录方法
loginController.login();
}else if(option.equals("2")) {
//注册方法
registerController.register();
}else if(option.equals("3")) {
//退出方法
}else{
System.out.println("输入错误,请重新输入");
}
}
public void setLoginController(LoginController loginController) {
this.loginController = loginController;
}
public void setRegisterController(RegisterController registerController) {
this.registerController = registerController;
}
}
LoginController
package com.alter.spring.controller;
import com.alter.spring.service.UserService;
import com.alter.spring.model.User;
import java.util.Scanner;
public class LoginController {
private UserService userService;
public void login(){
Scanner sc = new Scanner(System.in);
System.out.println("请输入用户名:");
String username = sc.next();
//查询数据库有没有这个用户
User queryUsername = userService.queryUsername(username);
if(queryUsername!=null){
System.out.println("请输入密码:");
String password = sc.next();
//查询数据库
User user = userService.login(username, password);
//判断
if(user != null){
System.out.println("欢迎"+user.getUsername()+"登录");
}else{
System.out.println("用户名或密码错误。请重新输入:");
login();
}
}else {
System.out.println("用户不存在,请重新输入");
login();
}
}
public void setUserService(UserService userService) {
this.userService = userService;
}
}
RegisterController
package com.alter.spring.controller;
import com.alter.spring.model.User;
import com.alter.spring.service.UserService;
import java.util.Scanner;
public class RegisterController {
private UserService userService;
public void register(){
Scanner sc = new Scanner(System.in);
System.out.println("请输入用户名:");
String username = sc.next();
//查询用户名是否存在
User user = userService.queryUsername(username);
if(user==null){
System.out.println("请输入密码:");
String password = sc.next();
//调用Service保存数据
userService.register(username,password);
System.out.println("注册成功!");
}else{
System.out.println("用户已存在,请重新注册。");
register();
}
}
public void setUserService(UserService userService) {
this.userService = userService;
}
}
Service实现
package com.alter.spring.service.impl;
import com.alter.spring.dao.UserDAO;
import com.alter.spring.service.UserService;
import com.alter.spring.model.User;
public class UserServiceImpl implements UserService {
private UserDAO userDAO;
@Override
public User login(String username, String password) {
return userDAO.login(username,password);
}
@Override
public User queryUsername(String username) {
return userDAO.queryUsername(username);
}
@Override
public void register(String username,String password) {
userDAO.register(username,password);
}
public void setUserDAO(UserDAO userDAO) {
this.userDAO = userDAO;
}
}
UserDAO实现
package com.alter.spring.dao.impl;
import com.alter.spring.dao.UserDAO;
import com.alter.spring.model.User;
import org.apache.commons.dbutils.BasicRowProcessor;
import org.apache.commons.dbutils.GenerousBeanProcessor;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import java.sql.SQLException;
public class UserDAOImpl implements UserDAO {
private QueryRunner queryRunner;
@Override
public User login(String username, String password) {
// 编写sql语句
String sql = "select * from user where username =? and password =?";
// 执行查询
try {
return queryRunner.query(sql, new BeanHandler<User>(User.class,new BasicRowProcessor(new GenerousBeanProcessor())), username, password);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
@Override
public User queryUsername(String username) {
// 编写sql语句
String sql = "select * from user where username =?";
// 执行查询
try {
return queryRunner.query(sql, new BeanHandler<User>(User.class,new BasicRowProcessor(new GenerousBeanProcessor())), username);
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
@Override
public void register(String username, String password) {
// 编写sql语句
String sql = "insert into user(username,password) values(?,?)";
try {
queryRunner.update(sql, username,password);
} catch (SQLException e) {
e.printStackTrace();
}
}
public void setQueryRunner(QueryRunner queryRunner) {
this.queryRunner = queryRunner;
}
}
版本二 注解式IoC
我们发现用xml配置很多重复代码,并且需要对整个相关目结构非常清楚。因为引入了注解来简化xml代码。
具体的操作为用注解来代替xml中的bean
在Controller层的类上加上@Controller注解,Service实现类的加上@Service注解,在DAO层的实现类上加上@Repository注解,其他普通类加上@Component,并且在类的属性声明上加上@Autowired来自动注入依赖,以此来让框架识别。
xml
删除一般的类
<beans>
<!--扫描含有注解类-->
<context:component-scan base-package="com.alter.spring"/>
<!--读取配置文件-->
<bean id="configurer" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
<property name="location" value="database.properties"/>
</bean>
<!--德鲁伊连接池配置-->
<bean id="datasource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClass}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!--queryRunner配置-->
<bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
<!--构造方法:public QueryRunner(DataSource ds) {super(ds);}-->
<constructor-arg name="ds" ref="datasource"/>
</bean>
</beans>
Controller层
以loginController为例:
@Controller //在类的声明加上@Controller注解
public class LoginController {
@Autowired //在属性的声明上加上@Autowired注解,自动注入
private UserService userService;
...
}
Service实现类
@Service //在类的声明加上@Service注解
public class UserServiceImpl implements UserService {
@Autowired
private UserDAO userDAO;
...
}
DAO层实现类
@Repository //在类的声明加上 @Repository 注解
public class UserDAOImpl implements UserDAO {
@Autowired
private QueryRunner queryRunner;
...
}
版本三 Java配置式的IoC
注解版减少了一些不必要的xml配置,但是还是觉得xml配置比较麻烦,有没有不需要xml的?答案是肯定的,下面我们讲一讲不需要xml怎么配置。
在此我们需要引入几个注解
- @Bean 标记某个方法返回值为spring的bean,方法参数为依赖注入的请求。
- @Configuration 标记一个类为配置类
具体实现步骤:
1、新建一个java类作为配置类,加上@Configuration注解让Spring识别;
2、在配置类创建方法,返回值为bean的实例,添加@Bean注解;
此时,其他类是没有任何注解的。
创建配置类
因此我们创建一个配置类AppConfig。
@Configuration //声明这个类是配置文件
@PropertySource("database.properties") //读取properties文件
public class AppConfig {
//声明这些jdbc属性,用于链接数据库
@Value("${jdbc.driverClass}") //SpEL表达式,可以读取properties文件的指定属性值
private String driverClassName;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean //添加@bean注解,说明这个方法返回值是一个bean实例
public LoginController loginController(){
//创建一个对象
LoginController loginController = new LoginController();
//可以通过调用本类的方法来设置实例的属性
loginController.setUserService(userService());
//返回这个对象
return loginController;
}
@Bean
public MenuController menuController(LoginController loginController){
MenuController menuController = new MenuController();
menuController.setRegisterController(registerController());
//也可以通过在方法参数中声明容器中以存在的bean,再在方法内部使用
menuController.setLoginController(loginController);
return menuController;
}
@Bean
public RegisterController registerController(){
RegisterController registerController = new RegisterController();
registerController.setUserService(userService());
return registerController;
}
@Bean
public UserService userService(){
UserServiceImpl userService = new UserServiceImpl();
userService.setUserDAO(userDAO());
return userService;
}
@Bean
public UserDAO userDAO(){
UserDAOImpl userDAO = new UserDAOImpl();
userDAO.setQueryRunner(queryRunner(druidDataSource()));
return userDAO;
}
@Bean(destroyMethod = "close") //destroyMethod 表示容器销毁时,调用德鲁伊连接池的close方法。
public DruidDataSource druidDataSource(){
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName(driverClassName);
druidDataSource.setUrl(url);
druidDataSource.setUsername(username);
druidDataSource.setPassword(password);
return druidDataSource;
}
@Bean
public QueryRunner queryRunner(DataSource dataSource){
return new QueryRunner(dataSource);
}
}
测试类
注意此时测试类得到容器的方法改变了,因为不再需要xml文件。
public class App {
public static void main(String[] args) {
//读取配置类,创建容器
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
//从容器中取得bean实例,并调用方法
MenuController menuController = ac.getBean("menuController",MenuController.class);
menuController.mainUI();
}
}
版本四 扫描版
虽然不用xml,但是bean配置也是相当麻烦。那么怎么简化呢?
那就是采用注解来简化。
在此引入注解:
@ComponentScan 打开包扫描功能
实现步骤:
1、在类和属性上添加注解,和版本二的注解式添加方式一样;
2、在配置类上添加属性@ComponentScan 打开包扫描功能,并且指定扫描的包路径;
@Configuration
@PropertySource("database.properties")
@ComponentScan("com.alter.spring")//添加注解扫描包,并且指定扫描路径。
public class AppScanConfig {
@Value("${jdbc.driverClass}")
private String driverClassName;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean(destroyMethod = "close")
public DruidDataSource druidDataSource(){
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName(driverClassName);
druidDataSource.setUrl(url);
druidDataSource.setUsername(username);
druidDataSource.setPassword(password);
return druidDataSource;
}
@Bean
public QueryRunner queryRunner(DataSource dataSource){
return new QueryRunner(dataSource);
}
}