Spring 起步

209 阅读10分钟

Spring 是什么?

是一个开源的,轻量级的 Java 开发框架

Spring 的作用?

简化 java 应用程序的开发

使用了那些技术?

  1. 控制反转(IOC Inversion of Control)
  2. 面向切面编程(AOP Aspect Oriented Programming)

控制反转(IOC Inversion of Control)

IOC的底层就是对象工厂

什么是IOC(控制反转)

将对象的创建、依赖关系、调用交给 Spring 来管理

为什么使用 IOC

  • 降低代码间的耦合度
  • 减少代码量

IOC底层原理

使用xml解析、工厂模式、反射

  1. 配置xml文件
  2. 创建工厂类
	String classPath = “xml解析”;
    Class cl = Class.forName(classPath);
    cl.newInstance();

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

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

b)ApplicationContext接口:BeanFactory接口的子接口,提供更多更强大的功能,提供给开发人员使用(加载配置文件时候就会把在配置文件对象进行创建)推荐使用!

Bean 管理

  • 创建对象
  • 注入属性

创建对象

基于 xml 配置文件创建对象

<!--1 配置User对象创建-->
<bean id="user" class="com.happy.User"></bean>

注入属性

基于 xml 方式注入属性

  • set 方法进行注入
  • 有参构造函数注入

实现

  1. 通过 set 方法进行注入
//(1)传统方式: 创建类,定义属性和对应的set方法
public class Book {
    //创建属性
    private String bname;

    //创建属性对应的set方法
    public void setBname(String bname) {
        this.bname = bname;
    }
}
<!--(2)spring方式: set方法注入属性-->
<bean id="book" class="com.atguigu.spring5.Book">
    <!--使用property完成属性注入
        name:类里面属性名称
        value:向属性注入的值
    -->
    <property name="bname" value="Hello"></property>
    <property name="bauthor" value="World"></property>
</bean>
  1. 有参构造函数注入
//(1)传统方式:创建类,构建有参函数
public class Orders {
    //属性
    private String oname;
    private String address;
    //有参数构造
    public Orders(String oname,String address) {
        this.oname = oname;
        this.address = address;
    }
}
<!--(2)spring方式:有参数构造注入属性-->
<bean id="orders" class="com.atguigu.spring5.Orders">
    <constructor-arg name="oname" value="Hello"></constructor-arg>
    <constructor-arg name="address" value="China!"></constructor-arg>
</bean>
  1. 注入外部 bean
  2. 注入内部 bean
  3. 级联赋值
  4. 注入集合属性
<!--(2)在 spring 配置文件进行配置-->
<bean id="stu" class="com.atguigu.spring5.collectiontype.Stu">
    <!--数组类型属性注入-->
    <property name="courses">
        <array>
            <value>java课程</value>
            <value>数据库课程</value>
        </array>
    </property>
    <!--list类型属性注入-->
    <property name="list">
        <list>
            <value>张三</value>
            <value>小三</value>
        </list>
    </property>
    <!--map类型属性注入-->
    <property name="maps">
        <map>
            <entry key="JAVA" value="java"></entry>
            <entry key="PHP" value="php"></entry>
        </map>
    </property>
    <!--set类型属性注入-->
    <property name="sets">
        <set>
            <value>MySQL</value>
            <value>Redis</value>
        </set>
    </property>
</bean>

Bean 的类型 FactoryBean

Spring 中有两种类型的 Bean 一种是普通 Bean 一种是工厂 Bean

  • 普通 Bean:在配置文件中定义 bean 类型就是返回的 bean 的类型
  • 工厂 Bean:定义的类型和返回的类型不一样
    • 创建类,让该类作为工厂 bean,实现 FactoryBean 接口
    • 实现接口中的方法,定义返回的 bean 类型
public class MyBean implements FactoryBean<Course> {

    //定义返回bean
    @Override
    public Course getObject() throws Exception {
        Course course = new Course();
        course.setCname("abc");
        return course;
    }
}

<bean id="myBean" class="com.atguigu.spring5.factorybean.MyBean">
</bean>

@Test
public void test3() {
 ApplicationContext context =
 new ClassPathXmlApplicationContext("bean3.xml");
 Course course = context.getBean("myBean", Course.class);//返回值类型可以不是定义的bean类型!
 System.out.println(course);
}

bean 的作用域

默认情况下 bean 是单实例对象

scope属性

