Spring学习笔记

Spring概述

Spring:一个java框架,使用java语言开发的,轻量级的,开源的框架,可以在j2se,j2ee项目中使用,

Spring核心技术:IoC(Inversion of Control,控制反转),AOP(Aspect Oriented Programming)

Spring又叫做容器,spring作为容器,可以让spring创建java对象,给属性赋值。

Spring作用:实现解耦合,解决java对象之间的耦合,解决模块之间的耦合。

Spring作为容器适合管理的对象:service对象,dao对象,工具类对象

Spring作为容器不适合管理的对象:实体类,servlet,listener,filter等web中的对象,她们是tomcat创建和管理的。

Spring的地址

官网地址:https://spring.io/

Spring的优点:

Spring 是一个框架,是一个半成品的软件。有 20 个模块组成。它是一个容器管理对象, 容器是装东西的,Spring 容器不装文本,数字。装的是对象。Spring 是存储对象的容器。

(1) 轻量,

Spring 框架使用的 jar 都比较小,一般在 1M 以下或者几百 kb。Spring 核心功能的所需 的 jar 总共在 3M 左右。 Spring 框架运行占用的资源少,运行效率高。不依赖其他 jar

(2) 针对接口编程,解耦合

Spring 提供了 Ioc 控制反转,由容器管理对象,对象的依赖关系。原来在程序代码中的 对象创建方式,现在由容器完成。对象之间的依赖解耦合。 Spring 是于 2003 年兴起的一个轻量级的 Java 开发框架,它是为了解决企业应用开发 的复杂性而创建的。Spring 的核心是控制反转(IoC)和面向切面编程(AOP)。Spring 是可 以在 Java SE/EE 中使用的轻量级开源框架。

(3) AOP 编程的支持

通过 Spring 提供的 AOP 功能,方便进行面向切面的编程,许多不容易用传统 OOP 实现 的功能可以通过 AOP 轻松应付 在 Spring 中,开发人员可以从繁杂的事务管理代码中解脱出来,通过声明式方式灵活地 进行事务的管理,提高开发效率和质量。

(4) 方便集成各种优秀框架

Spring 不排斥各种优秀的开源框架,相反 Spring 可以降低各种框架的使用难度,Spring 提供了对各种优秀框架(如 Struts,Hibernate、MyBatis)等的直接支持。简化框架的使用。 Spring 像插线板一样,其他框架是插头,可以容易的组合到一起。需要使用哪个框架,就把 这个插头放入插线板。不需要可以轻易的移除。

IoC控制反转

2.1概念

控制反转(IoC,Inversion of Control),是一个概念,是一种思想。指将传统上由程序代 码直接操控的对象调用权交给容器,通过容器来实现对象的装配和管理。控制反转就是对对 象控制权的转移,从程序代码本身反转到了外部容器。通过容器实现对象的创建,属性赋值, 依赖的管理。

IoC 是一个概念,是一种思想,其实现方式多种多样。当前比较流行的实现方式是依赖 注入。应用广泛。

IoC把创建对象的过程交给Spring进行管理

1.IoC分为控制和反转:

控制:对象创建,属性赋值,对象生命周期管理

反转:把开发人员管理对象的权限转移给了代码之外的容器实现,由容器完成对象的管理。

正转:开发人员在代码中,使用new构造方法创建对象,开发人员掌握了对象的创建,属性赋值,对象从开始到销毁的全过程,开发人员有对对象全部控制。

通过容器,可以使用容器中的对象(容器已经创建了对象,对象属性赋值了,对象也组装好了)。

2.IoC的技术实现:

DI(依赖注入):Dependency Injection,是IoC的一种技术实现,程序只需要提供要使用的对象的名称就可以了,对象如何创建,如何从容器中查找,获取都由容器内部自己实现。

3.Spring框架使用的DI实现IoC

通过spring框架,只需要提供要使用的对象名词就可以了,从容器中获取名称对应的对象,Spring底层使用的是反射机制,通过反射创建对象,属性

2.1.2

1.IOC思想基于IOC容器完成,IOC容器底层就是对象工厂

2.Spring提供IOC容器实现两种方式:(两个接口)

1)BeanFactory:IOC容器基本实现,是Spring内部的使用接口,不提供开发人员进行使用,其在加载配置文件时候不会创建对象,在获取对象(使用)才去创建对象

2)ApplicationContext:BeanFactory接口的子接口,提供更多更强大的功能,一般由开发人员使用,并且在加载配置文件的时候就会创建对象。

2.2Spring的配置文件

<?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">
   
   <bean id="someService" class="com.hao.service.impl.SomeServiceImpl"/>
<!--bean里面放java对象,class是全限定类名,id是对象标识,是唯一值。-->
</beans>
spring标准配置文件:
1)根标签 beans
2)beans后面是约束文件说明
3)beans里面是bean声明
4)bean:bean就是java对象,spring容器管理的java对象,叫做bean

2.3spring容器创建对象的特点

1.容器对象ApplicationContext:接口

