Spring

Spring

IOC理论推导

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

我们使用一个Set接口实现,已经发生了革命性的变化

private UserDao userDao;

//利用Set进行动态实现值的注入;

public void setUserDao(UserDao userDao){

this.userDao = userDao;

}

之前,程序是主动创建对象,控制权在程序员手上.

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

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

IOC本质

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

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

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

IOC创建对象的方式了

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

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

    1. 下标赋值

    2. 类型

    3. 参数名

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

Spring配置

别名

Bean的配置

<!--

id : bean 的唯一标识符,也就是相当于我们学的对象名

class : bean 对象所对应的全限定名 : 包名 + 类型

name : 也是别名,而且name 可以同时取多个别名

-->

import

这个import,一般用于团队开发使用,它可以将多个配置文件,导入合并为一个

假设,现在这个项目中有多个人开发,这三个人复制不同的类开发,不同的类需要注册在不同的bean中,我们可以利用import将所有人的beans.xml合并为一个总的.

张三

李四

王五

applicationContext.xml

使用的时候,直接使用总的配置就可以了

依赖注入

构造器注入

Set方式注入[重点]

依赖注入:Set注入

依赖:bean对象的创建依赖于容器

注入:bean对象中的所有属性,由容器来注入

【环境搭建】

  1. 复杂类型

    package com.zhiyou.pojo;
    
    public class Address {
        private String address;
    
        public String getAddress() {
            return address;
        }
    
        public void setAddress(String address) {
            this.address = address;
        }
    }
    
  2. 真实测试对象

    public class Student {
        private String name;
        private Address address;
        private String[] books;
        private List<String> hobbys;
        private Map<String,String> card;
        private Set<String> games;
        private Properties info;
        private String wife;
    
  3. 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">
    
    <bean id="student" class="com.zhiyou.pojo.Student">
        <property name="name" value="张三"/>
    </bean>
    
    </beans>
    
  4. 测试类

    import com.zhiyou.pojo.Student;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    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.getName());
        }
    }
    

拓展方式注入

我们可以使用p命令空间和c命令空间进行注入

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

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

bean的作用域

Scope Description
singleton (Default) Scopes a single bean definition to a single object instance for each Spring IOC container.
prototype Scopes a single bean definition to any number of object instances.
request Scopes a single bean definition to the lifecycle of a single HTTP request. That is, each HTTP request has its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring ApplicationContext.
session Scopes a single bean definition to the lifecycle of an HTTP Session. Only valid in the context of a web-aware Spring ApplicationContext.
application Scopes a single bean definition to the lifecycle of a ServletContext. Only valid in the context of a web-aware Spring ApplicationContext.
websocket Scopes a single bean definition to the lifecycle of a WebSocket. Only valid in the context of a web-aware Spring ApplicationContext.

单例模式(Spring 默认机制)

<bean id="user2" class="com.zhiyou.pojo.User" c:age="18" c:name="王五"

scope="singleton">

原型模式:每次从容器中get的时候都会产生一个新对象

<bean id="accountService" class="com.zhiyou.pojo.User"

scope="prototype"/>

其余的 requse session application这些只能在web开发中使用到

Bean的自动装配

自动装配是Spring满足bean依赖的一种方式

Spring会在上下文中自动寻找,并自动给bean装配属性

在Spring中有三种装配方式

  1. 在xml中显示的配置
  2. 在java中显示的配置
  3. 隐式的自动装配bean[重要]

测试

环境搭建: 一个人有两个宠物

ByName自动装配

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

ByType自动装配

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

小结:

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

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

使用注解实现自动装配

jdk1.5支持的注解,Spring2.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        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>
    

@Autowired

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

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

科普:

@nullable   字段标记了这个注解,说明这个字段可以为null
public @interface Autowired{   boolean required() default true;}

测试代码:

public class People {    //如果显式定义了Autowired的require属性为false,说明这个对象可以为null,否则不允许为空    @Autowired(required = false)    private Cat cat;    @Autowired    private Dog dog;    private String name;}

如果@Autowired自动装配的环境比较复杂,自动装配无法通过一个注解[@Autowired]完成的时候,我们可以使用@Qualifier(value="xxx")去配合@Autowired的使用,指定唯一的bean对象注入.

小结:

​ @Resource和@Autowired的区别:

  • 都是用来自动装配的,都可以放在属性字段上
  • @Autowired 通过byname的方式实现
  • @Resource 默认通过byname的方式实现,如果找不到名字,则通过byType实现.如果连个都找不到就报错.
  • 执行顺序不同:@Autowired 通过byType的方式实现,@Resource 默认通过byname的方式实现.

使用注解开发

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

bean

属性如何注入

@Componentpublic class User {    public String name;        //相当于 <property name="name"  value="李四"/>    @Value("李四")    public void setName(String name) {        this.name = name;    }}

衍生的注解

@Component有几个衍生注解,我们在web开发中,会按照mvc三层架构分层

