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来创建对象不是一个原子操作(不可分割的操作序列,要么都成功,要么都失败),而是会被编译成如下三条指令:
- 给实例分配内存
- 初始化实例的构造
- 将实际对象指向分配的内存空间(此时实例应该已经不为空)
正常的思路是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 框架优点
- 方便解耦,简化开发,Spring就是一个大工厂,可以将所有对象创建和依赖关系维护,交给Spring管理。IOC的作用。
- AOP编程的支持,Spring提供面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等功能。(可扩展性)
- 声明式事务的支持,只需要通过配置就可以完成对事务的管理,而无需手动编程。
- 方便程序的测试,Spring对Junit4支持,可以通过注解方便的测试Spring程序。
- 方便集成各种优秀框架,Spring不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如:Struts2、Hibernate、MyBatis、Quartz等)的直接支持。
- 降低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&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);
}
}
}
3102

被折叠的 条评论
为什么被折叠?



