狂神说Spring

狂神说Spring

1.简介

在这里插入图片描述

SSH: Struct2 + Spring + Hibernate

SSM: SpringMVC + Spring + Mybatis

官网:https://spring.io/projects/spring-framework

下载地址:https://repo.spring.io/release/org/springframework/spring/

github:https://github.com/spring-projects/spring-framework

1.1 依赖

Spring web MVC

<!--    Spring-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.2.7.RELEASE</version>
    </dependency>
<!--    Spring-jdbc-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.2.7.RELEASE</version>
    </dependency>

1.2 优点:

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

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

1.3 组成

在这里插入图片描述

1.4 拓展

基于Spring的开发

在这里插入图片描述

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

在这里插入图片描述

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

2.IOC理论推导

service (业务层)就一个功能:调dao去查用户实际调用的是业务层,dao层他们不接触!

Beans.xml,正确命名:

bean.xml创建之后 IDEA上方有一行黄色 点击就可以出现小叶子

<?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">
    <!-- services -->
    <bean id="petStore" class="org.springframework.samples.jpetstore.services.PetStoreServiceImpl">
        <property name="accountDao" ref="accountDao"/>
        <property name="itemDao" ref="itemDao"/>
        <!-- additional collaborators and configuration for this bean go here -->
    </bean>
    <!-- more bean definitions for services go here -->
</beans>

用户

ApplicationContext context = new GenericGroovyApplicationContext("services.groovy", "daos.groovy");

首先dao层创建一个接口

public interface UserDao {

    public void getUser();
}

然后创建他的实现类

public class UserDaoImpl implements UserDao{
    @Override
    public void getUser() {
        System.out.println("hello word");
    }
}

如果还需要增加业务 再增加一个实现类

public class MysqlDaoImpl implements UserDao{
    @Override
    public void getUser() {
        System.out.println("我是Mysql实现类");
    }
}

service层 接口

service就是一个老大 自己的任务 安排小弟dao层去做

public interface UserService {
    public void getUser();
}

service实现类

每增加一个业务 都需要在业务层 重新生产一个dao层对象

public class UserServiceImpl implements UserService{
   // private UserDaoImpl userDao = new UserDaoImpl();
    private MysqlDaoImpl userDao = new MysqlDaoImpl();

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

用户(测试)

public class Mytest {

    public static void main(String[] args) {
        //用户实际调用的是业务层,dao层他们不接触!
        UserServiceImpl userService = new UserServiceImpl();
        userService.getUser();
    }
}

只要在service实现类中添加set方法,利用set进行动态实现值的注入

private UserDao userDao;

//利用set进行动态实现值的注入
public void setUserDao(UserDao userDao) {
    this.userDao = userDao;
}

用户 —用什么–自己调什么

public class Mytest {

    public static void main(String[] args) {
        //用户实际调用的是业务层,dao层他们不接触!
        UserServiceImpl userService = new UserServiceImpl();
        userService.setUserDao(new UserDaoImpl());
       // userService.setUserDao(new MysqlDaoImpl());
        userService.getUser();
    }
}
  • 之前,程序是主动创建的! 控制权在程序员手上!

  • 使用set注入后,程序不在具有主动性了,而是变成了被动的接受对象!

这种思想,从本质上解决了问题,我们程序员不用再去管理对象的创建了.系统的耦合性大大减低了,可以更加专注在业务的实现上!这是IOC的原型.

之前:主动权在程序员

在这里插入图片描述

现在:主动权在用户

在这里插入图片描述

IOC本质

在这里插入图片描述

在这里插入图片描述

控制反转IOC , 是一种设计思想 , DI(依赖注入)是实现IOC的一种方法,

控制反转是一种通过描述(xml或注解) 并通过第三方去生产或获取特定对象的方式. 在Spring中实现控制反转的是IOC容器, 其实现方法是依赖注入(DI)

3.HelloSpring

实体类—beans.xml—测试

Hello实体类

public class Hello {
    private String name;

    public String getName() {
        return name;
    }

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

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

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 http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!-- 使用Spring来创建对象 , 在Spring这些都称为Bean-->

    <bean id="hello" class="com.codeyuaiiiao.pojo.Hello">
        <property name="name" value="Spring"></property>
    </bean>

</beans>

测试

public class Mytest {

