Spring单例设计模式

单例设计模式

在应用程序中有保证最多只有一个实例

单例设计模式好处:
1. 提升运行效率
2. 实现数据共享 ( application对象 )

单例设计模式分类
1. 懒汉式
2. 饿汉式


懒汉式

创建SingleTon类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class SingleTon {

private static SingleTon singleton; // 由于对象需要被静态方法调用 所以方法也要是static 也要讲改为private (public 就直接调用对象)

// 构造方法 (其他类不能实例化这个类对象 对外提供访问入口)
private SingleTon() {}

// 实例方法 (必须通过对象调用 )
public static SingleTon getInstance() {
if(singleton==null)
{
// 多线程访问下 可能if同时成立 -- 添加锁
synchronized(SingleTon.class) {
if(singleton==null) // 双重检验
{
singleton=new SingleTon(); //创建实例化
}
}
}
return new SingleTon(); // 实例化
}

}

创建test类

1
2
3
4
5
6
7
8
9
public class Test {
public static void main(String[] args) {
//类加载时就进行实例化!!!
SingleTon singleton=SingleTon.getInstance(); //不能直接new SingTon() -- 调用静态方法
SingleTon singleton1=SingleTon.getInstance();
System.out.println(singleton==singleton1);

}
}

饿汉式

懒汉式加了锁(效率降低) – 解决懒汉式中可能同时访问一个对象的问题

只需要更改SingleTon类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class SingleTon {

//在类加载时就实例化
private static SingleTon singleton=new SingleTon(); //对比懒汉式 将实例方法内容删除 直接new一个对象

//构造方法
private SingleTon() {}

//实例方法
public static SingleTon getInstance() {
return new SingleTon();
}

}

总结

1. private static SingleTon singleton=new SingleTon();  直接new一个对象
2. 实例方法内容全部删除(不用管锁的事情)

Spring加载属性文件和scope属性

加载properties文件

src下新建xxx.properties文件

1
2
3
4
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm
jdbc.password=njdxrjgc7777777.
jdbc.username=root

在spring配置文件中引入xmlns:context

1
2
3
4
xmlns:context="http://www.springframework.org/schema/context"  //上面加的是xmlns

http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd //加到location里面

使用context:property-placeholder标签

1
<context:property-placeholder location="classpath:db.properties,classpath:xx.properties"/>   // 多个就用逗号隔开

之前配置jdbc用${}更改

1
2
3
4
5
6
7
 <!-- 数据源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driver}"></property> //将原来的直接改成 ${} 形式
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>

配置文件完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">

<context:property-placeholder location="classpath:db.properties,classpath:xx.properties"/>

<!-- 数据源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>

</beans>

从spring容器中取出属性文件值

修改注解扫描(注解要扫描)

1
2
3
 //具体写哪些类用了注解 (多个就用逗号隔开)
<context:component-scan base-package="com.bjsxt.advice,com.bjsxt.test">
</context:component-scan>

类中加载注解 @value(“${key}”)

key和变量名可以不相同
变量类型任意 (key对应的value可以转换成这个类型就行)

1
2
@value("${jdbc.uri}")  //取出jdbc.uri的值
private String test;

scope属性(bean标签属性)

控制对象有效范围(单例 多例等)

bean标签

对应的对象默认是单例(无论获取多少次都是同一对象)

scope属性取值

1. singleton(默认) : 单例
2. prototype :多例
3. request : 每次请求都重新实例化
4. session : 每个会话对象内,对象都是单例
5. application : 在application对象内是单例
6. global session spring : 退出的一个对象 依赖于spring-webmvc-protlet 类似于session

Spring自动注入

自动注入

(spring配置文件中对象名) == (ref=”id”中id名字) 就可以使用自动注解(不需要配置property)

1
2
3
4
5
//有两个类People和Teacher (People里面有Teacher的对象)
<bean id="teacher" class="com.bjsxt.test.Teacher"></bean>
<bean id="people" class="com.bjsxt.test.People" autowire="byName">
<!-- <property name="teacher" ref="teacher"></property> --> //以前都需要配置property
</bean>

解决方案

1. 在<bean>标签中通过 autowire=""配置 (只在本bean有效)
2. 在最大的<beans>标签中通过 default-autowire=""配置 (所有bean有效)

五大取值