通过ApplicationContext对象,获取要使用的其他Java对象,执行getBean(“<bean>的id”)

2.Spring默认的是调用类的无参数构造方法,创建对象。

3.spring读取配置文件,一次创建好所有的java对象,都放到map中。

2.4获取容器中定义的对象信息

//获取配置文件中定义的对象个数
int num=cxt.getBeanDefinitionCount();
//获取配置文件中所有对象的名称
String names[]=cxt.getBeanDefinitionNames();

2.5DI:给属性赋值

spring调用类的无参构造方法,创建对象,对象创建后给属性赋值

给属性赋值可以使用

1)xml配置文件中的标签和属性;

2)使用注解

DI分类:1.set注入,也叫作设值注入;2.构造注入

2.5.1基于xml的DI

在xml配置文件中使用标签和属性,完成对象创建,属性赋值

1)set注入,也叫作设值注入

概念:spring调用类中的set方法,在set方法中可以完成属性赋值,推荐使用。

    <!--
       DI:给属性赋值
       简单类型:java中的基本数据类型和String
       1.set注入:spring调用类的set方法,通过set方法完成属性赋值
           简单类型的set注入
           语法:<bean id="xxx" class="yyyy">
                       <property name="属性名" value="简单类型属性值"/>
                       ....
                </bean>
   -->
   <bean id="myStudent" class="com.hao.jh01.Student">
       <!--赋值的时候会自动调用类中的set方法,比如name=“age”,调用setAge方法-->
       <property name="age" value="10"/><!--执行了setAge=10-->
       <property name="name" value="张三"/><!--执行了setName="张三"-->
   </bean>
       <!--
2.set注入:
           引用类类型的set注入:
            语法:<bean id="xxx" class="yyyy">
                       <property name="属性名" ref="bean的id"/>
                       ....
                </bean>
   -->
   <bean id="myStudent" class="com.hao.jh02.Student">
       <property name="age" value="10"/><!--执行了setAge=10-->
       <property name="name" value="张三"/><!--执行了setName="张三"-->
       <property name="school" ref="mySchool"/>
   </bean>
   <bean id="mySchool" class="com.hao.jh02.School">
       <property name="name" value="新乡学院"/>
       <property name="address" value="红旗区"/>
   </bean>

给属性注入空值的方法:

    <bean id="myStudent" class="com.hao.jh02.Student">
       <property name="age" value="10"/><!--执行了setAge=10-->
       <property name="name"><!--表示给name注入null值,输出为null-->
      <null/>
       </property>
   </bean>

属性值包含特殊符号的时候

1)使用xml实体符号进行转义

&lt;相当于<

&gt;相当于>

2)把带有特殊符号的内容写到CDATA

<property name="name"><!--表示给name注入<<南京>>,输出<<南京>>-->
      <value><![CDATA[<<南京>>]]></value>
       </property>

2)构造注入

构造注入:spring调用类中的有参构造方法,在创建对象的同时,给属性赋值。

使用name属性赋值:

    <!--
       构造注入:Spring调用类的有参数构造方法,创建对象同时给属性赋值
       语法:
           <bean id="xxx" class="yyy">
               <constructor-arg>:表示一个构造方法的形参
               标签有属性: name:构造方法的形参名
                          index:构造方法的参数位置
                          value:简单类型的形参值
                          ref:引用类型的形参值
           <bean>
   -->
   <bean id="myStudent" class="com.hao.jh03.Student">
       <constructor-arg name="age" value="11"/>
       <constructor-arg name="name" value="张三"/>
       <constructor-arg name="school" ref="mySchool"/>
   </bean>
   <bean id="mySchool" class="com.hao.jh03.School">
       <property name="name" value="新乡学院"/>
       <property name="address" value="红旗区"/>
   </bean>

使用index属性赋值

    <!--构造注入,使用index,参数的位置,构造方法参数从左到右的位置依次是0,1,2-->
   <bean id="myStudent02" class="com.hao.jh03.Student">
       <constructor-arg index="0" value="12"/>
       <constructor-arg index="1" value="李四"/>
       <constructor-arg index="2" ref="mySchool"/>
   </bean>
   <!--省略index,如果省略的话,属性顺序一定要与类属性顺序一致-->
   <bean id="myStudent03" class="com.hao.jh03.Student">
       <constructor-arg  value="12"/>
       <constructor-arg  value="李四"/>
       <constructor-arg  ref="mySchool"/>
   </bean>
   <bean id="mySchool" class="com.hao.jh03.School">
       <property name="name" value="新乡学院"/>
       <property name="address" value="红旗区"/>
   </bean>

3)引用类型的自动注入

概念:spring可以根据某些规则给引用类型完成赋值,只对引用类型有效

规则有两个分别为byName和byType

①:byName(按名称注入):java类中引用类型属性名称和spring容器中bean的id名称一样的,且数据类型也是一样的,这些bean能够赋值给引用类型。

相关代码:

<!--
引用类型自动注入:spring根据byName,byType规则给引用类型赋值
1.byName(按名称注入):java类中引用类型的属性名称和spring容器中的bean的id名称一样,且数据类型一样,这样的bean能够赋值给引用类型
语法:
<bean id="xxx" class="yy" autowire="byName">
简单属性赋值
</bean>
-->

<bean id="myStudent" class="com.hao.jh04.Student" autowire="byName">
<property name="age" value="10"/>
<property name="name" value="张三"/>
<!--
<property name="school" ref="mySchool"/>
-->
</bean>
<bean id="school" class="com.hao.jh04.School">
<property name="name" value="新乡"/>
<property name="address" value="红旗区"/>
</bean>

②:byType(按类型注入):java类中引用类型的数据类型和spring容器中的bean的class值是同源关系的,这样的bean赋值给引用类型。

    <!--

2.byType(按类型注入):java类中引用类型的数据类型和bean的class是同源的,这些的bean能够赋值给引用类型。

同源关系:
1.java中引用类型的数据类型和bean的class值是一样的。
2.java中引用类型的数据类型和bean的class值是父子类关系的。
3.java中引用类型的数据类型和bean的class值是接口和实现类关系的。
语法:
<bean id="xxx" class="yy" autowire="byType">
简单属性赋值
</bean>
注意:在xml配置文件中,符合条件的对象只能有一个,多余一个会报错。
-->

<bean id="myStudent" class="com.hao.jh05.Student" autowire="byType">
<property name="age" value="10"/>
<property name="name" value="张三"/>
<!--
<property name="school" ref="mySchool"/>
-->
</bean>
<bean id="mySchool" class="com.hao.jh05.School">
<property name="name" value="新乡"/>
<property name="address" value="红旗区"/>
</bean>

4)在项目中使用多个spring配置文件

分多个配置文件的方式:1)按照功能模块分,一个模块一个配置文件,2)按类的功能分,数据库操作相关的类在一个文件,service类在一个配置文件,配置redis,事务等等的一个配置文件。

spring管理多个配置文件,常用的是包含关系的配置文件,项目中有一个总的文件,里面有一个import标签包含其他的多个配置文件

语法:

总的文件(xml)
<import resource="其他的文件的路径1"/>
<import resource="其他的文件的路径2"/>
关键字“classpath:" :表示类路径,也就是类文件(class)所在的目录。spring到类路径中加载文件
什么时候使用classpath:在一个文件中要使用其他的文件,需要使用classpath。
包含关系的配置文件,可以使用通配符(*:表示任意字符)注意:总的文件名称,不能包含在通配符范围内。

5)注入集合类型的属性(注入简单类型)

package com.hao.collectType;

import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class Student {
private String[] course;
private List<String> list;
private Map<String,String> map;
private Set<String> set;

public void setSet(Set<String> set) {
this.set = set;
}

public void setCourse(String[] course) {
this.course = course;
}

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

public void setMap(Map<String, String> map) {
this.map = map;
}
public void test(){
System.out.println(Arrays.toString(course));
System.out.println(list);
System.out.println(map);
System.out.println(set);
}
}

配置文件:

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

<bean id="student" class="com.hao.collectType.Student">
<property name="course">
<array>
<value>java课程</value>
<value>MySql课程</value>
</array>
</property>
<property name="list">
<list>
<value>张三</value>
<value>李四</value>
</list>
</property>
<property name="map">
<map>
<entry key="1" value="中国"></entry>
<entry key="2" value="美国"></entry>
</map>
</property>
<property name="set">
<set>
<value>MySql</value>
<value>PHP</value>
</set>
</property>
</bean>
</beans>

6)注入集合类型的属性(注入对象类型)

实体类:

package com.hao.collectType;

public class Course {
private String cname;
public void setCname(String cname){
this.cname=cname;
}

@Override
public String toString() {
return "Course{" +
"cname='" + cname + '\'' +
'}';
}
}
package com.hao.collectType;

import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class Student {

private List<Course> courseList;

public void setCourseList(List<Course> courseList){
this.courseList=courseList;
}

public void test(){
System.out.println(courseList);
}
}

配置文件

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

<bean id="student" class="com.hao.collectType.Student">
<property name="courseList">
<list>
<ref bean="course01"></ref>
<ref bean="course02"></ref>
</list>
</property>
</bean>
<bean id="course01" class="com.hao.collectType.Course">
<property name="cname" value="语文"/>
</bean>
<bean id="course02" class="com.hao.collectType.Course">
<property name="cname" value="数学" />
</bean>

</beans>

测试:

public class MyTest {
@Test
public void test001(){
String config="applicationContext.xml";
ApplicationContext applicationContext=new ClassPathXmlApplicationContext(config);
Student student=applicationContext.getBean("student", Student.class);
student.test();
}
}

2.5.2基于注解的DI

基于注解的DI:使用spring提供的注解,完成java对象创建,属性赋值。

注解使用的核心步骤:

1.在源代码中加入注解,例如@Component

2.在spring的配置文件,加入组件扫描器的标签。

@Component

@Component:表示创建对象,对象放到容器中,作用是<bean>