    public static void main(String[] args) {
        //获取Spring的上下文对象
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        Hello hello = (Hello)context.getBean("hello");
        System.out.println(hello.toString());
    }
}

beans.xml中

ref:引用Spring容器中创建好的对象
value:具体的值,基本数据类型!

上一个例子改变

dao层不变

在这里插入图片描述

service层不变

在这里插入图片描述

只增加一个beans.xml

<bean id="userImpl" class="com.codefuheng.dao.UserDaoImpl"/>
<bean id="mysqlImpl" class="com.codefuheng.dao.MysqlDaoImpl"/>
<bean id="oracleImpl" class="com.codefuheng.dao.OracleDaoImpl"/>

<bean id="UserServiceImpl" class="com.codefuheng.service.UserServiceImpl">
    <property name="userDao" ref="oracleImpl"></property>
</bean>

测试

public static void main(String[] args) {
	//获取ApplicationContext  拿到Spring容器
	ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
	//需要什么 直接get什么
UserServiceImpl userServiceImpl = 		(UserServiceImpl)context.getBean("UserServiceImpl");

	userServiceImpl.getUser();
}

在这里插入图片描述

4.IOC创建对象的方法

  1. 使用无参构造创建对象,默认

  2. 假设我们要使用有参构造创建对象.

    1. 下标赋值

      <!--    第一种:通过下标创建-->
      <bean id="User" class="com.codefuheng.pojo.User">
      	<constructor-arg index="0" value="java"></constructor-arg>
      </bean>
      
    2. 通过类型创建

      <!--    第二种: 通过类型创建,不建议使用-->
      <bean id="User" class="com.codefuheng.pojo.User">
      	<constructor-arg type="java.lang.String" value="java"/>
      </bean>
      
    3. 参数名

    <!--    第三种: 直接通过参数名来设置(推荐)-->
    <bean id="user" class="com.codefuheng.pojo.User">
        <constructor-arg name="name" value="java"/>
    </bean>
    

总结:在配置文件加载的时候,容器中管理的对象就已经初始化了!

5.Spring配置

5.1 别名

<!--别名:如果添加了别名 我们也可以用别名来创建对象-->
<alias name="user" alias="user2"></alias>

5.2 Bean的配置

<!--    id: Bean的唯一标识符, 也就是我们学到的对象名
        class: bean对象所对应的权限定名: 包名+类名
        name: 也是别名,而且name可以同时取多个别名
				name的别名 可以用空格 ,  ;  多种方式分割
-->
    <bean id="user" class="com.codefuheng.pojo.User" name="user2 user3,user4;user5">
        <constructor-arg name="name" value="java"/>
    </bean>

5.3 import

在这里插入图片描述

applicationContext.xml

<import resource="beans.xml"></import>
<import resource="beans2.xml"></import>
<import resource="beans3.xml"></import>

使用的时候只需要使用总的xml就可以 applicationContext.xml

6. 依赖注入(DI)

概念

  • 依赖注入(Dependency Injection,DI)。
  • 依赖 : 指Bean对象的创建依赖于容器 。 Bean对象的依赖资源 。
  • 注入 : 指Bean对象所依赖的资源 , 由容器来设置和装配 。

6.1 构造器注入

之前的案例已经讲过了

6.2 set方式注入 [重点]

[环境搭建]

1.复杂类型

Address

public class Address {
    private String address;

    public String getAddress() {
        return address;
    }

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

    @Override
    public String toString() {
        return "Address{" +
                "address='" + address + '\'' +
                '}';
    }
}

2.真实测试对象

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;
}

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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--第一种:普通值注入 value-->
    <bean id="Student" class="com.codefuheng.pojo.Student">
        <property name="name" value="付恒"/>
    </bean>
</beans>

测试类

public class Mytest {
    public static void main(String[] args) {
        ApplicationContext Context = new ClassPathXmlApplicationContext("Beans.xml");
        Student student = (Student) Context.getBean("Student");
        System.out.println(student.getAddress());
    }
}

不同类型的注入方式(array map set list…)

 <bean id="address" class="com.codefuheng.pojo.Address">
        <property name="address" value="南京"/>
    </bean>

