spring (根据狂神说上课写的笔记)

目录

 

1、spring

1.1简介

 1.2优点

1.3组成

 1.4扩展

2、Ioc理论推导

IOC本质

3、第一个Spring

4、IOC创建对象的方式 

5、Spring配置 

5.1 别名

5.2 Bean配置

5.3 import

6、依赖注入

6.1 构造器注入

6.2 Set方式注入【重点】

6.3 拓展方式注入

6.4 bean的作用域

7、Bean的自动装配

8、使用注解开发

衍生注解

9、使用Java的方式配置Spring

10、代理模式(aop的底层)

 动态代理 


1、spring

1.1简介

  • Spring:春天------>给软件行业带来了春天!
  • 2002,首次推出了Spring框架的雏形:interface21框架!
  • Spring框架即以interface21框架为基础,经过重新设计,并不断丰富其内涵,于2004年3月24日发布了1.0正式版。
  • Rod Johnson,Spring Framework创始人,著名作者。很难想象Rod Johnson的学历,真的让好多人大吃一惊,他是悉尼大学的博士,然而他的专业不是计算机,而是音乐学。
  • Spring理念:使现有的技术更加容易使用,本身是一个大杂烩,整合了现有的技术框架!
  • SSH:Struct2 + Spring + Hibernate!
  • SSM:SpringMVC + Spring + Mybatis!

官网:Spring Framework

官方下载地址:JFrog

GitHub:GitHub - spring-projects/spring-framework: Spring Framework

导入maven的包

<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.0.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.2.0.RELEASE</version>
</dependency>

 1.2优点

  • Spring是一个开源免费的框架
  • Spring是一个轻量级的、非入侵式的框架!
  • 控制反转(IOC),面向切面编程(AOP)!
  • 支持事务的处理,对框架整合的支持!

总结一句话:Spring就是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架!

1.3组成

在这里插入图片描述

 1.4扩展

现在的Java开发,基于Spring的开发!

  • Spring Boot
    • 一个快速开发的脚手架
    • 基于SpringBoot可以快速的开发单个微服务
    • 约定大于配置
  • Spring Cloud
    • SpringCloud是基于SpringBoot实现的

因为现在大多数公司都在使用SpringBoot进行快速开发,学习SpringBoot的前提,需要完全掌握Spring以及SpringMVC!承上启下的作用!

弊端:发展了太久之后,违背了原来的理念!配置十分繁琐,人称:“配置地狱”

2、Ioc理论推导

        1、UserDao 接口

public interface UserDao {
    void getUser();
}

        2、UserDaoImpl 实现类

public class UserDaoImpl implements UserDao {
    public void getUser() {
        System.out.println("默认获取用户数据");
    }
}

        3、UserService 业务接口

public interface UserService {
    void getUser();
}

        4、UserServiceImpl 业务实现类

public class UserServiceImpl implements UserService {

    private UserDao userDao = new UserDaoImpl();

    public void getUser() {
        userDao.getUser();
    }
}

        5、测试

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

        //用户实际调用的是业务层,dao层他们不需要接触!
        UserService userService = new UserServiceImpl();
        userService.getUser();
    }
}

在我们之前的业务中,用户的需求可能会影响我们原来的代码,我们需要根据用户的需求去修改原代码!如果程序代码量十分大,修改一次的成本代价十分昂贵!

在这里插入图片描述

使用set接口,让程序改变,用户想用哪个就用那个,不用去该底层代码了

 private UserDao userDao;

    //利用set进行动态实现值的注入!
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
  • 之前,程序是主动创建对象!控制权在程序员手上!
  • 使用了set注入后,程序不再具有主动性,而是变成了被动的接收对象!

在这里插入图片描述

IOC本质

控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法。没有IoC的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。

采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。
控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)。

3、第一个Spring

根据上面的方法改写成一个Spring框架实现的,(必须要有set方法)

多写几个方法实现UserDao接口

public class UserMysqlImpl implements UserDao{
    @Override
    public void getUserDao() {
        System.out.println("调用mysql数据库");
    }
}
public class UserSqlServerImpl implements UserDao{
    @Override
    public void getUserDao() {
        System.out.println("SqlServer欢迎你");
    }

}

