Spring框架入门

spring

spring是什么
spring的使用
	xml实现
	注解实现
	名词解释
	bean的生命周期
代理模式
	静态代理
	动态代理
		jdk代理
		cglib代理
AOP面向切面编程
	注解实现
	xml实现
	名词解释
声明式事务管理
	mybatis和spring的整合
	xml实现
	注解实现
	事务传播机制
	事务隔离级别

spring是什么

Spring很重要
支撑了整个java体系

Aop实际上就是动态代理
声明式事务管理用得多写
3天左右结束spring
Se  web  
垃圾回收机制
Spring不仅是解决了垃圾回收
而是更好的管理了对象

spring是春天的意思,意味着这个框架的出现java程序员的春天就到了

在java因为有自动垃圾回收机制的存在,在很多的时候需要使用一个对象的时候就去创建这个对象,在创建并使用完成后就不会再管理这个对象,意味着对象和内存无法得到更好的管理,为了解决这个问题,所以spring出现了。

在spring中,最主要的作用,管理了对象的完整的生命周期,从对象的创建、使用、销毁、对象之间的依赖关系这些都可以全部交给spring进行管理。

所以spring可以看做是一个容器,这个容器中装了很多对象(bean),并且维护起来,程序员在使用的时候只需要从容器中获取对象的对象即可。

spring的实现

Spring快速上手
Sping是一个容器
可以在不同java项目
也可以在web项目中运行
使用一个新的框架

引入依赖
Spring-context 依赖于  Spring-core

添加依赖

<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.3.18</version>
</dependency>

xml实现spring

Spring的实现方案
Xml方式   注解方式

一般两种凡是混合使用

首先创建一个spring的核心配置文件 ApplicationContext.xml

将对象交给spring管理

《bean》 反射创建对象
放入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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="userDao" class="com.zlt.dao.impl.UserDaoImpl"></bean>

    <bean id="userService" class="com.zlt.service.impl.UserServiceImpl">
        <!--调用类中的set方法 name的值对应的是set后面的名称 property表示类中一个有name的值对应的set方法
            ref 调用容器中的引用进行赋值
            value 是直接赋值 一般是基本数据类型或者String 才会用value
        -->
        <property name="userDao" ref="userDao"/>
    </bean>

    <bean id="userController" class="com.zlt.controller.UserController">
        <property name="userService" ref="userService"/>
    </bean>

</beans>

测试代码

=
整个过程中
我们自己只new了spring的容器 对象

package com.zlt;

import static org.junit.Assert.assertTrue;

import com.zlt.controller.UserController;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Unit test for simple App.
 */
public class AppTest 
{
    /**
     * Rigorous Test :-)
     */
    @Test
    public void shouldAnswerWithTrue() {
        BeanFactory beanFactory = new ClassPathXmlApplicationContext("ApplicationContext.xml");
        UserController userController = beanFactory.getBean("userController", UserController.class);
        userController.login("zhangsan","123456");

    }
}

案例1

项目结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6btFpPtI-1681890082163)(assets/1661244823127.png)]

=

引入依赖

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

    <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.3.18</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.22</version>
      <scope>provided</scope>
    </dependency>

    <!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.2.8</version>
    </dependency>

    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.38</version>
    </dependency>


  </dependencies>

控制层

package com.zlt.controller;

import com.zlt.entity.User;
import com.zlt.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller // 标记当前类是控制层
public class UserController {

    @Autowired
    private UserService userService;

    public void setUserService(UserService userService) {
        this.userService = userService;
    }


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

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

    public User login (String username,String password){
        System.out.println("controller执行");
        return userService.login(username,password);
    }
}

服务层接口

package com.zlt.service;

import com.zlt.entity.User;

public interface UserService {
    User login(String username, String password);
}

服务层实现类

package com.zlt.service.impl;

import com.zlt.dao.UserDao;
import com.zlt.entity.User;
import com.zlt.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service // 标记当前类是业务层
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public User login(String username, String password) {
        System.out.println("service执行");
        return userDao.selectUser(username,password);
    }
}

持久层接口

package com.zlt.dao;

import com.zlt.entity.User;

public interface UserDao {
    User selectUser(String username, String password);
}

持久层实现类

package com.zlt.dao.impl;

import com.zlt.dao.UserDao;
import com.zlt.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import javax.sql.DataSource;

@Repository // 标记当前类是持久层
public class UserDaoImpl implements UserDao {


    @Autowired
    private DataSource dataSource;

    @Override
    public User selectUser(String username, String password) {
        System.out.println("dao执行" + dataSource);
        return new User(100,username,password);
    }
}

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"
       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">
    <!--开启注解 让项目支持spring的注解  -->
<!--    <context:annotation-config></context:annotation-config>-->


    <!--注解扫描 会去扫描指定的包及其子包 中的类是否包含指定的spring注解
    本身也带有开启注解的功能 所以 context:annotation-config 就可以不用写了

    -->
<!--    <context:component-scan base-package="com.zlt"></context:component-scan>-->

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" >
        <!--注入的属性是基本数据类型或者String 可以用value来注入值-->
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql:///qq61"/>
        <property name="username" value="kinglee"/>
        <property name="password" value="root"/>
    </bean>


    <!--反射创建对象放入spring的容器-->
    <bean id="userDao" class="com.zlt.dao.impl.UserDaoImpl">

    </bean>

    <bean id="userService" class="com.zlt.service.impl.UserServiceImpl">
        <!--调用对象的set方法 方法名根据name的值确定
        所以name的值不能随便乱写 如果是对象类型一般使用ref赋值 ref的值需要在spring的容器中找到-->
        <property name="userDao" ref="userDao"/>
    </bean>



    <!--
        id 属性表示bean在容器中的唯一标识符 如果没有写 默认的id是 类的全路径#第几个 com.zlt.controller.UserController#0
        class 类的全路径 可以通过这个反射创建对象
        init-method 初始化方法 对象创建后执行
        destroy-method 对象销毁之前执行
        scope 作用范围
            singleton 单例模式 默认值
            prototype 工厂模式 每获取一次都会创建新的对象  不能和destroy-method一起使用
            如果是在web项目中 request 请求作用域 session 会话作用域 globalSession 全局作用域
        autowire 自动装配
            byType 默认从容器寻找对应的类型的bean注入进去 如果匹配到多个会不知道注入哪一个bean 所以会报错的
            byName 通过名称注入进去 id和属性名相同就可以注入
            constructor 通过构造进行注入 也就是说需要有对应的带参构造 才可以注入
            no 和default 都相当于不注入
        lazy-init 懒加载 如果设置为true则容器创建是不会初始化对象 默认是false
    -->
    <bean  class="com.zlt.controller.UserController" init-method="init" destroy-method="destory" scope="singleton" autowire="byType" lazy-init="false">