    <bean id="student" class="com.codefuheng.pojo.Student">
        <!--第一种:普通值注入 value-->
        <property name="name" value="付恒"/>
        <!--第二种:Bean注入  ref-->
        <property name="address" ref="address"/>
		<!--第三种: 数组-->
        <property name="books">
            <array>
                <value>红楼梦</value>
                <value>西游记</value>
                <value>水浒传</value>
                <value>三国演义</value>
            </array>
        </property>
		<!--第四种:list-->
        <property name="hobbies">
            <list>
                <value>打游戏</value>
                <value>听歌</value>
                <value>看电影</value>
            </list>
        </property>
		<!--第五种:Map-->
        <property name="card">
            <map>
                <entry key="身份证" value="15313234652341"/>
                <entry key="银行卡" value="4564653141465354"/>
            </map>
        </property>
		<!--第六种:set-->
        <property name="games">
            <set>
                <value>王者荣耀alue>
                <value>斗地主</value>
                <value>狼人杀/value>
            </set>
        </property>
		<!--null-->
        <property name="wife">
            <null/>
        </property>
		<!--Properties-->
        <property name="info">
            <props>
                <prop key="姓名">付恒</prop>
                <prop key="学号">161302105</prop>
            </props>
        </property>

    </bean>

6.3 拓展方式注入

我们可以使用p命名空间和c命名空间来注入

官方位置

在这里插入图片描述

使用

在这里插入图片描述

测试:

在这里插入图片描述

在这里插入图片描述

后面加 类名.class 就不用强转了

注意点:p命名空间和c命名空间不能直接使用,需要导入xml约束

  xmlns:p="http://www.springframework.org/schema/p"
  xmlns:c="http://www.springframework.org/schema/c"

6.4 Bean的作用域(了解)

在这里插入图片描述

在Spring中,那些组成应用程序的主体及由Spring IoC容器所管理的对象,被称之为bean。简单地讲,bean就是由IoC容器初始化、装配及管理的对象 .

图片

几种作用域中,request、session作用域仅在基于web的应用中使用(不必关心你所采用的是什么web应用框架),只能用在基于web的Spring ApplicationContext环境。

Singleton

当一个bean的作用域为Singleton,那么Spring IoC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。Singleton是单例类型,就是在创建起容器时就同时自动创建了一个bean的对象,不管你是否使用,他都存在了,每次获取到的对象都是同一个对象。注意,Singleton作用域是Spring中的缺省作用域。要在XML中将bean定义成singleton,可以这样配置:

 <bean id="ServiceImpl" class="cn.csdn.service.ServiceImpl" scope="singleton">

测试:

 @Test
 public void test03(){
     ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
     User user = (User) context.getBean("user");
     User user2 = (User) context.getBean("user");
     System.out.println(user==user2);
 }
Prototype

当一个bean的作用域为Prototype,表示一个bean定义对应多个对象实例。Prototype作用域的bean会导致在每次对该bean请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)时都会创建一个新的bean实例。Prototype是原型类型,它在我们创建容器的时候并没有实例化,而是当我们获取bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。根据经验,对有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。在XML中将bean定义成prototype,可以这样配置:

 <bean id="account" class="com.foo.DefaultAccount" scope="prototype"/>  
  或者
 <bean id="account" class="com.foo.DefaultAccount" singleton="false"/>
Request

当一个bean的作用域为Request,表示在一次HTTP请求中,一个bean定义对应一个实例;即每个HTTP请求都会有各自的bean实例,它们依据某个bean定义创建而成。该作用域仅在基于web的Spring ApplicationContext情形下有效。考虑下面bean定义:

 <bean id="loginAction" class=cn.csdn.LoginAction" scope="request"/>

针对每次HTTP请求,Spring容器会根据loginAction bean的定义创建一个全新的LoginAction bean实例,且该loginAction bean实例仅在当前HTTP request内有效,因此可以根据需要放心的更改所建实例的内部状态,而其他请求中根据loginAction bean定义创建的实例,将不会看到这些特定于某个请求的状态变化。当处理请求结束,request作用域的bean实例将被销毁。

Session

当一个bean的作用域为Session,表示在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。考虑下面bean定义:

 <bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>

针对某个HTTP Session,Spring容器会根据userPreferences bean定义创建一个全新的userPreferences bean实例,且该userPreferences bean仅在当前HTTP Session内有效。与request作用域一样,可以根据需要放心的更改所创建实例的内部状态,而别的HTTP Session中根据userPreferences创建的实例,将不会看到这些特定于某个HTTP Session的状态变化。当HTTP Session最终被废弃的时候,在该HTTP Session作用域内的bean也会被废弃掉。

7.Bean的自动装配

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