通过设置 bean 标签中的 scope 属性来控制 bean 是为单实例(singleton)还是多实例(prototype)

bean 生命周期

正常 bean 生命周期

  1. 通过无参构造器创建 bean 实例
  2. 注入 bean 属性
  3. 调用 bean 的初始化方法(需要配置)
  4. 获取到 bean,可以使用了
  5. 当容器关闭时调用 bean 的销毁方法(需要配置)

配置后置处理器 bean 的生命周期

  1. 通过无参构造器创建 bean 实例
  2. 注入 bean 属性
  3. 将 bean 实例传递到后置处理器中的 postProcessBeforeInitialization方法
  4. 调用 bean 的初始化方法(需要配置)
  5. 将 bean 实例传递到后置处理器中的 postProcessAfterInitialization方法
  6. 获取到 bean,可以使用了
  7. 当容器关闭时调用 bean 的销毁方法(需要配置)
public class Orders {
    //无参数构造
    public Orders() {
    System.out.println("第一步 执行无参数构造创建 bean 实例");
    }
    private String oname;
    public void setOname(String oname) {
    this.oname = oname;
    System.out.println("第二步 调用 set 方法设置属性值");
    }
    //创建执行的初始化的方法
    public void initMethod() {
    System.out.println("第三步 执行初始化的方法");
    }
    //创建执行的销毁的方法
    public void destroyMethod() {
    System.out.println("第五步 执行销毁的方法");
    }
}
        
public class MyBeanPost implements BeanPostProcessor {//创建后置处理器实现类
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("在初始化之前执行的方法");
        return bean;
    }
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("在初始化之后执行的方法");
        return bean;
    }
}

<!--配置文件的bean参数配置-->
<bean id="orders" class="com.atguigu.spring5.bean.Orders" init-method="initMethod" destroy-method="destroyMethod">	<!--配置初始化方法和销毁方法-->
    <property name="oname" value="手机"></property><!--这里就是通过set方式(注入属性)赋值-->
</bean>

<!--配置后置处理器-->
<bean id="myBeanPost" class="com.atguigu.spring5.bean.MyBeanPost"></bean>

 @Test
 public void testBean3() {
    // ApplicationContext context =
    // new ClassPathXmlApplicationContext("bean4.xml");
     ClassPathXmlApplicationContext context =
     new ClassPathXmlApplicationContext("bean4.xml");
     Orders orders = context.getBean("orders", Orders.class);
     System.out.println("第四步 获取创建 bean 实例对象");
     System.out.println(orders);
     //手动让 bean 实例销毁
     context.close();
 }
        

bean 管理外部属性文件

  1. 直接配置信息
<!--直接配置连接池-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/userDb"></property>
        <property name="username" value="root"></property>
        <property name="password" value="root"></property>
    </bean>
  1. 引入外部属性文件
//创建配置文件后缀位 properties
prop.driverClass=com.mysql.jdbc.Driver
prop.url=jdbc:mysql://localhost:3306/userDb
prop.userName=root
prop.password=root

<!--引入外部属性文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>

Bean 的基于注解的管理方式

使用步骤

  1. 引入依赖 spring-aop jar 包
  2. 开启扫描组件
<!--开启组件扫描
 1 如果扫描多个包,多个包使用逗号隔开
 2 扫描包上层目录
-->
<context:component-scan base-package="com.atguigu"></context:component-scan>
  1. 创建类,在类上方添加对象注解
//在注解里面 value 属性值可以省略不写,
//默认值是类名称,首字母小写
//UserService -- userService
@Component(value = "userService") //注解等同于XML配置文件:<bean id="userService" class=".."/>
public class UserService {
   public void add() {
   System.out.println("service add.......");
   }
}
  1. 开启组件扫描的详细配置
<!--示例 1
 use-default-filters="false" 表示现在不使用默认 filter,自己配置 filter
 context:include-filter ,设置扫描哪些内容
-->
<context:component-scan base-package="com.atguigu" use-defaultfilters="false">
 <context:include-filter type="annotation"

expression="org.springframework.stereotype.Controller"/><!--代表只扫描Controller注解的类-->
</context:component-scan>
<!--示例 2
 下面配置扫描包所有内容
 context:exclude-filter: 设置哪些内容不进行扫描
-->
<context:component-scan base-package="com.atguigu">
 <context:exclude-filter type="annotation"