下面编写beans.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--使用Spring来创建对象,在Spring这些都称为Bean
类型 变量名 = new 类型();
Hello hello = new Hello();
 

id = 变量名
class = new的对象
propetry 相当于给对象的属性值赋值




-->

    <bean id="mysql" class="com.chen.dao.UserMysqlImpl"/>
    <bean id="sqlserver" class="com.chen.dao.UserSqlServerImpl"/>
    <bean id="UserServiceImpl" class="com.chen.service.UserServiceImpl">
        <property name="userDao" ref="mysql"/>
<!--    ref : 创建好的对象   value : 具体值,基本数据类型-->
    </bean>

</beans>

测试

import com.chen.service.UserServiceImpl;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    public static void main(String[] args) {
        //获取applicationContext对象 拿到spring容器
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        
        
        UserServiceImpl userServiceImpl = (UserServiceImpl) context.getBean("UserServiceImpl");
        userServiceImpl.getUserService();
    }
}

思考问题?

  • Hello对象是谁创建的?
    Hello对象是由Spring创建的。
  • Hello对象的属性是怎么设置的?
    Hello对象的属性是由Spring容器设置的。

 控制反转

控制:谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建的,使用Spring后,对象是由Spring来创建的。

反转:程序本身不创建对象,而变成被动的接收对象。

依赖注入:就是利用set方法来进行注入的。

IOC是一种编程思想,由主动的编程变成被动的接收。

可以通过new ClassPathXmlApplicationContext去浏览一下底层源码。

我们彻底不用在程序中去改动了,要实现不同的操作,只需要在xml配置文件中进行修改,所谓的IOC,一句话搞定:对象由Spring来创建,管理,装配!

4、IOC创建对象的方式 

  1. 使用无参构造,默认!
  2. 我们使用有惨构造方法
    1. 下标
      <!--第一种创建有参构造的方法,用下标去创建,index表示下标,下标从0开始-->
          <bean id="user" class="com.chen.pojo.User">
              <constructor-arg index="0" value="cfy"/>
          </bean>
    2. 类型
      <!--第二种方式:通过类型的创建,不建议使用    -->
      <bean id="user" class="com.kuang.pojo.User">
          <constructor-arg type="java.lang.String" value="lifa"/>
      </bean>

    3. 参数名
      <!--第三种方式:直接通过参数名来设置    -->
      <bean id="user" class="com.kuang.pojo.User">
          <constructor-arg name="name" value="朱增增"/>
      </bean>

      总结,在配置文件加载的时候,容器中的对象已经初始化

5、Spring配置 

5.1 别名

  <!--别名,如果添加了别名,我们也可以使用别名获取到这个对象-->
    <alias name="user" alias="userNew"/>

5.2 Bean配置

    <!--
    id:bean的唯一标识符,也就是相当于我们学的对象名
    class:bean对象所对应的全限定名:包名+类名
    name:也是别名,而且name可以同时取多个别名
        -->
    <bean id="userT" class="com.kuang.pojo.UserT" name="user2 u2,u3;u4">
        <property name="name" value="朱增增"/>
    </bean>

5.3 import

这个import。一般用于团队开发使用,它可以将多个配置文件,导入合并为一个。
假设,现在项目中有多个人开发,这三个人负责不同的类开发,不同的类需要注册在不同的bean中,我们可以利用import将所有人的beans.xml合并为一个总的!

<import resource="bean.xml"/>
<import resource="bean2.xml"/>
<import resource="bean3.xml"/>

6、依赖注入

6.1 构造器注入

前面有

6.2 Set方式注入【重点】

  • 依赖注入:Set注入
    • 依赖:bean对象的创建依赖于容器!
    • 注入:bean对象中的所有属性,由容器来注入!

先创建两个类

Address

public class Address {
    private String address;

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

Student


public class Student {

    private String name;
    private Address address;
    private String[] books;
    private List<String> hobbies;
    private Map<String,String> card;
    private Set<String> games;
    private String wife;
    private Properties info;
}

配置文件

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="address" class="com.chen.pojo.Address">
        <property name="address" value="成都"/>
    </bean>