在Spring中有三种装配方式

  • 在xml中显示装配
  • 在java中显示装配
  • 隐式的自动装配bean [重要]

7.1.测试

1.环境搭建

  • 一人 (人有两宠)
  • 一狗
  • 一猫

7.2 byName自动装配

byName:会自动在容器上下文中查找,和自己对象set方法后面的值对应的beanid

<bean id="cat" class="com.codefuheng.pojo.Cat"></bean>
<bean id="dog" class="com.codefuheng.pojo.Dog"></bean>

<!--    byName:会自动在容器上下文中查找,和自己对象set方法后面的值对应的beanid-->
<bean id="people" class="com.codefuheng.pojo.People" autowire="byName">
    <property name="name" value="付恒"></property>
</bean>

7.3 byType 自动装配

<bean class="com.codefuheng.pojo.Cat"></bean>
<bean class="com.codefuheng.pojo.Dog"></bean>

<!--    byType:会自动在容器上下文中查找,和自己对象属性类型相同的bean-->
<bean id="people" class="com.codefuheng.pojo.People" autowire="byType">
    <property name="name" value="付恒"></property>
</bean>

小结:

  • byName的时候 , 需要保证所有bean的id唯一 ,并且这个bean需要和自动注入的属性的set方法的值一致
  • byType的时候 , 需要保证所有bean的class唯一 ,并且这个bean需要和自动注入的属性的类型一致

7.4 使用注解 实现自动转配 (重点)

jdk1.5支持注解------Spring 2.5 支持注解

使用注解须知:

  1. 导入约束 : context约束
  2. 配置注解的支持 : context:annotation-config/>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans.xsd 
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd">
<!--指定要扫描的包, 这个包下的注解就会生效-->
<Context:component-scan base-package="com.codefuheng"/>
<!--注解生效-->
<context:annotation-config/>  
</beans>

@Autowired

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

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

科普:

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

在这里插入图片描述

@Autowired ( required = false)

如果显示的定义了@Autowired的required的属性为false,说明这个对象可以为nulll.否则不允许为空

在这里插入图片描述

实体类

public class People {

    private String name;
    @Autowired
    private Dog dog;
    @Autowired
    private Cat cat;
}

@Resource

可以指定名字

public class People {

    private String name;

    @Resource(name = "dog22")
    private Dog dog;
    @Resource(name = "cat22")
    private Cat cat;
}

beans.xml中

<bean id="cat" class="com.codefuheng.pojo.Cat"/>

	<bean id="cat22" class="com.codefuheng.pojo.Cat"/>

<bean id="dog" class="com.codefuheng.pojo.Dog"/>

	<bean id="dog22" class="com.codefuheng.pojo.Dog"/>

<bean id="people" class="com.codefuheng.pojo.People" />

小结:

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

8.使用注解开发

applicationContext.xml

<!--指定要扫描的包, 这个包下的注解就会生效-->
<Context:component-scan base-package="com.codefuheng.pojo"/>
<Context:annotation-config/>

在Spring4之后 , 要使用注解开发, 必须保证 aop的包导入了

在这里插入图片描述

使用注解需要导入context约束, 增加注解的支持!

  1. bean

  2. 属性如何注入

    /**
     * 等价于:<bean id="user" class="com.codefuheng.pojo.User"/>
     * @Component  组件
     */
    @Component
    public class User {
        /**
         * 相当于<property name="name" value = "付恒"
         */
        @Value("付恒")
        public String name;
    }
    
  3. 衍生的注解

  • @Component 有几个衍生注解 , 我们在web开发中, 会按照mvc三层架构 分层!
    • dao [@Repository]
    • service [@Service]
    • controller [@Controller]
    • 这个四个注解功能都是一样的 , 都是代表某个类注册到Spring中, 装配Bean
  1. 自动装配
  • @ Autowired : 自动装配通过类型,名字;
    • 如果@Autowired 不能唯一自动装配上属性,则需要通过@Qualifier(value=“xxx”)
  • @Nullable : 字段标记了这个注解 , 说明这个字段可以为null;
  • @Resource : 自动装配通过名字, 类型.
  1. 作用域

    @scope

    • singleton:默认的,Spring会采用单例模式创建这个对象。关闭工厂 ,所有的对象都会销毁。
    • prototype:多例模式。关闭工厂 ,所有的对象不会销毁。内部的垃圾回收机制会回收
    @Controller("user")
    @Scope("prototype")
    public class User {
       @Value("付恒")
       public String name;
    }
    
  2. 小结

