控制反转_Spring:IOC 控制反转

b055579b4e88560fae2af68500b3067f.png

Spring 概述

Spring 是什么

Spring 是分层的 Java SE/EE 应用 full-stack (全栈式) 轻量级开源框架。

全栈式:对各种主流技术和框架都进行了整合,同时对三层架构都提供解决方案。

轻量级和重量级的划分主要依据就是看它使用了多少服务,启动时需要加载的资源多少以及耦合度等等。

提供了表现层 Spring MVC 和持久层 Spring JDBC Template 以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的 Java EE 企业应用开源框架。

Spring 两大核心:

  • IOC - Inverse Of Control 控制反转:把对象的创建权交给 Spring

  • AOP - Aspect Oriented Programming 面向切面编程:在不修改源码的情况下,对方法进行增强

Spring 发展历程

EJB - Enterprise Java Beans:

  • 1997 年,IBM 提出了 EJB 的思想

  • 1998 年,SUN 制定开发标准规范 EJB 1.0

  • 1999 年,EJB 1.1 发布

  • 2001 年,EJB 2.0 发布

  • 2003 年,EJB 2.1 发布

  • 2006 年,EJB 3.0 发布

Spring:

  • Rod Johnson( Spring 之父)改变 Java 世界的大师级人物

  • 2002 年编著《Expert one on one J2EE design and development》

  • 指出了 Java EE 和 EJB 组件框架中的存在的一些主要缺陷;提出普通 java 类依赖注入更为简单的解决方案

  • 2004 年编著《Expert one-on-one J2EE Development without EJB》阐述了 Java EE 开发时不使用 EJB 的解决方式(Spring 雏形),同年 4 月 spring 1.0 诞生

  • 2006 年 10 月,发布 Spring 2.0

  • 2009 年 12 月,发布 Spring 3.0

  • 2013 年 12 月,发布 Spring 4.0

  • 2017 年 9 月, 发布最新 Spring 5.0 通用版(GA)

Spring 优势

耦合:程序间的依赖关系

解耦:降低程序间的依赖关系;体现在编译期不依赖,运行期才依赖

JDBC 例子:

public class JDBCTest {

    @Test
    public void test1() throws ClassNotFoundException, SQLException {
        // 1.注册驱动
        // 存在编译期依赖:耦合重的体现
        // DriverManager.registerDriver(new com.mysql.jdbc.Driver());
        // 去掉 new 关键字:编译期不依赖,运行期才依赖。虽然解决了编译期依赖,但是仍存在硬编码问题
        Class.forName("com.mysql.jdbc.Driver");
        ...
    }
}

解耦思路:配置文件 + 反射

Spring:

1)方便解耦,简化开发

  • Spring 就是一个容器,可以将所有对象创建和关系维护交给 Spring 管理

  • 什么是耦合度?对象之间的关系,通常说当一个模块 (对象) 更改时也需要更改其他模块 (对象),这就是耦合,耦合度过高会使代码的维护成本增加。要尽量解耦

2)AOP 编程的支持

  • Spring 提供面向切面编程,方便实现程序进行权限拦截,运行监控等功能。

3)声明式事务的支持

  • 通过配置完成事务的管理,无需手动编程

4)方便测试,降低 Java EE API 的使用

  • Spring 对 Junit 4 支持,可以使用注解测试

5)方便集成各种优秀框架

  • 不排除各种优秀的开源框架,内部提供了对各种优秀框架的直接支持

Spring 体系结构

以下八大模块可根据需求引入项目使用:

Data Access/Integration

  • JDBC

  • ORM

  • OXM

  • JMS

  • Transactions

Web

  • WebSocket

  • Servlet

  • Web

  • Portlet

AOP

Aspects

Instrumentation

Messaging

Core Container - 相当于盖房子的地基

  • Beans

  • Core

  • Context

  • SpEL

Test

初识 IOC

概述

控制反转(Inverse Of Control)是一种设计思想,它的目的是指导我们设计出更加松耦合的程序。

控制:在 java 中指的是对象的控制权限(创建、销毁)。

反转:指的是对象控制权从由“开发者在类中手动控制”反转到由“ Spring 容器控制”。

例子:

  • 传统方式 - 需要一个 userDao 实例,需要开发者自己手动创建 new UserDao();

  • IOC 方式 - 需要一个 userDao 实例,直接从 spring 的 IOC 容器获得,对象的创建权交给了spring 控制

自定义 IOC 容器

介绍

需求:实现 Service 层与 Dao 层代码解耦合

步骤分析:

  1. 创建 java 项目,导入自定义 IOC 相关坐标

  2. 编写 Dao 接口和实现类

  3. 编写 Service 接口和实现类

  4. 编写测试代码

实现

创建 java 项目,导入自定义 IOC 相关坐标

<?xml  version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0modelVersion>

    <groupId>com.rendagroupId>
    <artifactId>jdbc_springartifactId>
    <version>1.0-SNAPSHOTversion>

    
    <properties>
        <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
        <maven.compiler.encoding>UTF-8maven.compiler.encoding>
        <java.version>1.11java.version>
        <maven.compiler.source>1.11maven.compiler.source>
        <maven.compiler.target>1.11maven.compiler.target>
    properties>

    <dependencies>
        <dependency>
            <groupId>dom4jgroupId>
            <artifactId>dom4jartifactId>
            <version>1.6.1version>
        dependency>
        <dependency>
            <groupId>jaxengroupId>
            <artifactId>jaxenartifactId>
            <version>1.1.6version>
        dependency>
        <dependency>
            <groupId>junitgroupId>
            <artifactId>junitartifactId>
            <version>4.12version>
        dependency>
    dependencies>

project>

编写 Dao 接口和实现类

public interface IUserDao {
    void save();
}
public class UserDaoImpl implements IUserDao {
    @Override
    public void save() {
        System.out.println("UserDao: Successfully Saved.");
    }
}

编写 Service 接口和实现类

public interface IUserService {
    void save();
}
public class UserServiceImpl implements IUserService {
    private IUserDao userDao = new UserDaoImpl();

    @Override
    public void save() {
        userDao.save();
    }
}

编写测试代码

public class SpringTest {
    @Test
    public void test1() {
        // 获取业务层对象
        IUserService userService = new UserServiceImpl();
        // 调用 save 方法
        userService.save();
    }
}