1. default(默认): 根据全局default-autowire=""值 (默认全局和局部的在没有配置的时候都相当于no)
2. no: 不自动注入
3. byName: 通过名称自动注入 在Spring容器中找类的id
4. byType : 通过类型注入 
    4.1 不能有两个相同类型的bean!!!!!!
5. constructor : 根据构造方法注入
    5.1 提供对应参数的构造方法
    5.2 底层还是byName  构造方法参数名和其他bean的id相同

Spring代理设计模式

代理设计模式

代理设计模式优点
1. 保护真实对象(不见到源码)
2. 职责加强 (增强功能)
3. 扩展 (接口扩展)

代理设计模式
1. 真实对象 (老总)
2. 代理对象 (秘书)
3. 抽象对象(抽象功能)


静态代理设计模式

由代理对象代理所有真实对象的功能

1. 自己编写代理类(特点)
2. 每个代理的功能需要单独编写

动态代理设计模式

为了解决静态代理模式的缺点

分类

1. JDK 动态代理    不需额外jar(JDK自带)
2. cglib 动态代理  需额外jar

JDK和cglib对比

JDK动态代理(false)

优点:
       jdk自带 不需要导入额外jar

缺点:
       1. 真实对象必须实现接口
       2. 利用反射机制(效率不高)

异常:
       希望把接口对象 --> 具体真实对象

cglib动态代理(true)

优点:
       1. 基于字节码 生成真实对象的子类
       2. 运行效率高
       3. 不需要实现接口    

缺点:
       1. 需要导入额外jar

异常:
       使用spring aop时,只要出现Proxy和真实对象转换异常

异常解决:
        proxy-target-class更改 true(cglib动态代理) / false(JDK动态代理)

配置文件aop标签(配合注解)

1
2
3
<aop:aspectj-autoproxy proxy-target-class="true">  //注解一定要打开true  用更高效的cglib

</aop:aspectj-autoproxy>

AOP

AOP 面向切面编程

在原有纵向执行流程中添加横切面(不需要修改原有代码)

面向切面编程 特性:
1. 高扩展性
2. 原有代码释放了部分逻辑(横切面会分担一些功能)

面向切面编程:

在程序原有纵向执行流程中,针对某一个/某一些方法添加通知,形成横切面过程 -- 面向切面编程

面向切面编程概念图:

1. 原有功能: 切点(pointcut)
2. 前置通知: 切点之前执行的功能 (before advice)
3. 后置通知: 切点之后执行的功能 (after advice)
4. 异常通知: 切点执行过程中出现异常 (throws advice)
5. 切面: 所有功能总称
6. 织入: 将切面嵌入到原有功能的过程

两种AOP实现方式

1. Schema-based  
    1. 每个通知都需要实现接口/类
    2. 配置spring配置文件在<aop:config>配置

2. AspectJ  
    1. 每个通知不需要实现接口/类
    2. 配置spring配置文件在<aop:config>的子标签<aop:aspect>中配置

使用Schema-based方式

总体实现步骤

1.导入jar包
2.编写demo类(具有几个方法)
3.编写配置文件
    3.1 导入aop的xmls和location
    3.2 编写aop的相关信息(切点 前置和后置通知类的连接)
    3.3 前置通知类/后置通知类/demo类的bean方法
4.编写前置通知类(实现MethodBeforeAdvice接口)
5.编写后置通知类(实现AfterReturningAdvic接口)
6.编写测试类(IOC 控制反转)

具体实现代码

1.导入jar包

2.Demo类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Demo {

public void demo1(){
System.out.println("demo1");
}

public void demo2(){
System.out.println("demo2"); //切点是demo02方法
}

public void demo3(){
System.out.println("demo3");
}

}

3.配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?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" <!-- 导入aop的xmlns -->
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop <!-- 导入aop的地址 -->
http://www.springframework.org/schema/aop/spring-aop.xsd">

<bean id="mybefore" class="com.bjsxt.advice.MyBeforeAdvice"></bean> <!-- 前置通知类 -->
<bean id="myafter" class="com.bjsxt.advice.MyAfterAdvice"></bean> <!-- 后置通知类 -->