<!--        <property name="userService" ref="userService"/>-->
    </bean>


</beans>

bean标签的属性介绍

id 表示bean在容器中的唯一标识 一般是类名首字母小写 如果没有写 默认值为 全路径#第几个  com.zlt.controller.UserController#0
        class 类的全路径
        init-method 初始化方法 对象创建后会被调用一次
        destroy-method 销毁方法 容器销毁之前会被调用一次
        scope="singleton" 作用范围
            singleton 单例模式 默认值
            prototype 工厂模式 设置为工厂模式的时候销毁方法不会执行
            如果是在web项目中 request 请求作用域 session 会话作用域 globalSession 全局作用域
        autowire 自动装配
            byType 默认从容器中寻找对应的类型的bean注入进去  如果匹配到多个就会不知道注入谁进去
            byName 根据id和属性名进行自动注入
            constructor 通过构造来进行注入
            no 和 default 都相当于不自动注入
        lazy-init 懒加载 设置为true的时候容器创建时不会去初始化对象 默认false

构造器注入

使用bean的时候需要注入值,常用是property标签注入,也可以通过构造器来进行注入

<bean id="userController"  class="com.zlt.controller.UserController" init-method="init" destroy-method="destory" lazy-init="true" >
        <!--通过构造器来注入
        name 参数名称
        index 表示第几个参数
        type 表示参数类型
        ref 引入类型注入
        value 普通类型注入
        -->
        <constructor-arg   ref="userService"/>
        <constructor-arg   value="123456"/>
        <constructor-arg value="zhangsan111"/>
    </bean>

集合注入

List注入
<bean id="userController"  class="com.zlt.controller.UserController" init-method="init" destroy-method="destory" lazy-init="true" >
    <property name="list" >
            <list>
                <!--注入普通类型-->
                <value>123123</value>
                <value>aaaaaa</value>
                <!--注入容器中存在的对象-->
                <ref bean="userService"/>
                <!--注入容器中不存在的对象-->
                <bean class="com.zlt.entity.User">
                    <property name="uid" value="1"/>
                    <property name="username" value="zs"/>
                    <property name="password" value="123456"/>
                </bean>
            </list>
        </property>
    </bean>
数组注入
<bean id="userController"  class="com.zlt.controller.UserController" init-method="init" destroy-method="destory" lazy-init="true" >
       
        
        <property name="array">
            <array>
                <!--注入普通类型-->
                <value>123123</value>
                <value>aaaaaa</value>
                <!--注入容器中存在的对象-->
                <ref bean="userService"/>
                <!--注入容器中不存在的对象-->
                <bean class="com.zlt.entity.User">
                    <property name="uid" value="1"/>
                    <property name="username" value="zs"/>
                    <property name="password" value="123456"/>
                </bean>
            </array>
        </property>
    </bean>
Set
<bean id="userController"  class="com.zlt.controller.UserController" init-method="init" destroy-method="destory" lazy-init="true" >
        <property name="sets">
            <set>
                <!--注入普通类型-->
                <value>123123</value>
                <value>aaaaaa</value>
                <!--注入容器中存在的对象-->
                <ref bean="userService"/>
                <!--注入容器中不存在的对象-->
                <bean class="com.zlt.entity.User">
                    <property name="uid" value="1"/>
                    <property name="username" value="zs"/>
                    <property name="password" value="123456"/>
                </bean>
            </set>
        </property>
    </bean>
Map
<bean id="userController"  class="com.zlt.controller.UserController" init-method="init" destroy-method="destory" lazy-init="true" >
        <property name="map">
            <map>
                <entry key="aa" value="bb"/>
                <entry key="bb" value-ref="userService"/>
                <entry key-ref="userService" value="bb"/>
                <entry key-ref="userService1" value-ref="userService1"/>
                <entry>
                    <key>
                        <bean class="com.zlt.entity.User"/>
                    </key>
                    <value>123123</value>
                </entry>
                <entry>
                    <key>
                        <bean class="com.zlt.entity.User"/>
                    </key>
                    <bean class="com.zlt.entity.User"/>
                </entry>
            </map>
        </property>
    </bean>
Properties
 <bean id="userController"  class="com.zlt.controller.UserController" init-method="init" destroy-method="destory" lazy-init="true" >
      <property name="properties">
            <props>
                <prop key="aa">bb</prop>
                <prop key="aa1">bb1</prop>
                <prop key="aa2">bb2</prop>
            </props>
        </property>
    </bean>

注解实现

配置文件如下:

更改配置文件的头  也就是xsd

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xoRKnXnb-1681890082164)(assets/1661225287393.png)]

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

    <context:annotation-config/>

</beans>
Context.xsd 加入后
会有新的标签出现

<context:component-scan base-package="com.zlt">
注解在那些地方
注解扫描 
只会扫描指定的包以及子包
    
	注解加上后 
	容器中就应该有三个对象

	我们还需要把这三个对象关联起来
    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"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans.xsd
         http://www.springframework.org/schema/context
         http://www.springframework.org/schema/context/spring-context.xsd">
<!--    开启注解支持-->
    <!--<context:annotation-config></context:annotation-config>-->
    <!--注解扫描 默认开启注解支持-->
    <context:component-scan base-package="com.zlt">
        <!--设置需要扫描的注解-->
<!--        <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>-->
        <!--设置不扫描的注解-->
<!--        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Component"/>-->
    </context:component-scan>


</beans>
一般applicationContext.xml  也可以命名为beans.xml
注解解释
@Controller 一般写在类上 标记当前是控制器 将当前类的对象放入容器中 id默认是类名首字母小写
@Service 一般写在类上 标记当前是业务层 将当前类的对象放入容器中 id默认是类名首字母小写
@Repository 一般写在类上 标记当前是持久层 将当前类的对象放入容器中 id默认是类名首字母小写
@Component 一般写在类上 不标记是哪一层 将当前类的对象放入容器中 id默认是类名首字母小写
注入
@Autowired 自动装配 默认从容器中获取bean并且自动注入到标记的内容上 可以标记在属性上 可以标记在set方法上 还可以标记在构造器上
    required 表示是否必须注入 如果是true 没找到注入的值会报错 如果是false就不会报错
    	默认通过byType 没有就通过byName 
    @Qualifier("userServiceImpl")  一般配合@Autowired  一起使用
    @Autowired
    public UserController(@Qualifier("userServiceImpl") UserService userService) {
        this.userService = userService;
    }
@Resource(name = "userDaoImpl") 可以指定名称去进行注入
作用范围
    @Scope("prototype") 一般加在类上 指定作用范围
