Spring学习笔记

目录

1 IOC容器

控制反转:所有的类都会在spring容器中登记,告诉spring你是个什么东西,你需要什么东西,然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转。

1.1 概念

  1. IOC是控制反转,可以用来降低代码之间的耦合度;
  2. 将对象的创建和对象之间的调用过程,交给spring管理;
  3. IOC思想是基于容器实现的,其底层就是对象工厂;

1.2 IOC的底层原理

  • 所使用的技术:xml文件+工厂模式+反射
  • 实现流程
    在这里插入图片描述
  • 如何实现降低耦合度:如果需要调整UserDao的位置,那么只需要修改xml文件bean标签中UserDao的全类名即可,从而降低了UserService和UserDao之间的耦合度。

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

1.3.1 BeanFactory接口

  • IOC 容器基本实现,是 Spring 内部的使用接口,不提供开发人员进行使用。
  • 特点加载配置文件时候不会创建对象,在获取对象(使用)才去创建对象,是一种懒加载的方式。
    在这里插入图片描述

1.3.2 ApplicationContext接口

  • BeanFactory 接口的子接口,提供更多更强大的功能,一般由开发人员进行使用。
  • 特点:加载配置文件时候就会创建配置文件中的所有对象。

1.3.3 为什么开发中使用ApplicationContext接口

相比于ApplicationContext,BeanFactory的懒加载貌似更加节省资源,但是我们通常希望在服务启动的时候就完成这些消耗大量资源的事情—加载xml中所有的对象,这样在后续的操作中就不需要使用资源来进行加载。

1.3.4 ApplicationContext接口的两个实现类

  • FileSystemXmlApplicationContext:创建对象时,传入的是绝对路径
  • ClassPathXmlApplicationContext:创建对象时,传入的是相对于src的路径
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
ApplicationContext context1 = new FileSystemXmlApplicationContext("D:\\研究生材料\\java代码\\spring-demo1\\src\\bean1.xml");

1.4 IOC操作之bean管理

  • bean管理的两个操作:
    • Spring创建对象;
    • Spring注入属性;
  • bean管理操作的两种方式:
    • 基于xml配置文件方式实现
    • 基于注解方式实现
  • DI(依赖注入):是IOC的一种具体实现方式,就是注入属性。所谓依赖注入,即在运行期由容器将依赖关系注入到组件之中。讲的通俗点,就是在运行期,由Spring根据配置文件,将其他对象的引用通过组件的提供的setter方法进行设定。

1.4.0 bean是什么?

bean相当于类的代理,通过bean可以实现创建对象和属性的注入。

1.4.1 基于xml配置文件方式实现

1.4.1.1 创建对象

在这里插入图片描述

  • 在Spring中 配置文件中,使用 bean 标签,标签里面添加对应属性,就可以实现对象创建;
  • Spring默认使用无参构造实现对象的创建,所以类中一定要存在无参构造器;
  • 在 bean 标签有很多属性,常用的属性包括:
    • id 属性:自定义唯一标识
    • class 属性:类全路径(包类路径)
1.4.1.2 注入属性的几种方式
1.4.1.2.1 通过Setter方法注入属性

在 spring 配置文件中进行配置,通过property标签给对象的属性赋值.
在这里插入图片描述
User类
在这里插入图片描述
在单元测试方法中获取User对象
在这里插入图片描述

1.4.1.2.2 使用有参数构造进行注入

在 spring 配置文件中进行配置,通过constructor-arg标签实现属性注入
在这里插入图片描述
在这里插入图片描述

1.4.1.2.3 p 名称空间注入(了解)

使用 p 名称空间注入,可以简化基于 xml 配置方式的Setter注入

第一步 添加 p 名称空间在配置文件中
在这里插入图片描述

第二步 进行属性注入,在 bean 标签里面进行操作
在这里插入图片描述

1.4.1.3 通过xml 注入其他类型属性
  • 对于自定义类属性的赋值,需要使用外部bean、内部bean和级联赋值的方式
1.4.1.3.1 字面量
  • null 值
    <bean id="user1" class="cn.zhou.spring.User" >
	<!--在property标签内添加null标签-->    
        <property name="name">
            <null></null>
        </property>
        <property name="age" value="19">
        </property>
    </bean>
  • 属性值包含特殊符号
    • 方式1:可以将特殊符号转义,例如<>可以转义为&lt; &gt;
    • 方式2:把带特殊符号内容写到 CDATA,<![CDATA[内容]]>,这种方式必须将内容写在value标签中。
    <bean id="user1" class="cn.zhou.spring.User" >
        <property name="name">
            <value><![CDATA[<<张三>>]]></value>
        </property>
    </bean>