问题:当前 service 对象和 Dao 对象耦合度太高,而且每次 new 的都是一个新的对象,导致服务器压力过大。

解耦合的原则是编译期不依赖,而运行期依赖就行了。

采用反射方式:

public class UserServiceImpl implements IUserService {
    private IUserDao userDao;

    public UserServiceImpl() throws IllegalAccessException, ClassNotFoundException, InstantiationException {
        // 传统方式使用 new 方式,导致编译期依赖,耦合度太高
        // userDao = new UserDaoImpl();

        // 反射方式,存在硬编码问题
        userDao = (IUserDao) Class.forName("com.renda.dao.impl.UserDaoImpl").newInstance();
    }

    @Override
    public void save() {
        userDao.save();
    }
}

测试代码:

@Test
public void test1() throws IllegalAccessException, InstantiationException, ClassNotFoundException {
    IUserService userService = new UserServiceImpl();
    userService.save();
}

改造步骤分析:

  1. 准备一个配置文件

  2. 编写一个工厂工具类,工厂类使用 dom4j 来解析配置文件,获取到类的全路径

  3. 使用反射生成对应类的实例对象,存到 Map 中,这个 Map 就是 IOC 容器

为了解决反射方式的硬编码问题,先编写 beans.xml 配置文件

<beans>
    
    <bean id="userDao" class="com.renda.dao.impl.UserDaoImpl">bean>
beans>

编写 BeanFactory 工具类

public class BeanFactory {

    private static Map iocMap = new HashMap<>();// 程序启动时,初始化对象实例static {// 读取配置文件
        InputStream resourceAsStream = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");try {// 解析 xml(dom4j)
            SAXReader saxReader = new SAXReader();
            Document document = saxReader.read(resourceAsStream);// 编写 xpath 表达式
            String xpath = "//bean";// 获取到所有的 bean 标签
            List list = document.selectNodes(xpath);// 遍历并使用反射创建对象实例,存到 map 集合(ioc 容器)中for (Element element : list) {
                String id = element.attributeValue("id");
                String className = element.attributeValue("class");// 使用反射生成实例对象
                Object o = Class.forName(className).newInstance();// 存到 map 中:key-id,value-o
                iocMap.put(id, o);
            }
        } catch (DocumentException | InstantiationException | IllegalAccessException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }public static Object getBean(String beanId) {return iocMap.get(beanId);
    }
}

修改 UserServiceImpl 实现类

public UserServiceImpl() throws IllegalAccessException, ClassNotFoundException, InstantiationException {
    // 传统方式使用 new 方式,导致编译期依赖,耦合度太高
    // userDao = new UserDaoImpl();

    // 反射方式,存在硬编码问题
    // userDao = (IUserDao) Class.forName("com.renda.dao.impl.UserDaoImpl").newInstance();

    // 反射方式 + 配置文件
    userDao = (IUserDao) BeanFactory.getBean("userDao");
}

小结

  • 其实升级后的 BeanFactory 就是一个简单的 Spring 的 IOC 容器所具备的功能。

  • 之前需要一个 userDao 实例,开发者自己手动创建 new UserDao();

  • 现在需要一个 userDao 实例,直接从 spring 的 IOC 容器获得,对象的创建权交给了 spring 控制

  • 最终目标:代码解耦合

Spring 快速入门

介绍

需求:借助 spring 的 IOC 实现 service 层与 DAO 层代码解耦合

步骤分析:

  1. 创建 java 项目,导入 spring 开发基本坐标

  2. 编写 DAO 接口和实现类

  3. 创建 spring 核心配置文件

  4. 在 spring 配置文件中配置 UserDaoImpl

  5. 使用 spring 相关 API 获得 Bean 实例

实现

创建 java 项目,导入 spring 开发基本坐标

<dependencies>
    <dependency>
        <groupId>org.springframeworkgroupId>
        <artifactId>spring-contextartifactId>
        <version>5.1.5.RELEASEversion>
    dependency>
    <dependency>
        <groupId>junitgroupId>
        <artifactId>junitartifactId>
        <version>4.12version>
    dependency>
dependencies>

编写 Dao 接口和实现类

public interface IUserDao {
    void save();
}
public class UserDaoImpl implements IUserDao {
    @Override
    public void save() {
        System.out.println("UserDao: save...");
    }
}

创建 spring 核心配置文件 applicationContext.xml,并在 spring 配置文件中配置 UserDaoImpl Bean

<?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:p="http://www.springframework.org/schema/p"xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    
    <bean id="userDao" class="com.renda.dao.impl.UserDaoImpl"/>

beans>

使用 spring 相关 API 获得 Bean 实例

@Test
public void test1(){
    // 获取到了 spring 上下文对象,借助上下文对象可以获取到 IOC 容器中的 bean 对象 ,加载的同时就创建了 bean 对象存到容器中
    // ApplicationContext XmlApplicationContext = new FileSystemXmlApplicationContext("D:\\gitee_repository\\stage-6-module-2\\code\\spring_quickstart\\src\\main\\resources\\applicationContext.xml");
    ApplicationContext XmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

    // 使用上下文对象从 IOC 容器中获取到了 bean 对象
    // 方法一:根据 bean id 在容器中找对应的 bean 对象
    // IUserDao userDao = (IUserDao) XmlApplicationContext.getBean("userDao");
    // 方法二:根据类型从容器中匹配 Bean 实例,当容器中相同类型的 Bean 有多个时,则此方法会报错。
    // IUserDao userDao = XmlApplicationContext.getBean(IUserDao.class);
    // 方法三:根据 Bean 的 id 和类型获得 Bean 实例,解决容器中相同类型 Bean 有多个情况。
    IUserDao userDao = XmlApplicationContext.getBean("userDao", IUserDao.class);

    // 调用方法
    userDao.save();
}

Spring 的开发步骤总结

  1. 导入坐标

  2. 创建 Bean

  3. 创建 applicationContext.xml

  4. 在配置文件中进行 Bean 配置

  5. 创建 ApplicationContext 对象,执行 getBean

Spring 相关 API

API 继承体系介绍

Spring 的 API 体系异常庞大,现在只关注两个 BeanFactory 和 ApplicationContext

