Spring中的IOC容器和DI注入

Spring和单例模式

对象创建在堆中

crtl + h 看类的继承体系

spring 框架创建对象 是单例模式 只创建一个对象

单例模式

简介

单例模式是oop(面向对象编程)语言的一种概念 顾名思义 就是一个类只能有一个实例化对象

单例模式分为两种:1 懒汉式加载 2 饿汉式加载 他们又分别有传统实现和优化的推荐实现

spring框架创建对象就是单例模式

懒汉式单例

特点;当需要使用对象的时候才进行实例化。

由于可能有多个线程同时使用对象 因此需要考虑线程安全问题,防止并发访时生成多个实例。

通常使用加锁来解决冲突 是用时间换空间的方案

传统实现代码:

public class Singleton {
//    设置构造方法
    private Singleton(){};
//    声明一个Singleton对象为obj
    private static Singleton obj;
//    加锁保证obj只能实例化一次 时间换空间
    public static synchronized Singleton getInstance(){
        if (obj==null){
            obj=new Singleton();
        }
        return obj;
    }
}

传统实现方法中,每次获取实例都要被synchronized关键字串行化 即使已经生成了对象实例

而我们加锁的目的是为了防止生成多个实例,因此其实只需要对生成实例的代码加锁,生成实例后,可支持并发访问,提高性能

优化后代码

package com.xsy.lx.service.entity;

public class Singleton1 {
    //设置私有构造方法
    private Singleton1(){}
    // 最后解释volatile关键字
    private  volatile static Singleton1  obj;
    //获取实例对象的方法
    public static Singleton1  getInstance(){
        //如果已有实例则直接返回,不走锁
        if(obj==null){
            //仅在没生成实例时加锁控制,使并发访问串行化
            synchronized(Singleton.class){
                //多个线程会按序执行到此处,需要再次检查是否已经实例化
                if(obj==null){
                    obj = new Singleton1();
                }
            }
        }
        return  obj;
    }
}

优化实现 由于检查了两次对象是否已经实例化,因此该方法又被称为“双检发” 能够保证线程安全的同时提升对象实例化后的调用性能

饿汉式单例

饿汉单例的特点是:类加载时便实例化

能够在第一时间实例化对象供其他方法使用 是拿空间换时间的方案

传统代码实现

package com.xsy.lx.service.entity;

public class Singleton3 {

    private Singleton3(){};
    private static Singleton3 obj=new Singleton3();

    public static Singleton3 getintance(){
        return obj;
    }
}

通过static关键字 在类加载时创建对象

优化实现:

上述传统方法中,由于类加载时就实例化对象,因此当我们调用这个类的其他静态方法时,也会触发类加载器,从而实例化单例独享,会导致空间的暂时浪费。

由于静态内部类中的对象不会默认加载,直到调用了获取该内部类属性的方法 因此可用静态内部类封装实例变量

package com.xsy.lx.service.entity;

public class Singleton4 {
    // 私有构造函数
    private Singleton4() {}

    // 静态内部类
    private static class SingletonHolder {
        private static Singleton4 instance = new Singleton4();
    }
    public static Singleton4 getInstance(){
        return SingletonHolder.instance;
    }
}

上述两种实现方法中,最推荐这种优化后的饿汉式实现,利用static保证线程安全,利用静态内部类节约了空间,实现了lazy-loading(懒加载),而且代码非常简短,可谓是一箭三雕。

单例模式细节深入分析

设计模式是开发岗位中的高频考点,在设计模式中,单例模式是基本必考的

为什么面试官爱问单例模式?原因如下:

1单例模式是我们平时编程中最常用的设计模式之一

2 单例模式实现简单,不会占用太多面试时间

3 单例模式的实现有很多种 而且有优劣之分 能够体现面试者的基本功和水平

虽然懒汉式单例优化版的实现代码相对复杂 但是有一些细节值得学习和考究

比如这行声明实例的代码中为什么要使用volatile关键字?

private volatile static Singleton obj;

如果不添加的话 可能出现获取实例为null的情况!