xml与注解:

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

xml 与 注解最佳实践:

  • xml 用来管理bean;

  • 注解只负责完成属性的注入;

  • 我们在使用的过程中 , 只需要注意一个问题 : 必须让注解生效 , 就需要开启注解的支持.

  •   <!--指定要扫描的包, 这个包下的注解就会生效-->
      <Context:component-scan base-package="com.codefuheng.pojo"/>
      <!--加载注解-->
      <Context:annotation-config/>
    

注解说明

  • @ Autowired : 自动装配通过类型,名字;
    • 如果@Autowired 不能唯一自动装配上属性,则需要通过@Qualifier(value=“xxx”)
  • @Nullable : 字段标记了这个注解 , 说明这个字段可以为null;
  • @Resource : 自动装配通过名字, 类型.
  • @component : 组件 , 放在类上 , 说明这个类被Spring 管理了 , 就是bean !

9.使用java的方式配置Spring

实体类

public class User {
    private String name;

    public String getName() {
        return name;
    }
    @Value("付恒")
    public void setName(String name) {
        this.name = name;
    }
}

配置类

package com.codefuheng.config;

import com.codefuheng.pojo.User;
import org.springframework.context.annotation.*;

/**
 * @author:fuheng
 */
@Configuration
@ComponentScan("com.codefuheng.pojo")
@Import(config2.class)
public class config {
    /**
     * 注册一个Bean,就相当于我们之前写的一个bean标签
     * 这个方法的名字,就相当于bean标签中的id属性
     * 这个方法的返回值,就相当于bean标签中的class属性
     * @return
     */
    @Bean
    public User getUser(){
        //就是返回要注入到bean中的对象
        return new User();
    }
}

测试类

public class Mytest {

    @Test
    public void getUser(){
        ApplicationContext Context = new AnnotationConfigApplicationContext(config.class);
        User getUser = Context.getBean("getUser", User.class);
        System.out.println(getUser.getName());

    }
}
  • 这种纯java的配置方式,在Spring Boot中随处可见

10.代理模式

为什么要学习代理模式? 因为这就是SpringAOP的底层! [SpringAOP 和SpringMVC]面试必考

代理模式的分类:

  • 静态代理
  • 动态代理

在这里插入图片描述

10.1 静态代理

角色分析:

  • 抽象角色 : 一般会使用接口或者抽象类来解决
  • 真实角色: 被代理的角色
  • 代理角色: 代理真实角色 , 代理真实角色后, 我们一般会做一些附属操作.
  • 客户 : 访问代理对象的人

代码步骤:

  1. 接口

    /**
     * @author: fuheng
     * 租房
     */
    public interface Rent {
        public void rentHouse();
    }
    
  2. 真实角色

    /**
     * @author: fuheng
     * 房东
     */
    public class Host implements Rent{
    
        public void rentHouse() {
            System.out.println("房东出租房子");
        }
    }
    
  3. 代理角色

    /**
     * @author: fuheng
     * 中介代理
     * 中介实现接口 出租房子
     * 中介 每次都能代理一个房东 出租房子(每次传进一个房东)
     */
    public class Proxy implements Rent{
        
        private Host host;
        //租房
        public void rentHouse() {
            host.rentHouse();
        }
    
        public Proxy(Host host) {
            this.host = host;
        }
    }
    
  4. 客户端访问代理角色

/**
 * @author: fuheng
 * 客户 我租房子
 */
public class Client {
    public static void main(String[] args) {
        //new 一个房东 房东要租房子
        Host host = new Host();
        //代理 , 中介帮房东租房子, 但是呢? 代理一般会有一些附属操作
        Proxy proxy = new Proxy(host);
        //你不用面对房东, 直接找中介租房即可!
        proxy.rentHouse();
    }
}

代理模式的好处:

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

缺点:

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

10.2 动态代理

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

需要了解两个类: Proxy : 生成动态代理实例的 , invocationHandler: 调用处理程序并返回一个结果的

invocationHandler

1.接口

/**
 * @author: fuheng
 * 租房
 */
public interface Rent {
    public void rentHouse();
}

2.实现类

/**
 * @author: fuheng
 * 房东
 */
public class Host implements Rent{

    public void rentHouse() {
        System.out.println("房东出租房子");
    }
}

3.代理处理程序