<aop:config>
<aop:pointcut expression="execution(* com.bjsxt.test.Demo.demo02())" id="mypoint"/> <!-- 要给aop的pointcut切点编程 execution(* )的格式 -->
<aop:advisor advice-ref="mybefore" pointcut-ref="mypoint"/> <!-- 将前置通知类加到上面的切点 -->
<aop:advisor advice-ref="myafter" pointcut-ref="mypoint"/> <!-- 将后置通知类加到上面的切点 -->
</aop:config>

<bean id="demo" class="com.bjsxt.test.Demo"></bean> <!-- 找方法的bean 用于test类找 -->

</beans>

4.前置通知类

1
2
3
4
5
6
public class MyBeforeAdvice implements MethodBeforeAdvice{
@Override
public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
System.out.println("执行前置通知!");
}
}

5.后置通知类

1
2
3
4
5
6
public class MyAfterAdvice implements AfterReturningAdvice {
@Override
public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable {
System.out.println("执行后置通知!");
}
}

6.测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Test {
public static void main(String[] args) {

//原来的写法:
//Demo demo=new Demo();
//demo.demo1();
//demo.demo2();
//demo.demo3();

//因为我们是IOC 控制反转 将new和管理对象给了spring
ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
Demo demo = ac.getBean("demo",Demo.class); //通过找配置文件中的bean
demo.demo1();
demo.demo2();
demo.demo3();
}

}

总结

执行结果

前置通知实现的接口方法参数

1
2
3
4
5
6
7
8
9
10
public class MyBeforeAdvice implements MethodBeforeAdvice{
@Override
public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {

//agr0 切点方法对象 Method对象
//agr1 切点方法参数
//agr2 切点在那个对象中

}
}

后置通知实现的接口方法参数

1
2
3
4
5
6
7
8
9
10
11
public class MyAfterAdvice implements AfterReturningAdvice {
@Override
public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable {

//agr0 切点方法返回值
//agr1 切点方法对象
//agr2 切点方法参数
//agr3 切点方法所在类的对象

}
}

配置异常通知(AspectJ方式)

只要切点报异常才能触发 异常通知(throws advice)
不实现接口(方法随便写) 然后配置在aop:aspect标签下面

准备类和异常通知方法

1
2
3
4
5
public class MtThrowAdvice {
public void myexception(Exception e) {
System.out.println("异常了!!!"+e.getMessage()); //自己写的报错的方法 输出错误原因
}
}

创建Demo类(切点方法)

1
2
3
4
5
6
7
8
public class Demo {

public void demo1(){
int i=5/0; //这里出异常
System.out.println("demo1");
}

}

更改配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?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.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">


<bean id="mythrow" class="com.bjsxt.advice.MtThrowAdvice"></bean> //给写的异常类写bean

<aop:config>
<aop:aspect ref="mythrow"> //相比于schema-based方式它可以自动找是前置还是后置通知 我们的aspectJ需要明确ref跳到哪个异常类
<aop:pointcut expression="execution(* com.bjsxt.test.Demo.demo01())" id="mypoint"/> //这行主要写切点: 是哪个包哪个类的哪个方法 然后给切点一个id
<aop:after-throwing method="myexception" pointcut-ref="mypoint" throwing="e"/> //异常通知: 方法是刚才那个类的哪个 然后切点是哪个 扔出来的信息是方法的哪个对象
</aop:aspect>
</aop:config>

<bean id="demo" class="com.bjsxt.test.Demo"></bean> //给切点的类写bean

</beans>

添加测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Test {
public static void main(String[] args) {

//原来的写法:
//Demo demo=new Demo();
//demo.demo1();
//demo.demo2();
//demo.demo3();

//因为我们是IOC 控制反转 将new和管理对象给了spring
ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
Demo demo = ac.getBean("demo",Demo.class);
demo.demo1(); //测试方法
}

}

异常通知(Schema-based)

实现接口(方法必须叫afterThrowing) 然后配置在aop:config下面

准备异常类

1. 必须实现throwsAdvice接口
2. 实现的方法有两种 (1个参数 / 4个参数)
3. 异常类型必须要和切点报错的异常一致(一般就写最大的Exception!!!!)
1
2
3
4
5
6
7
8
  public class MyThrow implements ThrowsAdvice {

//第一种
public void afterThrowing(Exception ex) throws Throwable{ //里面用最大的Exception
System.out.println("aaaa");
}

}