    <bean id="student" class="com.chen.pojo.Student">
        <property name="name" value="陈富友"/>
        <!--对象注入-->
        <property name="address" ref="address"/>
        <!--数组注入-->
        <property name="books">
            <array>
                <value>红楼梦</value>
                <value>水浒传</value>
                <value>三国演义</value>
            </array>
        </property>
        <!--list注入-->
        <property name="hobbies">
            <list>
                <value>听歌</value>
                <value>美女</value>
            </list>
        </property>
        <!--map注入-->
        <property name="card">
            <map>
                <entry key="身份证" value="123456123456781234"/>
                <entry key="学号" value="1234123456"/>
            </map>
        </property>
        <!--set注入-->
        <property name="games">
            <set>
                <value>LOL</value>
                <value>CS</value>
                <value>王者</value>
            </set>
        </property>
        <!--空注入-->
<!--        <property name="wife" value=""/>-->
        <property name="wife">
            <null/>
        </property>
        <!--Properties注入-->
        <property name="info">
            <props>
                <prop key="driver">2020121124</prop>
                <prop key="url">JDBC</prop>
                <prop key="username">root</prop>
                <prop key="password">123456</prop>
            </props>
        </property>
    </bean>
</beans>

6.3 拓展方式注入

使用p/c命名空间进行注入

直接上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:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="user" class="com.chen.pojo.User" p:age="18" p:name="陈富友" scope="singleton"/>

    <bean id="user2" class="com.chen.pojo.User" c:age="20" c:name="陈富友" scope="prototype"/>
</beans>

 实体类

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

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private String name;
    private int age;
}

6.4 bean的作用域

在这里插入图片描述

xml 文件

<!--单例模式(Spring默认机制)-->
<bean id="user2" class="com.kuang.pojo.User" c:name="狂神" c:age="22" scope="singleton"/>


<!--原型模式:每次从容器中get的时候,都会产生一个新对象!-->
<bean id="user2" class="com.kuang.pojo.User" c:name="狂神" c:age="22" scope="prototype"/>


<!--其余的request、session、application、这些只能在web开发中用到!-->

7、Bean的自动装配

  • 自动装配是Spring满足bean依赖的一种方式
  • Spring会在上下文中自动寻找,并自动给bean装配属性

在Spring中有三种装配的方式:

  1. 在xml中显式的配置;
  2. 在java中显式配置;
  3. 隐式的自动装配bean【重要】

编写三个测试类

 xml 文件

  <!--
        byName:会自动在容器上下文中查找,和自己对象set方法后面的值对应的bean id!
            -->
        <bean id="people" class="com.kuang.pojo.People" autowire="byName">
            <property name="name" value="朱增增"/>
        </bean>
        <!--
        byType:会自动在容器上下文中查找,和自己对象属性类型相同的bean!
            -->
        <bean id="people" class="com.kuang.pojo.People" autowire="byType">
            <property name="name" value="朱增增"/>
        </bean>

上面的截图是使用了注解自动装配的

但是我们使用注解开发我们的在xml文件中配置

注意自己观察和上面的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: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/>

@Autowired

直接在属性上使用即可!也可以在set方法上使用!

使用Autowried我们就可以不用编写set方法了,前提是你这个自动配置的属性在IOC(Spring)容器中存在,且符合名字ByName!

@Nullable 字段标记了了这个注解,说明这个字段可以为null;

结合完整的xml文件和注解类进行观测

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: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/>
<!--    <bean id="cat" class="com.chen.pojo.Cat"/>-->
<!--    <bean id="dog" class="com.chen.pojo.Dog"/>-->
    <bean id="cat1" class="com.chen.pojo.Cat"/>
    <bean id="dog1" class="com.chen.pojo.Dog"/>
    <bean id="cat2" class="com.chen.pojo.Cat"/>
    <bean id="dog2" class="com.chen.pojo.Dog"/>

<!--    自动装箱 byName 会自动在容器上下文中查找,和自己对象set方法后面的值对应Bean id-->
<!--    byType: 会自动在容器上下文中查找,和自己对象属性类型相同-->
    <bean id="people" class="com.chen.pojo.People"/>

</beans>

java 实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class People {

    private String name;
    @Autowired
    @Qualifier(value = "dog1")
    private Dog dog;
//    @Resource(name = "cat")//指定装配的名字。
    @Autowired
    @Qualifier(value = "cat1")
    private Cat cat;
}

 也可以用@Resource 这个就不用配置文件