初始化方法和销毁方法
    @PostConstruct   // post  邮寄   之后
    public void init(){
        System.out.println("init" );
    }

    @PreDestroy
    public void destory(){
        System.out.println("destory");
    }

通过xml和注解实现对比,发现注解实现明显简单更多,所以一般情况下自己写的类可以使用注解来实现,如果项目中添加了其他的依赖需要放入其他依赖中的对象进容器,就只能通过xml来放入。所以spring在实现的时候,可以xml和注解混合实现效果更佳。

注解好像比xml更好用

但是为什么还有xml
可以方便配置其他东西

Druid数据库连接相关的类是第三方提供的  不好加注解
所以要用配置文件  方式进行配置

案例2

项目结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NsTUzi2d-1681890082164)(assets/1661245075971.png)]

依赖

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

    <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.3.18</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.22</version>
      <scope>provided</scope>
    </dependency>

    <!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.2.8</version>
    </dependency>

    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.38</version>
    </dependency>


  </dependencies>

=

控制层

package com.zlt.controller;

import com.zlt.entity.User;
import com.zlt.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import java.util.*;

@Controller // 标记当前类是控制层
@Scope("prototype")
public class UserController {

    @Resource(name = "userServiceImpl")
    private UserService userService;

    @Value("aaa")
    private String a ;

    @Value("${ccc}")
    private String b;

    private List<Object> params;

    public void setParams(List<Object> params) {
        this.params = params;
    }

    private Object [] arrays;

    public void setArrays(Object[] arrays) {
        this.arrays = arrays;
    }

    private Set<Object> sets;

    public void setSets(Set<Object> sets) {
        this.sets = sets;
    }

    private Map<Object,Object> maps;

    public void setMaps(Map<Object, Object> maps) {
        this.maps = maps;
    }

    private Properties properties;

    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    public void setA(String a) {
        this.a = a;
    }

    public void setUserService(UserService userService) {
        this.userService = userService;
    }

   /* public UserController(String b,UserService userService, String a) {
        this.userService = userService;
        this.a = a;
        this.b = b;
    }*/

   @PostConstruct
    public void init(){
        System.out.println("init");
    }

    @PreDestroy
    public void destory(){
        System.out.println("destory");
    }

    public User login (String username,String password){
        System.out.println("a=" + a);
        System.out.println("b=" + b);
        System.out.println("list=" + params);
        System.out.println("arrays=" + Arrays.toString(arrays));
        System.out.println("sets=" + sets);
        System.out.println("maps=" + maps);
        System.out.println("properties=" + properties);
        System.out.println("controller执行");
        return userService.login(username,password);
    }
}

服务层接口

package com.zlt.service;

import com.zlt.entity.User;

public interface UserService {
    User login(String username, String password);
}

服务层实现类

package com.zlt.service.impl;

import com.zlt.dao.UserDao;
import com.zlt.entity.User;
import com.zlt.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service // 标记当前类是业务层
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public User login(String username, String password) {
        System.out.println("service执行");
        return userDao.selectUser(username,password);
    }
}

持久层接口

package com.zlt.dao;

import com.zlt.entity.User;

public interface UserDao {
    User selectUser(String username, String password);
}

持久层实现类

package com.zlt.dao.impl;

import com.zlt.dao.UserDao;
import com.zlt.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import javax.sql.DataSource;

@Repository // 标记当前类是持久层
public class UserDaoImpl implements UserDao {


//    @Autowired
//    private DataSource dataSource;

    @Override
    public User selectUser(String username, String password) {
        System.out.println("dao执行");
        return new User(100,username,password);
    }
}

实体类

package com.zlt.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class User {

    private Integer uid;

    private String username;

    private String password;


}

spring核心配置文件1

<?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
         http://www.springframework.org/schema/context/spring-context.xsd">
    <!--开启注解 让项目支持spring的注解  -->
<!--    <context:annotation-config></context:annotation-config>-->


    <!--注解扫描 会去扫描指定的包及其子包 中的类是否包含指定的spring注解
    本身也带有开启注解的功能 所以 context:annotation-config 就可以不用写了

    -->
<!--    <context:component-scan base-package="com.zlt"></context:component-scan>-->

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" >
        <!--注入的属性是基本数据类型或者String 可以用value来注入值-->
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql:///qq61"/>
        <property name="username" value="kinglee"/>
        <property name="password" value="root"/>
    </bean>


    <!--反射创建对象放入spring的容器-->
    <bean id="userDao" class="com.zlt.dao.impl.UserDaoImpl">

    </bean>

    <bean id="userService" class="com.zlt.service.impl.UserServiceImpl">
        <!--调用对象的set方法 方法名根据name的值确定
        所以name的值不能随便乱写 如果是对象类型一般使用ref赋值 ref的值需要在spring的容器中找到-->
        <property name="userDao" ref="userDao"/>
    </bean>



    <!--
        id 属性表示bean在容器中的唯一标识符 如果没有写 默认的id是 类的全路径#第几个 com.zlt.controller.UserController#0
        class 类的全路径 可以通过这个反射创建对象
        init-method 初始化方法 对象创建后执行
        destroy-method 对象销毁之前执行
        scope 作用范围
            singleton 单例模式 默认值
            prototype 工厂模式 每获取一次都会创建新的对象  不能和destroy-method一起使用
            如果是在web项目中 request 请求作用域 session 会话作用域 globalSession 全局作用域
        autowire 自动装配
            byType 默认从容器寻找对应的类型的bean注入进去 如果匹配到多个会不知道注入哪一个bean 所以会报错的
            byName 通过名称注入进去 id和属性名相同就可以注入
            constructor 通过构造进行注入 也就是说需要有对应的带参构造 才可以注入
            no 和default 都相当于不注入
        lazy-init 懒加载 如果设置为true则容器创建是不会初始化对象 默认是false
    -->
    <bean  class="com.zlt.controller.UserController" init-method="init" destroy-method="destory">
        <!--给对象的属性进行赋值 property 是会去调用属性的set方法 set方法有对应的命名规范 -->
        <!--如果是对象属性 可以考虑使用 ref来注入-->
        <property name="userService" ref="userService"/>
        <!--如果是基本数据类型或者String-->
        <property name="a" value="zhangsan"></property>

        <!--如果包含了带参构造,可以使用构造器来进行对应的注入
            name 是参数名
            type是 参数类型
            index 表示第几个参数
            value 基本数据类型或者String赋值使用
            ref 对象赋值使用
        -->
        <!--<constructor-arg  ref="userService"  />
        <constructor-arg    value="ccc"/>
        <constructor-arg    value="bbb"/>-->

        <!--集合注入-->
        <!--list注入-->
        <property name="params">
            <list>
                <!--如果是容器中不存在的对象 注入-->
                <bean class="com.zlt.entity.User">
                    <constructor-arg value="1"/>
                    <constructor-arg value="张三"/>
                    <constructor-arg value="123456"/>
                </bean>