因为使用new来创建对象不是一个原子操作(不可分割的操作序列,要么都成功,要么都失败),而是会被编译成如下三条指令:

  1. 给实例分配内存
  2. 初始化实例的构造
  3. 将实际对象指向分配的内存空间(此时实例应该已经不为空)

正常的思路是123一定按顺序执行。

但事实上,Java会对进行指令重排序。

JVM根据处理器的特性,充分利用多级缓存,多核等进行适当的指令重排序,使程序在保证业务运行的同时,充分利用CPU的执行特点,最大的发挥机器的性能。

即JVM虚拟机在执行上面三条指令时,可能按照132的顺序执行。

假设当13执行完,2还未执行时,如果另外一个线程调用getInstance(),会在判断对象是否为null时返回false(因为3已执行,对象指向了内存空间,已不为空),然后直接返回实例。但由于此时2还没执行,实例并未完全初始化,只是分配了内存空间,就会导致使用对象时出现错误(引用逃逸)。

注意:final字段不能保证初始化过程中的可见性,也无法禁止指令重排序!

而voliate关键字可以通过内存屏障禁止指令重排序,保证创建对象时的123步骤按顺序执行,从而解决上述问题。

连接池池化思想

核心思想:预创建并维护一组可重用的资源(如数据库连接),使用时从池中获取,用完后归还而不是销毁。类似“共享单车”模式,避免重复创建和销毁的开销。

为什么使用连接池

1 性能提升:避免重复创建 /关闭连接(TCP三次握手,认证等耗时操作)

2 资源复用:连接可以被多个操作重复使用

3 连接管理:控制最大连接数,可以防止数据库过载

4 稳定性:提供连接有效性检测,断线重连等机制

Druid 连接池是什么

阿里巴巴开源的的数据库连接池,具有:

高性能:比传统连接池(如DBCP,C3P0)更高效

监控功能:内置强大的监控统计功能

安全防护:支持SQL注入防护,WailFilter等

扩展性:支持插件扩展

一句话总结:Druid是一个功能全面、性能优秀的数据库连接池实现,广泛用于Java企业级应用

spring框架描述

spring是一个开放源代码的设计层面框架,他解决的是业务逻辑和其他代码的松耦问题

它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 J2EE 应用程序开发提供集成的框架。

Spring 框架优点

  1. 方便解耦,简化开发,Spring就是一个大工厂,可以将所有对象创建和依赖关系维护,交给Spring管理。IOC的作用。
  2. AOP编程的支持,Spring提供面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等功能。(可扩展性)
  3. 声明式事务的支持,只需要通过配置就可以完成对事务的管理,而无需手动编程。
  4. 方便程序的测试,Spring对Junit4支持,可以通过注解方便的测试Spring程序。
  5. 方便集成各种优秀框架,Spring不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如:Struts2、Hibernate、MyBatis、Quartz等)的直接支持。
  6. 降低JavaEE API的使用难度,Spring 对JavaEE开发中非常难用的一些API(JDBC、JavaMail、远程调用等),都提供了封装,使这些API应用难度大大降低。

Spring的IOC核心技术

什么是IOC?

IOC – Inverse of Control,控制反转,将对象的创建权力反转给Spring框架!!

控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。

解决问题:使用IOC可以解决的程序耦合性高的问题!!Spring的工厂读取配置文件。

ioc 技术总结

ApplicationContext接口,工厂的接口,使用该接口可以获取到具体的Bean对象。该接口下有两个具体的实现类。 springCould 配置中心

ClassPathXmlApplicationContext,加载类路径下的Spring配置文件。

FileSystemXmlApplicationContext,加载本地磁盘下的Spring配置文件。

Spring框架的Bean管理的配置文件方式

id属性:Bean起个名字,在约束中采用ID的约束,唯一,

取值要求:必须以字母开始,可以使用字母,数字,连字符,下划线,句号,冒号 id:不能出现特殊字符

class属性,Bean对象的全路径。

scope属性,scope属性代表Bean的作用范围。

singleton单例(默认值),最常用的方式。

prototype多例