@Data
@AllArgsConstructor
@NoArgsConstructor
public class People {

    private String name;
    @Resource
    private Dog dog;

    @Resource(name = "cat")//指定装配的名字。
    private Cat cat;
}

@Resource和@Autowired的区别:

  • 都是用来自动装配的,都可以放在属性字段上
  • @Autowired通过byType的方式实现,而且必须要求这个对象存在!【常用】
  • @Resource默认通过byName的方式实现,如果找不到名字,则通过byType实现!如果两个都找不到的情况下,就报错!【常用】
  • 执行顺序不同:@Autowired通过byType的方式实现。

8、使用注解开发

使用注解需要导入约束,配置注解的支持!

<?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:component-scan base-package="com.kuang.pojo"/>
<!--开启注解支持-->
    <context:annotation-config/>

对User进行注解开发

@Component//等价于<bean id="user" class="com.chen.pojo.User"/>
public class User {
    @Value("chenfuyou") //等价于这句话<property name="name" value="chenfuyou"/>
    public String name;
}

 @Value("chenfuyou")这就话也可以放在set方法上

衍生注解

我们这些注解,就是替代了在配置文件当中配置步骤而已!更加的方便快捷!

@Component三个衍生注解

为了更好的进行分层,Spring可以使用其它三个注解,功能一样,目前使用哪一个功能都一样。

  • @Controller: web层
  • @Service:service层
  • @Repository:dao层

写上这些注释,就相当于将这个类交给Spring管理装配了!

在Bean的自动装配(上面有)

  • @Autowired:自动装配通过类型,名字。如果Autowired不能唯一自动装配上属性,则需要通过@Qualifier(value = "xxx")去配置。
  • @Nullable 字段标记了了这个注解,说明这个字段可以为null;
  • @Resource:自动装配通过名字,类型。

作用域

@scope

  • singleton:默认的,Spring会采用单例模式创建这个对象。关闭工厂 ,所有的对象都会销毁。
  • prototype:多例模式。关闭工厂 ,所有的对象不会销毁。内部的垃圾回收机制会回收

小结

xml与注解:

  • xml更加万能,适用于任何场合!维护简单方便
  • 注解不是自己类使用不了,维护相队复杂!

xml与注解最佳实践:

  • xml用来管理bean;
  • 注解只负责完成属性的注入;
  • 我们在使用的过程中,只需要注意一个问题:必须让注解生效,就需要开启注解的支持

 再次提醒

   <!--指定要扫描的包,这个包下的注解就会生效-->
    <context:component-scan base-package="com.kuang"/>
    <!--开启注解的支持    -->
    <context:annotation-config/>

作用:

  • 进行注解驱动注册,从而使注解生效
  • 用于激活那些已经在spring容器里注册过的bean上面的注解,也就是显示的向Spring注册
  • 如果不扫描包,就需要手动配置bean
  • 如果不加注解驱动,则注入的值为null!

9、使用Java的方式配置Spring

JavaConfig 原来是 Spring 的一个子项目,它通过 Java 类的方式提供 Bean 的定义信息,在 Spring4 的版本, JavaConfig 已正式成为 Spring4 的核心功能 。

实体类(沿用上面的User)

@Component//等价于<bean id="user" class="com.chen.pojo.User"/>
public class User {
    @Value("chenfuyou") //等价于这句话<property name="name" value="chenfuyou"/>
    public String name;
}

写配置文件的Java类(在Springboot有用)

//这个也是Spring容器托管,注册到容器中,因为他本来就是一个@Component(组件)
//@Configuration 代表一个配置类 就和我们之前看的beans.xml
@Configuration
@ComponentScan("com.chen.pojo")
public class ChenConfig {

    //注册一个bean,就相当于我们之前写的一个bean标签
    //这个方法的名字,就相当于bean标签中的id属性
    //这个方法的返回值,就相当于bean标签中的class属性
    @Bean
    public User getUser(){
        return new User();
    }
}

测试

public class MyTest {
    @Test
    public void Test(){
        //不去写配置文件进行bean注入
        ApplicationContext context = new AnnotationConfigApplicationContext(ChenConfig.class);
        User user = (User) context.getBean("getUser");
        System.out.println(user.name);
    }
}