<!--                容器中存在的对象注入-->
                <ref bean="userService"/>
                <!--基本数据类型或者String-->
                <value>zhangsan</value>
            </list>
        </property>

        <!--数组注入-->
        <property name="arrays">
            <array>
                <!--如果是容器中不存在的对象 注入-->
                <bean class="com.zlt.entity.User">
                    <constructor-arg value="1"/>
                    <constructor-arg value="张三"/>
                    <constructor-arg value="123456"/>
                </bean>
                <!--                容器中存在的对象注入-->
                <ref bean="userService"/>
                <!--基本数据类型或者String-->
                <value>zhangsan</value>
            </array>
        </property>

<!--        set集合注入-->
        <property name="sets">
            <set>
                <!--如果是容器中不存在的对象 注入-->
                <bean class="com.zlt.entity.User">
                    <constructor-arg value="1"/>
                    <constructor-arg value="张三"/>
                    <constructor-arg value="123456"/>
                </bean>
                <!--                容器中存在的对象注入-->
                <ref bean="userService"/>
                <!--基本数据类型或者String-->
                <value>zhangsan</value>
            </set>
        </property>

        <!--map集合注入-->
        <property name="maps">
            <map>
                <!--kv都是基本数据类型或者String-->
                <entry key="a" value="b"/>
                <!--key 是对象并且容器中存在   value是基本数据类型或者String-->
                <entry key-ref="userService" value="123"/>
                <!--key 是对象并且容器中不存在   value是基本数据类型或者String-->
                <entry value="123">
                    <key>
                        <bean class="com.zlt.entity.User">
                            <constructor-arg value="1"/>
                            <constructor-arg value="张三"/>
                            <constructor-arg value="123456"/>
                        </bean>
                    </key>
                </entry>
                <!--key 是基本数据类型或者String value容器中存在-->
                <entry key="123" value-ref="userService"/>
                <!--key 是基本数据类型或者String value容器中不存在-->
                <entry key="123" >
                    <bean class="com.zlt.entity.User">
                        <constructor-arg value="1"/>
                        <constructor-arg value="张三"/>
                        <constructor-arg value="123456"/>
                    </bean>
                </entry>
            </map>
        </property>

        <property name="properties">
            <props>
                <prop key="a">b</prop>
                <prop key="c">d</prop>
            </props>
        </property>
    </bean>


</beans>

spring核心位置文件2

<?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
         http://www.springframework.org/schema/context/spring-context.xsd">
    <!--开启注解 让项目支持spring的注解  -->

    <!--在spring中引入外部配置文件-->
    <context:property-placeholder location="classpath:aaaa.properties"/>
<!--    <context:annotation-config></context:annotation-config>-->


    <!--注解扫描 会去扫描指定的包及其子包 中的类是否包含指定的spring注解
    本身也带有开启注解的功能 所以 context:annotation-config 就可以不用写了

    -->
    <context:component-scan base-package="com.zlt">
        <!--需要扫描的注解-->
<!--        <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>-->
        <!--哪一些注解不需要扫描-->
<!--        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>-->
    </context:component-scan>

</beans>

资源文件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WZKT79kU-1681890082165)(assets/1661245240922.png)]

aaa=bbb
ccc=ddd

测试类

package com.zlt;

import com.zlt.controller.UserController;
import com.zlt.entity.User;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.Arrays;

/**
 * Hello world!
 *
 */
public class App 
{
    public static void main( String[] args )
    {
        //创建spring的容器并初始化
        ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("beans.xml");
        String[] beanNamesForType = classPathXmlApplicationContext.getBeanNamesForType(UserController.class);
        System.out.println(Arrays.toString(beanNamesForType));
        UserController bean = (UserController) classPathXmlApplicationContext.getBean("userController");
        bean.login("","");
        classPathXmlApplicationContext.close();// 关闭容器

        //从spring的容器中获取对象
        /*UserController userController = (UserController) classPathXmlApplicationContext.getBean("userController");
        User zhangsan = userController.login("zhangsan", "123456");
        System.out.println(zhangsan);*/
    }
}

名词解释

IOC(Inverse of control) 控制反转,将这个对象的创建及控制权从程序员的手中交给了spring的容器进行管理,程序员不再负责创建和管理对象,这个叫做控制反转

一个对象的完整的生命周期交给了spring容器管理

1.谁控制了谁:在传统的模式下,如果需要一个对象一般是我们自己去new一个对象,然后使用,对象的创建依赖于程序员,在使用了spring 以后,对象的创建都是交给了spring的容器,所以spring控制了对象

2.控制的是什么:控制的对象

3.为什么是反转:对象的控制权由程序员交给了spring管理,使用对象由以前的主动创建对象变成由spring提供对象,所以叫做反转

4.哪些反转了:对象的获取方式反转了

DI(Dependency injection) 依赖注入,在容器中一个对象的属性赋值依赖于容器中的其他对象,将容器中的其他对象注入到这个对象的属性中,所以DI也算是IOC的一部分

Bean的生命周期

spring中bean的完整的生命周期,由spring的容器创建到容器销毁,中间经历的事情

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nRcRRHAU-1681890082165)(spring.assets/181453414212066.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z9G5fIZx-1681890082165)(spring.assets/181454040628981.png)]

bean的完整的生命周期经历如下几个方法的调用,这些方法可以划分为几个类别

  1. Bean自身的方法:这些方法包括了Bean自身调用的方法和配置文件中的init-method和destory-method指定的方法
  2. Bean级生命周期接口的方法:包括了BeanNameAwre、BeanFactoryAwre、InitializingBean和DiposableBean这些接口的方法
  3. 容器级别的方法:主要包括了InstantiationAwreBeanPostProcessor和BeanPostProcessor这两个接口实现的方法,一般称为后处理器
  4. 工厂后处理器的方法包括:AspectjWavingEnabler,ConfigurationClassPOSTProcessor,CustomAutoAireConfigurer等有用的工厂后处理器接口的方法,

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IVg4MWU6-1681890082166)(spring.assets/java0-1558500658.jpg)]

