Spring介绍

学习目录

Spring的介绍
Spring的IOC介绍(控制反转)
Spring的DI介绍(依赖注入)
Spring的AOP介绍(横向切面技术)(->动态代码)
低侵入式的扩展新的功能
Spring和Mybatis的整合介绍 (SSM)
Spring中的JDBCTemplate
Spring中的事务配置
Spring中的设计模式
参考书籍
《Spring实战》
Spring的介绍
Spring是一个企业级的Java应用开发的框架
Spring是一个开源框架,轻量级,基础版本只有2M大小
Spring主要使用开发Java应用程序,该框架的目标是使开发的应用更加容易使用
官网:https://spring.io/
Spring的优势:
简化Java的开发
基于轻量级和最小侵入式开发
通过依赖注入和面向接口实现松耦合
基于切面进行声明式开发
通过切面和模板减少样式代码
Spring的体系结构
在这里插入图片描述Spring框架中主要包含核心容器、数据访问/集成、Web模块,其他的AOP,测试模块等
核心容器
Spring的核心容器是有Spring-core、Spring-Beans\Spring-context\Spring-expression等模块组成
Spring-core:是Spring的基础组成部分,包括IOC和DI等功能
Spring-beans:提供Beanfactory,容器工厂,管理和创建对象
Spring-context:是在core和beans的基础上建立,类似于JDNI的的方式来访问对象
Spring-expression:是Spring的表达式语言
数据访问和集成
数据访问集成层包括JDBC、ORM、OXM、JMS和事务处理模块
WEB页面
WEB主要提供和页面相关的的模块组成

Spring的IOC介绍

Spring的IOC的思想
Spring的核心思想之一:IOC(Inversion Of Control)控制反转
控制反转表达的意思是什么?
对象的创建交给外部的容器完成,这个就是控制反转
SPring使用控制反转来实现对象的使用不用在程序中写死
控制反转解决对象处理问题【把对象交给别人来创建】
那么对象和对象之间的依赖关系Spring中的是如何处理的?-》依赖注入(Dependency injection)
Spring中使用依赖注入来实现对象之间的依赖关系
在创建对象之后。对象的关系处理就是依赖注入
控制反转是通过外部容器完成的,而Spring中提供了一个容器,称该容器为:IOC容器
无论对象的创建,处理对象之间的依赖关系,对象创建的时间,对象创建的数量,都是在SPring索提供的IOC的容器上来配置对象的信息
IOC的思想的核心在于:资源不在是使用资源方来管理,而是将资源交给第三方管理(IOC容器),这样来处理带来很多好处:
• 资源的集中管理,实现资源的可配置和易管理
• 降低了使用资源双方的依赖程度,也就是耦合度问题
Spring 容器管理对象
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最核心的相关依赖
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.java)


public class User {
    private String id;
    private String name;

    //getter和setter方法省略
}

IOC容器管理对象
以前我们通过哦new User的方法创建对象
User user = new User();
现在使用IOC的容器。可以让IOC容器帮创建对象,在applicationContext.xml文件中添加User的对应信息

    <!--使用bean节点来创建对象 id属性表示对象 class属性表示要交给容器管理的对象的全路径-->
    <bean id="user"  class="com.tulun.bean.User"/>

通过容器来获取User对象

        //获取IOC的容器
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        //通过容器来获取当前的对象
        User user = (User) applicationContext.getBean("user");
        System.out.println(user);

执行的结果:
在这里插入图片描述通过日志可知,对象已经被创建出来了
问:Spring 容器如何获取到对象的呢?

Spring的IOC容器介绍

Spring中的core模块:IOC容器,解决对象的创建和对象之间的依赖关系
如何得到IOC容器?
在这里插入图片描述这个是Spring IOC容器中接口的继承关系,其中ApplicationContext是BeanFactory的子接口之一,换句话说,
Beanfactory是IOC容器中最底层的接口,而ApplicationContext四其高级接口之一,在于对BeanFactory做了许多的功能上的扩展。在大部分的应用场景下,都是使用ApplicationConext作为Spring IOC的容器
ApplicationContext的常见的实现类
• ClassPathXmlApplicationContext

        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

读取classpath的资源文件使用该类
• 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();//初始化容器
    

在Web环境下读取配置信息
在这里插入图片描述

Bean的实例化方式