10、代理模式(aop的底层)

静态代理和动态代理

先理解代理这个东西,可以反复观看狂神说的23三种设计模式,

代理我自己的理解哈,有错误的可以随时找我,我也不是很明白,哈哈哈!

就相当于,找个人帮你完成繁琐的任务,然后你可以做其他的事情,不干扰你的工作

一个公司可能需要在项目前后打印日志 在不修改代码的前提下

静态代理可以完成,就是给每一个类创建一个代理类就相当于找一个经纪人,让这个代理类实现和目标类相同的接口

创建代理对象时,通过构造器塞入一个目标对象,然后在代理对象的方法内部调用目标对象同名方法,并在调用前后打印日志。

代理对象 = 增强代码 + 目标对象(原对象)

实列

接口

public interface UserService {
    public void add();
    public void delete();
    public void update();
    public void insert();
}

目标类

public class UserServiceImpl implements UserService{
    @Override
    public void add() {
        System.out.println("增加一个用户");
    }

    @Override
    public void delete() {
        System.out.println("删除一个用户");
    }

    @Override
    public void update() {
        System.out.println("修改一个用户");
    }

    @Override
    public void insert() {
        System.out.println("额增加一个用户");
    }
}

代理类

public class UserServiceProxy implements UserService{
    private UserService userService;

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

    @Override
    public void add() {
        log("add");
        userService.add();
    }

    @Override
    public void delete() {
        log("delete");
        userService.delete();
    }

    @Override
    public void update() {
        log("update");
        userService.update();
    }

    @Override
    public void insert() {
        log("insert");
        userService.insert();
    }
    public void log(String msg){
        System.out.println("使用了一个"+ msg + "方法");
    }
}

实现


public class Client {
    public static void main(String[] args) {
        UserService userService = new UserServiceImpl();
        UserServiceProxy userServiceProxy = new UserServiceProxy();

        userServiceProxy.setUserService(userService);
        userServiceProxy.delete();
    }
}

结果

静态代理模式的好处:

  • 可以使真实角色的操作更加纯粹!不用去关注一些公共的业务
  • 公共角色就交给代理角色!实现了业务的分工!
  • 公共业务发生扩展的时候,方便集中管理!

缺点:

  • 一个真实角色就会产生一个代理角色,代码量会翻倍,开发效率会变

 动态代理 

  • 动态代理和静态代理角色一样
  • 动态代理的代理类是动态生成的,不是我们直接写好的!
  • 动态代理分为两大类:基于接口的动态代理,基于类的动态代理
    • 基于接口 — JDK动态代理【我们在这里使用】
    • 基于类:cglib
    • java字节码实现:javassist

需要了解两个类:Proxy:代理;InvocationHandler:调用处理程序。

动态代理(我也需要去了解更多的方法)

实现工具类

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

public class ProxyInvocationHandler implements InvocationHandler {
    //被代理的接口
    private Object target;

    public void setTarget(Object target) {
        this.target = target;
    }
    //生出代理类
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                target.getClass().getInterfaces(),this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log(method.getName());
        Object result = method.invoke(target,args);
        return result;
    }
    public void log(String msg){
        System.out.println("使用了" + msg +"方法");
    }
}

测试

public class ceshi {
    public static void main(String[] args) {
        UserServiceImpl userService = new UserServiceImpl();
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        pih.setTarget(userService);
        //用接口去编写,接口强转
        UserService proxy = (UserService) pih.getProxy();
        proxy.add();
    }
}

动态代理的好处:

  • 可以使真实角色的操作更加纯粹!不用去关注一些公共的业务
  • 公共角色就交给代理角色!实现了业务的分工!
  • 公共业务发生扩展的时候,方便集中管理!
  • 一个动态代理类代理的是一个接口,一般就是对应的一类业务
  • 一个动态代理类可以代理多个类,只要是实现了同一个接口即可!

11、AOP

11.1什么是aop

AOP (Aspect Orient Programming),直译过来就是 面向切面编程。AOP 是一种编程思想,是面向对象编程(OOP)的一种补充。面向对象编程将程序抽象成各个层次的对象,而面向切面编程是将程序抽象成各个切面。

 所谓切面,相当于应用对象间的横切点,我们可以将其单独抽象为单独的模块。