如图所示吗,Bean的生命周期还是有点复杂的,对比上图来解释一下

  1. spring启动,查找并加载需要被spring管理的bean,进行bean的初始化
  2. bean实例化以后,将bean的引用和值注入到对应的bean的属性中
  3. 如果bean实现了BeanNameAwre接口的话,spring将bean的id传递给setBeanName的方法
  4. 如果bean实现了BeanFactoryAwre接口的话,spring将调用setBeanFactory方法将BeanFactory容器传入其中
  5. 如果Bean实现了ApplicationContextAwre接口,spring将调用bean的setApplicationContext方法将bean的上下文引用注入其中
  6. 如果Bean实现了BeanPOSTProcessor接口,spring就调用postProcessBeforeInitialization方法预初始化
  7. 如果Bean实现了InitializingBean接口,spring将调用他们的afterPropertiesSet
  8. 如果bean设置了自定义初始化方法,就会调用init-method指定的方法
  9. 如果bean实现了BeanPostProcessor接口,spring就会调用他们的postProcessAfterInitialization
  10. 此时bean准备好了可以开始使用,在使用的过程中,bean一直处于程序的上下文对象中,直到上下文被销毁
  11. 如果bean实现了DisposableBean接口,spring将调用他的destory方法
  12. 如果bean设置了自定义销毁方法,就会调用destory-method指定的方法
  13. 销毁完成

代理模式

在java中代理模式是使用的较多的一种设计模式,主要是提供了一个代理对象对外提供方法,然后通过代理对象去访问目标对象,这个月的好处就是可以在代理对象中实现功能增强

代理模式主要分为静态代理和动态代理(jdk代理,cglib代理)

代理模式最主要的就是在不改变原本代码的前提下实现功能增强

静态代理

首先创建一个接口

public interface UserDao{
    
    User login(String username,String password);
    
}

实现目标对象

public class UserDaoImpl implements UserDao{
    public User login(String username,String password){
        System.out.println("目标对象实现登录");
        return new User(username,password);
    }
}

实现代理对象

public class ProxyUserDao implements UserDao{
    
    private UserDao target;
    
    public ProxyUserDao(UserDao userDao){
        this.target = userDao;
    }
    
    public User login(String username,String password){
        System.out.println("代理对象实现增强1");
		User user = target.login(username,password);
        System.out.println("代理对象实现增强2");
        return user;
    }
}

测试代码

@Test
public void test(){
    UserDao target = new UserDaoImpl();//创建目标对象
	UserDao proxy = new  ProxyUserDao(target);//创建代理对象
	User user = proxy.login("zhangsan","12345");
}

静态代理中可以实现目标对象的功能增强,但是需要每一个方法都单独实现增强,可能会出现大量重复代码

动态代理

动态代理可以统一实现功能增强,而不需要每一个功能单独的去实现功能增强,实现方法主要有两种:jdk自带的代理和cglib代理

jdk代理

要求目标对象必须实现一个接口,先创建一个接口

package com.zlt.dao;

import com.zlt.entity.User;

public interface UserDao {

    boolean reg(User user);

    User login(String username, String password);
}

目标对象

package com.zlt.dao.impl;

import com.zlt.dao.UserDao;
import com.zlt.entity.User;
import org.springframework.stereotype.Repository;

@Repository
public class UserDaoImpl implements UserDao {
    @Override
    public boolean reg(User user) {
        System.out.println("dao中实现了注册" + user);
        return user != null;
    }

    @Override
    public User login(String username, String password) {
        System.out.println("dao中实现了登录" + username);
        return new User(1l,username,password);
    }
}

实现代理对象的方案执行器

package com.zlt.invocation;

import com.zlt.dao.UserDao;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class UserDaoInvocation implements InvocationHandler {

    private Object target;

    public UserDaoInvocation(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println(method.getName() + "方法增强1");
        Object invoke = method.invoke(target, args);
        System.out.println(method.getName() + "方法增强2");
        return invoke;
    }
}

测试

 @Test
    public void test(){
        //目标对象
        UserDao userDao = new UserDaoImpl();
        //执行器
        UserDaoInvocation userDaoInvocation = new UserDaoInvocation(userDao);
        //1. 类加载器  2目标对象实现的接口  3 执行器
        UserDao o = (UserDao) Proxy.newProxyInstance(userDao.getClass().getClassLoader(), userDao.getClass().getInterfaces(), userDaoInvocation);
        //代理对象执行方法
        User zhangsan = o.login("zhangsan", "123");
        boolean lisi = o.reg(new User(2l,"lisi", "123"));
        System.out.println(lisi);

    }
cglib代理

实现动态代理不需要实现接口都可以实现

添加依赖

<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>2.2.2</version>
</dependency>

创建目标对象

package com.zlt.dao.impl;

import com.zlt.dao.UserDao;
import com.zlt.entity.User;
import org.springframework.stereotype.Repository;

public class UserDaoImpl2 {
    
    public boolean reg(User user) {
        System.out.println("dao中实现了注册" + user);
        return user != null;
    }

    public User login(String username, String password) {
        System.out.println("dao中实现了登录" + username);
        return new User(1l,username,password);
    }
}

创建方法拦截器

package com.zlt.interceptor;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class UserDaoImterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("功能增强1");
        //调用目标对象
        Object o1 = methodProxy.invokeSuper(o, objects);
        System.out.println("功能增强2");
        return o1;
    }
}

测试代码

@Test
    public void test2(){
        //指定目录存放生成的动态代理的类
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"D://file3");
        //创建Enhancer 对象 相当于Proxy类
        Enhancer enhancer = new Enhancer();
        //设置代理的类的目标对象
        enhancer.setSuperclass(UserDaoImpl2.class);
        //创建方法拦截器的对象
        UserDaoImterceptor userDaoImterceptor = new UserDaoImterceptor();
        //设置回调函数 设置增强的功能
        enhancer.setCallback(userDaoImterceptor);
        //创建代理对象
        UserDaoImpl2 o = (UserDaoImpl2) enhancer.create();
        User zhangsan = o.login("zhangsan", "123");
        System.out.println(zhangsan);

    }

可以看到运行后会生成代理的class文件存储在指定的位置,其中可以反编译对应的class文件看出,代理类继承了传进去的目标类,在继承后,重写了里面的方法, 调用了方法的拦截器,执行interceptor方法实现功能增强

AOP

OOP (Object Oriented Programming) 面向对象编程

AOP (Aspect Oritented Programming) 面向切面编程

OOP 到AOP 不是替换的关系,而是一种扩展,使用了AOP后,OOP还是会继续使用

AOP 主要就是在不改变原本代码的前提下,新增功能上去不影响原本的功能

AOP在spring中是非常重要的一个功能,可以理解为一个业务就是一条线,当使用一把刀在这条线的指定位置砍下去,添加新的功能到断开出,最后在进行织入,最后连起来称为了一条新的先,新的功能就可以实现

添加依赖

<!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>5.3.18</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjrt -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.9.6</version>
    <scope>runtime</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.3.18</version>
</dependency>

注解实现

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y9FrK0UJ-1681890082166)(assets/1661323818595.png)]

增加
 http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd
         xmlns:aop="http://www.springframework.org/schema/aop"

