依赖注入(DI):
依赖注入(DI)是指IC容器在运行期间动态地将某种依赖资源注入到对象中。
例如,将对象B注人(赋值)给对象A的成员变量。
依赖注入的基本思想是,明确地定义组件接口,独立开发各个组件,然后根据组件的依赖关系组装运行。
依赖注入的类型:
构造方法注入
package com.itheima;
public class User1 {
private int id;
private String name;
private String password;
public User1(int id, String name, String password) {
this.id = id;
this.name = name;
this.password = password;
}
@Override
public String toString() {
return "User1{" +
"id=" + id +
", name='" + name + '\\'' +
", password='" + password + '\\'' +
'}';
}
}
配置文件
<?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="user1" class="com.itheima.User1">
<constructor-arg name="id" value="1"></constructor-arg>
<constructor-arg name="name" value="张三"></constructor-arg>
<constructor-arg name="password" value="123"></constructor-arg>
</bean>
</beans>
测试类
package com.itheima;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestUser1 {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext-User.xml");
User1 user1 = applicationContext.getBean("user1", User1.class);
System.out.println(user1);
}
}
属性 setter 方法注入
package com.itheima;
public class User2 {
private int id;
private String name;
private String password;
public void setId(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User1{" +
"id=" + id +
", name='" + name + '\\'' +
", password='" + password + '\\'' +
'}';
}
}
配置文件
<?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="user2" class="com.itheima.User2">
<property name="id" value="2"/>
<property name="name" value="李四"/>
<property name="password" value="456"/>
</bean>
</beans>
测试方法
package com.itheima;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestUser2 {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext-User2.xml");
User2 user2 = applicationContext.getBean("user2", User2.class);
System.out.println(user2);
}
}
下面是一个例子实现依赖注入:
使用依赖注入实现登陆验证
编写Service层
创建UserService接口
package com.itheima.service;
//业务逻辑层接口
public interface UserService {
public boolean login(String name, String password);
}
创建UserService接口的实现类UserServiceImpl
package com.itheima.service.impl;
//import com.itheima.service.UserService;
import com.itheima.service.UserService;
import org.springframework.jdbc.core.JdbcTemplate;
public class UserServiceImpl implements UserService {
private JdbcTemplate jdbcTemplate;
@Override
public boolean login(String username, String password) {
String sql = "SELECT count(*) FROM users WHERE username = ? AND password = ?";
Integer count = jdbcTemplate.queryForObject(sql, new Object[]{username, password}, Integer.class);
return count != null && count > 0;
}
//此Setter方法用于注入JdbcTemplate实例。
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
}
- jdbcTemplate.queryForObject: 使用**
JdbcTemplate
的queryForObject
**方法执行SQL查询。- sql: 第一个参数是要执行的SQL查询字符串。
- new Object[]{username, password}: 第二个参数是一个对象数组,包含要绑定到SQL查询中的参数值(用户名和密码)。
- Integer.class: 第三个参数是返回结果的类型,表示期望结果是一个整数。
这种方法利用**JdbcTemplate
**简化了数据库操作,避免了手动管理数据库连接和结果集的繁琐步骤,同时通过占位符防止SQL注入攻击
下面解释一下sql注入攻击:
public boolean findAdmin(Admin admin)
{
String sql = "select count(*) from admin where username='" + admin.getUsername() + "'and password='" + admin.getPassword() + "'" //SQL查询语句
try {
Resultset res = this.conn.creatStatement().executQuery(sql);
//执行sql语句
if(res.next())
{
int i = res.getInt(1);//获取第一列的值
if (i>0)
{return true;} //如果结果大于0,则返回true
}
} catch (Exception e) {
e.printStackTree();//打印异常信息
}
return false;
}
正常情况下,当一个用户 username='test'且password='test'时,执行了sql语句 select count() from admin where username = 'test' and password = 'test'
但是当输入username="'or 1=1--"时,在java程序中String类型变量sql 为 " select count() from admin where username = ' 'or 1=1-- ' and password = ' ' "这句sql语句在执行时,"--" 将"and"及之后的语句都注释掉了,相当于执行了select count(*) from admin where username = ' 'or 1=1 而1=1是永远为true的,所以该语句的执行结果实际上是admin表的行数,而不是符合输入的username和password的行数,从而顺利通过验证。
编写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>">
<!-- 配置JDBC数据源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost/spring?useUnicode=true
&characterEncoding=utf-8
&serverTimezone=Asia/Shanghai
&useSSL=false"/>
<property name="username" value="bge"/>
<property name="password" value="123456"/>
</bean>
<!-- 配置JDBC模板 -->
<bean id="JdbcTemplate"
class="org.springframework.jdbc.core.JdbcTemplate">
<!-- 默认必须使用数据源 -->
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- userService bean配置了UserServiceImpl类,
并通过<property>标签指定要注入的jdbcTemplate bean -->
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl">
<property name="jdbcTemplate" ref="JdbcTemplate"/>
</bean>
</beans>
编写测试类
package com.itheima;
import com.itheima.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.Scanner;
public class TestSpring {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) applicationContext.getBean("userService");
Scanner scanner = new Scanner(System.in);
System.out.println("请输入要登录的用户账号");
String username = scanner.nextLine();
System.out.println("请输入密码");
String password = scanner.nextLine();
boolean flag = userService.login(username, password);
if (flag)
System.out.println("登录成功");
else
System.out.println("登录失败");
}
}
依赖注入的工作原理
- Spring容器启动: Spring容器根据配置文件**
applicationContext.xml
**创建并初始化所有的bean。 - 创建
dataSource
bean: Spring容器首先创建并初始化**dataSource
** bean。 - 创建
jdbcTemplate
bean: Spring容器接着创建并初始化**jdbcTemplate
** bean,并将**dataSource
** bean注入到**jdbcTemplate
** bean中。 - 创建
userService
bean: Spring容器创建并初始化**userService
** bean,即**UserServiceImpl
**实例。 - Setter注入:
- Spring容器识别到**
<property name="jdbcTemplate" ref="jdbcTemplate"/>
**。 - 容器调用**
UserServiceImpl
的setJdbcTemplate
方法,并传递jdbcTemplate
** bean实例。
- Spring容器识别到**
Spring容器在创建**UserServiceImpl
实例时,会自动调用setJdbcTemplate
方法,将JdbcTemplate
实例注入到UserServiceImpl
**中,从而实现依赖注入。
总结
依赖注入(DI)和控制反转(IoC)是从不同角度来描述了同一件事情。依赖注入是从应用程序的角度描述,即应用程序依赖IoC容器创建并注入它所需要的外部资源:而控制反转是从IoC容器的角度描述,即IoC容器控制应用程序,由IoC容器反向地向应用程序注人应用程序所需要的外部资源。这里所说的外部资源可以是外部实例对象,也可以是外部文件对象等。