属性:value,表示对象名称,也就是bean的id属性值

位置:在类的上面,表示创建此类的对象

@Component(value=“myStudent”)(value可以省略)等同于

<bean id="myStudent" class="com.hao.jh01.Student"/>

其中如果只写@Component的话,会被框架自动赋一个值,这个值为类名首字母小写。

 

 

配置文件:

<?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: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 https://www.springframework.org/schema/context/spring-context.xsd">

<!--声明组件扫描器,使用注解必须加入这个语句
component-scan:翻译过来是组件扫描器,组件是java对象
属性:base-package 注解在你的项目中的包名,
框架会扫描这个包和子包中的所有类,找类中的所有注解
遇到注解后,按照注解表示的功能,去创建对象,给属性赋值。
-->
<context:component-scan base-package="com.hao.jh01"></context:component-scan>
</beans>

和@Component功能相同的创建对象的注解

1)@Repository:放在dao接口的实现类上面,表示创建dao对象,持久层对象,能访问数据库

2)@Service:放在业务层接口的实现类上面,表示创建业务层对象,业务层对象有业务的功能

3)@Controller:放在控制器类的上面,表示创建控制器对象。属于表示层对象,控制器对象能接受请求,把请求的处理结果显示给用户。

这四个注解都能创建对象,但是@Repository,@Service,@Controller有角色说明,表示对象是分层的,对象是属于不同层的,具有额外功能。

扫描多个包的三种方式:

<!--第一种:使用多次组件扫描器-->
<context:component-scan base-package="com.hao.jh01"/>
<context:component-scan base-package="com.hao.jh02"/>
<!--第二种:使用分隔符(;或者,)指定多个包-->
<context:component-scan base-package="com.hao.jh01;com.hao.jh02"/>
<!--第三种:指定父包-->
<context:component-scan base-package="com.hao"/>

@Value

package com.hao.jh02;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component("myStudent")
public class Student {
/**
* 简单类型属性赋值:@Value
* @Value:简单属性赋值属性
* 属性:value简单类型属性值
* 位置:1)在属性定义的上面,无需set方法,推荐使用
* 2)在set方法的上面
*/
@Value(value="zhangsan")
private String name;
@Value(value = "11")
private int age;

public Student() {
System.out.println("Student类的无参构造被调用了");
}

@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}

@AutoWired 引用类型(注入对象属性):使用注解方式进行属性注入

引用类型:@AutoWired:spring框架提供的,给引用类型赋值的,使用自动注入原理,支持byName,byType。默认是byType。

它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作

@Autowired属性:required:boolean类型的属性,默认为true

true:spring在启动的时候,创建容器对象的时候,会检查引用类型是否赋值成功,如果赋值失败,终止程序执行,并报错

false:如果引用类型赋值失败,程序正常执行,不报错,引用类型的值为null

@Autowired使用位置:1)在属性定义的上面,无需set方法,推荐使用

2)在set方法的上面。

示例:

//UserDao
package com.hao.dao;

public interface UserDao {
public void add();
}
//UserDao实现类
package com.hao.dao.impl;

import com.hao.dao.UserDao;
import org.springframework.stereotype.Repository;

@Repository(value = "userDao")
public class UserDaoImpl implements UserDao {
@Override
public void add() {
System.out.println("UserDaoImpl add..........");
}
}
//UserService
package com.hao.service;

import com.hao.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service(value="userService")
public class UserService {
@Autowired//根据类型进行注入,找到UserDao类进行注入
private UserDao userDao;
public void add(){
System.out.println("UserDao add......");
userDao.add();
}
}
<!--配置文件-->
<?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: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
https://www.springframework.org/schema/context/spring-context.xsd">


<context:component-scan base-package="com.hao"></context:component-scan>
</beans>
//测试文件
@Test
public void test002(){
String config="applicationContext.xml";
ApplicationContext applicationContext=new ClassPathXmlApplicationContext(config);
UserService userService=applicationContext.getBean("userService", UserService.class);
userService.add();
}

@Qualifier:根据名称进行注入

@Qualifier注解的使用,和@Autowired一起使用

当一个接口有多个实现类的时候,只使用@Autowired注解可能编译器不知道注入哪个实现类,因此会导致编译失败,在@Autowired下面使用@Qualifier的话这可以避免此问题

<!--配置文件-->
<?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: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
https://www.springframework.org/schema/context/spring-context.xsd">


<context:component-scan base-package="com.hao"></context:component-scan>
</beans>
//Dao接口
package com.hao.dao;

public interface UserDao {
public void add();
}
//Dao接口实现类UserDaoImple01
package com.hao.dao.impl;

import com.hao.dao.UserDao;
import org.springframework.stereotype.Repository;

@Repository(value = "userDaoImpl01")
public class UserDaoImpl01 implements UserDao {
@Override
public void add() {
System.out.println("UserDaoImpl01 add..........");
}
}
//Dao接口实现类UserDaoImpl02
package com.hao.dao.impl;

import com.hao.dao.UserDao;
import org.springframework.stereotype.Repository;