Spring容器中配置Bean的方式主要有两种
基于配置形式
基于注解性
基于XML配置方式装配Bean
bean基于配置方式实例化有三种形式
• 通过无参构造实例化
• 基于静态工厂方法实例化
• 基于普通工厂的方式实例化
基于无参构造方式
上面的demo就是基于无参构造来实例化的对象

    <!--使用bean节点来创建对象 id属性表示对象 class属性表示要交给容器管理的对象的全路径-->
    <bean id="user"  class="com.tulun.bean.User"/>

注意:如果不指定构造函数,系统会生成默认的无参构造函数
如果指定有有参构造函数,必须显性的指定一个无参构造函数,否则实例化对象会抛出异常
基于静态工厂方法实例化
首先使用一个工厂的静态方法返回一个对象

public class Factory {
    //创建User对象的静态方法
    public static User getUserBean() {
        return new User();
    }
}

在配置文件中使用静态方法返回对象

   <bean id="user1" class="com.tulun.Factory" factory-method="getUserBean"/>

基于普通工厂方式实例化
通过工厂的非静态方法得到当前的一个对象

public class Factory {
    //创建User对象的普工方法
    public User getUserBean1() {
        return new User();
    }
}

配置文件中使用工厂的非静态方法返回对象

    <!--通过非静态工厂访问对象-->
    <!--首先创建工厂对象-->
    <bean id="factory" class="com.tulun.Factory"/>
    
    <!--指定工厂对象和工厂方法-->
    <bean id="user2" class="com.tulun.bean.User" factory-bean="factory" factory-method="getUserBean1"/>

涉及代码:
在这里插入图片描述在这里插入图片描述基于注解方式装配Bean
基于注解的形式要比xml配置文件的方式更加简洁
• Spring配置问价引入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>

• Spring 的配置文件中开启注解的扫描

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

<!--扫描属性上面的注解(不建议使用)-->
<context:annotation-config></context:annotation-config>

注:必须要引入context约束,否则无法出现context标签
• 在类上添加@Component注解

@Component(value = "user")
//    <bean id="user"  class="com.tulun.bean.User"/> 等同于在配置中配置bean
public class User {
    private String id;
    private String name;
}