/**
 * @author: fuheng
 * 用这个类 自动生成代理类
 */
public class ProxyInvocationHandler implements InvocationHandler {
    //被代理的接口
    private Rent rent;

    public void setRent(Rent rent) {
        this.rent = rent;
    }

    /**
     * 生成得到代理类
     * @return
     */
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                    rent.getClass().getInterfaces(),this);
    }

    /**
     * 处理代理实例 并返回结果
       添加的方法在这里调用
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //动态代理的本质 , 就是使用反射机制实现
        Object result = method.invoke(rent, args);
        return result;
    }
}

4.测试类

/**
 * @author: fuheng
 * 测试类
 */
public class Client {
    public static void main(String[] args) {
        //真实角色
        Host host = new Host();
        //代理角色 : 现在没有
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        //通过调用程序处理角色来处理我们要调用的接口对象!
        pih.setRent(host);
        //这里的proxy就是动态生成的   我们并没有写
        Rent proxy = (Rent) pih.getProxy();
        proxy.rentHouse();
    }
}

代理工具类
ProxyInvocationHandler

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

/**
 * @author: fuheng
 * 用这个类 自动生成代理类
 */
public class ProxyInvocationHandler implements InvocationHandler {
    //被代理的接口
    private Object target;

    public void setTarget(Object target) {
        this.target = target;
    }

    /**
     * 生成得到代理类
     * @return
     */
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                target.getClass().getInterfaces(),this);
    }

    /**
     * 处理代理实例 并返回结果
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //动态代理的本质 , 就是使用反射机制实现
        Object result = method.invoke(target, args);
        return result;
    }
}

测试类

/**
 * @author: fuheng
 * 测试类
 */
public class Cliect {
    public static void main(String[] args) {
        //真实角色
        Host host = new Host();
        //代理角色 ,不存在
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        //设置要代理的对象
        pih.setTarget(host);
        //动态生成代理类
        Rent proxy = (Rent) pih.getProxy();
        //调用方法
        proxy.rentHouse();

    }
}

11.AOP

11.1什么是AOP

在这里插入图片描述

在这里插入图片描述

11.2AOP在Spring中的作用

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

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

11.3使用Spring实现AOP

[重点] 使用AOP织入, 需要导入一个依赖包!

AspectJ Weaver

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.5</version>
</dependency>

方式一: 使用Spring 的 API接口 {主要Spring API接口实现}

<aop:advisor >执行环绕增加

aop:pointcut 切入点

beforeLog

public class Log implements MethodBeforeAdvice {
    /**
     * 前置日志
     * @param method 要执行的目标对象的方法
     * @param args 参数
     * @param target 目标对象
     * @throws Throwable
     */
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了");
    }
}

afterLog

public class AfterLog implements AfterReturningAdvice {

    /**
     *
     * @param returnValue 返回值
     */
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("执行了"+method.getName()+"的方法,返回结果为: "+returnValue);
    }
}

applicationContext.xml

<!--    注册Bean-->
<bean id="userService" class="com.codefuheng.service.UserServiceImpl"/>
<bean id="log" class="com.codefuheng.log.Log"/>
<bean id="afterLog" class="com.codefuheng.log.AfterLog"/>

<!--方式一: 使用原生Spring API接口-->
<!--配置aop : 需要导入aop的约束-->
<aop:config>
    <!--切入点: expression:表达式, execution(要执行的位置!* * * * *)-->
    <aop:pointcut id="pointcut" expression="execution(* com.codefuheng.service.UserServiceImpl.*(..))"/>
    <!--执行环绕增加-->
    <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
    <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>

测试类

public class Mytest {

    @Test
    public void test(){
        ApplicationContext Context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //动态代理代理的是接口:注意点
        UserService userService = (UserService) Context.getBean("userService");
        userService.add();
    }
}

方式二: 自定义类实现AOP {主要是切面定义}

自定义类

/**
 * @author: fuheng
 * 自定义类
 */
public class DiyPoint {
    public void after(){
        System.out.println("=================之后==================");
    }

    public void before(){
        System.out.println("==================之前==================");
    }
}

applicationContext.xml

<!--切入的位置得注册-->
<bean id="userService" class="com.codefuheng.service.UserServiceImpl"/>

<!--方式二 : 自定义类-->
<bean id="diy" class="com.codefuheng.diy.DiyPoint"/>