1.4.1.3.2 外部bean
  • 外部bean指的是被引用的bean直接定义在beans标签下,与引用bean属于同一级。
  • 需要在xml配置文件中添加两个类的bean标签,然后属性的值使用ref指向另一个类的bean,具体操作如下:
<!--1 service 和 dao 对象创建--> 
<bean id="userService" class="com.atguigu.spring5.service.UserService">
 <!--注入 userDao 对象
 name 属性:类里面属性名称
 ref 属性:创建 userDao 对象 bean 标签 id 值
 -->
 <property name="userDao" ref="userDaoImpl"></property>
</bean> 
<bean id="userDaoImpl" class="com.atguigu.spring5.dao.UserDaoImpl"></bean>
public class UserService {
 //创建 UserDao 类型属性,生成 set 方法
 private UserDao userDao;
 public void setUserDao(UserDao userDao) {
 this.userDao = userDao;
 }
 public void add() {
 System.out.println("service add...............");
 userDao.update();
 } }

在这里插入图片描述

1.4.1.3.3 内部 bean
  • 内部 bean指的是被引用的bean定义再引用bean的内部,两者是从属的关系。
  • 内部 bean和外部bean的区别:内部bean只能被定义它的bean使用,而外部bean可以被多个bean对象引用,两者相当于局部变量和成员变量的关系
<!--内部 bean--> <bean id="emp" class="com.atguigu.spring5.bean.Emp">
 <!--设置两个普通属性-->
 <property name="ename" value="lucy"></property>
 <property name="gender" value="女"></property>
 <!--设置对象类型属性-->
 <property name="dept">
  <!--在属性内部定义bean-->
 <bean id="dept" class="com.atguigu.spring5.bean.Dept">
 <property name="dname" value="安保部"></property>
 </bean>
 
 </property>
 
</bean>
1.4.1.3.4 级联赋值
  • 方式1:就是外部bean的写法
    在这里插入图片描述
  • 方式2:直接在bean中使用property标签给自定义类的属性赋值,注意需要在类中添加自定义类变量的getter方法。
    在这里插入图片描述
    在这里插入图片描述
1.4.1.4 xml 注入集合属性
1.4.1.4.1 注入数组、 List 集合、 Map 集合类型属性
  1. 创建类,定义数组、list、map、set 类型属性,生成对应 set 方法。
    在这里插入图片描述
  2. 在 spring 配置文件进行配置
    在这里插入图片描述
1.4.1.4.2 在集合里面设置对象类型值

将集合中的value标签改为ref标签,在ref标签的bean属性中指定对象bean的id。

     <property name="courseList">
         <list>
             <ref bean="course1"></ref>
             <ref bean="course2"></ref>
         </list>
     </property>

    <bean id="course1" class="cn.zhou.spring.collectiontest.Course">
        <property name="course_id" value="1"></property>
        <property name="course_name" value="zs"></property>
    </bean>
    <bean id="course2" class="cn.zhou.spring.collectiontest.Course">
        <property name="course_id" value="2"></property>
        <property name="course_name" value="ls"></property>
    </bean>
1.4.1.4.3 把集合注入部分提取出来
  1. 导入util命名空间
<?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:p="http://www.springframework.org/schema/p"
 xmlns:util="http://www.springframework.org/schema/util"
 xsi:schemaLocation="http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans.xsd
 http://www.springframework.org/schema/util 
http://www.springframework.org/schema/util/spring-util.xsd">
  1. 使用 util 标签完成 list 集合注入提取
    <!--    提取 list 集合类型属性注入-->
    <util:list id="list1">
        <value>12312</value>
        <value>12312</value>
    </util:list>

    <property name="list" ref="list1">
    </property>

1.4.2 Spring中两种类型的bean

1.4.2.1 普通bean

返回类型和配置文件中定义 bean 的类型相同。

// 配置文件中定义类型Student
<bean id="stu" class="cn.zhou.spring.collectiontest.Student" >
// 创建对象时返回的类型Student
        ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml");
        Student stu1 = context.getBean("stu",Student.class);
1.4.2.2 工厂 bean

在配置文件定义 bean 类型可以和返回类型不一样.

实现步骤
创建类,让这个类作为工厂 bean,实现接口 FactoryBean,实现接口里面的方法,在实现的方法中定义返回的 bean 类型。

public class MyBean implements FactoryBean<Student> {

    @Override
    public Student getObject() throws Exception {
        Student student = new Student();
        return student;
    }

    @Override
    public Class<?> getObjectType() {
        return null;
    }

    @Override
    public boolean isSingleton() {
        return false;
    }
}

在这里插入图片描述