@Repository(value="userDaoImpl02")
public class UserDaoImpl02 implements UserDao {
@Override
public void add() {
System.out.println("UserDaoImpl02 add............");
}
}
//UserService
package com.hao.service;

import com.hao.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service(value="userService")
public class UserService {
@Autowired
@Qualifier(value="userDaoImpl02")
private UserDao userDao;
public void add(){
System.out.println("UserDao add......");
userDao.add();
}
}
//测试文件
@Test
public void test002(){
String config="applicationContext.xml";
ApplicationContext applicationContext=new ClassPathXmlApplicationContext(config);
UserService userService=applicationContext.getBean("userService", UserService.class);
userService.add();
}

@Resource 引用类型(注入对象类型属性)

@Resource:来自jdk中,给引用类型赋值的,支持byName,byType,默认是byName,spring支持这个注解的使用。

使用位置:1)在属性定义的上面,无需set方法,推荐使用

2)在set方法的上面。

@Rrsource先使用byName赋值,如果赋值失败,再使用byType。

package com.hao.service;

import com.hao.dao.UserDao;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

@Service(value="userService")
public class UserService {
//根据名称进行注入 @Resource(name="userDao")
@Resource //根据类型进行注入
private UserDao userDao;
public void add(){
System.out.println("UserDao add......");
userDao.add();
}
}

2.6工厂bean

1.Spring有两种类型bean,一种普通bean,另外一种工厂bean(FactoryBean)

2.普通bean:在配置文件中定义的bean类型是什么类则返回什么类型

applicationContext.getBean("id")//返回自己定义的类

3.工厂bean:在配置文件定义bean类型可以和返回类型不一样。

2.7bean的作用域

1.在Spring里面,设置创建bean实例是单实例还是多实例。

2.在Spring里面,默认情况下,bean是单实例对象。

3.如何设置单实例还是多实例

1)在spring配置文件bean标签里面有属性(scope)用于设置单实例或者多实例。

2)scope属性值

第一个值 默认值,singleton,表示的是单实例对象

第二个值 prototype,表示的是多实例对象。

3)singleton与prototype的区别

第一:singleton是单实例,prototype是多实例

第二:设置scope值是singleton的时候,加载Spring配置文件时候就会创建单实例对象。

设置scope值是prototype的时候,不是在加载Spring配置文件时候创建对象,在调用getBean方法时候创建多实例对象。

bean的生命周期

生命周期:从对象创建到对象销毁的过程。

bean生命周期:

1)通过构造器床架bean实例(无参数构造)

2)为bean的属性设置值和对其他bean引用(调用set方法)

3)调用bean的初始化方法(需要进行配置)

4)bean此时可以使用了(获取到了对象)

5)当容器关闭的时候,调用bean的销毁的方法(需要进行配置销毁的方法)

AOP面向切面编程

3.1 增加功能,导致的问题

在源代码中,业务方法中增加的功能

1)源代码可能改动的比较多

2)重复代码比较多

3)代码难以维护

3.2AOP概念

AOP(Aspect Orient Programming):面向切面编程,不修改源代码进行功能增强。

Aspect:表示切面,给业务方法增加的功能,叫做切面,切面一般都是非业务功能,而且切面功能一般都是可以复用的。例如:日志功能,事务功能,权限检查,参数检查,统计信息等等。

怎么理解面向切面编程:以切面为核心设计开发你的应用

1)设计项目时,找出切面的功能,

2)安排切面的执行时间,执行的位置

AOP底层原理

1.AOP底层使用动态代理,其中有两种情况的动态代理

第一种:有接口的情况下,使用JDK动态代理

创建接口实现类代理对象,增强类的方法

第二种:没有接口情况,使用CGLIB动态代理。

创建子类的代理对象,增强类的方法。

3.3AOP的作用

1)让切面功能复用

2)让开发人员专注业务逻辑,提高开发效率。

3)实现业务功能和其他非业务功能解耦合。

4)给存在的业务方法,增加功能,不用修改原来的代码。

3.3AOP中的术语

1)Aspect:切面,给业务方法增加的功能,把通知(Advice)应用到切入点的过程

2)JoinPoint:连接点,连接切面的业务方法,在这个业务方法执行时,会同时执行切面的功能。类中哪些方法可以被增强,这些被增强的方法就是连接点。

3)Pointcut:切入点,是一个或者多个连接点集合。表示这些方法执行时,都能增加切面的功能。表示切面执行的位置。实际被真正增强的方法,就被称为切入点。

4)target:目标对象,给哪个对象增加切面的功能,哪个对象就是目标对象。

5)Advice:通知(增强),表示切面的执行时间,表示的是在目标方法之前执行切面,还是在目标方法之后执行切面。实际被增强的逻辑部分,称为通知

AOP中重要的三个要素:Aspect,Pointcut,Advice,这个概念的理解是:在Advice的时间,在Pointcut的位置,执行Aspect。

AOP是一个动态的思想,在程序运行期间,创建代理(ServiceProxy),使用代理执行方法时,增加切面的功能,这个代理对象存在于内存中。