修改配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       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.xs">
    <context:component-scan base-package="com.zlt"/>
    <!--
    proxy-target-class="true" 如果是true 就是cglib代理 如果是false就是jdk 默认是false
    -->
    <aop:aspectj-autoproxy />

</beans>

添加一个切面类

Signature  签名;署名;签字;签署;明显特征;鲜明特色;识别标志
Proceeding  会议录;进程;
package com.zlt.aspect;

import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Component //将对象放入容器中
@Aspect //标记当前类是一个切面类
public class UserAspect {

    private Logger logger = Logger.getLogger(UserAspect.class);


    @Before("execution( public com.zlt.entity.User com.zlt.service.impl.UserServiceImpl.login(java.lang.String,java.lang.String))")
    public void before(JoinPoint joinPoint){
        logger.debug("before执行");
        Object[] args = joinPoint.getArgs();
        logger.debug("参数为:" + Arrays.toString(args));
        logger.debug("拦截的方法为:" + joinPoint.getSignature().getDeclaringTypeName() + joinPoint.getSignature().getName());
        logger.debug("拦截的位置:" + joinPoint.getStaticPart());
        logger.debug("拦截的代理对象:" + joinPoint.getThis());
        logger.debug("拦截的目标对象:" + joinPoint.getTarget());
    }

    /**
     * 方法正常执行完成 才会执行 相当于After
     * @param joinPoint
     */
    @AfterReturning("execution( public com.zlt.entity.User com.zlt.service.impl.UserServiceImpl.login(java.lang.String,java.lang.String))")
    public void afterReturning(JoinPoint joinPoint){
        logger.debug("AfterReturning");
        Object[] args = joinPoint.getArgs();
        logger.debug("参数为:" + Arrays.toString(args));
        logger.debug("拦截的方法为:" + joinPoint.getSignature().getDeclaringTypeName() + joinPoint.getSignature().getName());
        logger.debug("拦截的位置:" + joinPoint.getStaticPart());
        logger.debug("拦截的代理对象:" + joinPoint.getThis());
        logger.debug("拦截的目标对象:" + joinPoint.getTarget());
    }

    /**
     * 方法抛出异常会执行 相当于catch
     * @param joinPoint
     * @param e
     */
    @AfterThrowing(value = "execution( public com.zlt.entity.User com.zlt.service.impl.UserServiceImpl.login(java.lang.String,java.lang.String))",throwing = "e")
    public void afterThrowing(JoinPoint joinPoint,RuntimeException e){
        logger.debug("afterThrowing");
        Object[] args = joinPoint.getArgs();
        logger.debug("参数为:" + Arrays.toString(args));
        logger.debug("拦截的方法为:" + joinPoint.getSignature().getDeclaringTypeName() + joinPoint.getSignature().getName());
        logger.debug("拦截的位置:" + joinPoint.getStaticPart());
        logger.debug("拦截的代理对象:" + joinPoint.getThis());
        logger.debug("拦截的目标对象:" + joinPoint.getTarget());
        logger.debug("抛出异常",e);
    }

    /**
     * 无论怎样都会执行相当于finally
     * @param joinPoint
     */
    @After(value = "execution( public com.zlt.entity.User com.zlt.service.impl.UserServiceImpl.login(java.lang.String,java.lang.String))")
    public void after(JoinPoint joinPoint){
        logger.debug("After");
        Object[] args = joinPoint.getArgs();
        logger.debug("参数为:" + Arrays.toString(args));
        logger.debug("拦截的方法为:" + joinPoint.getSignature().getDeclaringTypeName() + joinPoint.getSignature().getName());
        logger.debug("拦截的位置:" + joinPoint.getStaticPart());
        logger.debug("拦截的代理对象:" + joinPoint.getThis());
        logger.debug("拦截的目标对象:" + joinPoint.getTarget());
    }

    /**
     * 环绕执行
     * @param pjb
     * @return
     * @throws Throwable
     */
    @Around(value = "execution( public com.zlt.entity.User com.zlt.service.impl.UserServiceImpl.login(java.lang.String,java.lang.String))")
    public Object around(ProceedingJoinPoint pjb) throws Throwable {
        logger.debug("around方法之前");
        Object proceed = pjb.proceed();//目标方法正常执行
        logger.debug("around方法之后");
        return proceed;

    }

}

切入点语法

直接精准到一个方法上面去
execution( public com.zlt.entity.User com.zlt.service.impl.UserServiceImpl.login(java.lang.String,java.lang.String))
任意权限修饰符
execution(  com.zlt.entity.User com.zlt.service.impl.UserServiceImpl.login(java.lang.String,java.lang.String))
无返回类型
execution( void com.zlt.service.impl.UserServiceImpl.login(java.lang.String,java.lang.String))
有返回类型
execution( !void com.zlt.service.impl.UserServiceImpl.login(java.lang.String,java.lang.String))
任意返回类型
execution( * com.zlt.service.impl.UserServiceImpl.login(java.lang.String,java.lang.String))
任意参数
execution( * com.zlt.service.impl.UserServiceImpl.login(..))
类中的任意方法
execution( * com.zlt.service.impl.UserServiceImpl.*(..))
类中以指定内容开头的方法
execution( * com.zlt.service.impl.UserServiceImpl.select*(..))
包中的任意类的任意方法不包含子包下面的类
execution( * com.zlt.service.impl.*.*(..))
包中及其下的任意类的任意方法
execution( * com.zlt.service..*.*(..))

xml实现aop

添加切面类

package com.zlt.aspect;

import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Component //将对象放入容器中
public class UserAspect {

    private Logger logger = Logger.getLogger(UserAspect.class);


    public void before(JoinPoint joinPoint){
        logger.debug("before执行");
        Object[] args = joinPoint.getArgs();
        logger.debug("参数为:" + Arrays.toString(args));
        logger.debug("拦截的方法为:" + joinPoint.getSignature().getDeclaringTypeName() + joinPoint.getSignature().getName());
        logger.debug("拦截的位置:" + joinPoint.getStaticPart());
        logger.debug("拦截的代理对象:" + joinPoint.getThis());
        logger.debug("拦截的目标对象:" + joinPoint.getTarget());
    }

    /**
     * 方法正常执行完成 才会执行 相当于After
     * @param joinPoint
     */
    public void afterReturning(JoinPoint joinPoint){
        logger.debug("AfterReturning");
        Object[] args = joinPoint.getArgs();
        logger.debug("参数为:" + Arrays.toString(args));
        logger.debug("拦截的方法为:" + joinPoint.getSignature().getDeclaringTypeName() + joinPoint.getSignature().getName());
        logger.debug("拦截的位置:" + joinPoint.getStaticPart());
        logger.debug("拦截的代理对象:" + joinPoint.getThis());
        logger.debug("拦截的目标对象:" + joinPoint.getTarget());
    }