准备配置文件(展示细节)

只在aop:config标签里面去写

1
2
3
4
<aop:config>
<aop:pointcut expression="execution(* com.bjsxt.test.Demo.demo01())" id="mypoint"/>
<aop:advisor advice-ref="mythrow" pointcut-ref="mypoint"/>
</aop:config>

对比两种方法的区别

1
2
3
4
5
6
7
1.AspectJ:
1.1 不实现接口(显示异常的方法随便取名字)
1.2 配置文件结构 aop:config -- aop:aspect(ref写跳转的异常类位置) -- aop:pointcut(切点) /aop:after-throwing(异常通知)

2.schema-based:
2.1 实现throwsAdvice接口(显示异常的方法必须叫afterThrowing)
2.2 配置文件结构 aop:config -- aop:pointcut(切点) / aop :advisor (前置/后置/异常通知都在这)

环绕通知(Schema-based)

前置和后置通知 –> 一个通知(环绕)

创建类实现MethodInterceptor接口

1
2
3
4
5
6
7
8
9
10
11
public class MyArround implements MethodInterceptor{

@Override
public Object invoke(MethodInvocation arg0) throws Throwable {
System.out.println("环绕-前置");
Object result = arg0.proceed(); //放行 调用切点方式
System.out.println("环绕-后置");
return result;
}

}

配置文件(展示细节)

1
2
3
4
5
6
7
8
<bean id="myarround" class="com.bjsxt.advice.MyArround"></bean>

<aop:config>
<aop:pointcut expression="execution(* com.bjsxt.test.Demo.demo01())" id="mypoint"/> //配置切点
<aop:advisor advice-ref="myarround" pointcut-ref="mypoint" /> //配置环绕的信息
</aop:config>

<bean id="demo" class="com.bjsxt.test.Demo"></bean>

环绕通知(AspectJ)

创建MyAdvice类(不实现接口)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

public class MyAdvice {
public void mybefore(String name1,int age1) {
System.out.println("前置"+name1+" "+age1);
}

public void myafter1() {
System.out.println("后置1");
}

public void myafter2() {
System.out.println("后置2");
}

public void mythrow() {
System.out.println("异常");
}

public Object myarround(ProceedingJoinPoint p) throws Throwable { //环绕的类 ProceedingJoinPoint就是需要的类
System.out.println("执行环绕");
System.out.println("环绕-前置");
Object result = p.proceed(); //和另外一种方法一样的执行
System.out.println("环绕-后置");
return result;
}

}

创建Demo类(切点方法)

1
2
3
4
5
6
7
8
public class Demo {

public void demo1(String name,int age){ //现在切点传入的是name 和 age (后面会在配置和自己写的方法内的属性不同!!!)
//int i=5/0;
System.out.println("demo1"+name+" "+age);
}

}

配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<bean id="myarround" class="com.bjsxt.advice.MyArround"></bean>
<bean id="myadvice" class="com.bjsxt.advice.MyAdvice"></bean> //自己写的环绕的类

<aop:config>
<aop:aspect ref="myadvice"> //弹到我们写的类(环绕 前置 后置等方法)
<aop:pointcut expression="execution(* com.bjsxt.test.Demo.demo01(String,int)) and args(name1,age1)" id="mypoint"/> //切点信息
<aop:before method="mybefore" pointcut-ref="mypoint" arg-names="name1,age1" /> //前置通知信息

//有两种:
1.after 异常也可以执行
2.after-returning 异常就不会执行

<aop:after method="myafter1" pointcut-ref="mypoint"/>
<aop:after-returning method="myafter2" pointcut-ref="mypoint"/>

<aop:after-throwing method="mythrow" pointcut-ref="mypoint"/> //异常通知信息
<aop:around method="myarround" pointcut-ref="mypoint"/> //环绕的具体标签 aop:around
</aop:aspect>
</aop:config>

<bean id="demo" class="com.bjsxt.test.Demo"></bean> //Demo类信息

测试Test类

1
2
3
4
5
6
7
8
public class Test {
public static void main(String[] args) {
//因为我们是IOC 控制反转 将new和管理对象给了spring
ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
Demo demo = ac.getBean("demo",Demo.class); //通过bean去找
demo.demo1("zhangsan",123); //传入参数执行方法
}
}

关于配置文件中的细节