<aop:config>
    <!--自定义切面: ref  要引用的类-->
    <aop:aspect ref="diy">
        <!--切入点-->
        <aop:pointcut id="pointcut" expression="execution(* com.codefuheng.service.UserServiceImpl.*(..))"/>
         <!--通知-->
        <aop:before method="before" pointcut-ref="pointcut"/>
        <aop:after method="after" pointcut-ref="pointcut"/>
    </aop:aspect>
</aop:config>

第三种 : 使用注解实现

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ClHRVzbj-1600918845409)(Spring(B站狂神说java)].assets/image-20200630184343364.png)

AnnotationPointCut类

/**
 * @author: fuheng
 */
@Aspect//标注这个类是一个切面
public class AnnotationPointCut {

    @Before("execution(* com.codefuheng.service.UserServiceImpl.*(..))")
    public void before(){
        System.out.println("==============注解:方法执行前===================");
    }

    @After("execution(* com.codefuheng.service.UserServiceImpl.*(..))")
    public void After(){
        System.out.println("==============注解:方法执行后===================");
    }

}

applicationContext.xml

<bean id="userService" class="com.codefuheng.service.UserServiceImpl"/>

<bean id="annotationPointCut" class="com.codefuheng.diy.AnnotationPointCut"/>
<!--开启注解支持-->
<aop:aspectj-autoproxy/>

12.整合Mybatis

步骤:

  1. 导入相关jar包

    • junit

    • Mybatis

    • mysql数据库

    • Spring

    • Spring-jdbc

    • aop织入

    • mybatis-Spring [最新的new]

    • lombok

    •   <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.2.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.6</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.5</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.5</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.20</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.2.7.RELEASE</version>
        </dependency>
        <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.12</version>
                <scope>provided</scope>
        </dependency>
      
  2. 编写配置文件

  3. 测试

12.1回忆Mybatis

  1. 编写实体类
  2. 编写核心配置文件
  3. 编写接口
  4. 编写Mapper.xml
  5. 测试

12.2 Mybatis-Spring

方式一:

spring-dao.xml

  1. 编写数据源配置

    <!--DataSource : 使用Spring的数据源替代Mybatis的配置
       我们这里使用Spring提供的jdbc :org.springframework.jdbc.datasource
       连接数据库
    -->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/codefuheng?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
        <property name="username" value="codefuheng"/>
        <property name="password" value="123321"/>
    </bean>
    
  2. sqlSessionFactory

     <!--sqlSessionFactory mybatis中其他的配置信息-->
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <!--        数据库-->
            <property name="dataSource" ref="dataSource"/>
    <!--        mybatis中的xml配置文件-->
            <property name  ="configLocation" value="classpath:mybatis-config.xml"/>
    <!--        mapper配置-->
            <property name="mapperLocations" value="classpath:com/codefuheng/mapper/*.xml"/>
        </bean>
    
  3. sqlSessionTemplate

    <!--SqlSessionTemplate : 就是我们使用的sqlSession-->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <!--        只能使用构造器注入sqlSessionFactory , 因为他没有set方法-->
        <constructor-arg index="0" ref="sqlSessionFactory"/>
    </bean>
    
  4. 需要给接口加实现类 [ ]

    public class UserMapperImpl implements UserMapper{
    
        private SqlSessionTemplate sqlSession;
    
        public void setSqlSession(SqlSessionTemplate sqlSession) {
            this.sqlSession = sqlSession;
        }
    
        public List<User> getUserList() {
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            return mapper.getUserList();
        }
    }
    
  5. 将自己写的实现类, 注入到Spring中.(applicationContext.xml)

    <!--  方式一:  注入UserMapperImpl实现类-->
        <bean id="userMapper" class="com.codefuheng.mapper.UserMapperImpl">
            <property name="sqlSession" ref="sqlSession"/>
        </bean>
    
  6. 测试使用即可 !

    public class Mytest {
        @Test
        public void gettest(){
            ApplicationContext Context = new ClassPathXmlApplicationContext("applicationContext.xml");
            UserMapper userMapper = Context.getBean("userMapper2", UserMapper.class);
            List<User> userList = userMapper.getUserList();
            for (User user : userList) {
                System.out.println(user);
            }
        }
    }
    

方式二:

实现类继承SqlSessionDaoSupport

4.继承接口实现类UserMapperImpl2

public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper {

    public List<User> getUserList() {
        return getSqlSession().getMapper(UserMapper.class).getUserList();
    }
}