11.2 AOP在Spring中的作用

提供声明式事务;允许用户自定义切面

  • 横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志,安全,缓存,事务等等…
  • 切面(ASPECT):横切关注点被模块化的特殊对象。即,它是一个类。
  • 通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。
  • 目标(Target):被通知对象。
  • 代理(Proxy):向目标对象应用通知之后创建的对象。
  • 切入点(PointCut):切面通知执行的“地点”的定义。
  • 连接点(JointPoint):与切入点匹配的执行点。

(取自狂神说上课的截图)

有关aop的

 

 即AOP在不改变原有代码的情况下,去增加新的功能

编写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:context="http://www.springframework.org/schema/context" 
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <!--开启注解支持-->
    <context:annotation-config/>
    <bean id="annotationPointCut" class="com.chen.log.AnnotationPointCut"/>
    <!--开启aop注解支持 JDK(默认)proxy-target-class="false"  cglib  proxy-target-class="true"-->
    <aop:aspectj-autoproxy/>

导入依赖

<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.4</version>
</dependency>

方式一: 使用Spring的API接口【主要是SpringAPI接口实现】

自定义接口

public interface UserService {
    public void add();
    public void delete();
    public void update();
    public void select();
}

实现接口

public class UserServiceImpl implements UserService {
    @Override
    public void add() {
        System.out.println("增加了一个用户");
    }

    @Override
    public void delete() {
        System.out.println("删除了一个用户");
    }

    @Override
    public void update() {
        System.out.println("更新了一个用户");
    }

    @Override
    public void select() {
        System.out.println("查看了一个用户");
    }
}

定义一个log包

定义我们的增强类,一个Log前置增强和一个AfterLog后置增强类

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class Log implements MethodBeforeAdvice {
//    method——被调用的方法
//    Args -方法的参数
//    target——方法调用的目标。 可能是null
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println(target.getClass().getName() + "的" + method.getName() + "方法");
    }
}
import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;

public class AfterLog implements AfterReturningAdvice {
    //用反射, method.getName() 获得方法名
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("执行了"+ method.getName() + "的方法,结果为" +returnValue);
    }
}

编写xml文件

    <bean id="log" class="com.chen.log.Log"/>
    <bean id="after" class="com.chen.log.AfterLog"/>


    <!--方式一,使用原生态的Spring API接口-->
    <aop:config>
        <!--切入点:expression:表达式,execution(要执行的位置  )-->
        <aop:pointcut id="pointcut" expression="execution(* com.chen.service.UserServiceImpl.*(..))"/>

        <!--执行环绕增加!-->
        <!--    advice-ref执行的切入的方法  pointcut-ref 切入的地点  -->
        <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="after" pointcut-ref="pointcut"/>

    </aop:config>

测试

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

        //动态代理代理的是接口:注意点
        UserService userService = (UserService) context.getBean("userService");

        userService.add();
//        userService.select();
    }
}

方式二: 自定义类来实现AOP【主要是切面定义】

定义类

public class DiyPointCut {
    public void before(){
        System.out.println("方法执行前");
    }
    public void after(){
        System.out.println("方法执行后");
    }
}

xml文件

    <bean id="diy" class="com.chen.log.DiyPointCut"/>
<!--    配置aop-->
    <aop:config>
        <!--自定义切面,ref 要使用的类-->
        <aop:aspect ref="diy">
            <!--切入点-->
            <aop:pointcut id="point" expression="execution(* com.chen.service.UserServiceImpl.*(..))"/>
            <!--通知  切入进去-->
            <aop:before method="before" pointcut-ref="point"/>
            <aop:after method="after" pointcut-ref="point"/>
        </aop:aspect>
    </aop:config>

方式三: 使用注解实现!

需要开启注解支持

    <!--开启aop注解支持 JDK(默认)proxy-target-class="false"  cglib  proxy-target-class="true"-->
    <aop:aspectj-autoproxy/>

实现类