3.4 什么时候使用AOP

要给某些方法增加一些相同的功能,并且源代码不能修改或者难以修改的时候。给业务方法增加非业务功能时也可以使用AOP。

3.5 AOP技术思想的实现

使用框架AOP,实现AOP的框架有很多,有名的有两个

1)Spring:Spring框架实现AOP思想中的部分功能,Spring框架实现AOP的操作比较繁琐,

2)Aspectj:独立的框架,专门是AOP,属于Eclipse。Spring框架一般都是基于AspectJ实现AOP操作。

3.6使用AspectJ框架实现AOP

AspectJ:AspectJ不是Spring的组成部分,它是一个基于Java语言的AOP框架。一般把AspectJ和Spring框架一起使用,进行AOP操作。

AspectJ框架可以使用注解(实际开发中推荐使用)和xml配置文件两种方式实现AOP

3.6.1基于注解方式实现AOP

AspectJ表示切面执行时间,用的通知(Advice),这个通知可以使用注解表示,

一共有五个注解,表示切面的五个执行时间,这些注解叫做通知注解:

@Before:前置注解

@AfterReturning:后置注解(如果程序有异常,不会执行)

@Around:环绕通知

@AfterThrowing:异常通知

@After:最终通知 (有没有异常都会执行)

如果要使用这些注解,要在pom.xml中加入依赖

	<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.12</version>
</dependency>

相关代码:

<!--配置文件-->
<?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:context="http://www.springframework.org/schema/context"
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/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

<context:component-scan base-package="com.hao.user"></context:component-scan>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
//被增强的类
package com.hao.user;

import org.springframework.stereotype.Component;

@Component(value="user")
public class User {
public void add(){
System.out.println("User add.........");
}
}
//增强User类的类
package com.hao.user;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Component(value="userProxy")
@Aspect
public class UserProxy {
@Before(value="execution(* com.hao.user.User.add(..))")
public void before(){
System.out.println("User的增强功能,在User类的Add方法之前执行");
}

@Around(value="execution(* com.hao.user.User.add(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint)throws Throwable{
System.out.println("User类的功能增强,在User类的add方法执行前后都会执行");
proceedingJoinPoint.proceed();//执行被增强的方法
System.out.println("之后执行");
}
}
//测试类
@Test
public void test001(){
String config="applicationContext.xml";
ApplicationContext applicationContext=new ClassPathXmlApplicationContext(config);
User user=applicationContext.getBean("user", User.class);
user.add();
}
相同切入点的抽取
    @Pointcut(value="execution(* com.hao.user.User.add(..))")
public void pointcut(){}

@Around(value="pointcut()")
public void around(ProceedingJoinPoint proceedingJoinPoint)throws Throwable{
System.out.println("User类的功能增强,在User类的add方法执行前后都会执行");
proceedingJoinPoint.proceed();//执行被增强的方法
System.out.println("之后执行");
}
当有多个增强类对同一个方法进行增强时,可以设置增强类优先级

1)在增强类上面添加注解@Order(数字类型值),数字类型值越小优先级越高。

3.6.2 Pointcut 切面执行的位置

切入点表达式作用:可以知道对哪个类里面的那个方法进行增强。

Pointcut:表示切面执行的位置,使用AspectJ中切入点表达式

切入点表达式语法:execution(访问权限 方法返回值 方法声明(参数) 异常类型)

execution([权限修饰符] [返回类型] [类全路径] [方法名称] (参数列表))

举例1:对com.hao.dao.UserDao类里面的add进行增强

execution(* com.hao.dao.UserDao.add(..))

*表示权限类型:public private等等.....

其中返回类型可以省略不写

com.hao.dao.UserDao.add(..)表示方法声明,其中..表示参数列表

举例二:对com.hao.dao.UserDao类里面的所有方法进行增强

execution(* com.hao.dao.UserDao.*(..))

第一个*表示权限类型,第二个*表示UserDao类中的所有方法

举例三:对com.hao.dao包里面的所有类所有的的方法进行增强

execution(* com.hao.dao.*.*(..))

3.6.3 @AfterReturning:后置通知

属性:value 切入点表达式

returning 自定义的变量,表示目标方法的返回值的

自定义变量名称必须和通知方法的形参名一样。

位置:在方法的上面

特点:

1.在目标方法之后执行的

2.能获取到目标方法的执行结果

3.不会影响目标方法的执行。

JdbcTemplate

JdbcTemplate:Spring框架对JDBC进行封装,使用JdbcTemplate方便实现对数据库的操作。

准备工作:

1,引入相关的依赖

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.10</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.12</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-orm -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>5.3.12</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-tx -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.3.12</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>

2.在Spring配置文件配置数据库连接池

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/springdb"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>

3.配置JdbcTemplate,注入DataSource

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>

4.创建service类,创建dao类,在dao注入jdbcTemplate对象。

Spring整合MyBatis