BeanFactory 是 ApplicationContext 的父接口,ApplicationContext 接口下又有 FileSystemXmlApplicationContext 和 ClassPathXmlApplicationContext 子接口

BeanFactory

BeanFactory 是 IOC 容器的核心接口,它定义了 IOC 的基本功能

特点:在第一次调用 getBean() 方法时,创建指定对象的实例

@Test
public void test2(){
    // 核心接口,不会创建 bean 对象存到容器中
    BeanFactory xmlBeanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));

    // getBean 的时候才真正创建 bean 对象
    IUserDao userDao = (IUserDao) xmlBeanFactory.getBean("userDao");

    // 调用方法
    userDao.save();
}

ApplicationContext

代表应用上下文对象,可以获得 spring 中 IOC 容器的 Bean 对象

特点:在 spring 容器启动时,加载并创建所有对象的实例

常用实现类:

  1. ClassPathXmlApplicationContext - 它是从类的根路径下加载配置文件,推荐使用这种。

  2. FileSystemXmlApplicationContext - 它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。

  3. AnnotationConfigApplicationContext - 当使用注解配置容器对象时,需要使用此类来创建 spring 容器,它用来读取注解。

常用方法:

  1. Object getBean(String name); - 根据 Bean 的 id 从容器中获得 Bean 实例,返回是 Object,需要强转。

  2. T getBean(ClassrequiredType); - 根据类型从容器中匹配 Bean 实例,当容器中相同类型的 Bean 有多个时,则此方法会报错。

  3. T getBean(String name,ClassrequiredType); - 根据 Bean 的 id 和类型获得 Bean 实例,解决容器中相同类型 Bean 有多个情况。

Spring 配置文件

Bean 标签基本配置

用于配置对象交由 Spring 来创建。

默认情况下它调用的是类中的无参构造函数,如果没有无参构造函数则不能创建成功。


<bean id="" class=""/>

Bean 标签范围配置

<bean id="" class="" scope="">bean>

scope 属性指对象的作用范围,取值如下:

  • singleton - 默认值,单例的

  • prototype - 多例的

  • request - WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 request 域中

  • session - WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 session 域中

  • global session - WEB 项目中,应用在 Portlet 环境,如果没有 Portlet 环境那么 global Session 相当于 session

测试 scope 属性:

@Test
public void test3(){
    ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

    IUserDao userDao_1 = (IUserDao) classPathXmlApplicationContext.getBean("userDao");
    IUserDao userDao_2 = (IUserDao) classPathXmlApplicationContext.getBean("userDao");

    System.out.println(userDao_1);
    System.out.println(userDao_2);
}

当 scope 的取值为 singleton 时:

- Bean 的实例化个数:1 个
- Bean 的实例化时机:当 Spring 核心文件被加载时,实例化配置的 Bean 实例
- Bean 的生命周期:
        + 对象创建:当应用加载,创建容器时,对象就被创建了
        + 对象运行:只要容器在,对象一直活着
        + 对象销毁:当应用卸载,销毁容器时,对象就被销毁了

当 scope 的取值为 prototype 时:

- Bean 的实例化个数:多个
- Bean 的实例化时机:当调用 getBean() 方法时实例化 Bean
- Bean 的生命周期:
          + 对象创建:当使用对象时,创建新的对象实例
          + 对象运行:只要对象在使用中,就一直活着
          + 对象销毁:当对象长时间不用时,被 Java 的垃圾回收器回收了

Bean 生命周期配置

applicationContext.xml

<bean id="userDao" class="com.renda.dao.impl.UserDaoImpl" init-method="init" destroy-method="destroy"/>
  • init-method:指定类中的初始化方法名称

  • destroy-method:指定类中销毁方法名称

public class UserDaoImpl implements IUserDao {
    public void init() {
        System.out.println("UserDao: Initialize...");
    }

    public void destroy() {
        System.out.println("UserDao: Destroy");
    }

    ...
}

测试代码

@Test
public void test4(){
    ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

    IUserDao userDao_1 = (IUserDao) classPathXmlApplicationContext.getBean("userDao");
    IUserDao userDao_2 = (IUserDao) classPathXmlApplicationContext.getBean("userDao");

    System.out.println(userDao_1);
    System.out.println(userDao_2);

    // 触发 destroy 方法
    classPathXmlApplicationContext.close();
}

Bean 实例化三种方式

无参构造方法实例化

它会根据默认无参构造方法来创建类对象,如果 bean 中没有默认无参构造函数,将会创建失败

<bean id="userDao" class="com.renda.dao.impl.UserDaoImpl"/>
工厂静态方法实例化

应用场景:依赖的 jar 包中有个 A 类,A 类中有个静态方法 m1,m1 方法的返回值是一个 B 对象。如果频繁使用 B 对象,此时可以将 B 对象的创建权交给 spring 的 IOC 容器,以后在使用 B 对象时,无需调用 A 类中的 m1 方法,直接从 IOC 容器获得。

public class StaticFactoryBean {
    public static IUserDao createUserDao(){
        return  new UserDaoImpl();
    }
}
<?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:p="http://www.springframework.org/schema/p"xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    
    



    
    <bean id="userDao" class="com.renda.factory.StaticFactoryBean" factory-method="createUserDao"/>

beans>
工厂普通方法实例化

应用场景:依赖的 jar 包中有个 A 类,A 类中有个普通方法 m1,m1 方法的返回值是一个 B 对象。如果我们频繁使用 B 对象,此时我们可以将 B 对象的创建权交给 spring 的 IOC 容器,以后我们在使用 B 对象时,无需调用 A 类中的 m1 方法,直接从 IOC 容器获得。

public class DynamicFactoryBean {
    public IUserDao createUserDao(){
        return  new UserDaoImpl();
    }
}
<?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:p="http://www.springframework.org/schema/p"xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    
    



    


    
    <bean id="dynamicFactoryBean" class="com.renda.factory.DynamicFactoryBean"/>
    <bean id="userDao" factory-bean="dynamicFactoryBean" factory-method="createUserDao"/>

beans>

Bean 依赖注入方式

依赖注入 DI(Dependency Injection):它是 Spring 框架核心 IOC 的具体实现。