    /**
     * 方法抛出异常会执行 相当于catch
     * @param joinPoint
     * @param e
     */
    public void afterThrowing(JoinPoint joinPoint,RuntimeException e){
        logger.debug("afterThrowing");
        Object[] args = joinPoint.getArgs();
        logger.debug("参数为:" + Arrays.toString(args));
        logger.debug("拦截的方法为:" + joinPoint.getSignature().getDeclaringTypeName() + joinPoint.getSignature().getName());
        logger.debug("拦截的位置:" + joinPoint.getStaticPart());
        logger.debug("拦截的代理对象:" + joinPoint.getThis());
        logger.debug("拦截的目标对象:" + joinPoint.getTarget());
        logger.debug("抛出异常",e);
    }

    /**
     * 无论怎样都会执行相当于finally
     * @param joinPoint
     */
    public void after(JoinPoint joinPoint){
        logger.debug("After");
        Object[] args = joinPoint.getArgs();
        logger.debug("参数为:" + Arrays.toString(args));
        logger.debug("拦截的方法为:" + joinPoint.getSignature().getDeclaringTypeName() + joinPoint.getSignature().getName());
        logger.debug("拦截的位置:" + joinPoint.getStaticPart());
        logger.debug("拦截的代理对象:" + joinPoint.getThis());
        logger.debug("拦截的目标对象:" + joinPoint.getTarget());
    }

    /**
     * 环绕执行
     * @param pjb
     * @return
     * @throws Throwable
     */
    public Object around(ProceedingJoinPoint pjb) throws Throwable {
        logger.debug("around方法之前");
        Object proceed = pjb.proceed();//目标方法正常执行
        logger.debug("around方法之后");
        return proceed;

    }

}

配置文件如下

<?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/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.zlt"/>
    <!--
    proxy-target-class="true" 如果是true 就是cglib代理 如果是false就是jdk 默认是false
    -->
    <aop:aspectj-autoproxy />

    <aop:config>
        <!--定义切面-->
        <aop:aspect ref="userAspect">
            <!--定义切入点-->
            <aop:pointcut id="pc" expression="execution( public com.zlt.entity.User com.zlt.service.impl.UserServiceImpl.login(java.lang.String,java.lang.String))"/>
            <aop:before method="before" pointcut-ref="pc"/>
            <aop:after-returning method="afterReturning" pointcut-ref="pc"/>
            <aop:after-throwing method="afterThrowing" pointcut-ref="pc" throwing="e"/>
            <aop:after method="after" pointcut-ref="pc"/>
            <aop:around method="around" pointcut-ref="pc"/>
        </aop:aspect>
    </aop:config>

</beans>

名词解释

pointcut 切入点 定义切入的连接点, 一般对应的就是表达式
aspect 切面 拥有具体功能的一个类
advice 通知 切面的具体实现 对应的就是切面类中的方法
joinpoint 连接点 程序运行中可以插入切面的地方 在spring中只能是方法 比如login方法
target 目标对象 切入的对象 这个对象包含了业务代码的具体实现 比如:UserServiceImpl类的对象
proxy 代理对象 目标对象应用了通知以后创建的一个新的对象,这个对象中包含了原本的业务实现和扩展实现
weaving 织入 将通知应用到目标对象后创建代理对象的过程

事务管理

主要就是整合spring和mybatis,整合以后mybatis的事务就可以交给spring来进行管理,本质实现还是aop

添加依赖

<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.3.18</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.12</version>
      <scope>provided</scope>
    </dependency>

    <!-- https://mvnrepository.com/artifact/cglib/cglib -->
    <dependency>
      <groupId>cglib</groupId>
      <artifactId>cglib</artifactId>
      <version>2.2.2</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>5.3.18</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjrt -->
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjrt</artifactId>
      <version>1.9.6</version>
      <scope>runtime</scope>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>5.3.18</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.17</version>
    </dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-tx -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-tx</artifactId>
    <version>5.3.18</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-orm -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-orm</artifactId>
    <version>5.3.18</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.3.18</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.4.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.38</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>1.3.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.2.8</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis.generator/mybatis-generator-core -->
<dependency>
    <groupId>org.mybatis.generator</groupId>
    <artifactId>mybatis-generator-core</artifactId>
    <version>1.3.7</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper -->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.2.0</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:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans.xsd
         http://www.springframework.org/schema/context
         http://www.springframework.org/schema/context/spring-context.xsd
         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">
    <context:component-scan base-package="com.zlt"/>

    <aop:aspectj-autoproxy/>

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

    <!--创建数据源-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <property name="driverClassName" value="${driver}"/>
        <property name="url" value="${url}"/>
        <property name="username" value="${username}"/>
        <property name="password" value="${password}"/>
    </bean>
    <!--会话工厂-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--注入数据源-->
        <property name="dataSource" ref="dataSource"/>
        <property name="mapperLocations" value="classpath:mapper/**.xml"/>
        <!--别名配置-->
<!--        <property name="typeAliasesPackage" value="com.zlt.entity"/>-->
<!--        引入mybatis的配置文件-->
<!--        <property name="configLocation" value="classpath:config.xml"/>-->
        <!--添加插件-->
        <property name="plugins">
            <array>
                <bean class="com.github.pagehelper.PageInterceptor">
                    <property name="properties">
                        <props>
                            <prop key="helperDialect">mysql</prop>
                            <prop key="reasonable">true</prop>
                        </props>
                    </property>
                </bean>
            </array>
        </property>
    </bean>

<!--    配置接口扫描-->
    <!--    那个会话工厂扫描的-->
    <bean id="scannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.zlt.dao"/>
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
    </bean>

    <!--事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
<!--    事务交给注解管理  有默认值,如果id正确 其实可以不写-->
    <tx:annotation-driven transaction-manager="transactionManager"/>
</beans>

事务添加在业务层如下:

package com.zlt.service.impl;