1. 关于后置的标签(执行顺序和配置顺序有关):
    1.1 after 异常也可以执行   
    1.2 after-returning 异常就不会执行

2. 关于前置标签的属性和使用问题
        <aop:pointcut expression="execution(* com.bjsxt.test.Demo.demo01(String,int)) and args(name1,age1)" id="mypoint"/>
        <aop:before method="mybefore" pointcut-ref="mypoint" arg-names="name1,age1" />
    注:     2.1 execution(* com.bjsxt.test.Demo.demo01()) 在后面加 and args()
         2.2 args()有几个参数 下面的arg-names就要有几个参数
         2.3 arg-names="" 里面的参数名称必须和通知方法参数名对应(和你写的前置方法的参数一样!!)

使用注解(基于Aspect)

告诉spring哪些类下的包有注解(默认不会自动找)

配置文件(引入xmlns:context)

1
2
3
4
5
6
7
8
9
10
<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" // 1. 引入这一行 xmlns:context
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context // 2. 引入相关的location地址
http://www.springframework.org/schema/context/spring-context.xsd">

@Component

1. 相当于bean标签(引入相关类)
2. 相当于bean id=""  @Component(没有参数) -- 将类名首字母变小写(Demo -- demo)  
3. @Component(可以随意自定义名称)

实现步骤

配置文件注解在哪些包

1
2
//多个包就用逗号隔开
<context:component-scan base-package="com.bjsxt.advice,com.bjsxt.test"></context:component-scan>

配置Demo切片类

1
2
3
4
5
6
7
@Component   //加这个注解 表明现在是给切片类写了id="demo"
public class Demo {
@Pointcut("execution(* com.bjsxt.test.Demo.demo01())") //demo01是切片
public void demo1(){
System.out.println("demo01");
}
}

配置前置/后置类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Component  //要引入Demo切片类
@Aspect //告诉我是一个切片类
public class MyAdvice {

@Before("com.bjsxt.test.Demo.demo01()") //方法前面要用是哪个切片
public void mybefore() {
System.out.println("前置");
}

@After("com.bjsxt.test.Demo.demo01()")
public void myafter() {
System.out.println("后置");
}

}

配置文件更改代理

1
2
3
4
5
6
7

<!--
true 使用cglib动态代理(注解)
false 使用jdk动态代理
-->

<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>

Spring整合Mybatis

Spring整合Mybatis

导入jar包

创建数据库

spring的bean标签代替mybatis全局配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?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">

<!-- 数据封装类
替换mybatis里面连接数据库的文件 spring-jdbc.jar -->
<bean id="dataSouce" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/ssm"></property>
<property name="username" value="root"></property>
<property name="password" value="njdxrjgc7777777."></property>
</bean>


<!-- 创建SqlSessionFactory对象 -->
<bean id="factory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSouce"></property> <!-- 跳转到关于有数据库信息的bean -->
</bean>

<!-- 扫描器 相当于mybatis配置文件中mappers下的package标签 扫描mapper包下会给对应接口创建对象 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.bjsxt.mapper"></property>
<property name="sqlSessionFactory" ref="factory"></property> <!-- 跳转到factory的bean里面 -->
</bean>

</beans>

准备实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class Airport {
private int id;
private String portName;
private String cityName;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getPortName() {
return portName;
}
public void setPortName(String portName) {
this.portName = portName;
}
public String getCityName() {
return cityName;
}
public void setCityName(String cityName) {
this.cityName = cityName;
}
@Override
public String toString() {
return "Airport [id=" + id + ", portName=" + portName + ", cityName=" + cityName + "]";
}
}

使用接口绑定写sql语句

1
2
3
4
5
6
public interface AirportMapper {

@Select("select * from airport") //注解的方式写sql语句
List<Airport> selAll(); //返回值是实体类的一个list形式

}
1
--------

使用接口写查询结果方法

1
2
3
public interface AirportService {
List<Airport> show(); //返回值是实体类的一个list形式
}

使用实现类实现接口方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class AirportServiceImpl implements AirportService {

//添加接口实现
private AirportMapper airportMapper;

//生成get和set方法
public AirportMapper getAirportMapper() {
return airportMapper;
}

public void setAirportMapper(AirportMapper airportMapper) {
this.airportMapper = airportMapper;
}

//实现service层接口方法
@Override
public List<Airport> show() {
return airportMapper.selAll();
}
}