在编写程序时,通过控制反转,把对象的创建交给了 Spring,但是代码中不可能出现没有依赖的情况。IOC 解耦只是降低他们的依赖关系,但不会消除。例如:业务层仍会调用持久层的方法。

这种业务层和持久层的依赖关系,在使用 Spring 之后,就让 Spring 来维护了。简单的说,就是通过框架把持久层对象传入业务层,而不用手动去获取。

Bean 依赖注入方式

构造方法

UserServiceImpl 中创建有参构造

public class UserServiceImpl implements IUserService {
    IUserDao userDao;

    public UserServiceImpl(IUserDao userDao) {
        this.userDao = userDao;
    }

    public UserServiceImpl() {
    }

    @Override
    public void save() {
        // 调用 dao 层的 save 方法
        userDao.save();
    }
}

配置 Spring 容器调用有参构造时进行注入

<bean id="userDao" class="com.renda.dao.impl.UserDaoImpl"/>
<bean id="userService" class="com.renda.service.impl.UserServiceImpl">
    
    <constructor-arg name="userDao" ref="userDao"/>
bean>
`set` 方法

UserServiceImpl 中创建 set 方法

public class UserServiceImpl implements IUserService {
    IUserDao userDao;

    public UserServiceImpl(IUserDao userDao) {
        this.userDao = userDao;
    }

    public UserServiceImpl() {
    }

    @Override
    public void save() {
        // 调用 dao 层的 save 方法
        userDao.save();
    }

    public void setUserDao(IUserDao userDao) {
        this.userDao = userDao;
    }
}

配置 Spring 容器调用 set 方法进行注入

<bean id="userDao" class="com.renda.dao.impl.UserDaoImpl"/>
<bean id="userService" class="com.renda.service.impl.UserServiceImpl">
    
    
    
    
    <property name="userDao" ref="userDao">property>
bean>
P 命名空间注入

P 命名空间注入本质也是 set 方法注入,但比起上述的 set 方法注入更加方便,主要体现在配置文件 applicationContext.xml 中。

首先,需要引入 P 命名空间:

xmlns:p="http://www.springframework.org/schema/p"

其次,需要修改注入方式:

<bean id="userDao" class="com.renda.dao.impl.UserDaoImpl"/>
<bean id="userService" class="com.renda.service.impl.UserServiceImpl" p:userDao-ref="userDao"/>

Bean 依赖注入的数据类型

上面操作,都是注入 Bean 对象,除了对象的引用可以注入,普通数据类型和集合都可以在容器中进行注入。

注入数据的三种数据类型:

  1. 普通数据类型

  2. 引用数据类型

  3. 集合数据类型

之前的操作都是对 UserDao 对象的引用进行注入的,属于引用数据类型注入。下面将以 set 方法注入为例,演示普通数据类型和集合数据类型的注入。

注入普通数据类型
public class UserDaoImpl implements IUserDao {
    private String username;
    private Integer age;

    public void setUsername(String username) {
        this.username = username;
    }

    public void setAge(Integer age) {
        this.age = age;


    @Override
    public void save() {
        System.out.println(username);
        System.out.println(age);
        System.out.println("UserDao: save...");
    }
}
<bean id="userDao" class="com.renda.dao.impl.UserDaoImpl">
    
    <property name="username" value="张人大"/>
    <property name="age" value="25"/>
bean>
注入集合数据类型

List 集合注入

public class User {
    private String username;
    private Integer age;

    public void setUsername(String username) {
        this.username = username;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}
public class UserDaoImpl implements IUserDao {
    private List list;public void setList(List list) {this.list = list;
    }@Overridepublic void save() {
        System.out.println("List:" + list);
    }
}
<bean id="user" class="com.renda.domain.User">
    <property name="username" value="布莱尔"/>
    <property name="age" value="18"/>
bean>

<bean id="userDao" class="com.renda.dao.impl.UserDaoImpl">
    <property name="list">
        <list>
            <value>aaavalue>
            <ref bean="user"/>
        list>
    property>
bean>

Set 集合注入

public class UserDaoImpl implements IUserDao {
    private Set set;public void setSet(Set set) {this.set = set;
    }@Overridepublic void save() {
        System.out.println("Set:" + set);
    }
}
<bean id="user" class="com.renda.domain.User">
    <property name="username" value="布莱尔"/>
    <property name="age" value="18"/>
bean>

<bean id="userDao" class="com.renda.dao.impl.UserDaoImpl">
    <property name="set">
        <set>
            <value>bbbvalue>
            <ref bean="user"/>
        set>
    property>
bean>

Array 数组注入

public class UserDaoImpl implements IUserDao {
    private Object[] array;

    public void setArray(Object[] array) {
        this.array = array;
    }

    @Override
    public void save() {
        System.out.println("Array:" + Arrays.toString(array));
    }
}
<bean id="userDao" class="com.renda.dao.impl.UserDaoImpl">
    <property name="array">
        <array>
            <value>cccvalue>
            <ref bean="user"/>
        array>
    property>
bean>

Map 集合注入

public class UserDaoImpl implements IUserDao {
    private Map map;public void setMap(Map map) {this.map = map;
    }@Overridepublic void save() {
        System.out.println("Map:" + map);
    }
}
<bean id="userDao" class="com.renda.dao.impl.UserDaoImpl">
    <property name="map">
        <map>
            <entry key="k1" value="ddd"/>
            <entry key="k2" value-ref="user"/>
        map>
    property>
bean>

Properties 配置注入

public class UserDaoImpl implements IUserDao {
    private Properties properties;

    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    @Override
    public void save() {
        System.out.println("Properties:" + properties);
    }
}
<bean id="userDao" class="com.renda.dao.impl.UserDaoImpl">
    <property name="properties">
        <props>
            <prop key="k1">v1prop>
            <prop key="k2">v2prop>
            <prop key="k3">v3prop>
        props>
    property>
bean>

配置文件模块化

实际开发中,Spring 的配置内容非常多,这就导致 Spring 配置很繁杂且体积很大,所以,可以将部分配置拆解到其他配置文件中,也就是所谓的配置文件模块化。

并列的多个配置文件:

ApplicationContext ac = new ClassPathXmlApplicationContext("beans1.xml", "beans2.xml", "...");

主从配置文件:

<import resource="applicationContext-xxx.xml"/>

注意:

同一个 xml 中不能出现相同名称的 bean,如果出现会报错。

多个 xml 如果出现相同名称的 bean,不会报错,但是后加载的会覆盖前加载的 bean

小结

Spring 的重点配置

<bean> 标签:创建对象并放到 spring 的 IOC 容器
    id 属性: 在容器中 Bean 实例的唯一标识,不允许重复
    class 属性: 要实例化的 Bean 的全限定名
    scope 属性: Bean 的作用范围,常用是 Singleton (默认) 和 prototype

<constructor-arg> 标签:属性注入
    name 属性:属性名称
    value 属性:注入的普通属性值
    ref 属性:注入的对象引用值

<property> 标签:属性注入
    name 属性:属性名称
    value 属性:注入的普通属性值
    ref 属性:注入的对象引用值
    <list>
    <set>
    <array>
    <map>
    <properties>

<import> 标签: 导入其他的 Spring 的分文件

IOC 实战 - DbUtils

DbUtils 是什么?

DbUtils 是 Apache 的一款用于简化 Dao 代码的工具类,它底层封装了 JDBC 技术。

核心对象:

QueryRunner queryRunner = new QueryRunner(DataSource dataSource);

核心方法:

  • int update() - 执行增、删、改语句

  • T query() - 执行查询语句

  • ResultSetHandler - 这是一个接口,主要作用是将数据库返回的记录封装到实体对象

查询数据库所有账户信息到 Account 实体中:

public class DbUtilsTest {
    @Test
    public void findAllTest() throws Exception {
        // 创建 DBUtils 工具类,传入连接池
        QueryRunner queryRunner = new QueryRunner(JdbcUtils.getDataSource());
        // 编写 sql
        String sql = "select * from account";
        // 执行 sql
        List list = queryRunner.query(sql, new BeanListHandler (Account.class));// 打印结果for (Account account : list) {
            System.out.println(account);
        }
    }
}

Spring 的 `xml` 整合 `DbUtils`

介绍

需求:基于 Spring 的 xml 配置实现账户的 CRUD 案例

步骤分析:

  1. 准备数据库环境

  2. 创建 java 项目,导入坐标

  3. 编写 Account 实体类

  4. 编写 AccountDao 接口和实现类

  5. 编写 AccountService 接口和实现类

  6. 编写 spring 核心配置文件

  7. 编写测试代码

实现

准备数据库环境

CREATE DATABASE `spring_db`;
USE `spring_db`;
CREATE TABLE `account` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `name` varchar(32) DEFAULT NULL,
    `money` double DEFAULT NULL,
    PRIMARY KEY (`id`)
);
insert  into `account`(`id`,`name`,`money`) values (1,'tom',1000),
(2,'jerry',1000);

创建 java 项目,导入坐标

<?xml  version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0modelVersion>

    <groupId>com.rendagroupId>
    <artifactId>spring_dbutilsartifactId>
    <version>1.0-SNAPSHOTversion>

    
    <properties>
        <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
        <maven.compiler.encoding>UTF-8maven.compiler.encoding>
        <java.version>1.11java.version>
        <maven.compiler.source>1.11maven.compiler.source>
        <maven.compiler.target>1.11maven.compiler.target>
    properties>

    <dependencies>
        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <version>5.1.47version>
        dependency>
        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>druidartifactId>
            <version>1.1.9version>
        dependency>
        <dependency>
            <groupId>commons-dbutilsgroupId>
            <artifactId>commons-dbutilsartifactId>
            <version>1.6version>
        dependency>
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-contextartifactId>
            <version>5.1.5.RELEASEversion>
        dependency>
        <dependency>
            <groupId>junitgroupId>
            <artifactId>junitartifactId>
            <version>4.12version>
        dependency>

        <dependency>
            <groupId>javax.annotationgroupId>
            <artifactId>javax.annotation-apiartifactId>
            <version>1.3.2version>
        dependency>
    dependencies>
project>

编写 Account 实体类

public class Account {

    private Integer id;
    private String name;
    private Double money;

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", money=" + money +
                '}';
    }

    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 getMoney() {
        return money;
    }

    public void setMoney(Double money) {
        this.money = money;
    }
}

编写 AccountDao 接口和实现类

public interface AccountDao {

    List findAll();

    Account findById(Integer id);

    void save(Account account);

    void update(Account account);

    void delete(Integer id);

}
public class AccountDaoImpl implements AccountDao {
    private QueryRunner queryRunner;

    public void setQueryRunner(QueryRunner queryRunner) {
        this.queryRunner = queryRunner;
    }