1.4.3 bean 作用域

  • bean标签中的scope属性是用来设置bean 实例是单实例singleton还是多实例prototype
  • 默认情况下,scope的值为singleton,单实例;
  • 单实例的情况下,通过此bean标签创建的对象都是同一个,而且单实例的bean的对象在xml文件加载的时候就已经创建完成;
  • 多实例的情况下,通过此bean标签创建的对象都是独立的,多实例bean的对象不是在加载 spring 配置文件时候创建 对象,在调用getBean 方法时候创建多实例对象。
    在这里插入图片描述
    在这里插入图片描述

1.4.4 bean的生命周期

1.4.4.1 7步生命周期

bean的生命周期总共有7步:
(1)通过构造器创建 bean 实例(无参数构造)
(2)为 bean 的属性设置值和对其他 bean 引用(调用 set 方法)
(3)把 bean 实例传递 bean 后置处理器的方法 postProcessBeforeInitialization
(4)调用 bean 的初始化的方法(需要进行配置初始化的方法)
(5)把 bean 实例传递 bean 后置处理器的方法 postProcessAfterInitialization
(6)bean 可以使用了(对象获取到了)
(7)当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)
在这里插入图片描述

对于ApplicationContext接口来说,在加载xml配置文件时就会创建所有的对象,所以前五步会执行;第六步是主动获取已经创建好的对象;第七步是容器关闭时,销毁bean。

1.4.4.2 初始化方法和销毁方法的配置

在类中创建初始化和销毁的方法,并且要将其配置到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("第五步 执行销毁的方法");
 } }
 
 <bean id="orders" class="com.atguigu.spring5.bean.Orders" init-method="initMethod" destroy-method="destroyMethod">
 <property name="oname" value="手机"></property>
</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();
 }
1.4.4.3 添加后置处理器

(1)创建类,实现接口 BeanPostProcessor,创建后置处理器
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;
} }
(2)配置后置处理器,后置处理器配置在beans标签下,所以的bean都会调用后置处理器。

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

1.4.5 xml 自动装配

自动装配:根据指定装配规则(属性名称或者属性类型),Spring 自动将匹配的属性值进行注入。

1.4.5.1 根据属性名称自动注入
<!--实现自动装配
 Emp类中有一个Dept类型的属性
 bean 标签属性 autowire,配置自动装配
 autowire 属性常用两个值:
 byName 根据属性名称注入 ,注入值 bean 的 id 值和类属性名称一样
 byType 根据属性类型注入
-->
<bean id="emp" class="com.atguigu.spring5.autowire.Emp" autowire="byName"></bean> 
<bean id="dept" class="com.atguigu.spring5.autowire.Dept"></bean>
1.4.5.1 根据属性类型自动注入

根据属性类型自动注入时,beans标签下只能存在一个此类型的属性。

<bean id="emp" class="com.atguigu.spring5.autowire.Emp" autowire="byType"></bean>
 <bean id="dept" class="com.atguigu.spring5.autowire.Dept"></bean>

1.4.6 xml引入外部属性文件

  1. 引入druid连接池依赖 jar 包
    在这里插入图片描述
    在这里插入图片描述

  2. 创建外部属性文件,properties 格式文件,写入数据库信息

  3. 把外部 properties 属性文件引入到 spring 配置文件中

需要先引入 context 名称空间

<beans xmlns="http://www.springframework.org/schema/beans" 
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 xmlns:p="http://www.springframework.org/schema/p" 
 xmlns:util="http://www.springframework.org/schema/util" 
 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/util 
http://www.springframework.org/schema/util/spring-util.xsd 
 http://www.springframework.org/schema/context 
http://www.springframework.org/schema/context/spring-context.xsd">

通过context标签引入外部属性文件

<!--引入外部属性文件-->
 <context:property-placeholder location="classpath:jdbc.properties"/>
  1. 配置连接池
<!--配置连接池--> 
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
 <property name="driverClassName" value="${prop.driverClass}"></property>
 <property name="url" value="${prop.url}"></property>
 <property name="username" value="${prop.userName}"></property>
 <property name="password" value="${prop.password}"></property>
</bean>

1.4.7 基于注解方式实现bean管理

1.4.7.1 什么是注解

(1)注解是代码特殊标记,格式:@注解名称(属性名称=属性值, 属性名称=属性值…)
(2)使用注解,注解作用在类上面,方法上面,属性上面
(3)使用注解目的:简化 xml 配置

1.4.7.2 Spring 针对 Bean 管理中创建对象提供的注解

(1)@Component
(2)@Service
(3)@Controller
(4)@Repository

  • 上面四个注解功能是一样的,都可以用来创建 bean 实例,但是为了在开发中更容易区分,一般在不同类型的包下使用不同的注解。