expression="org.springframework.stereotype.Controller"/><!--表示Controller注解的类之外一切都进行扫描-->
</context:component-scan>
  1. 基于注解的方式实现属性的注入
@Service
public class UserService {
 //定义 dao 类型属性
 //不需要添加 set 方法
 //添加注入属性注解
 @Autowired
 private UserDao userDao;
 public void add() {
   System.out.println("service add.......");
   userDao.add();
   }
}

//Dao实现类
@Repository
//@Repository(value = "userDaoImpl1")
public class UserDaoImpl implements UserDao {
    @Override
    public void add() {
        System.out.println("dao add.....");
    }
}

注入属性的注解

  • @Qualifier 根据名称注入
  • @Autowired 根据类型注入
  • @Resource 可以根据类型也可以根据名称注入(属于 javax 包下的注解,不推荐使用)
  • @Value 注入普通类型的属性

使用配置类代替 xml 文件

@Configuration //作为配置类,替代 xml 配置文件
@ComponentScan(basePackages = {"com.atguigu"})
public class SpringConfig {
    
}

@Test
public void testService2() {
 //加载配置类
 ApplicationContext context
 = new AnnotationConfigApplicationContext(SpringConfig.class);
 UserService userService = context.getBean("userService",
UserService.class);
 System.out.println(userService);
 userService.add();
}

自动装配

blog.csdn.net/qq_36761831…

除了以上配置xml文件的方式,还以可以通过注解的方式来实现。 使用@Autowired、@Resource注解来自动装配指定的bean。在使用@Autowired注解之前需要在Spring配置文件进行配置,<context:annotation-config />。在启动spring IoC时,容器自动装载了一个AutowiredAnnotationBeanPostProcessor后置处理器,当容器扫描到@Autowied、@Resource或@Inject时,就会在IoC容器自动查找需要的bean,并装配给该对象的属性。在使用@Autowired时,首先在容器中查询对应类型的bean:

如果查询结果刚好为一个,就将该bean装配给@Autowired指定的数据;

如果查询的结果不止一个,那么@Autowired会根据名称来查找;

如果上述查找的结果为空,那么会抛出异常。解决方法时,使用required=false。

@Autowired可用于:构造函数、成员变量、Setter方法

注:@Autowired@Resource之间的区别:

(1) @Autowired默认是按照类型装配注入的,默认情况下它要求依赖对象必须存在(可以设置它required属性为false)。

(2) @Resource默认是按照名称来装配注入的,只有当找不到与名称匹配的bean才会按照类型来装配注入。

面向切面编程AOP

什么是 AOP

在不修改源代码的情况下,添加新的功能

AOP 如何实现

底层原理使用动态代理模式

AOP 中的术语

  1. 连接点:类里面哪些方法可以被增强,这些方法称为连接点

  2. 切入点:实际被真正增强的方法称为切入点

  3. 通知(增强):实际增强的逻辑部分称为通知,且分为以下五种类型:

    1. 前置通知
    2. 后置通知
    3. 环绕通知
    4. 异常通知
    5. 最终通知
  4. 切面:把通知应用到切入点过程

AOP实操

切入点表达式

用于指示那些地方需要被增强

1)语法结构: execution([权限修饰符] [返回类型] [类全路径] [方法名称]([参数列表]) )
(2)例子如下:
    例 1:对 com.atguigu.dao.BookDao 类里面的 add 进行增强
		execution(* com.atguigu.dao.BookDao.add(..))
 	例 2:对 com.atguigu.dao.BookDao 类里面的所有的方法进行增强
		execution(* com.atguigu.dao.BookDao.* (..))
    例 3:对 com.atguigu.dao 包里面所有类,类里面所有方法进行增强
		execution(* com.atguigu.dao.*.* (..))

基于 AspectJ框架实现 AOP 操作

  1. 基于 xml 配置文件
  2. 基于注解的方式实现

基于注解的方式

//1、创建类,在类里面定义方法
public class User {
   public void add() {
   System.out.println("add.......");
   }
}
//2、创建增强类(编写增强逻辑)
//(1)在增强类里面,创建方法,让不同方法代表不同通知类型
//增强的类
public class UserProxy {
   public void before() {//前置通知
   System.out.println("before......");
   }
}

<!--3、进行通知的配置-->
<?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 http://www.springframework.org/schema/context/spring-context.xsd
                        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!-- 开启注解扫描 -->
    <context:component-scan base-package="com.atguigu.spring5.aopanno"></context:component-scan>

    <!-- 开启Aspect生成代理对象-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