​ request应用在Web项目中,每次HTTP请求都会创建一个新的Bean

​ session应用在Web项目中,同一个HTTP Session 共享一个Bean

Bean对象的创建和销毁的两个属性配置

说明:Spring初始化bean或销毁bean时,有时需要作一些处理工作,因此spring可以在创建和拆卸bean的时候调用bean的两个生命周期方法

init-method,当bean被载入到容器的时候调用init-method属性指定的方法

destroy-method,当bean从容器中删除的时候调用destroy-method属性指定的方法

单例的对象销毁:跟着容器工厂关闭才销毁

多例的对象销毁:垃圾回收机制进行回收的

实例化Bean对象的三种方式

默认是无参数的构造方法(默认方式,基本上使用)

<bean id="us" class="com.qcbyjy.service.UserServiceImpl" />

静态工厂实例化方式

package com.xsy.lx.service.factory;

import com.xsy.lx.service.UserService;
import com.xsy.lx.service.impl.UserServiceImpl;

public class StaticFactory {
//    静态工厂方式
    public static UserService createUs(){
        System.out.println("xsy使用静态工厂创建UserServiceImpl对象");
//        中间可以编写更多逻辑 权限校验
        return new UserServiceImpl();
    }
}

<bean id="us" class="com.xsy.lx.service.factory.StaticFactory" factory-method="createUs"  />   

动态工厂实例化方法

package com.xsy.lx.service.factory;

        import com.xsy.lx.service.UserService;
        import com.xsy.lx.service.impl.UserServiceImpl;

public class DynamicFactory {
    //    实例化工厂实例化方式
    public  UserService createUs(){
        System.out.println("实例化工厂的方式进行创建");
        return new UserServiceImpl();
    }

}
    <bean id="dfactory" class="com.xsy.lx.service.factory.DynamicFactory" />
    <bean id="us1" factory-bean="dfactory" factory-method="createUs" />

DI依赖注入

依赖注入的概述

IOC和DI的概念

​ IOC:Inverse of Control,控制反转,将对象的创建权反转给Spring!!

​ Di:Dependency Injection,依赖注入,在Spring框架负责创建Bean对象时,动态的将依赖对象注入到Bean组件中

属性的set方法注入值

编写属性,提供该属性对应的set方法 编写配置文件完成属性值的注入

一共需要创建5个文件 持久层OrderDao OrderDaoImpl 服务层OrderService OrderServiceImpl

OrderDao中代码

package com.xsy.lx.dao;

public interface OrderDao {
    public void saveOrder();
}

OrderDaoImpl中代码

package com.xsy.lx.service.impl;

import com.xsy.lx.dao.OrderDao;

public class OrderDaoImpl implements OrderDao {

    @Override
    public void saveOrder() {
        System.out.println("xsy持久层:保存定单");
    }
}

OrderService中代码

package com.xsy.lx.service;

public interface OrderService {
    public void saveOrder();
}

OrderServiceImpl

package com.xsy.lx.service.impl;

import com.xsy.lx.dao.OrderDao;
import com.xsy.lx.service.OrderService;

public class OrderServiceImpl implements OrderService {

    // 编写成员属性,一定需要提供该属性的set方法
    private OrderDao orderDao;
    private String msg;
    private int age;

    // 一定需要提供该属性的set方法,IOC容器底层就通过属性的set方法方式注入值
    public void setOrderDao(OrderDao orderDao) {
        this.orderDao = orderDao;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public void saveOrder() {
        System.out.println("xsy业务层:保存订单"+msg+"-"+age);
        orderDao.saveOrder();
    }
}

测试类中内容

package com.xsy.lx;

import com.xsy.lx.service.OrderService;
import com.xsy.lx.service.impl.OrderServiceImpl;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class DiTest {

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

        // 检查od bean是否存在
        Object odBean = context.getBean("od");
        System.out.println("od bean: " + odBean);

        // 检查os bean是否存在
        OrderService orderService = (OrderService) context.getBean("os");
        System.out.println("os bean: " + orderService);

        orderService.saveOrder();
    }
}
测试结果
od bean: com.xsy.lx.service.impl.OrderDaoImpl@18d87d80
os bean: com.xsy.lx.service.impl.OrderServiceImpl@618425b5
xsy业务层:保存订单你好-30
xsy持久层:保存定单