1.4.7.3 基于注解方式实现对象创建的步骤
  1. 引入依赖
    在这里插入图片描述
  2. 在xml配置文件中开启组件扫描
    与1.4.6中类似,需要先引入context命名空间,然后使用context标签开启组件扫描。
    <!--  如果需要扫描多个包,包名使用逗号隔开,或者可以写它们共同的父包  -->
    <context:component-scan base-package="cn.zhou.spring.annotation,cn.zhou.spring.dao"></context:component-scan>
  1. 创建类,在类上面添加创建对象注解
//注解括号中的内容可以省略,默认值是首字母小写的类名称。
//UserService -- userService
@Component(value = "userService") // 相当于<bean id="userService" class=".."/>
@Component
public class UserService {
 public void add() {
 System.out.println("service add.......");
 } }
  1. 获取对象和xml方式一样
//        单实例bean对象在加载xml文件时创建
        ApplicationContext context = new ClassPathXmlApplicationContext("bean5.xml");
//        多实例bean对象在调用getBean时创建
        UserService service= context.getBean("userService", UserService .class);
        System.out.println(service);
1.4.7.4 开启组件扫描细节配置
  1. 设置扫描哪些内容
    use-default-filters=“false” 表示现在不使用默认 filter,自己配置 filter,默认的filter会扫描包下所有带注解的类。
<!--示例 1
 context:include-filter ,设置扫描哪些内容
-->
<context:component-scan base-package="cn.zhou.spring" use-default-filters="false">
// 表示只扫描包下注解为Controller和Component的类
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/>
</context:component-scan>
  1. 设置不扫描哪些内容
<!--示例 2
 下面配置扫描包所有内容
 context:exclude-filter: 设置哪些内容不进行扫描
-->
<context:component-scan base-package="com.atguigu">
 <context:exclude-filter type="annotation" 
expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
1.4.7.5 基于注解方式实现属性注入
  1. @Autowired:根据属性类型进行自动装配。如果接口的实现类有多个,这种方式就无法使用。
1. 创建UserDao的实现类UserDaoImp ,并添加创建对象的注解@Repository
@Repository
public class UserDaoImp implements UserDao{
    @Override
    public void add() {
        System.out.println("123");
    }
}
2. 创建UserService类,并添加创建对象的注解@Service,在属性userDao上添加注解 @Autowired,此时根据属性类型UserDao 进行自动装配。
@Service
public class UserService {
 //定义 dao 类型属性
 //不需要添加 set 方法
 //添加注入属性注解
    @Autowired
    UserDao userDao;

    public void add(){
        userDao.add();
    }
}
3. 调用userService对象的add方法
    ApplicationContext context = new ClassPathXmlApplicationContext("bean5.xml");
    UserService userService = context.getBean("userService", UserService.class);
    userService.add();
  1. @Qualifier:根据名称进行注入,且此注解必须和@Autowired一起使用
@Service
public class UserService {
    @Autowired
    @Qualifier(value = "userDaoImp")
    UserDao userDao;

    public void add(){
        userDao.add();
    }
}
  1. @Resource:可以根据类型注入,也可以根据名称注入,但是此注解位于javax.annotation.Resource包下,不是Spring中的注解,一般不推荐使用
@Service
public class UserService {
//    @Resource  根据类型进行注入
    @Resource(name = "userDaoImp") // 根据名称进行注入
    UserDao userDao;

    public void add(){
        userDao.add();
    }
}
  1. @Value:注入普通类型(非自定义类)属性
    @Value(value = "1")
    private int age;
    
    @Value(value = "张三")
    private String name;
1.4.7.6 完全注解开发

(1)创建配置类,替代 xml 配置文件

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

(2)编写测试类

@Test
public void testService2() {
 //加载配置类,不同于加载xml配置文件
 ApplicationContext context
 = new AnnotationConfigApplicationContext(SpringConfig.class);
 // 获取对象与之前相同
 UserService userService = context.getBean("userService", 
UserService.class);
 System.out.println(userService);
 userService.add();

2 AOP–面向切面编程

2.1 概念

AOP可以通过预编译方式和运行其动态代理实现在不修改源代码的情况下给程序动态统一添加某种特定功能的一种技术。

2.2 优点

利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低提高程序的可重用性,同时提高了开发的效率。

2.3 使用场景

将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。

2.4 通过登录实例了解AOP

在这里插入图片描述

2.5 AOP 底层使用动态代理

2.5.1 两种情况的动态代理

  1. 有接口情况,使用 JDK 动态代理,创建接口实现类代理对象,增强类的方法。

  2. 没有接口情况,使用 CGLIB 动态代理,创建子类的代理对象,增强类的方法。

2.5.2 JDK 动态代理的实现

(1)创建接口,定义方法

public interface UserDao {
 public int add(int a,int b);
 public String update(String id);
}

(2)创建接口实现类,实现方法

public class UserDaoImpl implements UserDao {
	 @Override
	 public int add(int a, int b) {
	 return a+b;
	 }
	 @Override
	 public String update(String id) {
	 return id;
	 }
  }

(3)使用 Proxy 类创建接口代理对象

public class JDKProxy {
 public static void main(String[] args) {
	 //创建接口实现类代理对象
	 Class[] interfaces = {UserDao.class};
	 UserDaoImpl userDao = new UserDaoImpl();
	 UserDao dao = 
	(UserDao)Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, 
	new UserDaoProxy(userDao));
	 int result = dao.add(1, 2);
	 System.out.println("result:"+result);
	 }
 }


