Spring框架
Spring IOC介绍
Spring的核心思想之一就是 IOC Inversion Of Control即 控制反转
那么什么是控制反转呢?
控制反转就是将对象的创建交给外部容器完成,Spring使用控制反转来实现对象的使用不用在程序中写死
Spring中使用依赖注入来实现对象之间的依赖关系,在创建对象之后。对象的关系处理就是依赖注入。
如果想要实现控制反转就要依靠外部容器,此时Spring就提供了一个外部容器——IOC容器
无论对象的创建,处理对象之间的依赖关系,对象创建的时间,对象创建的数量,都是在Spring提供的IOC的容器上来配置对象的信息
IOC的思想的核心在于:资源不在是使用资源方来管理,而是将资源交给第三方管理(IOC容器),这样来处理带来很多好处:
• 资源的集中管理,实现资源的可配置和易管理
• 降低了使用资源双方的依赖程度,也就是耦合度问题
Spring容器管理对象
Spring的依赖
<!--Spring核心依赖jar包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.1.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.1.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.1.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>4.1.7.RELEASE</version>
</dependency>
<!--log4j日志-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!--注解相关jar包-->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
Spring的配置文件(applicationContect.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-3.0.xsd">
</beans>
创建资源
我们假设创建了一个User类
public class User {
private String id;
private String name;
//getter和setter方法省略
}
IOC容器管理对象
在applicationContext.xml文件中添加User的对应信息
通过容器来获取对象
Spring IOC容器介绍
这个是Spring IOC容器中接口的继承关系,其中ApplicationContext是BeanFactory的子接口之一,换句话说
Beanfactory是IOC容器中最底层的接口,而ApplicationContext四其高级接口之一,在于对BeanFactory做了许多的功能上的扩展。在大部分的应用场景下,都是使用ApplicationConext作为Spring IOC的容器。
ApplicationContext的常见的实现类
- ClassPathXmlApplicationContext 读取classpath的资源文件使用该类
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
- FileSystemXmlApplicationContext 读取指定路径下的资源文件
FileSystemXmlApplicationContext xmlApplicationContext = new FileSystemXmlApplicationContext("c:/java/applicationContext.xml");
- XmlWebApplicationContext
XmlWebApplicationContext wa = new XmlWebApplicationContext()//并没有初始化容器
wa.setServletContext(sevletcontext);//需要指定servletContext对象
wa.setConfigLocation("/applicationContext.xml");//指定配置文件的路径,开头是以web为根目录的路径
wa.refresh();//初始化容器
Bean的实例化方式有两种:
- 基于配置形式
- 基于注解性
基于XML配置方式装配Bean
- 通过无参构造实例化
- 基于静态工厂方法实例化
- 基于普通工厂的方式实例化
通过无参构造实例化
基于静态工厂方法实例化
首先要创建一个Factory类,里面包含静态方法
在applicationContext.xml文件里用静态方法返回对象
基于普通工厂的方式实例化
Factory类
基于注解方式配置Bean
- 首先向配置文件中引入context约束
<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">
</beans>
- 然后在配置文件中开启注解扫描
如果不引入context约束就无法使用context标签
<!--开启注解扫描: base-package(必填)指令包路径: 会扫描类方法、属性上是否有注解-->
<context:component-scan base-package="com.tulun.bean"></context:component-scan>
<!--扫描属性上面的注解(不建议使用)-->
<context:annotation-config></context:annotation-config>
- 在类上添加@Component标签
Spring中主要提供了4个注解来标注Bean
@Component 通用的标注形式,是Spring 提供的通用的组件注解
@Repository 对Dao实现类进行标注
@Service 对Service实现类进行标注
@Controller 对Controller实现类进行标注
后端的业务分层一般业务请求顺序:Controller(进行URL结束和返回)-》Service(业务逻辑)-》Dao(访问数据层)
@Controller、@Service 、@Repository是@Component的衍生,功能一样,是可以互换的,使用不同的注解主要是为了区分被注解的类处于不同的业务层,使逻辑更加清晰,这四个注解主要定义bean,创建bean,使用上是要标注在类上
Spring配置中bean标签属性介绍
DI(Dependency Injection)介绍
DI——Dependency Injection,即“依赖注入”,组件之间依赖关系由容器在运行期决定,即容器动态的将某个依赖关系注入到组件当中。
基于配置形式注入依赖
有参构造函数注入依赖
有参构造函数的依赖注入主要使用标签
name属性:表示类中的属性名
value属性:当前属性名赋值
<!--依赖注入方式1:有参构造-->
<bean id="user" class="com.tulun.bean.User">
<!--id属性的注入-->
<constructor-arg name="id" value="22"></constructor-arg>
<!--name属性的注入-->
<constructor-arg name="name" value="zhangsan"></constructor-arg>
</bean>
set方法注入依赖
set方法注入依赖时,使用property标签
注入的属性可以是基本的类型,也可以是集合类型,也可以是自定义类型
自定义类型
集合类型
基于注解形式注入依赖
- @Value 注入普通形式的依赖
- @Resource:注入对象类型
- @Autowired 注入对象类型
@Value注解
@Resource:注入对象类型
@Autowired 注入对象类型
@Resource 和 @Aoutowire的异同点
在Spring中,@Resource 和 @Aoutowire都是注入依赖的时候使用。
@Resource 和 @Aoutowire是可以替换使用的。
共同点:
@Resource 和 @Aoutowire是可以作为注入属性的修饰,当接口仅有单一实现类时,两个注解效果一样,可互相替换,不影响使用。
不同点:
@Resource 是Java中提供的注解,@Resource存在两个属性,分别为name和type,
在Spring中是通过name来解释bean的名字,而type属性则解析为bean的类型,所以如果使用name属性时,使用byName自动注入策略,而使用type属性是则使用byType自动注入策略,如果不指定name或者type属性时,这是将通过反射机制使用byName 自动注入策略。
@Autowired是Spring 提供的注解,在Spring的2.5版本引入,@Autowired只根据type进行注入,不会去匹配name,如果type无法识别中注入对象是,需要依赖@Qualifier或者是@Primary一起修饰。
SpringAOP介绍
AOP(Aspect Oritented Programing)面向切面编程,扩展功能不需要修改源代码,面向切面主要应用在性能监控,事务管理,安全检查,缓存等等
AOP的设计原理和思想
AOP横向抽象机制 使用动态代理的方式实现
AOP的核心思想是动态代理
pubilc interface UserDao{
public void add();
}
//该接口的真正实现类
pubilc class Userlml implements UserDao{
public void add(){
//业务方法
}
}
//实现一个代理辅助类,实现InvocationHandler接口
public class UserProxy implements InvocationHandler{
@Override
public Object invoke{Object proxy,Method method,Obiect[] args) throws Throwable{
//日志功能
method.invoke(Object,args);
return null;
}
}
使用动态代理的范式来实现日志添加功能。代理类和实现类是平级关系。在有接口的情况下,使用的是JDK动态代理,在没有接口的情况下,使用CGLib的形式进行功能扩展
Java程序的执行流
我们首先要知道程序的执行过程就是调用方法的过程
基于时间序列,将方法的调用排成一条线,每个方法的调用则可以看成是Java执行流找的那一个节点,该节点称之为JoinPoint(连接点)。一个Java程序的运行过程,就是若干个连接点连接起来依次执行的过程
AOP中将每一个方法的调用,即连接点作为编程入口,针对方法的调用进行编程,相当于程序横向切割成若干个面,每个面被则称之为横向切面
AOP是针对方法的编程思路
切面的本质是每一个方法的调用,选择切面实际是选择方法的过程,被选择的切面在AOP术语中被称之为切入点(Point Cut),切入点实际上是所有的连接点(Join Point)挑选自己需要的连接点的过程
在Spring中,AOP是如何来铺货方法的调用呢?使用了代理模式的Java程序执行流
使用代理模式的Java程序执行流
假设在Java代码中,实例对象是通过代理模式创建的代理对象,访问实例对象必须通过代理。
代理模式属于Java中经常用到的,代理对象可以为某些对象提供除了本身功能以外的一些功能。
Spring的代理层可以知道所作的业务的每一次的实例对象的方法调用,Spring就可以在代理过程中插入Spring自己的业务代码
Spring中AOP的工作原理
AOP的编程首先选择自己需要的连接点,AOP通过在代理模式下对某个连接点细化对切入进行编程。
AOP是根据Proxy提供的类型名和方法名确定感兴趣的切入点,返回advice,Proxy得到通知,然后执行通知。
AOP相关术语
- Join point(连接点):所谓连接点是指那些被拦截的点,在Spring中,这些点指的是方法,Spring中只支持方法类型的连接点类的方法被增强,这些方法称之为连接点
- Pointcut(切入点):切入点指的是我们需要对那些方法进行拦截的定义,在类中有很多的方法可以被增强。例:在实际操作过程中,只是增强类的add方法,实际增强的方法称之为切入点
- advice(通知/增强):通知指拦截到JoinPoint之后需要做的事情就是通知。通知分为前置通知,后置通知,异常通知,最终通知,环绕通知
前置通知:在方法之前执行
后置通知:在方法之后执行
异常通知:在方法出现异常时通知
最终通知:在后置之后执行
环绕通知:在方法之前和之后通知 - Aspect(切面):是切入点和通知的结合,被增强应用到切入点的过程
- Introduction(引介):是一种特殊的通知在不修改代码的前提下,引介可以在运行期为类动态的添加方法和属性
- Target(目标对象):代理的目标对象(要增强的类)
- Weaving(织入):是把增强应用到目标的过程,把advice应用到target的过程
- Proxy(代理):一个类被AOP织入增强后,产生的一个结果代理类
AOP结合Aspectj的使用
在SPring实现AOP功能通过Aspect就实现,aspectj是一个基于Java的AOP框架,它不是Spring的一部分,只是和Spring结合在一起完成AOP的操作。
AOP结合Aspectj基于XML配置的实现
引入相关jar包
<!--AOp相关jar-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.1.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.0</version>
</dependency>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
导入AOP相关约束条件
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>
实现类
增强类
在配置文件中配置切入点
在配置中我们要了解的是execution函数,通过execution函数,可以定义切点的方法切入。
切入点:实际增强的方法
表达式
execution(<访问限定符>?<返回类型><方法名>(<参数>)<异常>)
例:
AOP结合Aspect基于注解的方式实现
基于注解的AOP实现方式:在增强类上添加**@Aspect 注解**。在该类中哪一个方法来作为增强,就在方法上添加以下注解
//前置增强
@Before(value = "execution(* com.tulun.bean.Book7.readBook(..))")
@After(value = "")//后置通知
@AfterThrowing//异常通知
@Around(value = "")//环绕通知 需要在方法入参上添加ProceedingJoinPoint参数
@AfterReturning//最终通知
Spring的JdbcTemplate操作
引入JDBC相关jar包
<!--Spring的JDBC模板操作-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.39</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.0.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>4.0.6.RELEASE</version>
</dependency>
JDBCTemplate的使用
/**
* JDBC模板使用的介绍
*/
public class JDBCTemplateTL {
public static void main(String[] args) {
//创建对象,设置数据源信息
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/mybatis");
dataSource.setUser("root");
dataSource.setPassword("123456");
//创建jdbcTemplate对象,设置数据源
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
//调用jdbcTemplateTemplate提供的方法
//更新操作
String updateSql ="update user set sex =? where id = ?";
// jdbcTemplate.update(updateSql,new Object[]{1,1});
//删除操作
String deleteSql="delete from user where id = ?";
// jdbcTemplate.update(deleteSql,new Object[]{35});
//jdbcTemplate 对数据的变更(update\insert \delete)都可以通过update方法进行处理
//查询单个对象 queryForObject
String selectSql1="select * from user where id=? ";
// <T> T queryForObject(String sql, Object[] value, RowMapper<T> var3) throws DataAccessException;
User7 user7 = jdbcTemplate.queryForObject(selectSql1, new Object[]{1}, new UserMapper());
// System.out.println(user7);
//查询多个对象 query
String selectSql2 = "select * from user where sex=?";
List <User7> list = jdbcTemplate.query(selectSql2, new Object[]{1}, new UserMapper());
System.out.println(list);
}
}
Spring对事务的管理
Spring对事务的管理有两种方式,分别是:编程式的事务管理(很少使用)与使用声明式的事务管理
使用声明式的事务管理又分为:基于XML配置形式实现、基于注解实现
基于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:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--配置数据源-->
<bean id="dataSource" class="com.mchange.v2.c3p0.DriverManagerDataSource">
<!--配置连接数据库的核心配置4个参数-->
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="user" value="root"/>
<property name="password" value="123456"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/mybatis"/>
</bean>
<!--<!–基于注解的配置–>-->
<!--<!–第一步:配置事务管理器–>-->
<!--<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">-->
<!--<!–注入数据源DataSource–>-->
<!--<property name="dataSource" ref="dataSource"/>-->
<!--</bean>-->
<!--<!–第二步:配置事务注解:开启事务注解–>-->
<!--<tx:annotation-driven transaction-manager="transactionManager"/>-->
<!--基于XML的配置-->
<!--第一步:配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入数据源DataSource-->
<property name="dataSource" ref="dataSource"/>
</bean>
<!--第二步:配置事务增强-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--做事务处理-->
<tx:attributes>
<!--
设定进行事务操作方法的匹配规则
add*指定增强的方法名 表示add开头的都可以
propagation:事务传播行为
isolation:指定隔离级别。。。
-->
<tx:method name="add*" propagation="REQUIRED" isolation="DEFAULT"/>
</tx:attributes>
</tx:advice>
<!--第三步:配置切面-->
<aop:config>
<!--切入点-->
<aop:pointcut id="pointcut1" expression="execution(* com.tulun.dao.UserDao.*(..))"/>
<!--切面-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut1"/>
</aop:config>
</beans>
基于注解实现
<!--第一步:配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入数据源DataSource-->
<property name="dataSource" ref="dataSource"/>
</bean>
<!--第二步:配置事务注解:开启事务注解-->
<tx:annotation-driven transaction-manager="transactionManager"/>
基于注解形式实现会用到@Transactional标签,@Transactional表示是事务,该注解可以添加在类上或者是方法上,添加在类上即该类的所有方法都使用事务,添加在方法上即当前的方法是支持事务的。
Spring中的设计模式
代理模式(Proxy)
代理模式是为其他对象提供一种代理来控制对这个对象的方法
代理类在运行时创建的代理称之为动态代理
结构示意图:
在Spring中代理类的使用,在AOP的横向切面技术中有使用,在SPring中使用JdkDynamicAopProxy(基于JDK自带的代理模式)和Cglib2AopProxy(CGlib实现的代码)
简单工厂模式(Simple Factory)
简单工程模式又称之为静态工厂方法模式(Static Factory Method),属于创建性模式
• 在简单工厂模式中,可以根据参数的不同返回不同类的实例
• 简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同父类
简单工厂模式包含以下角色:
Factory:工厂角色,是简单工厂模式的核心,负责实现创建所有具体产品类的实例,工厂类可以被外界直接调用,创建所需的产品对象
Product:抽象产品角色,是所有具体产品角色的父类或者接口,负责描述所有实例所共有的公共方法
ConcreteProduct:具体产品角色,继承抽象产品角色,一般为多个,是简单共产模式的创建膜表,工厂返回的都是该角色的某一具体产品。
优点:只需要传入一个正确的参数,就可以后去所需要的对象而无须知道创建的细节
缺点:工厂类的职责相对过重,增加一个产品需要修改工厂类的判断逻辑,违背开闭原则
设计模式原则-开闭原则(Open close Principle)
一个软件实体如类,模块和函数应该对扩展开放,对修改关闭。
Spring中BeanFactory用到了简单的工厂模式,根据传入一个唯一的表示来获取Bean对象
<!--依赖注入方式1:有参构造-->
<bean id="user3" class="com.tulun.bean.User">
<!--id属性的注入-->
<constructor-arg name="id" value="22"></constructor-arg>
<!--name属性的注入-->
<constructor-arg name="name" value="zhangsan"></constructor-arg>
</bean>
<!--依赖注入方式2:set方法-->
<bean id="user4" class="com.tulun.bean.User" >
<!--id属性注入-->
<property name="id" value="12"/>
<!--name属性赋值-->
<property name="name" value="李四"/>
</bean>
工厂方法模式(Factory method)
通过定义工厂父类负责定义创建对象的公共方法,而子类负责生成具体的对象,将类的实例化(具体产品的创建)延迟到工厂类的子类(具体工厂)中完成,由子类工厂决定应该实例化哪一个类。
工厂方法模式中的角色:
抽象产品(Product):具体产品的父类,描述具体产品的公共接口
具体产品(Concrete Product:抽象产品的子类,工厂类创建的目标类,描述具体的产品
抽象工厂(Factory):具体工厂的父类,描述的是具体工厂的公共接口
具体工厂(Concrete Factory) :抽象工厂的子类,被外界调用 ,描述具体工厂,创建产品实例
**优点:**解决了简单工厂的缺点——违背了开放-关闭原则
是因为工厂模式将具体产品的创建推迟到工厂类的子类(具体工厂)中
此时工厂类步子啊负责常见所有的产品,而是给出具体工厂必须实现的接口,工厂方法模式在添加新产品的时候就不需要修改工厂类逻辑而是添加新的工厂子类
解决了简单工厂的缺点:违背了开放-关闭原则
是因为工厂模式将具体产品的创建推迟到工厂类的子类(具体工厂)中,此时工厂类的子类负责常见所有的产品,给出具体工厂必须实现的接口,工厂方法模式在添加新产品的时候就不需要修改工厂类逻辑而是添加新的工厂子类
缺点:每新增一个产品,除了新增产品类外,还需要提供与之对应的具体工厂类,系统中类个数将是成对出现,增加系统的复杂性:每新增一个产品,除了新增产品类外,还需要提供与之对应的具体工厂类,系统中类个数将是成对出现,增加系统的复杂性。
Spring中使用工厂方法实例化bean
public class Factory {
//创建User对象的静态方法
public static User getUserBean() {
return new User();
}
}
在配置文件中,将其纳入Spring容器管理,需要通过factory-method指定静态方法名称
<!--通过静态工厂方法创建对象,直接使用class来执行静态类,Factory-method指定方法就行-->
<bean id="user1" class="com.tulun.Factory" factory-method="getUserBean"/>
单例模式
- 单例类只能有一个实例
- 单例类必须自己创建自己的唯一实例
- 单例类必须给其他对象提供这一唯一实例
- 保证类的对象在内存中的唯一性
/**
* 单例实现-饿汉式
* 为什么方法是静态的:
* 不能new对象却想调用类中方法,方法必然是静态的,
* 静态方法只能调用静态成员,所以对象也是静态的。
*
* 为什么对象的访问修饰符是private,不能是public 吗?
* 不能,如果访问修饰符是Public,则Single.s也可以得到该类对象,
* 这样就造成了不可控。
*
* 加载类的什么周期有关,static修饰的静态变量,类,在类加载时已经被初始化了,
* getInstance是在使用阶段操作,安全是有类加载机制保证
*
* 特点:线程安全,无法实现实例懒加载策略。
*/
public class Single1 {
private static final Single1 s=new Single1();
private Single1(){}
public static Single1 getInstance(){
return s;
}
public static void main(String[] args) {
//类加载过程
//7步 static -使用-销毁对象
Single1 single1 = Single1.getInstance();
}
}
/**
* 单例模式-懒汉式
*
* 懒汉式和饿汉式相比的区别就是懒汉式创建了延迟对象同时饿汉式的实例对象是被修饰为final类型。
*
* 优点:懒汉式的好处是显而易见的,它尽最大可能节省了内存空间。
* 缺点:在多线程编程中,使用懒汉式可能会造成类的对象在内存中不唯一,
* 虽然通过修改代码可以改正这些问题,但是效率却又降低了。
*
* 总结:
* 懒汉式在面试的时候经常会被提到,因为知识点比较多,而且还可以和多线程结合起来综合考量。
* 饿汉式在实际开发中使用的比较多。
*
* 特点:线程不安全,实现了实例懒加载策略。
*/
public class Single2 {
private static Single2 s = null;
private Single2() { }
public static Single2 getInstance() {
//两个线程同时进行if (s == null)判断,则都会进入if条件吗,就会创建对个实例
if (s == null)
s = new Single2();
return s;
}
}
/**
* 单例模式-全局锁式
*
* 特点:线程安全,且实现了懒加载策略,但是线程同步时效率不高(synchronized)。
*/
public class Single3 {
private static Single3 single3;
private Single3() {}
//synchronized修饰的是静态方法,锁类对象
public synchronized static Single3 getInstance() {
if (single3 == null)
single3 = new Single3();
return single3;
}
}
/**
* 静态代码块式
*
* 特点:线程安全,类主动加载时才初始化实例,实现了懒加载策略,且线程安全。
*/
public class Single4 {
private final static Single4 singleton4;
private Single4() { }
static {
singleton4 = new Single4();
}
public static Single4 getInstance() {
//使用之前将singleton4属性通过静态代码块实现
return singleton4;
}
public static void main(String[] args) {
//第一次调用Single4,JVM需要负责将Single4加载到内存中,在加载的过程处理静态代码块
Single4.getInstance();
//第二次调用Single4,JVM中已经存在Single4,直接使用getInstance
Single4.getInstance();
}
}
/**
* 单例模式-双重校验锁式
* 特点:线程安全,且实现了懒加载策略,同时保证了线程同步时的效率。
* 但是volatile强制当前线程每次读操作进行时,保证所有其他的线程的写操作已完成。
* volatile使得JVM内部的编译器舍弃了编译时优化,对于性能有一定的影响。
*/
public class Single5 {
private static volatile Single5 singleton5;
private Single5() {}
public static Single5 getInstance() {
if (singleton5 == null) {
synchronized (Single5.class) {
if (singleton5 == null) {
//内层if判断使用的时间(起作用时机)
//第一次两线程同时调用getInstance,都会进入外层if判断
//内层if判断是针对第二个进入synchronized代码块线程,此时第一个线程已经创建出对象
//第二个线程无需创建
singleton5 = new Single5();
}
}
}
return singleton5;
}
}
/**
* 单例模式-静态内部类式【推荐】静态代码块的思路是一样的
* 特点:线程安全,不存在线程同步问题,
* 且单例对象在程序第一次 getInstance() 时主动加载 SingletonHolder 和其 静态成员 INSTANCE,
* 因而实现了懒加载策略。
*/
public class Single6 {
private Single6() {}
private static class SingletonHolder {
private static final Single6 INSTANCE = new Single6();
}
public static Single6 getInstance() {
return Single6.SingletonHolder.INSTANCE;
}
}
/**
* 单例模式-枚举方式
*
* 特点:线程安全,不存在线程同步问题,且单例对象在枚举类型 INSTANCE
* 第一次引用时通过枚举的 构造函数 初始化,因而实现了懒加载策略。
*
* 这种方式是Effective Java作者 Josh Bloch 提倡的方式,
* 它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象,
* 可谓是很坚强的壁垒啊。不过,由于JDK 1.5中才加入enum特性
*/
public class Single7 {
private Single7() {}
enum SingletonEnum {
INSTANCE;
private final Single7 singleton7;
private SingletonEnum() {
singleton7 = new Single7();
}
}
public static Single7 getInstance() {
return SingletonEnum.INSTANCE.singleton7;
}
}
适配器模式
适配器模式分为两种:类适配器和对象适配器
类适配器模式使用继承的方式
对象适配器模式使用的组合模式
适配器模式中存在3个角色:
目标角色:客户所期望得到的接口
源角色:需要进行适配的接口
适配器角色:模式的核心,适配器将源接口转换成目标角色
适配器模式的优缺点:
优点:
1、有更好的复用性,系统直接使用现有的类,通过对现有的类适配就可以达到更好的复用
2、有更好的扩展性,实现适配器,可以调用自己开发的功能
缺点:
过多的使用适配器会是系统比较凌乱,明明调用A接口,内部却适配成了B接口
在Java中最主要的使用就是IO流中
Spring中用到的适配器模式是使用advice来增强代理类的功能
主要研究而适配器在IO流中的使用,字节输入/输出流、字符输入/输出流
策略模式
一个类的行为或它的方法可以在运行时更改,这种类型的设计模式属于行为型模式。
策略模式中有三个角色
抽象策略类(Strategy):定义了公共接口,各种不同的算法以不同的方式实现这个接口,环境角色使用这个接口调用不同的算法,一般使用接口或者抽象类实现
具体策略类(Concrete Strategy):实现抽象策略定义的接口,提供具体的算法实现
环境类(Context):持有一个策略类的应用,最终给客户端调用
在Spring中的使用
不同资源文件即是具体的策略类,ApplicationContext是抽象策略类(环境类),通过实例化的队形,可以直接通过getBean来获取IOC管理的对象实例,而屏蔽了对象实例的位置。