    @Override
    public List findAll() {
        List list = null;// 编写 sql
        String sql = "select * from account";try {// 执行 sql
            list = queryRunner.query(sql, new BeanListHandler(Account.class));
        } catch (SQLException e) {
            e.printStackTrace();
        }return list;
    }@Overridepublic Account findById(Integer id) {
        Account query = null;// 编写 sql
        String sql = "select * from account where id = ?";try {
            query = queryRunner.query(sql, new BeanHandler(Account.class), id);
        } catch (SQLException e) {
            e.printStackTrace();
        }return query;
    }@Overridepublic void save(Account account) {
        String sql = "insert into account values(null, ?, ?)";try {
            queryRunner.update(sql, account.getName(), account.getMoney());
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }@Overridepublic void update(Account account) {
        String sql = "update `account` set `name` = ?, `money` = ? where `id` = ?";try {
            queryRunner.update(sql, account.getName(), account.getMoney(), account.getId());
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }@Overridepublic void delete(Integer id) {
        String sql = "delete from `account` where `id` = ?";try {
            queryRunner.update(sql, id);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

编写 AccountService 接口和实现类

public interface AccountService {
    List findAll();

    Account findById(Integer id);

    void save(Account account);

    void update(Account account);

    void delete(Integer id);
}
public class AccountServiceImpl implements AccountService {
    private AccountDao accountDao;

    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }

    @Override
    public List findAll() {
        return accountDao.findAll();
    }

    @Override
    public Account findById(Integer id) {
        return accountDao.findById(id);
    }

    @Override
    public void save(Account account) {
        accountDao.save(account);
    }

    @Override
    public void update(Account account) {
        accountDao.update(account);
    }

    @Override
    public void delete(Integer id) {
        accountDao.delete(id);
    }
}

编写 spring 核心配置文件

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"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
        http://www.springframework.org/schema/context/spring-context.xsd
">

    
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql:///spring_db?characterEncoding=utf8&useSSL=false"/>
        <property name="username" value="root"/>
        <property name="password" value="password"/>
    bean>

    
    <bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
        <constructor-arg name="ds" ref="dataSource"/>
    bean>

    
    <bean id="accountDao" class="com.renda.dao.impl.AccountDaoImpl">
        <property name="queryRunner" ref="queryRunner"/>
    bean>

    
    <bean id="accountService" class="com.renda.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"/>
    bean>

beans>

编写测试代码

public class AccountServiceTest {

    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
    AccountService accountService = (AccountService) applicationContext.getBean("accountService");

    // 测试添加
    @Test
    public void testSave(){
        Account account = new Account();
        account.setName("renda");
        account.setMoney(888d);
        accountService.save(account);
    }

    // 测试查询
    @Test
    public void testFindById(){
        Account account = accountService.findById(3);
        System.out.println(account);
    }

    // 测试查询所有
    @Test
    public void testFindAll(){
        List all = accountService.findAll();for (Account account : all) {
            System.out.println(account);
        }
    }// 测试更新@Testpublic void testUpdate(){
        Account account = new Account();
        account.setId(3);
        account.setName("Blair");
        account.setMoney(2000d);
        accountService.update(account);
    }// 测试删除@Testpublic void testDelete(){
        accountService.delete(3);
    }
}

抽取 JDBC 配置文件

applicationContext.xml 加载 jdbc.properties 配置文件获得连接信息。
首先,需要引入 context 命名空间和约束路径:

* 命名空间:
    xmlns:context="http://www.springframework.org/schema/context"
* 约束路径:
    http://www.springframework.org/schema/context 
    http://www.springframework.org/schema/context/spring-context.xsd

<context:property-placeholder location="classpath:jdbc.properties"/>


<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="${jdbc.driverClassName}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
bean>

小结

* DataSource 的创建权交由 Spring 容器去完成
* QueryRunner 的创建权交由 Spring 容器去完成,使用构造方法传递 DataSource
* Spring 容器加载 properties 文件    
<context:property-placeholder location="xx.properties"/>
<property name="" value="${key}"/>

Spring 注解开发

Spring 是轻代码而重配置的框架,配置比较繁重,影响开发效率,所以注解开发是一种趋势,注解代替 xml 配置文件可以简化配置,提高开发效率。

Spring 常用注解

介绍

Spring 常用注解主要是替代 的配置

  • @Component - 使用在类上用于实例化 Bean

  • @Controller - 使用在 web 层类上用于实例化 Bean

  • @Service - 使用在 service 层类上用于实例化 Bean

  • @Repository - 使用在 dao 层类上用于实例化 Bean

  • @Autowired - 使用在字段上用于根据类型依赖注入;当使用注解注入属性时,set 方法可以省略

  • @Qualifier - 结合 @Autowired 一起使用,根据名称进行依赖注入;在自动按照类型注入基础之上,再按照 Bean 的 id 注入;它给字段注入时必须和 @Autowired 一起使用,但是给方法参数注入时可以独立使用

  • @Resource - 相当于 @Autowired + @Qualifier,按照名称进行注入

  • @Value - 注入普通属性

  • @Scope - 标注 Bean 的作用范围

  • @PostConstruct - 使用在方法上标注该方法是 Bean 的初始化方法

  • @PreDestroy - 使用在方法上标注该方法是 Bean 的销毁方法

说明:JDK 11 以后完全移除了 javax 扩展导致不能使用 @resource 注解,如果要使用它需要在 Maven 引入依赖:

<dependency>
    <groupId>javax.annotationgroupId>
    <artifactId>javax.annotation-apiartifactId>
    <version>1.3.2version>
dependency>

注意:使用注解进行开发时,需要在 applicationContext.xml 中配置组件扫描,作用是指定哪个包及其子包下的 Bean 需要进行扫描以便识别使用注解配置的类、字段和方法。


<context:component-scan base-package="com.renda">context:component-scan>
实现
Bean 实例化(IOC)
<bean id="userDao1" class="com.renda.dao.impl.UserDaoImpl">bean>

使用 @Component@Repository 标识 UserDaoImpl 需要 Spring 进行实例化。

// @Component(value = "userDao")
@Repository // 如果没有写 value 属性值,Bean 的 id 为:类名首字母小写
public class UserDaoImpl implements UserDao {
    ...
}
属性依赖注入(DI)
<bean id="userService" class="com.lagou.service.impl.UserServiceImpl">
    <property name="userDao" ref="userDao1"/>
bean>

使用 @Autowired 或者 @Autowired + @Qulifier 或者 @Resource 进行 userDao 的注入

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    // @Qualifier("userDao1")
    // @Resource(name = "userDao1")
    private UserDao userDao;

    public void setUserDao(UserDao userDao) {
        this.uDao = userDao;
    }
}
`@Value`

使用 @Value 进行字符串的注入,结合 SpEL (Spring Expression Language) 表达式获得配置参数

@Service
public class UserServiceImpl implements UserService {
    @Value("注入普通数据")
    private String str;

    @Value("${jdbc.driver}")
    private String driver;
}
`@Scope`
<bean scope=""/>

使用 @Scope 标注 Bean 的范围

@Service
@Scope("singleton")
public class UserServiceImpl implements UserService {
    ...
}
`Bean` 生命周期
<bean init-method="init" destroy-method="destory" />

使用 @PostConstruct 标注初始化方法,使用 @PreDestroy 标注销毁方法

@PostConstruct
public void init(){
    System.out.println("初始化方法....");
}

@PreDestroy
public void destroy(){
    System.out.println("销毁方法.....");
}
Spring 常用注解整合 `DbUtils`

步骤分析:

  1. 拷贝 xml 配置项目,改为注解配置项目

  2. 修改 AccountDaoImpl 实现类

  3. 修改 AccountServiceImpl 实现类

  4. 修改 Spring 核心配置文件