  • dao[@Repository]

  • service[@Service]

  • controller [@Controller]

这四个注解功能都是一样的,都是代表将某个类注册到Spring中,装配Bean

自动装配

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

作用域

@Component@Scope("singleton")public class User {    public String name;    //相当于 <property name="name"  value="李四"/>    @Value("李四")    public void setName(String name) {        this.name = name;    }}

小结

xml与注解:

  • ​ xml更加万能,适用于任何场合,维护简单方便

  • ​ 注解 不是自己类使用不了,维护相对复杂

xml与注解最佳实践:

  • ​ xml用来管理bean

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

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

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

使用Java的方式配置Spring

我们现在要完全不使用Spring的xml配置了,全权交给Java来做

JavaConfig是Spring的一个子项目,在Spring 4之后,它成为了一个核心功能

实体类

//这里这个注解的意思,就是说明这个类被Spring接管了,注册到了容器中@Componentpublic class User {    private String name;    @Override    public String toString() {        return "User{" +                "name='" + name + '\'' +                '}';    }    public String getName() {        return name;    }    @Value("zhangsan")  //属性注入值    public void setName(String name) {        this.name = name;    }}

配置文件

package com.zhiyou.config;import com.zhiyou.pojo.User;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Import;//这个也会被Spring容器托管,注册到容器中,因为他本身就是一个@Component//@Configuration 代表这是一个配置类,就和我们之前看的beans.xml@Configuration@ComponentScan("com.zhiyou.pojo")@Import(myconfig2.class)public class myconfig {    //注册一个bean,就相当于我们之前写的一个bean标签    //这个方法的名字,就相当于bean标签中的id属性    //这个方法的返回值,就相当于bean标签中的class属性    @Bean    public User user(){        return new User(); //就是返回要注入的bean的对象    }}

测试类

public class MyTest {    public static void main(String[] args) {        /**如果完全使用了配置类方式去做,我们就只能通过AnnotionConfig 上下文来获取容器         * 通过配置类的class对象加载         */        ApplicationContext context = new AnnotationConfigApplicationContext(myconfig.class);        User getUser =(User) context.getBean("user");        System.out.println(getUser.getName());    }}

这种纯Java的配置方式,在SpringBoot中随处可见

代理模式

为什么要学习代理模式?因为SpringAOP的底层 [SpringAOP 和 SpringMVC]

代理模式的分类:

  • 静态代理
  • 动态代理!!

静态代理

角色分析:

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

代码步骤:

  1. 接口

    //租房接口public interface Rent {    public void rent();}
    
  2. 真实角色

    //房东public class Host implements Rent{    public void rent() {        System.out.println("房东要出租房子!");    }}
    
  3. 代理角色

    public class Proxy implements Rent{    private Host host;    public Proxy(){    }    public Proxy(Host host){        this.host=host;    }    public void rent() {       host.rent();    }    //看房    public void seeHouse(){        System.out.println("中介带你看房");    }    //签合同    public void hetong(){        System.out.println("签租赁合同");    }    //收中介费    public void fare(){        System.out.println("收中介费");    }}
    
  4. 客户端访问

    public class Client {    public static void main(String[] args) {        //房东要租房子        Host host = new Host();        //代理,中介帮房东租房子,但是代理角色一般会有附属操作        Proxy proxy = new Proxy(host);        //你不用面对房东直接找中介租房即可        proxy.rent();    }}
    

代理模式的好处 :

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

缺点:

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

加深理解

聊聊AOP

动态代理

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

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

动态代理的好处:

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

AOP

什么是AOP

​ AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

AOP在Spring中的作用

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

以下名词需要了解下:

  • 横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志 , 安全 , 缓存 , 事务等等 ....

  • 切面(ASPECT):横切关注点 被模块化 的特殊对象。即,它是一个类。

  • 通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。

  • 目标(Target):被通知对象。

  • 代理(Proxy):向目标对象应用通知之后创建的对象。

  • 切入点(PointCut):切面通知 执行的 “地点”的定义。

  • 连接点(JointPoint):与切入点匹配的执行点。

SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice:

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

使用Spring实现Aop

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

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

方式一 : 使用Spring的API接口 [主要SpringAPI接口实现]

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

方式三 : 使用注解实现

posted @ 2022-03-09 14:34  AiLJ-Me  阅读(27)  评论(0)    收藏  举报