属性构造方法注入值

对于类成员变量 构造方法注入

package com.xsy.lx.entity;

public class Car {

    private String cname;    // 名称
    private Double money;    // 金额

    public Car(String cname, Double money) {
        this.cname = cname;
        this.money = money;
    }

    @Override
    public String toString() {
        return "Car{" +
                "cname='" + cname + '\'' +
                ", money=" + money +
                '}';
    }
}
  <!-- DI:依赖注入 -->
    <bean id="os" class="com.xsy.lx.service.impl.OrderServiceImpl">
        <property name="orderDao" ref="od"/>
        <property name="msg" value="你好"/>
        <property name="age" value="30"/>
    </bean>
    
  测试类代码
  @Test
    public void demo2(){
        // 1. 加载Spring配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

        // 2. 获取Car对象
        Car car = (Car) context.getBean("car");
        System.out.println("Car对象: " + car);
    }
    
输出结果
Car对象: Car{cname='大奔', money=400000.0}
    

数组 集合(List set Map)Properties等的注入
package com.xsy.lx.entity;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Properties;

public class CollectionBean {

    // 数组
    private String[] strs;

    private List<String> list;

    private Map<String, String> map;

    private Properties properties;

    public void setStrs(String[] strs) {
        this.strs = strs;
    }

    public void setList(List<String> list) {
        this.list = list;
    }

    public void setMap(Map<String, String> map) {
        this.map = map;
    }

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

    @Override
    public String toString() {
        return "CollectionBean{" +
                "strs=" + Arrays.toString(strs) +
                ", list=" + list +
                ", map=" + map +
                ", properties=" + properties +
                '}';
    }
}

<!-- 给集合属性注入值 -->
    <bean id="collectionBean" class="com.xsy.lx.entity.CollectionBean">
        <property name="strs">
            <array>
                <value>美美</value>
                <value>小凤</value>
            </array>
        </property>

        <property name="list">
            <list>
                <value>熊大</value>
                <value>熊二</value>
            </list>
        </property>

        <property name="map">
            <map>
                <entry key="aaa" value="老王"/>
                <entry key="bbb" value="小王"/>
            </map>
        </property>

        <property name="properties">
            <props>
                <prop key="username">root</prop>
                <prop key="password">123456</prop>
            </props>
        </property>
    </bean>
    
     测试类代码
     @Test
    public void demo3(){
        // 1. 加载Spring配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        CollectionBean collectionBean = (CollectionBean) context.getBean("collectionBean");
        System.out.println(collectionBean);

    }
     输出结果
     CollectionBean{strs=[美美, 小凤], list=[熊大, 熊二], map={aaa=老王, bbb=小王}, properties={password=123456, username=root}}

多配置文件方式

在src的目录下又多创建了一个配置文件,现在是两个核心的配置文件 那么加载这两个配置文件的方式有两种

主配置文件中包含其他的配置文件:
<import resource="applicationContext2.xml"/>

工厂创建的时候直接加载多个配置文件:
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
"applicationContext.xml","applicationContext2.xml");

Spring框架开发程序的方式

Spring框架开发方式

1 需求编写service和dao的类 演示代码

2 技术选择:持久层使用原始的JDBC的程序,连接池选择的是Durid连接池。创建maven工程,导入需要的jar包

首先我们使用的是Spring框架必须有Spring框架的核心依赖

log4j是写日志的 如果不需要可以去掉

我们需要编写测试类进行测试 所以需要用junit进行测试 导入测试用的核心依赖

我们需要用到druid的连接池 我们需要导入他且需要和数据库进行连接

  <dependencies>
        <!-- Spring核心框架 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>

        <!-- 日志相关 -->
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.2</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.12</version>
        </dependency>

        <!-- 测试框架 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

        <!-- 数据库相关 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.10</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.33</version>
        </dependency>
    </dependencies>