  5. 编写测试代码

修改 `AccountDaoImpl` 实现类
@Repository
public class AccountDaoImpl implements AccountDao {
    @Autowired
    private QueryRunner queryRunner;
    ...
}
修改 `AccountServiceImpl` 实现类
@Service(value = "accountService")
public class AccountServiceImpl implements AccountService {
    @Autowired
    private AccountDao accountDao;
    ...
}
修改 spring 核心配置文件
<?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"xsi:schemaLocation="
           http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    
    <context:component-scan base-package="com.renda"/>

    
    <context:property-placeholder location="classpath:jdbc.properties"/>

    
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    bean>

    
    <bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
        <constructor-arg name="ds" ref="dataSource"/>
    bean>

beans>
Spring 新注解

使用上面的注解还不能全部替代 xml 配置文件,还需要使用注解替代的配置如下:

非自定义的 Bean 的配置:<bean>
加载 properties 文件的配置:<context:property-placeholder>
组件扫描的配置:<context:component-scan>
引入其他文件:<import>
  • @Configuration - 用于指定当前类是一个 Spring 配置类,当创建容器时会从该类上加载注解

  • @Bean - 使用在方法上,标注将该方法的返回值存储到 Spring 容器中

  • @PropertySource - 用于加载 properties 文件中的配置

  • @ComponentScan - 用于指定 Spring 在初始化容器时要扫描的包

  • @Import - 用于导入其他配置类

Spring 纯注解整合 `DbUtils`

步骤分析:

  1. 编写 Spring 核心配置类

  2. 编写数据库配置信息类

  3. 编写测试代码

编写 Spring 核心配置类
@Configuration
@ComponentScan("com.renda")
@Import(DataSourceConfig.class)
public class SpringConfig {
    @Bean("queryRunner")
    public QueryRunner getQueryRunner(@Autowired DataSource dataSource){
        return new QueryRunner(dataSource);
    }
}
编写数据库配置信息类
@PropertySource("classpath:jdbc.properties")
public class DataSourceConfig {
    @Value("${jdbc.driverClassName}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;

    @Bean("dataSource")
    public DataSource getDataSource(){
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setDriverClassName(driver);
        druidDataSource.setUrl(url);
        druidDataSource.setUsername(username);
        druidDataSource.setPassword(password);
        return druidDataSource;
    }
}
编写测试代码
public class AccountServiceTest {

    /* ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("com/renda/service/applicationContext.xml");
    AccountService accountService = (AccountService) applicationContext.getBean("accountService"); */

    // 当前改成了纯注解形式
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
    AccountService accountService = (AccountService) applicationContext.getBean("accountService");

    ...
}

Spring 整合 Junit

普通 Junit 测试问题

在普通的测试类中,需要开发者手动加载配置文件并创建 Spring 容器,然后通过 Spring 相关 API 获得 Bean 实例;如果不这么做,那么无法从容器中获得对象。

开发者可以让 Spring Junit 负责创建 Spring 容器来简化这个操作,直接在测试类注入 Bean 实例;但是需要将配置文件的名称告诉它。

Spring 整合 Junit

步骤分析:

  1. 导入 spring 集成 Junit 的坐标

  2. 使用 @Runwith 注解替换原来的运行器

  3. 使用 @ContextConfiguration 指定配置文件或配置类

  4. 使用 @Autowired 注入需要测试的对象