//创建代理对象代码
class UserDaoProxy implements InvocationHandler {
	 //1 把创建的是谁的代理对象,把谁传递过来
	 //有参数构造传递
	 private Object obj;
	 public UserDaoProxy(Object obj) {
	 this.obj = obj;
	 }
	 //增强的逻辑
	 @Override
	 public Object invoke(Object proxy, Method method, Object[] args) throws 
	Throwable {
	 //方法之前
	 System.out.println("方法之前执行...."+method.getName()+" :传递的参
	数..."+ Arrays.toString(args));
	 //被增强的方法执行
	 Object res = method.invoke(obj, args);
	 //方法之后
	 System.out.println("方法之后执行...."+obj);
	 return res;
	 } 
 }

2.6 AOP相关术语

2.6.1 连接点

类中所有能够被增强的方法称为连接点。

2.6.2 切入点

真正被增强的方法称为切入点。

2.6.3 通知(增强)

实际增强的代码称为通知。

2.6.3.1 分类
  • 前置通知@Before:在方法执行前增强。
  • 后置通知@AfterReturning:在方法执行后增强,如果方法执行过程中出现异常,则不执行
  • 环绕通知@Around:在方法执行前后都增强。
  • 异常通知@AfterThrowing:方法执行过程中出现异常时增强。
  • 最终通知@After:无论方法是否出现异常,都会执行的增强。(类似于finally)

2.6.4 切面

切面是一个动作,是把通知应用到切入点的过程。

2.7 AOP的使用

2.7.1 概述

  1. Spring 框架一般都是基于 AspectJ 实现 AOP 操作,AspectJ 不是 Spring 组成部分,独立 AOP 框架,一般把 AspectJ 和 Spirng 框架一起使用,进行 AOP 操作。
  2. 基于 AspectJ 实现 AOP 操作的两种方式
    (1)基于 xml 配置文件实现
    (2)基于注解方式实现(使用)

2.7.2 切入点表达式

  1. 切入点表达式作用:知道对哪个类里面的哪个方法进行增强
  2. 语法结构: execution([权限修饰符] [返回类型] [类全路径].[方法名称] ([参数列表]) )
  3. 权限修饰符为*表示任意权限都行,返回类型可以省略,参数列表使用(..)
  4. 例子
    • 对 com.atguigu.dao.BookDao 类里面的 add方法 进行增强:execution(* com.atguigu.dao.BookDao.add(..))
    • 对 com.atguigu.dao.BookDao 类里面的所有的方法进行增强:execution(* com.atguigu.dao.BookDao.* (..))
    • 对 com.atguigu.dao 包里面所有类,类里面所有方法进行增强:execution(* com.atguigu.dao.*.* (..))

2.7.3 导包

在这里插入图片描述

2.7.4 基于注解的AOP操作步骤

2.7.4.1 创建被增强类

添加创建对象的注解@Component

@Component
public class User {

    public void add(){
        System.out.println("add...");
    }
}
2.7.4.2 创建增强类

添加创建对象的注解@Component

@Component
public class UserProxy {
    public void before(){
        System.out.println("before....");
    }
}
2.7.4.3 进行通知的配置

(1)在 spring 配置文件中,开启注解扫描

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

(2)在增强类上面添加注解 @Aspect

@Component
@Aspect //生成代理对象
public class UserProxy {