//增强的类
@Component
@Aspect  //生成代理对象
public class UserProxy {}

//被增强的类
@Component
public class User {}

//4、配置不同类型的通知
@Component
@Aspect  //生成代理对象
public class UserProxy {
      //相同切入点抽取
    @Pointcut(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
    public void pointdemo() {

    }

    //前置通知
    //@Before注解表示作为前置通知
    @Before(value = "pointdemo()")//相同切入点抽取使用!
    public void before() {
        System.out.println("before.........");
    }

    //后置通知(返回通知)
    @AfterReturning(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
    public void afterReturning() {
        System.out.println("afterReturning.........");
    }

    //最终通知
    @After(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
    public void after() {
        System.out.println("after.........");
    }

    //异常通知
    @AfterThrowing(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
    public void afterThrowing() {
        System.out.println("afterThrowing.........");
    }

    //环绕通知
    @Around(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("环绕之前.........");

        //被增强的方法执行
        proceedingJoinPoint.proceed();

        System.out.println("环绕之后.........");
    }
}

存在多个增强时设置优先级

//(1)在增强类上面添加注解 @Order(数字类型值),数字类型值越小优先级越高
@Component
@Aspect
@Order(1)
public class PersonProxy{ }

基于配置文件进行增强

<!--1、创建两个类,增强类和被增强类,创建方法(同上一样)-->
<!--2、在 spring 配置文件中创建两个类对象-->
<!--创建对象-->
<bean id="book" class="com.atguigu.spring5.aopxml.Book"></bean>
<bean id="bookProxy" class="com.atguigu.spring5.aopxml.BookProxy"></bean>
<!--3、在 spring 配置文件中配置切入点-->
<!--配置 aop 增强-->
<aop:config>
 <!--切入点-->
 <aop:pointcut id="p" expression="execution(* com.atguigu.spring5.aopxml.Book.buy(..))"/>
 <!--配置切面-->
 <aop:aspect ref="bookProxy">
 <!--增强作用在具体的方法上-->
 <aop:before method="before" pointcut-ref="p"/>
 </aop:aspect>
</aop:config>

事务

事务数据库操作的基本单元,逻辑上一组操作,要么都成功,要么都失败

事务的特性

  1. 原子性
  2. 一致性
  3. 操作性
  4. 持久性

事务操作的基本过程

  1. 开启事务
  2. 进行业务操作
  3. 没有发生异常,提交事务
  4. 发生了异常,事务回滚

Spring 事务管理的 API

PlatformTransactionManager;这个接口针对不同的框架提供不同的实现类

在 Spring 中进行事务管理操作

一般添加到业务逻辑层 两种进行事务管理的方式

  1. 编程式事务管理
  2. 声明式事务管理

声明式事务管理

底层采用的是 AOP原理 两种实现方式

  1. 基于 xml 配置文件
    1. 在 spring 配置文件中配置事务管理器 选择对应的事务管理器,注入数据源
    2. 引入响应的名称空间tx
    3. 配置事务通知
    4. 配置切入点和切面
  2. 基于注解方式实现
    1. 在 spring 配置文件中配置事务管理器 选择对应的事务管理器,注入数据源
    2. 在 spring 配置文件中开启事务注解 (1) 引入响应的名称空间tx (2) 开启事务注解
    3. 在需要使用事务管理的地方添加注解@Transactional

@Transactional 参数配置

  1. propagation 事务传播行为

2. isolation 事务隔离级别 事务有隔离性,多事务之间不会产生影响。如果不考虑隔离性将会产生很多问题。
产生是三个读的问题:赃读、不可重复度、虚(幻)读。 1. 赃读 一个未提交的事务读取到另一个未提交事务的数据 2. 不可重复读 一个未提交事务读取到另一个提交事务修改的数据 3. 虚读 一个未提交事务读取到另一个提交事务添加的数据 使用隔离级别来解决问题
3. timeout 超时时间
事务需要在规定时间内提交,如果不提交就进行回滚。 默认的时间是-1
4. readOnly 是否只读
默认为 false,也就是可以进行查询和修改,如果为 true 则只能进行查询操作
5. rollbackFor 回滚
设置出现哪些异常进行回滚
6. noRollbackFor 不会滚
设置出现哪些异常不进行回滚