在配置文件中加bean

1
2
3
4
5

<!-- 由spring管理service类 -->
<bean id="airportService" class="com.bjsxt.service.impl.AirportServiceImpl">
<property name="airportMapper" ref="airportMapper"></property> <!-- 实现类里面的那个对象在这里配置 -->
</bean>

test类

1
2
3
4
5
6
7
8
9
10
11
12
13

public class Test {
public static void main(String[] args) {

//找spring容器配置文件地址
ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml"); //默认从classes文件夹根目录开始寻找
//执行方法
AirportServiceImpl bean = ac.getBean("airportService",AirportServiceImpl.class); //通过bean去实例化
List<Airport> list = bean.show(); //调用接口实现类方法
System.out.println(list);

}
}

总结

代码框架

执行过程分析

1. 准备jar包和数据库    
2. 准备好实体类(和数据库对的上)
3. 准备配置文件
    1. 准备替换mybatis和数据库连接信息的bean
    2. 准备刚才替换mybatis的文件的读入的bean
    3. 准备创建扫描器(相当于mappers下面的package标签)
    4. 准备要被spring管理的service类
4. 准备接口绑定的接口写sql语句
5. 准备service接口写返回的方法
6. 准备实体类实现接口返回结果
    1. 要准备接口绑定接口的对象
    2. 生成get和set方法
    3. 实现接口方法 返回结果!!
7. 准备service的bean
8. 准备实现test测试类
    1. 通过ApplicationContext找配置文件
    2. 通过对象调用bean方法查找到配置文件中的关于service类的bean去找实现类实现

执行结果:


Spring给Bean注入

给Bean属性赋值(注入)

1. 通过有参构造方法设置
2. 设置注入(set方法)

设置注入(set方法)

属性:基本数据类型/String

value标签

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//第一种:
<bean id="peo" class="com.bjsxt.pojo.People">
<property name="id" value="222"></property>
<property name="name" value="zhangsan"></property>
</bean>

//第二种:
<bean id="peo" class="com.bjsxt.pojo.People">
<property name="id">
<value>222</value>
</property>

<property name="name">
<value>zhangsan</value>
</property>
</bean>

属性:Set<?>

set标签 – value标签

1
2
3
4
5
6
7
<property name="set">
<set>
<value>1</value>
<value>2</value>
<value>3</value>
</set>
</property>

属性:List<?>

list标签 – value标签

1
2
3
4
5
6
7
<property name="list">
<list>
<value>1</value>
<value>2</value>
<value>3</value>
</list>
</property>

属性:数组

array标签 – value标签

1
2
3
4
5
6
7
<property name="strs">
<array>
<value>1</value>
<value>2</value>
<value>3</value>
</array>
</property>

属性:map

map标签 – entry标签(key value属性)

1
2
3
4
5
6
<property name="map">
<map>
<entry key="a" value="b"></entry>
<entry key="c" value="d"></entry>
</map>
</property>

属性:properties

pros标签 – prop标签(key属性)

1
2
3
4
5
6
<property name="demo">
<props>
<prop key="key1">value1</prop>
<prop key="key2">value2</prop>
</props>
</property>

DI (依赖注入)

Dependency Injection:类A依赖类B对象,把B赋值给A的过程就叫做依赖注入

DI和Ioc其实是一样的!!!(Ioc是将创建对象的步骤交给spring去做)

1
2
3
4
5
6
7
8
<bean id="peo" class="com.bjsxt.pojo.People">
<property name="desk" ref="desk"></property> //ref是可以跳转到bean里面id叫做desk的bean
</bean>

<bean id="desk" class="com.bjsxt.pojo.Desktop">
<property name="id" value="1"></property>
<property name="price" value="77"></property>
</bean>

Spring

Spring概述

Rod Johnson提出轮子理论:不用重复发明轮子(直接使用写好的代码)

  • Spring核心功能

    1. IoC/DI    控制反转/依赖注入
    2. AOP       面向切面编程
    3. 声明式事务

框架分析

框架图(八个部分)

具体讲解

Test

test:spring提供测试功能

Core Container

1. Beans:创建类对象并管理对象
2. Core:核心类
3. Context:上下文参数 获取外部资源/管理注解
4. spEl:expression.jar