创建数据库 和对应的表结构

create database spring_db;
use spring_db;
create table account(
    id int primary key auto_increment,
    name varchar(40),
    money double
)character set utf8 collate utf8_general_ci;
insert into account(name,money) values('aaa',1000);
insert into account(name,money) values('bbb',1000);
insert into account(name,money) values('ccc',1000);

编写JavaBean的类

package com.xsy.demo.entity;

import java.io.Serializable;

public class Account implements Serializable {
    private static final  long serialVersionUID=7355810572012650248L;
    private Integer id;
    private String name;
    private Double money;

    public Integer getId() {
        return id;
    }

    public Account(Integer id, String name, Double money) {
        this.id = id;
        this.name = name;
        this.money = money;
    }

    public Account() {
    }

    public void setId(Integer id) {
        this.id = id;

    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

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

    public Double getMoney() {
        return money;
    }

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


编写AccountDao的接口和实现类

package com.xsy.demo.dao;

import com.xsy.demo.entity.Account;

import java.util.List;

public interface AccountDao {
    public List<Account> findAll();
}

package com.xsy.demo.dao.impl;

import com.xsy.demo.dao.AccountDao;
import com.xsy.demo.entity.Account;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class AccountDaoImpl implements AccountDao {

    //Spring配置文件中写好 一会进行DI注入
    private DataSource dataSource;
    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    @Override
    public List<Account> findAll() {
        List<Account> list = new ArrayList<>();
        Connection connection = null;
        PreparedStatement stmt = null;
        ResultSet rs = null;

        try {
            // 获取连接
            connection = dataSource.getConnection();
            // 编写sql语句
            String sql = "select * from account";
            // 预编译
            stmt = connection.prepareStatement(sql);
            // 查询
            rs = stmt.executeQuery();
            // 遍历,封装数据
            while (rs.next()) {
                Account account = new Account();
                account.setId(rs.getInt("id"));
                account.setName(rs.getString("name"));
                account.setMoney(rs.getDouble("money"));
                list.add(account);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 关闭资源(注意关闭顺序)
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (stmt != null) {
                try {
                    stmt.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
        return list;
    }
}

编写服务层AccountServiceImpl的接口和实现类 调用持久层的方法

package com.xsy.demo.service;

import com.xsy.demo.entity.Account;

import java.util.List;

public interface AccountService {
    public List<Account> findAll();
}


package com.xsy.demo.service.impl;

import com.xsy.demo.dao.AccountDao;
import com.xsy.demo.entity.Account;
import com.xsy.demo.service.AccountService;

import java.util.List;

public class AccountServiceImpl implements AccountService {

    // 依赖注入
    private AccountDao accountDao;

    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }
    /**
     * 查询所有的数据
     * @return 账户列表
     */
    @Override
    public List<Account> findAll() {
        return accountDao.findAll();
    }
}

编写配置文件

<?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">
    <!-- 配置Druid数据源 -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
          destroy-method="close">
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://localhost:3306/spring_db?useUnicode=true&amp;characterEncoding=utf8" />
        <property name="username" value="root" />
        <property name="password" value="123456" />
        <!-- 可选:配置连接池参数 -->
        <property name="initialSize" value="5" />
        <property name="maxActive" value="20" />
    </bean>

    <!-- DAO层配置 -->
    <bean id="accountDao" class="com.xsy.demo.dao.impl.AccountDaoImpl">
        <property name="dataSource" ref="dataSource" />
    </bean>

    <!-- Service层配置 -->
    <bean id="accountService" class="com.xsy.demo.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao" />
    </bean>
</beans>

编写测试类进行测试

package com.xsy.test;
import com.xsy.demo.entity.Account;
import com.xsy.demo.service.AccountService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import javax.sound.midi.Soundbank;
import java.util.List;
public class test1 {
    @Test
    public void demo1(){
       ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        AccountService accountService = (AccountService) ac.getBean("accountService");
        List<Account> all = accountService.findAll();
        for (Account account : all) {
            System.out.println(account);
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值