    @Before("execution(* cn.zhou.spring.aopanno.User.add(..))")
    public void before(){
        System.out.println("before....");
    }
}

(4)在 spring 配置文件中开启生成代理对象

<!-- 开启 Aspect 生成代理对象--> 
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
2.7.4.4 配置不同类型的通知

在增强类的里面,在作为通知方法上面添加通知类型注解,使用切入点表达式配置。

@Component
@Aspect
public class UserProxy {

//    前置通知
    @Before("execution(* cn.zhou.spring.aopanno.User.add(..))")
    public void before(){
        System.out.println("before....");
    }

//  后置通知(返回通知)
    @AfterReturning("execution(* cn.zhou.spring.aopanno.User.add(..))")
    public void afterReturn(){
        System.out.println("afterReturn....");
    }
//  最终通知
    @After("execution(* cn.zhou.spring.aopanno.User.add(..))")
    public void after() {
        System.out.println("after.........");
    }
//    异常通知
    @AfterThrowing("execution(* cn.zhou.spring.aopanno.User.add(..))")
    public void afterThrowing() {
        System.out.println("afterThrowing.........");
    }
//    环绕通知
    @Around("execution(* cn.zhou.spring.aopanno.User.add(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("环绕之前.........");
        //被增强的方法执行
        proceedingJoinPoint.proceed();
        System.out.println("环绕之后.........");
    }
}

没有异常的执行结果

环绕之前.........
before....
add方法执行
环绕之后.........
after.........
afterReturn....

有异常的执行结果

环绕之前.........
before....
add方法执行
after.........
afterThrowing.........

通过对比两个执行结果,可以看出发生异常后,afterReturnaround之后都不会执行。

2.7.4.5 相同的切入点抽取 @Pointcut注解

在这里插入图片描述

2.7.4.6 测试类
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
        User user = context.getBean("user", User.class);
        user.add();
    }
2.7.4.7 多个增强类对同一方法进行增强,如何设置执行次序

使用**@Order(int i)**注解,可以设置一个int类型的参数,参数越小,优先级越高。
在这里插入图片描述

2.7.4.8 完全注解开发

在这里插入图片描述

@Configuration
@ComponentScan(basePackages = {"com.atguigu"})
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class ConfigAop {
}

2.7.5 基于xml配置文件的AOP(一般不使用)

2.7.5.1 创建增强类和被增强类
//增强类
public class BookProxy {

    public void before(){
        System.out.println("before...");
    }
}
//被增强类
public class Book {

    public void seal(){
        System.out.println("seal....");
    }
}
2.7.5.2 在配置文件中进行配置
<!--创建对象--> 
<bean id="book" class="cn.zhou.spring.aopxml.Book"></bean> 
<bean id="bookProxy" class="cn.zhou.spring.aopxml.BookProxy"></bean>
<!--配置 aop 增强--> 
<aop:config>
 <!--切入点-->
<aop:pointcut id="p" expression="execution(* cn.zhou.spring.aopxml.Book.buy(..))"/>

 <!--配置切面-->
 <aop:aspect ref="bookProxy">
	 <!--增强作用在具体的方法上-->
	 <aop:before method="before" pointcut-ref="p"/>
 </aop:aspect>
</aop:config>

3 JdbcTemplate

3.1 概念

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

3.2 准备工作

3.2.1 导包

mysql的jar包需要和自己电脑上的mysql版本相同,如果使用的是mysql8,就不能使用5.1.7版本的jar包。
在这里插入图片描述

3.2.2 在 spring 配置文件配置数据库连接池

在这里插入图片描述

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

3.2.3 配置 JdbcTemplate 对象,注入 DataSource

<!-- JdbcTemplate 对象 --> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
 <!--注入 dataSource-->
 <property name="dataSource" ref="dataSource"></property>
</bean>

3.2.4 创建 service 类,创建 dao 类,在 dao 中注入 jdbcTemplate 对象

@Repository
public class UserDaoImp implements UserDao{
    @Autowired
    private JdbcTemplate jdbcTemplate;
}
@Service
public class UserService {
    @Autowired
    private UserDao userDao;
}

3.3 操作数据库

3.3.1 创建数据库及表

在这里插入图片描述

3.3.2 创建user表对应的实体类

在这里插入图片描述

3.3.3 在UserService中调用UserDao中的方法

在这里插入图片描述

3.3.4 在UserDao中操作数据库

update方法可以用于增删改
在这里插入图片描述

3.3.4.1 添加数据

在这里插入图片描述

3.3.4.2 修改数据

在这里插入图片描述

3.3.4.3 删除数据

在这里插入图片描述

3.3.5 测试

 @Test
    public void test(){
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
        UserService userService = context.getBean("userService", UserService.class);
        userService.add(new User(1,24,"张三"));
    }

在这里插入图片描述

3.3.6 查询数据

3.3.6.1 查询返回单个值

在这里插入图片描述
在这里插入图片描述

3.3.6.2 查询返回单个对象

在这里插入图片描述

3.3.6.3 查询返回对象集合

在这里插入图片描述

3.3.7 批量操作

3.3.7.1 批量添加

在这里插入图片描述
测试:插入三条记录
在这里插入图片描述

3.3.7.2 批量修改

在这里插入图片描述
测试:参数对应的位置要一致。

        List<Object[]> batchArgs = new ArrayList<>();
        batchArgs.add(new Object[]{"aa", 15, 1});
        batchArgs.add(new Object[]{"bb", 16, 2});
        batchArgs.add(new Object[]{"cc", 17, 3});
        userService.batchUpdate(batchArgs);