AOP

AOP:实现aop功能需要依赖

Aspects

Aspects:切面AOP依赖的包

Instrumentation

Messaging

Data Access / Integration

1. JDBC: spring对于JDBC封装后的代码
2. ORM:封装持久层框架的代码(ssh里面的Hibernate)
3. transactions:对应spring-tx.jar -- 声明式事务使用

Web

spring完成web相关功能
例:tomcat加载spring配置文件时需要sping-web包

spring容器

1. 容器(Container):Spring当作一个大容器
2. ApplicationContext接口(老版本的BeanFactory接口)的子接口
3. Spring3之后将sping框架的功能拆分为多个jar

Ioc 控制反转(Inversion of Control)

将原来要new实例化对象 –> 交给spring完成

  1. 控制: 控制类的对象

  2. 反转: 交给spring负责

  3. Ioc最大作用:解耦


Spring环境搭建

导入jar

新建applicationContext.xml配置文件

关于配置文件的知识点:

1
2
3
4
1. Spring配置文件信息内容 --> Spring容器ApplicationContext
2. Spring配置文件基于schema(文件扩展名.xsd)
3. schema是DTD升级版
4. Spring配置文件引入一个xsd就有一个namespace(xmlns就是去找相关文档位置)

具体写法:

1
2
3
4
5
6
7
8
9
10
11
12

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

<!-- id 表示获取到对象标识 class 创建哪个类的对象 -->
<bean id="peo" class="com.bjsxt.pojo.People"/> // bean标签去创建对象 id用来之后getBean中第一个参数调用 class是写具体类的全路径

</beans>

编写实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

public class People {
private int id;
private String name;

public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "People [id=" + id + ", name=" + name + "]";
}
}

编写测试类

分析使用

1. 对比getBean()和getBeanDefinitionNames()方法
     getBean()方法两个参数:
        第一个:配置文件中bean标签id值
        第二个:返回值类型(默认Object)         
     getBeanDefinitionNames() Spring容器中目前所有管理的所有对象

2. 要使用ApplicationContext容器 -- 我们暂时用ClassPathXmlApplicationContext去获取资源

具体实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Test {
public static void main(String[] args) {

ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
People people = ac.getBean("peo",People.class);
System.out.println(people);

String[] names = ac.getBeanDefinitionNames();
for (String string : names)
{
System.out.println(string);
}

}
}

Spring创建对象的三种方式

1. 构造方法创建
     无参构造创建:默认
     有参构造创建:需要明确配置

2. 实例工厂

3. 静态工厂

构造方法创建(bean标签更改)

实体类创建好无参构造方法和有参构造方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public People(){
super();
System.out.println("执行无参构造方法");
}

public People(int id,String name){
super();
this.id=id;
this.name=name;
System.out.println("执行有参构造方法1");
}

public People(String name,int id){
super();
this.id=id;
this.name=name;
System.out.println("执行有参构造方法2");
}

无参构造方法

1
<bean id="peo" class="com.bjsxt.pojo.People"/>   //直接简单的写id和class即可

有参构造方法(constructor-arg标签)

四个属性(可以区别用哪个有参构造方法 默认是用最下面的!!!!)

index:参数索引(0开始)
value:参数对应值
name:参数名
type:区别关键字和封装类(int和Integer)
1
2
3
4
<bean id="peo" class="com.bjsxt.pojo.People"> 
<constructor-arg index="0" name="id" type="int" value="123"></constructor-arg>
<constructor-arg index="1" name="name" type="java.lang.String" value="zhangsan"></constructor-arg>
</bean>

实例工厂(先工厂后对象)#

生成一个实例工厂

1
2
3
4
5
public class PeopleFactory{
public People newInstance(){ //一般都叫newInstance()
return new People(1,"测试");
}
}

配置文件配置工厂对象

1
2
3
4
5
6
7
// 原来代码格式:
PeopleFactory factory=new PeopleFactory()
People P1=factory.newInstance();

<bean id="factory" class="com.bjsxt.pojo.PeopleFactory"></bean> //用了后面类创建了一个工厂对象叫factory

<bean id="p1" factory-bean="factory" factory-method="newInstance"></bean> //用上面的factory工厂对象调用newInstance方法 返回为p1