  5. 创建测试方法进行测试

导入 spring 集成 Junit 的坐标
<dependency>
    <groupId>org.springframeworkgroupId>
    <artifactId>spring-testartifactId>
    <version>5.1.5.RELEASEversion>
dependency>
<dependency>
    <groupId>junitgroupId>
    <artifactId>junitartifactId>
    <version>4.12version>
dependency>
使用 `@Runwith` 注解替换原来的运行器
@RunWith(SpringJUnit4ClassRunner.class)
public class AccountServiceTest {
    ...
}
使用 `@ContextConfiguration` 指定配置文件或配置类
@RunWith(SpringJUnit4ClassRunner.class)
// @ContextConfiguration(value = {"classpath:applicationContext.xml"})
@ContextConfiguration(classes = {SpringConfig.class})
public class AccountServiceTest {
     ...   
}
使用 `@Autowired` 注入需要测试的对象
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {SpringConfig.class})
public class AccountServiceTest {
    @Autowired
    private AccountService accountService;
    ...   
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/411692.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

TAB选项卡

TAB选项卡&#xff1a;下载用Java Script模仿各种作业系统的选项卡&#xff0c;老外就是牛&#xff0c;不仅支援多样式的即时切换&#xff0c;同时也支援每个选项卡是否附带图示的切换选项&#xff0c;选项卡也可以上下切换。 转载于:https://www.cnblogs.com/meetrice/archive…

Kubernetes探索学习005--Kubernetes的Controller模型和ReplicaSet伸缩

1.Kubernetes的controller pattern 需要认识到Kubernetes操作Pod的逻辑&#xff0c;都是由控制器来完成的。 查看之前写过的nginx-deployment的YAML文件 [rootkubernetes01 ~]# cat nginx-deployment.yaml apiVersion: apps/v1 kind: Deployment metadata:name: nginx-deploym…

编程语言

语言的分类 高级语言&#xff1a;python、Java、PHP...... ----->字节码 低级语言&#xff1a;C、汇编 ----->机器码 区别&#xff1a;高级语言有回收内存的机制&#xff0c;…

kubernetes ui 搭建

1、部署Kubernetes云计算平台&#xff0c;至少准备两台服务器&#xff0c;此处为3台 Kubernetes Master节点&#xff1a;192.168.0.111 Kubernetes Node1节点&#xff1a;192.168.0.112 Kubernetes Node2节点&#xff1a;192.168.0.113 2、每台服务器主机都运行如下命令 system…

多项目加载顺序修改_React推出并发模式:可中断渲染、指定加载顺序、并行处理多状态...

警告&#xff1a;本文档介绍的实验功能在稳定版本中尚不可用。不要在生产应用程序中依赖 React 的实验性构建。这些功能可能会发生重大更改&#xff0c;而且直到功能成为 React 的一部分之前这类更改都不会发出警告。本文档面向早期使用者和对此感兴趣的用户。如果你不熟悉 Rea…

同步考勤数据 钉钉_作为学校,我为何选择微校wxiao考勤打卡?

随着移动互联网的兴起&#xff0c;越来越多的中小学摒弃了传统纸质考勤方式&#xff0c;采用智能考勤。目前&#xff0c;最常用的智能考勤方式可分为基于非移动端和移动端两种。其中&#xff0c;基于非移动端的包括人脸识别、指纹识别、虹膜识别考勤方式&#xff0c;基于移动端…

Guitar Por如何演奏刮弦

每当我们听到吉他现场演出的时候&#xff0c;看到吉他手在激烈的刮弦时&#xff0c;都觉得很酷&#xff0c;非常有感染力。刮弦在我们弹吉他或编曲时&#xff0c;会经常用到&#xff0c;虽然时间很短&#xff0c;但会为你加分不少。 那么我们应该如何演奏刮弦呢&#xff0c;我们…

LINQ - 對付 SQL Injection 的 免費補洞策略 (转)

LINQ - 對付 SQL Injection 的 "免費補洞策略" LINQ - 對付 SQL Injection 的 "免費補洞策略" 作者&#xff1a;黃忠成 一連串的 Mass SQL Injection 攻擊&#xff0c;讓我們回憶起數年前的 SQL Injection 攻擊&#xff0c;多年後的今天&#xff0c;我們仍…

Visual Studio .Net团队开发[转]

一、 开发前的准备1、 在装有Windows 2000或者Windows XP Professional的机器上安装.net Framework SDK、Visual Studio.Net、Visual Source Safe 6C。&#xff08;如果用户操作系统是Windows .Net Server&#xff0c;则无须安装.Net Framework SDK&#xff0c;.Net Server自带…

mysql数据库root密码在哪个文件中_mysql - 本地数据库忘记了root用户的密码

在连接本地mysql数据库一直都是客户端保存密码&#xff0c;然后一键登录。突然开发要用到这个数据库时&#xff0c;脑子嗡嗡的了……忘记了密码&#xff0c;试了很多遍还是记不起来。没办法只能去找方案了&#xff0c;网上还是有很多跟我一样的大佬的。这里记录下&#xff0c;省…

Inside IronPython: IronPython AST语法树(2/2)

接上篇: Inside IronPython: IronPython AST语法树(1/2) ASTs简介&#xff1a; 当 ipy.exe 执行demo.py的时候&#xff0c;IronPython语言自身的实现和DLR共同参与源代码的编译处理。从基本层面上来讲&#xff0c;编译器是流水化作业的&#xff1a;从语法解析器生成描述源代码…

如何开通支付宝

一、登录支付宝网站注册&#xff1a; 1、注册支付宝账户 1&#xff09;进入支付宝网站https://www.alipay.com/点击“免费注册”按钮 进入支付宝网站https://www.alipay.com/&#xff0c;如果图片有不显示的&#xff0c;请刷新一下&#xff0c;或者将鼠标放到红叉的位置&#x…

mysql的aborted_mysql参数:aborted_connects过大

mysql参数&#xff1a;aborted_connects过大(2015-12-31 11:32:08)标签&#xff1a;mysql运维it如果一个客户端在成功连接之后&#xff0c;不正常中断或结束&#xff0c;Aborted_connects将会增加1&#xff0c;并会将日志记录到error日志里。(log_warning设置为2才会记录)原因可…

小白学docker(1)---docker安装

最近学习下docker&#xff0c;并且工作需要&#xff0c;就总结下。 1、Docker安装 a、先查看下CentOS版本 b、查看下Linux内核版本 c、执行wget -qO- https://get.docker.com/|sh 其实就是一个执行脚本来快速安装开发环境docker d、启动sudo systemctl docker start 执行命令确…

.NET平台依赖注入机制及IoC的设计与实现

我们设计的分层架构&#xff0c;层与层之间应该是松散耦合的。因为是单向单一调用&#xff0c;所以&#xff0c;这里的“松散耦合”实际是指上层类不能具体依赖于下层类&#xff0c;而应该依赖于下层提供的一个接口。这样&#xff0c;上层类不能直接实例化下层中的类&#xff0…

php嵌套查询mysql语句_mysql 查询嵌套

问题描述 为使讨论简单易懂&#xff0c;我将问题稍作简化&#xff0c;去掉诸多的背景。 从前有一个皇帝&#xff0c;他有50个妃子&#xff0c;这些妃子很没有天理的给他生了100,000个儿子&#xff0c;于是&#xff0c;皇帝很苦恼&#xff0c;海量的儿子很难管理&#xff0c;而且…

征途pak文件修改_传奇技能,第十四祭:装备属性修改与增加新装备

技能献祭&#xff0c;Get 新技能&#xff1a;传奇技能——应用篇&#xff0c;增加新装备与绑特效跟航家学技能&#xff0c;用正式服带你飞&#xff0c;底部有配套学习资源场景&#xff1a;游戏中装备的属性是可以修改的&#xff0c;基础攻防属性可以直接在物品数据库中修改&…

本题要求实现一个用选择法对整数数组进行简单排序的函数。_通俗易懂讲 Python 算法:快速排序...

原文&#xff1a;https://stackabuse.com/quicksort-in-python/作者&#xff1a;Marcus Sanatan译者&#xff1a;老齐欢迎在 bilibili 搜索 freeCodeCamp 官方账号或者直接访问 https://space.bilibili.com/335505768 观看我们的技术视频介绍快速排序是一种流行的排序算法&…

vscode标记_高效扩展工具让 VS Code 如虎添翼

Codelf 变量命名神器Star&#xff1a;10688https://github.com/unbug/codelf新建项目&#xff0c;变量&#xff0c;类&#xff0c;方法&#xff0c;接口都需要命名&#xff0c;一个好的命名可以一眼看出这个地方的功能&#xff0c;CodeIf 一键起名不再难&#xff0c;输入关键词…

CSS 有关Position = absolute (绝对定位 是相对于谁而言)

css中有绝对定位法&#xff0c;以前一直搞不懂绝对定位是相对于谁而言的绝对定位。 现在搞清楚了&#xff0c;不是相对于父元素&#xff0c;也不是相对于BODY。 而是相对于所属元素树中&#xff0c;相邻最近的那个显示标识了position属性的元素。 比如 Code<div id"a&q…