3.3.7.3 批量删除

在这里插入图片描述

  List<Object[]> batchArgs = new ArrayList<>();
        batchArgs.add(new Object[]{1});
        batchArgs.add(new Object[]{2});
        batchArgs.add(new Object[]{3});
        userService.batchUpdate(batchArgs);

4 事务

4.1 概念

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

4.2 四大特性

  • 原子性:事务中的所有操作要么全部成功,要么全部失败。
  • 一致性:事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。
  • 隔离性:多个并发事务之间要相不影响。
  • 持久性 :事务一旦提交,那么对数据库中的数据的改变就是永久性的。

4.3 Spring 事务管理

4.3.1 概述

一般情况下,事务添加到 JavaEE 三层结构里面 Service 层(业务逻辑层),底层使用 的是AOP 原理。

4.3.2 两种方式

  • 编程式事务管理
  • 声明式事务管理(一般使用这种方式)
    • 基于注解的方式(一般使用这种方式)
    • 基于xml配置文件的方式

4.3.3 Spring 事务管理 API

Spring中提供了PlatformTransactionManager接口,代表事务管理器,这个接口针对不同的框架提供不同的实现类。
在这里插入图片描述

4.3.4 注解声明式事务管理

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

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

2、在 spring 配置文件,开启事务注解
(1)在 spring 配置文件引入名称空间 tx

<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" 
 xmlns:tx="http://www.springframework.org/schema/tx" 
 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 
 http://www.springframework.org/schema/tx 
http://www.springframework.org/schema/tx/spring-tx.xsd"> 

(2)开启事务注解

<!--开启事务注解--> 
<tx:annotation-driven transactionmanager="transactionManager"></tx:annotation-driven> 

3、在 service 类上面(或者 service 类里面方法上面)添加事务注解@Transactional
(1)这个注解可以添加到类上面,也可以添加方法上面
(2)如果把这个注解添加类上面,这个类里面所有的方法都添加事务
(3)如果把这个注解添加方法上面,为这个方法添加事务

@Service
@Transactional
public class UserService {}

4.3.5 声明式事务管理参数配置

4.3.5.1 @Transactional中的参数

在这里插入图片描述

4.3.5.2 propagation:事务传播行为

Spring框架事务传播行为有7种,其中最常用的是REQUIREDREQUIRES_NEW,默认情况为Propagation.REQUIRED
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.3.5.3 ioslation:事务隔离级别

(1)事务有特性成为隔离性,多事务操作之间不会产生影响。不考虑隔离性产生很多问题
(2)有三个读问题:脏读、不可重复读、虚(幻)读
(3)脏读:一个未提交事务读取到另一个未提交事务的数据
在这里插入图片描述

(4)不可重复读:一个未提交事务读取到另一提交事务修改数据
在这里插入图片描述

(5)虚读:一个未提交事务读取到另一提交事务添加数据
(6)解决:通过设置事务隔离级别,解决读问题
在这里插入图片描述

4.3.5.4 timeout:超时时间
  • 一旦超出给定的时间不提交,事务就会自动回滚。
  • 默认值为-1,表示不会自动进行回滚。
4.3.5.5 readOnly:是否只读
  • 读:查询操作,写:添加修改删除操作
  • readOnly 默认值 false,表示可以查询,可以添加修改删除操作
  • 设置 readOnly 值是 true,设置成 true 之后,只能查询
4.3.5.6 rollbackFor:回滚

设置出现哪些异常进行事务回滚,默认为空
在这里插入图片描述

4.3.5.7 noRollbackFor:不回滚

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

4.3.6 XML 声明式事务管理

  • 在 spring 配置文件中进行配置
    • 第一步 配置事务管理器
    • 第二步 配置通知
    • 第三步 配置切入点和切面
<!--1 创建事务管理器--> 
<bean id="transactionManager" 
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
 <!--注入数据源-->
 <property name="dataSource" ref="dataSource"></property>
</bean>
<!--2 配置通知--> <tx:advice id="txadvice">
 <!--配置事务参数-->
 <tx:attributes>
 <!--指定哪种规则的方法上面添加事务-->
 <tx:method name="accountMoney" propagation="REQUIRED"/>
 <!--<tx:method name="account*"/>-->
 </tx:attributes>
</tx:advice>
<!--3 配置切入点和切面--> <aop:config>
 <!--配置切入点-->
 <aop:pointcut id="pt" expression="execution(* com.atguigu.spring5.service.UserService.*(..))"/>
 <!--配置切面-->
 <aop:advisor advice-ref="txadvice" pointcut-ref="pt"/>
</aop:config>

4.3.7 完全注解声明式事务管理

创建配置类,使用配置类替代 xml 配置文件