静态工厂(快速创建对象)

不需要工厂(静态方法直接用)

1
2
3
4
5
public class PeopleFactory{
public static newInstance(){ //一般都叫newInstance() static静态!!!!
return new People(1,"测试");
}
}

配置文件调用方法

1
2
3
4
// 原来代码格式:
People P2=PeopleFactory.newInstance();

<bean id="p2" class="com.bjsxt.pojo.PeopleFactory" factory-method="newInstance"></bean> //直接调用静态newInstance方法

Mybatis运行原理总结

Mybatis运行原理

运行过程中涉及的类

1. Resources Mybatis中的IO流的工具类 : 加载配置文件
2. SqlSesssionFactoryBuilder()构建器 :创建SqlSessionFactory接口的实体类
3. XMLConfigBuilder() : Mybatis全局配置文件内容构造器类 --读取流内容并转为JAVA代码
4. Configuration: 封装全局配置文件所有内容 
5. DefaultSqlSessionFactory():是SqlSessionFactory()接口实现类
6. Transaction:事务类 -- 每一个sqlssion都有一个transaction对象
7. TransactionFactory:事务工厂
8. Executor: Mybatis执行器
9. SimpleExecutor: 默认执行器
10. BatchExcutor:批量操作
11. openSession:参数控制:
12. DefaultSqlSession:SqlSession接口实现类
13. ExcptionFactory:异常工厂

流程图

文字描述

1. Mybatis执行时先要通过Resources加载全局配置文件
2. 通过实例化SqlSessionFactoryBuilder构建器 -- 帮助SqlSessionFactory接口实现类DefaultSqlSessionFactory
3. 用XmlConfiguration解析全局配置文件 
4. 将解析结果给Configuration --> 最后又给了DefaultSqlSessionFactory

----

5. 由SqlSessionFactory工厂创建SqlSession
6. 每次创建SqlSession都必须要要用TransactionFactory创建Transaction对象
7. 有了事务对象之后就需要有Excutor执行器
8. 创建实例化DefaultSqlSession传给SqlSession接口
9. 进行不同的JDBC
10. 成功就提交 --关闭
11. 不成功就回滚事务

Mybatis注解

注解(简化配置文件)

Mybatis的注解简化mapper.xml文件(涉及动态SQL就不可用(×))

注解和全局配置文件mapper.xml共存!!!

注解的简单实用

使用注解:
        1. package标签(用接口里面写注解)
        2. mapper标签里面用class属性(原来是resource取对应配置文件)

例:
    1. 第一种用package的方式:    
     <mappers>
        <package name="com.bjsxt.com"/>  //因为后面的被对应的配置文件用别名起了
    </mappers>

    2. 第二种更改class属性

    <mappers>
        <mapper class="com.bjsxt.com.XXXMapper"/>
    </mappers>

package(接口内写注解)

1. 查询 @Select

1
2
@Select("select * from teacher")
List<Teacher> selAll();

2. 新增 @Insert

1
2
@Insert("insert into teacher values(default,#{name})" )
int insTeacher(Teacherteacher);

3. 修改 @Update

1
2
@Update("update teacher set name=#{name} where id=#{id}" )
int upd Teacher(Teacherteacher);

4. 刪除 @Delete

1
2
@Delete("delete from teacher whereid=#{0}" )
int delById(intid);

注解实现resultMap功能(老师和学生的例子)

其实就是一些注解名和resultMap标签对等

学生接口添加注解

1
2
@Select("select * from student wheret id=#{0}")  //相当于student配置文件里面写sql语句
List<Student> selByTid(int tid);

老师接口添加注解

1
2
3
4
5
6
7
8
9
@Results(       //相当于resultMap标签
value={
@Result(id=true,property="id",column="id"), //相当于id/result标签
@Result(property="name",column="name"),
@Result(property="list",column="id",many=@Many(select="com.bjsxt.mapper.StudentMapper.selByTid")) //Many相当于collection(集合对象)
})

@Select("select * from teacher")
List<Teacher> selTeacher();

总结

1.     @Results()   == resultMap标签
2.     @Result()    == id/result标签 (主键/其他键)
3. @Result(id=true) == id标签
4.      @Many()     == collection标签 (集合对象)
5.      @One()      == association标签 (单个对象)

,