//声明事物
@Aspect//标记这个类是一个切面
public class AnnotationPointCut {
    @Before("execution(* com.chen.service.UserServiceImpl.*(..))")
    public void before(){
        System.out.println("方法执行前");
    }
    @After("execution(* com.chen.service.UserServiceImpl.*(..))")
    public void after(){
        System.out.println("方法执行后");
    }
    //在环绕增强中,我们可以给定一个参数,代表我们要获取处理切入的点;
    @Around("execution(* com.chen.service.UserServiceImpl.*(..))")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("环绕前");
        //获得签名
        Signature signature = joinPoint.getSignature();
        System.out.println("signature" + signature);
        joinPoint.proceed();//执行方法
        System.out.println("环绕后");
    }


}

配置xml文件

    <bean id="annotationPointCut" class="com.chen.log.AnnotationPointCut"/>
    <!--开启aop注解支持 JDK(默认)proxy-target-class="false"  cglib  proxy-target-class="true"-->
    <aop:aspectj-autoproxy/>
    <bean id="userService" class="com.chen.service.UserServiceImpl"/>

12、整合Mybatis

导入依赖

<dependencies>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.3.18</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.10</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.8</version>
        </dependency>

    <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.9</version>
        </dependency>

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

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

        <!--Spring操作数据库的话,还需要一个spring-jdbc -->

        <!-- 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-spring -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.7</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>

    </dependencies>
    <!--    在build中配置resources,来防止我们资源导出失败的问题-->
    <build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>

12.1 Mybatis-Spring

什么是MyBatis-Spring?

MyBatis-Spring 会帮助你将 MyBatis 代码无缝地整合到 Spring 中。

文档链接:mybatis-spring – icon-default.png?t=M276http://mybatis.org/spring/zh/index.html

如果使用 Maven 作为构建工具,仅需要在 pom.xml 中加入以下代码即可:

整合实现一:

  1. 引入Spring配置文件spring-Mapper.xml
    <?xml version="1.0" encoding="GBK"?>
    <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
            https://www.springframework.org/schema/beans/spring-beans.xsd
    
    </beans>

  2. 配置数据源替换mybaits的数据源
        <!--DataSource:使用Spring的数据源替换Mybatis的配置 c3p0 dbcp druid
        我们这里使用Spring提供的JDBC:-->
        <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
            <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
            <property name="username" value="root"/>
            <property name="password" value="123456"/>
        </bean>

  3. 配置SqlSessionFactory,关联MyBatis
        <!--sqlSessionFactory-->
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <property name="dataSource" ref="dataSource" />
            <!--关联mybatis配置文件-->
            <property name="configLocation" value="classpath:mybatis-config.xml"/>
            <property name="mapperLocations" value="classpath:com/chen/mapper/*.xml"/>
        </bean>

    后面两个<property 是可变参数>

  4. 注册sqlSessionTemplate,关联sqlSessionFactory
        <!--SqlSessionTemplate:就是我们使用的sqlSession-->
        <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
            <!--只能使用构造器注入sqlSessionFactory,因为它没有set方法-->
            <constructor-arg index="0" ref="sqlSessionFactory" />
        </bean>

  5. 需要UserMapper接口的UserMapperImpl 实现类,私有化sqlSessionTemplate
    public class UserMapperImpl implements UserMapper {
    
        //我们的所有操作,都使用sqlSession来执行,在原来,现在都使用SqlsessionTemplate
        private SqlSessionTemplate sqlSession;
    
        public void setSqlSession(SqlSessionTemplate sqlSession) {
            this.sqlSession = sqlSession;
        }
    
        public List<User> selectUser() {
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            return mapper.selectUser();
        }
    }

  6. 将自己写的实现类,注入到Spring配置文件中。
        <bean id="userMapper" class="com.chen.mapper.UserMapperImpl">
            <property name="sqlSession" ref="sqlSession"/>
        </bean>

mybatis-config.xml文件

<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--configuration核心配置文件-->
<configuration>


    <!--配置日志-->
    <settings>
<!--        <setting name="logImpl" value="STDOUT_LOGGING"/>-->
        <setting name="logImpl" value="LOG4J"/>
<!--        开启驼峰命名规则-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
    <!--给实体类起别名-->
    <typeAliases>
        <package name="com.chen.pojo"/>
    </typeAliases>


</configuration>

整合实现二:

mybatis-spring1.2.3版以上的才有这个