5.将自己写的实现类, 注入到Spring中.(applicationContext.xml)

<!--    第二种:实现类继承SqlSessionDaoSupport-->
    <bean id="userMapper2" class="com.codefuheng.mapper.UserMapperImpl2">
        <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>

6.测试类

public class Mytest {
    @Test
    public void gettest(){
        ApplicationContext Context = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserMapper userMapper = Context.getBean("userMapper2", UserMapper.class);
        List<User> userList = userMapper.getUserList();
        for (User user : userList) {
            System.out.println(user);
        }
    }
}

13.声明式事务

13.1回顾事务

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

事务ACID原则:

  • 原子性
  • 一致性
  • 隔离性
    • 多个业务可能操作同一个资源, 防止数据损坏
  • 持久性
    • 事物一旦提交 , 无论系统发生什么问题 , 结果都不会再被影响 , 被持久化的写到存储器中!

13.2 Spring 中的事务管理

  • 声明式事务 : AOP
  • 编程式事务 : 需要再代码中 , 进行事务的管理

写在Spring-dao.xml中

1.配置声明式事务

<!--配置声明式事务-->
<bean id="transactionMapper" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

2.配置事务通知:

注意点: 导入tx的时候需要导入这三个…

idea自动导入时 需要更改我画()的4个地方

xmlns:tx="http://www.springframework.org/schema/tx(1)"
http://www.springframework.org/schema/tx(2)
       http://www.springframework.org/schema/tx(3)/spring-tx(4).xsd 
<!--    结合AOP实现事务的织入
        配置事务通知:
-->
    <tx:advice id="txAdvice" transaction-manager="transactionMapper">
    <!--给哪些方法配置事务-->
    <!--配置事务的传播特性: new  propagation = -->
        <tx:attributes>
            <tx:method name="add" propagation="REQUIRED"/>
            <tx:method name="select" read-only="true"/>
            <tx:method name="delete" propagation="REQUIRED"/>
            <tx:method name="update" propagation="REQUIRED"/>
            <tx:method name="*" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>

3.配置事务切入

<!--配置事务切入-->
<aop:config>
    <aop:pointcut id="txPointCut" expression="execution(* com.codefuheng.mapper.*.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>

思考:

为什么需要事务?

  • 如果不配置事务, 可能存在数据提交不一致的情况
  • 如果我们不在SPRING中去配置声明事务 , 我们就需要在代码中手动配置事务!

13.1回顾事务

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

事务ACID原则:

  • 原子性
  • 一致性
  • 隔离性
    • 多个业务可能操作同一个资源, 防止数据损坏
  • 持久性
    • 事物一旦提交 , 无论系统发生什么问题 , 结果都不会再被影响 , 被持久化的写到存储器中!

13.2 Spring 中的事务管理

  • 声明式事务 : AOP
  • 编程式事务 : 需要再代码中 , 进行事务的管理

写在Spring-dao.xml中

1.配置声明式事务

<!--配置声明式事务-->
<bean id="transactionMapper" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

2.配置事务通知:

注意点: 导入tx的时候需要导入这三个…

idea自动导入时 需要更改我画()的4个地方

xmlns:tx="http://www.springframework.org/schema/tx(1)"
http://www.springframework.org/schema/tx(2)
       http://www.springframework.org/schema/tx(3)/spring-tx(4).xsd 
<!--    结合AOP实现事务的织入
        配置事务通知:
-->
    <tx:advice id="txAdvice" transaction-manager="transactionMapper">
    <!--给哪些方法配置事务-->
    <!--配置事务的传播特性: new  propagation = -->
        <tx:attributes>
            <tx:method name="add" propagation="REQUIRED"/>
            <tx:method name="select" read-only="true"/>
            <tx:method name="delete" propagation="REQUIRED"/>
            <tx:method name="update" propagation="REQUIRED"/>
            <tx:method name="*" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>

3.配置事务切入

<!--配置事务切入-->
<aop:config>
    <aop:pointcut id="txPointCut" expression="execution(* com.codefuheng.mapper.*.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>

思考:

为什么需要事务?

  • 如果不配置事务, 可能存在数据提交不一致的情况
  • 如果我们不在SPRING中去配置声明事务 , 我们就需要在代码中手动配置事务!
  • 事务在项目的开发中十分重要 , 涉及到数据的一致性和完整性问题 , 不容马虎 !
  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值