将 MyBatis 与 Spring 进行整合,主要解决的问题就是将 SqlSessionFactory 对象交由 Spring 来管理。所以,该整合,只需要将 SqlSessionFactory 的对象生成器 SqlSessionFactoryBean 注 册在 Spring 容器中,再将其注入给 Dao 的实现类即可完成整合。 实现 Spring 与 MyBatis 的整合常用的方式:扫描的 Mapper 动态代理

实现步骤:

1.使用的mysql库,使用学生表

student2(id int 主键列,自动增长,

name varchar(80),

age int);

 create table student2(
-> id int(11) primary key not null auto_increment,
-> name varchar(20) default null,
-> age int(5) default null
-> )engine=innodb,character set=utf8;

2.创建maven项目

3.加入依赖gav

spring依赖,mybatis依赖,mysql依赖

mybatis-spring依赖(mybatis网站上提供的,用来在spring项目中,创建mybatis对象)

spring有关的依赖

注意:mybatis和spring整合的时候,事务是默认自动提交的。

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>

<!--Spring依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!--Spring事务依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!--Spring事务依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!--mybatis依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.1</version>
</dependency>
<!--Spring和Mybatis集成依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
<!--MySQL驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.9</version>
</dependency>
<!--阿里的连接池,德鲁伊-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>

加入的相关插件,防止idea不编译.xml文件

	<resources>
<resource>
<directory>src/main/java</directory><!--所在的目录-->
<includes><!--包括目录下的.properties,.xml 文件都会扫描到-->
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>

4.创建实体类Student

package com.hao.entity;

public class Student {
private Integer id;
private String name;
private Integer age;

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

public Integer getId() {
return id;
}

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

public String getName() {
return name;
}

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

public Integer getAge() {
return age;
}

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

5.创建dao接口和mapper文件写sql语句

package com.hao.dao;

import com.hao.entity.Student;
import org.springframework.stereotype.Repository;

import java.util.List;

public interface StudentDao {
int insertStudent(Student student);
List<Student> selectStudents();
}

StudentDao接口实现类:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.hao.dao.StudentDao">
<insert id="insertStudent">
insert into student2(name,age) values(#{name},#{age})
</insert>

<select id="selectStudents" resultType="com.hao.entity.Student">
select * form student2
</select>

</mapper>

6.写mybatis主配置文件

<?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>
<!--
<settings>
<setting name="logImpl" value="STDOUT_LOGGING" />
</settings>
-->

<typeAliases>
<package name="com.hao.entity"/>
</typeAliases>

<mappers>
<mapper resource="com/hao/dao/StudentDao.xml"/>
</mappers>
</configuration>

7.创建service接口和他的实现类

package com.hao.service;

import com.hao.entity.Student;

import java.util.List;

public interface StudentService {
public int addStudent(Student student);
public List<Student> queryStudent();
}

实现类:

package com.hao.service.impl;

import com.hao.dao.StudentDao;
import com.hao.entity.Student;
import com.hao.service.StudentService;

import java.util.List;

public class StudentServiceImpl implements StudentService {
private StudentDao studentDao;

public void setStudentDao(StudentDao studentDao) {
this.studentDao = studentDao;
}

@Override
public int addStudent(Student student) {
int rows=studentDao.insertStudent(student);
return rows;
}

@Override
public List<Student> queryStudent() {
List<Student> students=studentDao.selectStudents();
return students;
}
}

8.创建Spring配置文件

1)声明数据源DataSource,使用的阿里的Druid连接池

2)声明SqlSessionFactoryBean类,在这个类内部创建的是SqlSessionFactory对象。

3)声明MapperScannerConfiguration类,在内部创建dao代理对象,创建的对象都放在spring容器中。

4)声明Service对象,把3)中的到赋值给service属性

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

<!--声明数据源DataSource-->
<bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="url" value="jdbc:mysql://localhost:3306/springdb"></property>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<!--声明SqlSessionFactoryBean,在这个类的内部创建SqlSessionFactory-->
<!--其中factory就是创建的SqlSessionFactory对象的名字-->
<bean id="factory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--指定数据源-->
<property name="dataSource" ref="myDataSource"/>
<!--指定mybatis的主配置文件
Resources可以直接使用value属性赋值
-->
<property name="configLocation" value="classpath:mybatis.xml"/>
</bean>

<!--声明MapperScanConfigurer
MapperScannerConfigurer作用:循环basePackage所表示的包,把包中每个接口都找到,调用sqlSession.getMapper
把每个dao接口都创建出dao对象,dao代理对象放在容器中。
-->
<!--创建dao的代理-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--指定SqlSessionFactory对象的名称-->
<property name="sqlSessionFactoryBeanName" value="factory"/>
<!--指定基本包,dao接口所在的包名-->
<property name="basePackage" value="com.hao.dao"/>
</bean>
<bean id="studentService" class="com.hao.service.impl.StudentServiceImpl">
<property name="studentDao" ref="studentDao"/>
</bean>
</beans>

9.测试dao访问数据库

package com.hao;