使用注解形式来获取对象实例
在这里插入图片描述Spring中主要提供了4个注解来标注Bean
@Component 通用的标注形式
@Repository 对Dao实现类进行标注
@Service 对Service实现类进行标注
@Controller 对Controller实现类进行标注
后端的业务分层一般业务请求顺序:Controller(进行URL结束和返回)-》Service(业务逻辑)-》Dao(访问数据层)
@Component是Spring 提供的通用的组件注解
@Controller、@Service 、@Repository是@Component的衍生,功能一样,是可以互换的
使用不同的注解主要是为了区分被注解的类处于不同的业务层,使逻辑更加清晰
这四个注解主要定义bean,创建bean,使用上是要标注在类上,@Component(value=“user”)或者@Component(“user”)
Spring配置中bean标签属性介绍
id属性:给当前的对象取名字,可以使用特殊字符 (/*)
name属性:给当前对象取名字,不可以使用特殊字符
calss属性:表示要交给容器管理的对象的全路径
scope属性:表示当前的对象是单例还是多例 singleton:单例 prototype:多例 注解形式:@Scope("")
在这里插入图片描述Spring 中DI介绍
DI-Dependency Injection,即“依赖注入”,组件之间依赖关系由容器在运行期决定,即容器动态的将某个依赖关系注入到组件当中
DI理解的关键点“谁依赖谁,为什么需要依赖”
谁依赖谁:应用程序依赖IOC容器
为什么需要以依赖:应用程序需要IOC容器来提供对象需要的外部资源
注入什么:注入某个对象所依赖的外部资源
基于配置的形式注入依赖
有参构造函数注入依赖

    <!--依赖注入方式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>

有参构造函数的依赖注入主要使用标签 name属性:表示类中的属性名 value属性:当前属性名赋值
在类中必须包含有参构造函数:且和构造函数保持参数一致
在这里插入图片描述set方法注入依赖

 <!--依赖注入方式2:set方法-->
    <bean id="user4" class="com.tulun.bean.User" >
        <!--id属性注入-->
        <property name="id" value="12"/>
        <!--name属性赋值-->
        <property name="name" value="李四"/>
    </bean>

set方法注入依赖时,使用property标签 name属性表示类中的属性名 value属性表示当前属性的赋值
在这里插入图片描述注入的属性可以是基本的类型,也可以是集合类型,也可以是自定义类型

    <!--对象的注入-->
    <!--Person类中注入自定义的User-->
    <bean id="user5" class="com.tulun.bean.User"/>
    <bean id="person" class="com.tulun.bean.Person" >
        <!--user属性通过set方式注入-->
        <!--value:会按照String类型的字符串做解析 ref:Spring中管理对象的ID名-->
        <property name="user" ref="user5"/>
    </bean>

集合类型
注入List类型的依赖

<bean id="person1" class="com.tulun.bean.Person">
    <property name="list" >
        <list>
            <value>hello</value>
            <value>你好</value>
            <value>1234</value>
        </list>
    </property>
</bean>

其他形式的集合配置

   <!--数组的注入-->
    <bean id="person2" class="com.tulun.bean.Person">
        <property name="list" >
            <!--array标签:注入数组-->
            <array>
                <!--value是直接给定值-->
                <value></value>
            </array>
        </property>
    </bean>
    <!--set的注入-->
    <bean id="person3" class="com.tulun.bean.Person">
        <property name="list" >
            <!--set标签:注入set类型的集合-->
            <set>
                
            </set>
        </property>
    </bean>
    
    <!--map的注入-->
    <bean id="person4" class="com.tulun.bean.Person">
        <property name="map" >
            <!--map标签:注入Map类型的集合-->
            <map>
                <entry key="1" value="zhangsan"/>
                
            </map>
        </property>
    </bean>

基于注解的形式注入依赖
• @Value 注入普通形式的依赖
• @Resource:注入对象类型
• @Autowired 注入对象类型
@Value注解

@Component(value = "user")
public class User {
    @Value("1")
    private String id;
    
    @Value("张三")
    private String name;
}

注:使用Value注解注入依赖前提是Bean的装配支持注解扫描
@Resource注解
@Autowired注解

@Component(value = "person")
public class Person {
    //自定义类型属性
    //和@Resource功能一样
    @Autowired
    private User user;

}

@Resource 和 @Aoutowire的异同点
在Spring中,@Resource 和 @Aoutowire都是做Bean的注入时使用,@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注解一起修饰
依赖的解析过程
容器解析Bean的依赖过程:
• ApplicationContext是通过配置元数据来创建个初始化的,这些元数据描述了所有的bean,描述元数据可以通过XML,注解或者Java代码来指定
• 对于每一个bean,他的依赖注解,构造方法参数后者静态工厂方法参数的形式来表达。bean被创建后之后这些依赖会被提供给他
• 每一个属性或者构造方法的参数都要设定的值的实际定义,或者对容器内另一个bean的引用
容器在创建的时候,Spring容器会验证每一个bean的配置,在实际创建bean之前,bean的属性是不会被设置的。bean只会在需要他的时候被创建,创建bean的过程找那个可能会引起一些列的bean被创建,列如bean的依赖,其依赖的依赖等等会被一起创建和分配。注意:依赖之间解析不匹配可能会出现,即首循环创建有影响的bean。
循环依赖的问题

class User{
private Person person;
}
class Person{
private User user;
}

如果是通过构造方法的方式注入,就有可能造成无法解决的循环依赖
例如:ClassA需要通过构造方法注入一个ClassB的实例,classB同样需要通过构造方法注入ClassA的实例,
如果为ClassA和ClassB配置bean并且相互注入,IOC容器在运行时会发现这个循环引用,抛出异常:BeanCurrentlyInCreationException
解决循环依赖的途径需要去编辑代码,让这些类通过setter方法注入,避免使用构造方法注入
Spring AOP介绍
AOP(Aspect Oritented Programing)面向切面编程,扩展功能不需要修改源代码
面向切面主要应用在性能监控,事务管理,安全检查,缓存等等
AOP的设计原理和思想
AOP 横向抽象技术的介绍

public class User{
//添加用户方法
public void add(){
//业务逻辑
}
}

//扩展功能
添加日志功能,记录在什么时候添加了那个用户
纵向抽取机制解决

public class BaseUser{
//创建打印日志功能
public  void writeLog{
//添加日志的逻辑
}
}
public class User extends BaseUser{
public void add() {
//用户添加逻辑
//扩展功能,添加日志操作
// 调用已存在的日志打印的功能
super.writeLog();
}
}

问题:父类的中方法名发生改变,在子类中的方法名也需要改变
AOP横向抽象机制
底层使用:使用的动态代理的方式实现
第一种方式:
public interface UserDao{
public void add();
}
该接口的真正实现类

public class UserIml implements UserDao() {
public void add() {
//业务方法
}
}

实现一个代理辅助类,实现InvocationHandler接口

public class UserProxy implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //日志功能添
        method.invoke(object,args);
        return null;
    }
}

使用动态代理的范式来实现日志添加功能,代理类和实现类是平级关系。在有接口情况下,使用的是JDK的动态代理,在没有接口情况下,使用CGLib的形式进行功能扩展。
AOP的实现核心思想:动态代理

Java程序的执行流
程序运行过程就是方法的调用的过程
在这里插入图片描述基于时间序列,将方法的调用排成一条线,每个方法的调用则可以看成是Java执行流找那个的一个节点,在AOP的术语中,节点称之为JoinPoint(连接点)。一个Java的程序运行的过程。就是若干个连接点连接起来依次执行的过程。
AOP中将每一个方法的调用,即连接点作为编程的入口,针对方法调用进行编程,相当于程序横向切割成若干个面,每个面被称之为横向切面。
AOP是针对方法调用的编程思路
在这里插入图片描述AOP的针对切面进行编程的,需要选择那些切面(连接点)作为编程的对象呢
切面的本质是每一个方法的调用,选择切面实际是选择方法的过程,被选择的切面在AOP术语中被称之为切入点(Point Cut),切入点实际上是从所有的连接点(Join Point)挑选自己需要的连接点的过程
在这里插入图片描述AOP是针对的是方法的调用(连接点)的编程,如何选择自己需要的连接点–切入点?
在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框架
aspectj不是Spring的一部分,只是和Spring结合在一起完成AOP的操作
基于XML配置的方式实现
基于注解的形式实现
AOP结合Aspectj基于XML的实现方式
引入AOP需要的相关jar包
在Spring的基本jar包基础上,需要AOP相关的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>

实现类

//基础类
public class Book {
    //方法1:写方法
    public void  writeBook() {
        //业务逻辑
        System.out.println("writeBook");
    }

    //读方法
    public void  readBook() {
        //业务逻辑
        System.out.println("readBook");
    }
}

增强类

/**
 * 增强类
 * 完成添加日志
 */
public class DiyLog {
   
    public void log() {
        //业务逻辑
        System.out.println("打印日志");
    }
}

使用表达式来配置切入点
execution函数介绍
在通知中定义切点,通过execution函数,可以定义切点的方法切入
切入点:实际增强的方法
常用的表达式
execution(<访问限定符>?<返回类型><方法名>(<参数>)<异常>)
execution(* com.tulun.bean.Book.writeBook(…)) 表示类Book里面的writeBook的方法
execution(* com.tulun.bean.Book.(…)) 表示类Book下的所有的方法
execution(
.(…)) 表示所有的
例:
匹配所有的public方法 execution(public .(…))
匹配指定包路径下所有类的方法 execution(* com.tulun.bean.(…)) (不包含子包)
匹配指定包路径下所有类的方法 execution(
com.tulun.bean…(…))(包含包,子包的所有类)
匹配指定类的所有方法 execution(
com.tulun.bean.Book.(…))
匹配特定开头的方法 execution(
com*(…))
配置切入点

    <!--将基本对象交给spring管理-->
    <bean id="book" class="com.tulun.bean.Book7"/>
    <bean id="diyLog" class="com.tulun.bean.DiyLog"/>

    <!--配置AOP操作-->
    <aop:config>
        <!--配置切入点:使用表达式,在实际的方法上添加通知的方法称之为切入点-->
        <aop:pointcut id="pointcut1" expression="execution(* com.tulun.bean.Book7.writeBook(..))"/>

        <!--配置切面:把增强用到方法-->
        <aop:aspect ref="diyLog">
            <!--配置增强类型   method:增强类中的那个方法作为前置增强-->
            <aop:before method="log" pointcut-ref="pointcut1"/>
            <!--
            aop:before:前置通知
            aop:after 后置通知
            aop:after-returning:最终通知
            aop:after-throwing:异常通知
            aop:around:环绕通知
            -->
        </aop:aspect>
    </aop:config>

使用

    public static void main(String[] args) {
        //获取IOC的容器
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext2.xml");
        //通过容器来获取当前的对象(通过无参构造来实例对象方法)


        //获取book对象
        //验证前置通知
        Book7 book = (Book7) applicationContext.getBean("book");
        book.writeBook();
    }

注意:环绕通知

    //环绕通知  需要通过ProceedingJoinPoint类型的参数指定增强方法执行的时机
    public void round(ProceedingJoinPoint joinPoint) throws Throwable{
        System.out.println("方法执行前");
        //执行增强的方法
        joinPoint.proceed();
        System.out.println("方法执行之后");
    }

在这里插入图片描述问题:将基础类Book的所有方法添加前置通知?
execution(* com.tulun.bean.Book7.*(…)

AOP结合Aspect基于注解的方式实现

Bean对象装配
将接触类和增强类交给IOC容器管理

<bean id="book" class="com.tulun.bean.Book7"/>
    <bean id="diyLog" class="com.tulun.bean.DiyLog"/>

在Spring的配置文件中,开启AOP操作

    <!--开启AOP的操作-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

在增强类上添加注解

@Aspect
public class DiyLog {

    //前置增强
    @Before(value = "execution(* com.tulun.bean.Book7.readBook(..))")
    //<aop:before method="round" pointcut-ref="pointcut1"/>
    public void log() {
        //业务逻辑
        System.out.println("打印日志");
    }
}

基于注解的AOP实现方式:在增强类上添加@Aspect 注解。在该类中哪一个方法来作为增强,就在方法上添加以下注解
@Before(value = “execution(* com.tulun.bean.Book7.readBook(…))”) //前置通知
@After(value = “”) //后置通知
@AfterThrowing //异常通知
@Around(value = “”)//环绕通知 ,需要在方法入参上添加ProceedingJoinPoint参数
@AfterReturning //最终通知
Spring的JdbcTemplate操作
jdbcTemplate是Spring提供的操作数据库的操作,是基于JDBC实现的
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结合连接池(C3p0)和jdbcTemplate使用
场景:操作mybatis库中的user表,
通过ID查询用户信息
dao层接口

public class UserDao {
    private JdbcTemplate jdbcTemplate;

    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    public User7 getUserById(Long id){
        //查询SQL
        String sql = "select * from user where id = ?";
        User7 user = jdbcTemplate.queryForObject(sql, new Object[]{id}, new UserMapper());
        //使用jdbctemplate模板来获取数据库数据
        return user;
    }
}

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"
       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">


    <!--配置数据源-->
    <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>

    <!--配置jdbc模板-->
    <bean id="jdbcTenplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <!--配置数据源-->
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--配置UserDao-->
    <bean id="userDao" class="com.tulun.dao.UserDao">
        <!--注入JDBC模板-->
        <property name="jdbcTemplate" ref="jdbcTenplate"/>
    </bean>

</beans>

Spring 和Mybatis的整合使用

回顾mybatis:
创建mybatis的全局配置文件,配置数据源信息,配置映射
通过读取mybatis的全局配置文件创建sqlSessionFactory实例(会话工厂是需要创建一份就可以了)
通过会话工厂创建会话,SQLSession。
整合思想
Spring通过单例方式管理SQLSessionFactory
spring和mybatis整合生成代理对象,使用SQLSessionFactory创建SQLSession,整合自动完成
持久侧的Mapper都需要交给Spring管理
在mybatis中,SQLSessionFactory由SQLSessionFactoryBuilder来创建
在mybatis和spring整合古城中,使用SQLSessionFactoryBean来替代,SQLSessionFactoryBean有一个必须的属性DataSource,另外还需要一个通用的属性configLocation(用来指定mybatis的xml配置文件)
整合步骤
第一步:引入spring-mybatis的整合jar
引入spring的依赖,引入mybatis的依赖
还需要mybatis提供的一个专门整合spring的jar:mybatis-spring

        <!--mybatis和spring整合jar-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.3.0</version>
        </dependency>

第二步:创建bean类,mapper.xml和mapper.java文件
省略
注意:mybatis的全局配置文件中的数据源以及mapper映射放入到spring中

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>

    <!--数据源   在spring整合后,将数据源放在spring的配置文件中-->
    <!--<environments default="">-->
    <!--<environment id="">-->
    <!--<transactionManager type=""></transactionManager>-->
    <!--<dataSource type=""></dataSource>-->
    <!--</environment>-->
    <!--</environments>-->

    <!--mapper  在和spring整合后,可以放在spring配置文件中-->
    <!--<mappers>-->
    <!--<mapper resource=""/>-->
    <!--</mappers>-->
</configuration>

第三步:整合SQLSessionFactory创建

     <!--配置数据源-->
    <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>

    <!--配置SQLSessionFactory-->
    <bean id="sqlSessionFactory"  class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--配置数据源-->
        <property name="dataSource" ref="dataSource"/>

        <!--加载mybatis的配置-->
        <property name="configLocation" value="mybatis/mybatis.xml"/>

        <!--配置xml文件的映射,在mybatis的中,在<mapper>标签下添加的-->
        <property name="mapperLocations">
            <list>
                <value>mapper/User7Mapper.xml</value>
            </list>
        </property>

    </bean>

在这里插入图片描述第四步:通过代理对象操作

    <!--
    通过代理对象进行mapper的映射
    class即mybatis-spring包提供的MapperFactoryBean
    -->
    <bean id="userMapper"  class="org.mybatis.spring.mapper.MapperFactoryBean">
        <!--mapperInterface指定mapperjie接口-->
        <property name="mapperInterface" value="com.tulun.dao.User7Mapper"/>
        
        <!--指定SQLSessionFactory-->
        <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>

Spring的事务管理

Spring中事务相关的概念
什么是事务:
事务的特征:ACID -》原子性、一致性、隔离性、持久性
隔离不当引起的脏数据问题:脏读、不可重复读、幻读
隔离级别不同:
Spring中的事务管理
事务管理的API接口
PlatformTransactionManager 事务管理器
TransactionDefinition:事务定义信息(隔离,传播、超时、只读)
TransactionStatus:事务具体运行状态
参考资料:
https://baijiahao.baidu.com/s?id=1624322378090716314&wfr=spider&for=pc
Spring对事务的管理
两种方式:
• 编程式的事务管理(很少使用)
• 使用声明式的事务管理
-基于XML配置形式实现
-基于注解实现
基于xml形式的事务管理
配置文件的思想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"
       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: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
public class UserDao {
    private JdbcTemplate jdbcTemplate;

    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    @Transactional
    public User7 getUserById(Long id){
        //查询SQL
        String sql = "select * from user where id = ?";
        User7 user = jdbcTemplate.queryForObject(sql, new Object[]{id}, new UserMapper());
        //使用jdbctemplate模板来获取数据库数据
        return user;
    }
}

@Transactional表示是事务,该注解可以添加在类上或者是方法上,添加在类上即该类的所有方法都使用事务,添加在方法上即当前的方法是支持事务的

Spring中涉及的设计模式

Spring中涉及的设计模式主要有:
• 简单工厂模式
• 工厂方法模式
• 单例模式
• 适配器模式
• 包装类模式
• 代理模式
• 观察者模式
• 策略模式
• 模板模式
代理模式(Proxy)
为其他对象提供一种代理以控制对这个对象的方法
代理类在运行时创建的代理称之为动态代理
在这里插入图片描述
在Spring中代理类的使用,在AOP的横向切面技术中有使用,在SPring中使用JdkDynamicAopProxy(基于JDK自带的代理模式)和Cglib2AopProxy(CGlib实现的代码)

简单工厂模式(Simple Factory)

简单工厂介绍
简单工程模式又称之为静态工厂方法模式(Static Factory Method),属于创建性模式
• 在简单工厂模式中,可以根据参数的不同返回不同类的实例
• 简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同父类
在这里插入图片描述简单工厂模式包含以下角色:
Factory:工厂角色
Product:抽象产品角色
ConcreteProduct:具体产品角色
工厂角色:是简单工厂模式的核心,负责实现创建所有具体产品类的实例,工厂类可以被外界直接调用,创建所需的产品对象
抽象产品角色:是所有具体产品角色的父类或者接口,负责描述所有实例所共有的公共方法
具体产品角色:继承自抽象产品角色,一般为多个,是简单共产模式的创建膜表,工厂返回的都是该角色的某一具体产品
优缺点:
优点:只需要传入一个正确的参数,就可以后去所需要的对象而无须知道创建的细节
缺点:工厂类的职责相对过重,增加一个产品需要修改工厂类的判断逻辑,违背开闭原则
设计模式原则-开闭原则(Open close Principle)
一个软件实体如类,模块和函数应该对扩展开放,对修改关闭

代码演示

/**
 * 抽象产品角色:父类、接口
 */
public abstract class Video {
    public abstract void produce();
}


/**
 * 具体产品角色
 */
public class JavaVideo extends Video {
    @Override
    public void produce() {
        System.out.println("java 课程视频");
    }
}

/**
 * 具体产品类型
 * 特点:必须继承父类,重写方法
 *  实现接口提供方法
 */
public class CVideo extends Video{
    @Override
    public void produce() {
        System.out.println("C 课程视频");
    }
}

/**
 * 工厂角色
 */
public class VideoFactory {
    //注意:工厂方法的返回类型,是video
    public Video getVideo(String videoType){
        if ("java".equalsIgnoreCase(videoType)){
            return new JavaVideo();
        } else if ("c".equalsIgnoreCase(videoType)) {
            return new CVideo();
        }
        return null;
    }

    public Video getVideo(Class clazz){
        Video video = null;
        try {
            video = (Video) clazz.forName(clazz.getName()).newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        return video;
    }
}

/**
 * 测试类
 */
public class TestMain {
    public static void main(String[] args) {
        VideoFactory factory = new VideoFactory();
        Video video = factory.getVideo("c");
        //JavaVideo的具体产品实现类
        video.produce();
    }
}

Spring中的使用案例
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>

Java代码的调用

        //获取IOC的容器
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("ApplicationContext1.xml");
        User user1 = (User) applicationContext.getBean("user");

工厂方法模式(Factory Method)

工厂方法模式介绍
通过定义工厂父类负责定义创建对象的公共方法,而子类负责生成具体的对象,将类的实例化(具体产品的创建)延迟到工厂类的子类(具体工厂)中完成
由子类工厂决定应该实例化哪一个类
在这里插入图片描述工厂方法模式中的角色:
抽象产品(Product):具体产品的父类,描述具体产品的公共接口
具体产品(Concrete Product:抽象产品的子类,工厂类创建的目标类,描述具体的产品
抽象工厂(Factory):具体工厂的父类,描述的是具体工厂的公共接口
具体工厂(Concrete Factory) :抽象工厂的子类,被外界调用 ,描述具体工厂,创建产品实例
解决了简单工厂的缺点:违背了开放-关闭原则
是因为工厂模式将具体产品的创建推迟到工厂类的子类(具体工厂)中
此时工厂类步子啊负责常见所有的产品,而是给出具体工厂必须实现的接口,工厂方法模式在添加新产品的时候就不需要修改工厂类逻辑而是添加新的工厂子类
缺点:每新增一个产品,除了新增产品类外,还需要提供与之对应的具体工厂类,系统中类个数将是成对出现,增加系统的复杂性
代码演示

/**
 * 创建产品类,定义具体产品的公共接口
 */
public abstract class Video {
    public abstract void produce();
}


/**
 * 抽象工厂类:定义具体工厂的公共接口
 */
public abstract class Factory {
    //子实现类来创建具体产品
    public abstract Video structVideo();
}

/**
 *具体产品-JAVA视频类
 */
public class JavaVideo extends Video{
    @Override
    public void produce() {
        System.out.println("Java 视频课程");
    }
}

/**
 * 具体工厂-创建不同具体产品类的实例
 */
public class JavaFactory extends Factory{
    @Override
    public Video structVideo() {
        return new JavaVideo();
    }
}

/**
 *具体产品-C视频类
 */
public class CVideo extends Video {
    @Override
    public void produce() {
        System.out.println("C 课程视频");
    }
}

/**
 * 具体工厂类-创建不同具体产品类的实例
 */
public class CFactory extends Factory {
    @Override
    public Video structVideo() {
        return new CVideo();
    }
}

/**
 * 测试工厂方法类
 */
public class TestFactoryMethod {
    public static void main(String[] args) {
        //看Java课程视频
        JavaFactory javaFactory = new JavaFactory();
        Video video = javaFactory.structVideo();
        video.produce();

        //看C课程视频
        CFactory cFactory = new CFactory();
        cFactory.structVideo().produce();
    }
}

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;
    }
}

Spring中使用案例

Spring中默认的bean均是singleton,可以通过scope来定义
在SPring中的类AbstractBeanFactory中doGetBean中

适配器模式(Adapter Pattern)

适配器模式的介绍
将一个接口转化为客户所需要的另一个接口,使接口不兼容的一些类可以一起工作,也称为为包装类(Wrapper)
适配器模式分为两种:类适配器和对象适配器
类适配器模式使用继承的方式
对象适配器模式使用的组合模式
Spring中使用案例
Spring中默认的bean均是singleton,可以通过scope来定义
在SPring中的类AbstractBeanFactory中doGetBean中
适配器模式(Adapter Pattern)
适配器模式的介绍
将一个接口转化为客户所需要的另一个接口,使接口不兼容的一些类可以一起工作,也称为为包装类(Wrapper)
适配器模式分为两种:类适配器和对象适配器
类适配器模式使用继承的方式
对象适配器模式使用的组合模式
适配器模式中存在3个角色:
目标角色:客户所期望得到的接口
源角色:需要进行适配的接口
适配器角色:模式的核心,适配器将源接口转换成目标角色
适配器模式的优缺点:
优点:
1、有更好的复用性,系统直接使用现有的类,通过对现有的类适配就可以达到更好的复用
2、有更好的扩展性,实现适配器,可以调用自己开发的功能
缺点:过多的使用适配器会是系统比较凌乱,明明调用A接口,内部却适配成了B接口
在Java中最主要的使用就是IO流中
代码演示:

/**
 * 源角色的接口
 */
public interface TypeC {
    void isTypeC();
}

/**
 * 源角色接口的实现类
 */
public class TypeCImpl implements TypeC {
    @Override
    public void isTypeC() {
        System.out.println("typec充电口");
    }
}

/**
 * 有个传统的Micro USB的充电线
 * 用户要使用的角色:目标角色
 */
public interface MicroUSB {
    void isMicroUSB();
}

/**
 * 类适配器
 * 整个适配器adapter,将MicroUSB 转化成typec
 * 继承源角色的类实现目标角色的接口
 * 继承源角色的实现类实现目标角色的接口
 */
public class Adapter extends TypeCImpl implements MicroUSB {
    @Override
    public void isMicroUSB() {
        //typeC的实现
        isTypeC();
    }

    public static void main(String[] args) {
        MicroUSB usb = new Adapter();
        usb.isMicroUSB();
    }
}

/**
 * 对象适配器
 */
public class ObjectAdapter implements MicroUSB {
    //源角色的对象作为属性
    private TypeC typeC;

    public ObjectAdapter(TypeC typeC) {
        this.typeC = typeC;
    }

    @Override
    public void isMicroUSB() {
        typeC.isTypeC();
    }

    public static void main(String[] args) {
        //源对象
        TypeC typeC = new TypeCImpl();
        MicroUSB usb = new ObjectAdapter(typeC);
        usb.isMicroUSB();
    }
}

在这里插入图片描述Spring中的使用
在Spring的AOP中,使用advice(通知)来增强被代理类的功能
主要研究而适配器在IO流中的使用,字节输入/输出流、字符输入/输出流
策略模式(Strategy Pattern)
策略模式的介绍
一个类的行为或其方法可以在运行时更改
这种类型的设计模式属于行为型模式
在这里插入图片描述
策略模式中有三个角色
抽象策略类(Strategy):定义了公共接口,各种不同的算法以不同的方式实现这个接口,环境角色使用这个接口调用不同的算法,一般使用接口或者抽象类实现
具体策略类(Concrete Strategy):实现抽象策略定义的接口,提供具体的算法实现
环境类(Context):持有一个策略类的应用,最终给客户端调用
策略模式的优点:
1、上下文和具体策略时松耦合的关系,因此上下文只知道他使用某一个实现了Strategy接口类的实例,但是不知道具体的哪一个策略类
2、策略模式满足“开-闭原则”,当增加新的策略模式是,不需要修改上下文类的代码,上下文就可以引用新的策略的实例
代码示例

//抽象策略类
public abstract class Strategy {
    //策略方法
    public abstract void strategyMethod();
}


//具体策略类A
public class ConcreteStrategyA extends Strategy {
    @Override
    public void strategyMethod() {
        System.out.println("具体策略A的策略方法被访问!");
    }
}

//具体策略类B
public class ConcreteStrategyB extends Strategy {
    @Override
    public void strategyMethod() {
        System.out.println("具体策略B的策略方法被访问!");
    }
}

//环境类
public class Context {
    //持有策略对象的引用
    private Strategy strategy;
    public Strategy getStrategy() {
        return strategy;
    }
//    通过set方法将策略对象注入
    public void setStrategy(Strategy strategy) {
        this.strategy=strategy;
    }

    public void strategyMethod() {
        strategy.strategyMethod();
    }
}

//测试类
public class TestMain {
    public static void main(String[] args) {
//        环境类对象
        Context context = new Context();

        //给定具体策略对象的应用
        context.setStrategy(new ConcreteStrategyA());
        context.strategyMethod();

//        context.setStrategy(new ConcreteStrategyB());
//        context.strategyMethod();
    }
}

Spring中的使用案例
在Spring容器中注入不同类型的配置文件时,不同类型的配置信息的处理或者路径等不同,处理时的耦合性太高,通过IOC容器提供的处理不同类型资源的文件来达到实例化IOC容器
在这里插入图片描述不同资源文件即是具体的策略类,ApplicationContext是抽象策略类(环境类),通过实例化的队形,可以直接通过getBean来获取IOC管理的对象实例,而屏蔽了对象实例的位置

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值