import com.zlt.dao.UserMapper;
import com.zlt.dao.WeiboMapper;
import com.zlt.entity.User;
import com.zlt.entity.UserExample;
import com.zlt.entity.Weibo;
import com.zlt.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private WeiboMapper weiboMapper;
    @Autowired
    private UserMapper userMapper;

    
    /**
     * transactionManager 和 value 是一样的 都是配置事务管理器
     * propagation 事务的传播机制
     *     REQUIRED , 默认值 如果之前已经开启了事务,就直接使用之前的事务,如果之前没有开启事务,那么就开启一个事务
     *     SUPPORTS , 如果其他地方调用这个方法的时候,其他地方有事务就使用事务,否则这个方法就不使用事务			MANDATORY   强制性的
     *     MANDATORY , 必须在一个已有的事务中运行,否则会报错
     *     REQUIRES_NEW ,   不管之前是否开启了事务,都会创建一个新的事务,原本的事务挂起,直到这个方法执行完毕,原本的事务继续执行
     *     NOT_SUPPORTED , spring不会为他开启事务,相当于没有事务
     *     NEVER , 必须在一个没有的事务中运行,否则报错
     *     NESTED ; 如果当前存在事务,则开启嵌套事务在当前事务的内部运行,如果当前没有事务则和 REQUIRED 类似      NESTED  嵌套
     *     注意:
     *     REQUIRED  a 调用 b 如果两个方法都是 REQUIRED 一旦发生回滚两个方法都会回滚
     *     REQUIRES_NEW a 调用 b  如果b是REQUIRES_NEW 并且单独提交 a和b互不影响
     *     NESTED a 调用 b 如果a是 REQUIRED b是 NESTED b回滚 a不受影响  a回滚 b也会回滚
     * isolation 隔离级别  设置当前事务的隔离级别
     *      DEFAULT , 数据库是什么就是什么
     *     READ_UNCOMMITTED , 可读未提交
     *     READ_COMMITTED , 可读已提交
     *     REPEATABLE_READ , 可重复读
     *     SERIALIZABLE; 串行化
     * timeout 超时时间
     *
     *  readOnly 只读 如果设置为true 那么这个方法只能是查询 如果出现增删改都会报错
     *
     * rollbackFor 哪些会回滚  默认情况下只有运行时异常会回滚
     *
     * noRollbackFor 哪些不会回滚
     * @param username
     * @param password
     * @return
     */
    @Transactional
    @Override
    public User login(String username, String password) {
        //先通过username查询User对象
        UserExample userExample = new UserExample();
        userExample.createCriteria().andUsernameEqualTo(username);
        List<User> users = userMapper.selectByExample(userExample);
        if(users.isEmpty()){
            throw new RuntimeException("账号错误");
        }
        //修改登录时间
        User user = users.get(0);
        user.setRegtime(new Date());
        userMapper.updateByPrimaryKeySelective(user);
        user.setLogintime(new Date());
        userMapper.updateByPrimaryKeySelective(user);
        return user;
    }

    
}

测试

@Test
public void test()
{
    ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("ApplicationContext.xml");
    UserController userController = (UserController)  classPathXmlApplicationContext.getBean("userController");
    User wangwu9 = userController.login("zhangsan", "123");
    System.out.println(wangwu9);
}

xml实现

配置文件如下即可

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans.xsd
         http://www.springframework.org/schema/context
         http://www.springframework.org/schema/context/spring-context.xsd
         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">
    <context:component-scan base-package="com.zlt"/>

    <aop:aspectj-autoproxy/>

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

    <!--创建数据源-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <property name="driverClassName" value="${driver}"/>
        <property name="url" value="${url}"/>
        <property name="username" value="${username}"/>
        <property name="password" value="${password}"/>
    </bean>
    <!--会话工厂-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--注入数据源-->
        <property name="dataSource" ref="dataSource"/>
        <property name="mapperLocations" value="classpath:mapper/**.xml"/>
        <!--别名配置-->
<!--        <property name="typeAliasesPackage" value="com.zlt.entity"/>-->
<!--        引入mybatis的配置文件-->
<!--        <property name="configLocation" value="classpath:config.xml"/>-->
        <!--添加插件-->
        <property name="plugins">
            <array>
                <bean class="com.github.pagehelper.PageInterceptor">
                    <property name="properties">
                        <props>
                            <prop key="helperDialect">mysql</prop>
                            <prop key="reasonable">true</prop>
                        </props>
                    </property>
                </bean>
            </array>
        </property>
    </bean>

<!--    配置接口扫描-->
    <bean id="scannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.zlt.dao"/>
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
    </bean>

    <!--事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
<!--    事务交给注解管理  有默认值,如果id正确 其实可以不写-->
<!--    <tx:annotation-driven transaction-manager="transactionManager"/>-->
    <tx:advice transaction-manager="transactionManager" id="interceptor">
        <tx:attributes>
            <!--针对方法去配置事务-->
            <tx:method name="select*" read-only="true" propagation="REQUIRED" />
            <tx:method name="update*" read-only="false" />
            <tx:method name="insert*" read-only="false" />
            <tx:method name="delete*" read-only="false" />
            <tx:method name="*" />
        </tx:attributes>
    </tx:advice>

    <aop:config >
        <aop:advisor advice-ref="interceptor" pointcut="execution(* com.zlt.service..*.*(..))"/>
    </aop:config>


</beans>

方法上就不需要再添加@Transactional注解了

docker

把项目部署到docker上去

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-a8Sj4mWp-1681890082167)(assets/1661326065887.png)]

=

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S5YgEGgw-1681890082167)(assets/1661326079218.png)]

=

http://mirrors.aliyun.com/centos/7.9.2009/isos/x86_64/

=

https://mirrors.tuna.tsinghua.edu.cn/centos/7.9.2009/isos/x86_64/

=

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DQeLv1FG-1681890082167)(assets/1661326180575.png)]

om.zlt.entity"/>–>

    <!--添加插件-->
    <property name="plugins">
        <array>
            <bean class="com.github.pagehelper.PageInterceptor">
                <property name="properties">
                    <props>
                        <prop key="helperDialect">mysql</prop>
                        <prop key="reasonable">true</prop>
                    </props>
                </property>
            </bean>
        </array>
    </property>
</bean>
<bean id="scannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.zlt.dao"/>
    <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>

<!--事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>
<tx:advice transaction-manager="transactionManager" id="interceptor">
    <tx:attributes>
        <!--针对方法去配置事务-->
        <tx:method name="select*" read-only="true" propagation="REQUIRED" />
        <tx:method name="update*" read-only="false" />
        <tx:method name="insert*" read-only="false" />
        <tx:method name="delete*" read-only="false" />
        <tx:method name="*" />
    </tx:attributes>
</tx:advice>

<aop:config >
    <aop:advisor advice-ref="interceptor" pointcut="execution(* com.zlt.service..*.*(..))"/>
</aop:config>
```

方法上就不需要再添加@Transactional注解了

docker

把项目部署到docker上去

[外链图片转存中…(img-a8Sj4mWp-1681890082167)]

=

[外链图片转存中…(img-S5YgEGgw-1681890082167)]

=

http://mirrors.aliyun.com/centos/7.9.2009/isos/x86_64/

=

https://mirrors.tuna.tsinghua.edu.cn/centos/7.9.2009/isos/x86_64/

=

[外链图片转存中…(img-DQeLv1FG-1681890082167)]

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员zhi路

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值