import com.hao.dao.StudentDao;
import com.hao.entity.Student;
import com.hao.service.StudentService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
@Test
public void test001(){
String config="applicationContext.xml";
ApplicationContext applicationContext=new ClassPathXmlApplicationContext(config);
StudentDao studentDao=applicationContext.getBean("studentDao", StudentDao.class);
Student student=new Student();
student.setName("张三");
student.setAge(18);
studentDao.insertStudent(student);
}
@Test
public void test002(){
String config="applicationContext.xml";
ApplicationContext applicationContext=new ClassPathXmlApplicationContext(config);
StudentService studentService=applicationContext.getBean("studentService", StudentService.class);
Student student=new Student();
student.setName("李四");
student.setAge(18);
studentService.addStudent(student);
}
}

事务

事务概念:事务是数据库操作最基本单元,逻辑上一组操作,要么都成功,要么都失败,如果有一个失败所有操作都失败。

典型场景:银行转账。

事务的四个特性(ACID):原子性,一致性,隔离性,持久性。

事务操作:Spring事务管理介绍

1.事务添加到javaee三层结构里面的Service层(业务逻辑层)

三层结构:表示层,业务逻辑层,数据访问层。

2.在Spring进行事务管理操作

1)有两种方式:编程式事务管理和声明式事务管理(常使用的)

3.声明式事务管理

1)基于注解方式(使用,方便简单)

2)基于xml配置文件方式

4.在Spring进行声明式事务管理,底层使用AOP

5.Spring事务管理API,提供了一个接口代表事务管理器,这个接口针对不同的框架提供不同的实现类

PlatformTransactionManager接口

1)实现类为DataSourceTransactionManager,用于配置mybatis和jdbc数据源的事务管理器。

2)实现类为HibernateTransactionManager,用于配置Hibernate的事务管理器。

通过注解方式实现声明式事务管理

1.在spring配置文件配置事务管理器

  <!--创建事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入数据源-->
<property name="dataSource" ref="dataSource"/>
</bean>

2.在spring配置文件,开启事务注解

<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>

3.在service类上面(获取service类黎明方法上面)添加事务注解

1)@Transactional,这个注解可以添加到类上面,也可以添加到方法上面

2)如果把这个注解添加到类上面,这个类里面所有的方法都添加事务。

3)如果把这个注解添加方法上面,就是为这个方法添加事务。

声明式事务管理参数配置

1.在service类上面添加注解@Transactional,在这个注解里面可以配置事务相关参数。

 

 

propagation:事务传播行为,默认为REQUIRED

多事务方法直接进行调用,这个过程中事务是如何进行管理的。

事务方法:对数据库表中数据进行变化的操作。

 

 

 

 

isolation:事务隔离级别,系统默认的隔离级别是:REPEATABLE READ(可重复读)

1)事务有一种特性为隔离性,多事务操作之间不会产生影响。不考虑隔离性会产生很多问题。

2)产生的问题为:脏读,不可重复读,虚(幻)读

脏读:一个未提交的事务读取到另一个未提交事务的数据。

不可重复读:一个未提交事务读取到了另一个已提交事务修改的数据。

虚读:一个未提交事务读取到了另一个已经提交的事务添加的数据。

3)通过设置事务隔离性,解决读问题

 

 

timeout:超时时间

1)事务需要在一定时间内进行提交,如果不提交进行回滚

2)默认值是-1(即不超时),设置时间以秒为单位进行计算。

readOnly:是否只读,默认为false

1)读:查询操作,写:添加修改删除操作

2)readOnly默认值false,表示可以查询,可以进行添加修改删除操作

rollbackFor:回滚

设置出现哪些异常进行事务回滚

noRollbackFor:不回滚

设置出现哪些异常不进行事务回滚。

通过XML方式声明事务管理

1.在Spring配置文件中进行配置

第一步:配置事务管理器

第二步:配置通知

第三步:配置切入点和切面。

我的第一个Spring项目

1.新建maven项目

2.加入依赖,修改pom.xml

spring-context:spring依赖

junit:单元测试

3.开发人员定义类:接口和实现类

类也可以没有接口。

接口和实现类定义方法和没有Spring时的定义方式一样。

4.创建spring的配置文件,

作用:声明对象,把对象交给spring创建和管理。

使用<bean>表示对象声明,一个bean表示一个java对象。

5.使用容器中的对象

创建一个表示spring容器的对象,ApplicationContext

String config="beans.xml";//spring的配置文件
ApplicationContext cxt=new ClassPathXmlApplicationContext(config);//创建容器
/*
注意:容器一旦被创建,bean文件里面的所有对象就会被立刻创建出来。
此种方式优点:
获取对象的速度快,因为对象已经被创建好了
缺点:
占内存
在创建容器(ApplicationContext)对象时,会把配置文件中的所有对象都创建出来(spring默认规则),即使两个对象的路径一样,只是id名不一样也会被都创建出来。
*/

Spring创建对象,默认是调用类的无参构造方法。

从容器中根据名称获取对象,使用getBean(“对象名称”)



posted @ 2021-11-11 15:47  AMHAO  阅读(70)  评论(0编辑  收藏  举报