@Configuration //配置类
@ComponentScan(basePackages = "com.atguigu") //组件扫描
@EnableTransactionManagement //开启事务
public class TxConfig {
 //创建数据库连接池
 @Bean
 public DruidDataSource getDruidDataSource() {
 DruidDataSource dataSource = new DruidDataSource();
 dataSource.setDriverClassName("com.mysql.jdbc.Driver");
 dataSource.setUrl("jdbc:mysql:///user_db");
 dataSource.setUsername("root");
 dataSource.setPassword("root");
 return dataSource;
 }
 //创建 JdbcTemplate 对象
 @Bean
 public JdbcTemplate getJdbcTemplate(DataSource dataSource) {
 //到 ioc 容器中根据类型找到 dataSource
 JdbcTemplate jdbcTemplate = new JdbcTemplate();
 //注入 dataSource
 jdbcTemplate.setDataSource(dataSource);
 return jdbcTemplate;
 }
 //创建事务管理器
 @Bean
 public DataSourceTransactionManager 
getDataSourceTransactionManager(DataSource dataSource) {
 DataSourceTransactionManager transactionManager = new 
DataSourceTransactionManager();
 transactionManager.setDataSource(dataSource);
 return transactionManager;
 } }

测试

  AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TxConfig.class);
  AccountService accountService = con.getBean("accountService", AccountService.class);
  accountService.account();

5 Spring5 框架新功能

  1. 整个 Spring5 框架的代码基于 Java8,运行时兼容 JDK9,许多不建议使用的类和方
    法在代码库中删除
  2. Spring 5.0 框架自带了通用的日志封装
    • Spring5 已经移除 Log4jConfigListener,官方建议使用 Log4j2
    • Spring5 框架整合 Log4j2

5.1 日志功能使用步骤

第一步 引入 jar 包
在这里插入图片描述
第二步 创建 log4j2.xml 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > 
ALL -->
<!--Configuration 后面的 status 用于设置 log4j2 自身内部的信息输出,可以不设置,
当设置成 trace 时,可以看到 log4j2 内部各种详细输出-->
 <configuration status="INFO">
 <!--先定义所有的 appender-->
 <appenders>
 <!--输出日志信息到控制台-->
 <console name="Console" target="SYSTEM_OUT">
 <!--控制日志输出的格式-->
 <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-
5level %logger{36} - %msg%n"/>
 </console>
 </appenders>
 <!--然后定义 logger,只有定义 logger 并引入的 appender,appender 才会生效-->
 <!--root:用于指定项目的根日志,如果没有单独指定 Logger,则会使用 root 作为
默认的日志输出-->
 <loggers>
 <root level="info">
 <appender-ref ref="Console"/>
 </root>
 </loggers>
</configuration>

5.2 Spring5 框架核心容器支持@Nullable 注解

@Nullable 注解可以使用在方法上面,属性上面,参数上面,表示方法返回可以为空,属性值可以为空,参数值可以为空

  • 注解用在方法上面,方法返回值可以为空
    在这里插入图片描述

  • 注解使用在方法参数里面,方法参数可以为空
    在这里插入图片描述

  • 注解使用在属性上面,属性值可以为空
    在这里插入图片描述

  • 注意:这个注解只能起到提示功能。参考文章

5.3 Spring5 核心容器支持函数式风格 GenericApplicationContext

//函数式风格创建对象,交给 spring 进行管理
@Test
public void testGenericApplicationContext() {
 //1 创建 GenericApplicationContext 对象
 GenericApplicationContext context = new GenericApplicationContext();
 //2 调用 context 的方法对象注册
 context.refresh();
 context.registerBean("user1",User.class,() -> new User());
 //3 获取在 spring 注册的对象
 // 如果注册的时候没有指定beanName,则获取的时候使用全类名来获取
 // User user = (User)context.getBean("com.atguigu.spring5.test.User");
 User user = (User)context.getBean("user1");
 System.out.println(user);
}

5.4 Spring5 整合 JUnit5

  1. 引入 JUnit5 的 jar 包,要在Project Structure将其导入
    在这里插入图片描述
  2. 创建一个测试类,在类上添加注解
@SpringJUnitConfig(locations = "classpath:bean1.xml") // locations指向配置文件,classpath表示src的路径
public class TestJunit5 {
	
	// 自动注入属性,这里不需要使用读取xml,然后使用getBean()获取对象
    @Autowired
    public AccountService accountService;

    @Test
    public void test(){
    // 直接使用属性即可
        accountService.account();
    }

	// 之前的写法,做对比
    @Test
    public void test1() {
        ClassPathXmlApplicationContext con = new ClassPathXmlApplicationContext("bean1.xml");
        AccountService accountService = con.getBean("accountService", AccountService.class);
        accountService.account();
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值