dao继承Support类 , 直接利用 getSqlSession() 获得 , 然后直接注入SqlSessionFactory . 比起整合方式一 , 不需要管理SqlSessionTemplate , 而且对事务的支持更加友好 . 可跟踪源码查看。

实现:

  1. 将我们上面写的UserMapperImpl修改一下
    import com.chen.pojo.User;
    import org.mybatis.spring.support.SqlSessionDaoSupport;
    
    import java.util.List;
    
    public class UserServiceImpl2 extends SqlSessionDaoSupport implements UserMapper {
        //getSqlSession(),相当于获得SQLSession对象
        @Override
        public List<User> selectUser() {
            return getSqlSession().getMapper(UserMapper.class).selectUser();
        }
    }
    

  2. 注入到Spring配置文件中。
      <bean id="userMapper2" class="com.chen.mapper.UserServiceImpl2">
            <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
        </bean>
    

  3. 测试
    public class MyTest {
        @Test
        public void getUser() throws IOException {
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            UserMapper userMapper = context.getBean("userMapper2", UserMapper.class);
            for (User user : userMapper.selectUser()) {
                System.out.println(user);
            }
        }
    }

    和上面的一样

13、声明式事务

13.1 回顾事务

  • 把一组业务当成一个业务来做;要么都成功,要么都失败!
  • 事务在项目开发中,十分的重要,涉及到数据的一致性问题,不能马虎!
  • 确保完整性和一致性

 事物ACID原则

Spring在不同的事务管理API之上定义了一个抽象层,使得开发人员不必了解底层的事务管理API就可以使用Spring的事务管理机制。Spring支持编程式事务管理和声明式的事务管理。

编程式事务管理

  • 将事务管理代码嵌到业务方法中来控制事务的提交和回滚
  • 缺点:必须在每个事务操作业务逻辑中包含额外的事务管理代码

声明式事务管理(一般采用)

  • 一般情况下比编程式事务好用。
  • 将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理。
  • 将事务管理作为横切关注点,通过aop方法模块化。Spring中通过Spring AOP框架支持声明式事务管理。

目录

1、spring

1.1简介

 1.2优点

1.3组成

 1.4扩展

2、Ioc理论推导

IOC本质

3、第一个Spring

4、IOC创建对象的方式 

5、Spring配置 

5.1 别名

5.2 Bean配置

5.3 import

6、依赖注入

6.1 构造器注入

6.2 Set方式注入【重点】

6.3 拓展方式注入

6.4 bean的作用域

7、Bean的自动装配

8、使用注解开发

衍生注解

9、使用Java的方式配置Spring

10、代理模式(aop的底层)

 动态代理 

11、AOP

11.1什么是aop

11.2 AOP在Spring中的作用

12、整合Mybatis

12.1 Mybatis-Spring

整合实现一:

13、声明式事务

13.1 回顾事务


下面提及的propagation事物类型

 

直接上xml文件

<!--    配置声明事物-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <constructor-arg ref="dataSource" />
    </bean>
<!--    结合aop实现事物的注入-->
<!--    配置事物的通知-->
    <tx:advice id="interceptor" transaction-manager="transactionManager">
<!--        给哪些方法配置事物-->
<!--        配置事物的传播特性 new propagation.....-->
        <tx:attributes>
            <!-- 可以只要最后一行,那么方法名,propagation新特性里面有几个值-->
            <tx:method name="add" propagation="REQUIRED"/>
            <tx:method name="delete" propagation="REQUIRED"/>
            <tx:method name="update" propagation="REQUIRED"/>
            <tx:method name="query" read-only="true"/>
            <tx:method name="*" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>

<!--    配置事物切入,aop-->
    <aop:config>
        <!--配置切点-->
        <aop:pointcut id="txPointCut" expression="execution(* com.chen.mapper.*.*(..))"/>
        <!--通知插入切点-->
        <aop:advisor advice-ref="interceptor" pointcut-ref="txPointCut"/>
    </aop:config>

总结:

  • 如果不配置事务,可能存在数据提交不一致的情况;
  • 如果我们不在Spring中去配置声明式事务,我们就需要在代码中手动配置事务!
  • 事务在项目的开发中十分重要,涉及到数据的一致性和完整性问题,不容马虎!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值