Spring

Spring

相关学习代码

优点

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

组成

img

扩展

image-20220408120614401

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

弊端:发展了太久之后,违背了原来的理念,配置十分繁琐


IOC

理论推导

  1. UserDao接口
  2. UserDaoImpl实现类
  3. UserService业务接口
  4. UserServiceImpl 业务实现类
  • 通过不同类型的注入,可以不再管理对象的创建,而是通过用户选择

这种思想,从本质上解决了问题,不需要再去管理对象的创建了,系统的耦合性大大降低


本质

image-20220408145653284

HelloSpring

demo2

使用Spring创建对象,在Spring中这些都称为Bean

使用xml创建对象并且赋值,而不是通过new来创建对象,这种方式创建的对象就是bean

本质是使用set进行对象的注入

image-20220408153002289

  <property name="str" value="Spring"/>
  
  <property name="userDao" ref="mysqlImpl"/>
  • value:基本的值,基本数据类型
  • ref:引用Spring容器中创建好的对象

需要对象直接从Bean里面get就行了,不需要再创建对象了,这就是IOC

同样的,需要修改和引入直接简单修改配置文件就好了

Spring现在就相当于一个生产对象的大工厂,需要什么直接从工厂提货就行,不需要自己创造


IOC 创建对象的方式

对应的POJO类

package com.xuuxxi.pojo;

/**
 * @Author: Xuuxxi
 * @Date: 2022/4/8
 */
public class People {
    private Long id;
    private String name;
    private String password;

    public People(Long id, String name, String password) {
        this.id = id;
        this.name = name;
        this.password = password;
    }

    public People() {
    }

    @Override
    public String toString() {
        return "People{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", password='" + password + '\'' +
                '}';
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

赋值的三种方式 && 标签的意义

<!--第一种,下标赋值-->
<!--需要对应的构造函数-->
<bean id="t1" class="com.xuuxxi.pojo.People">
    <constructor-arg index="0" value="1"/>
    <constructor-arg index="1" value="tom"/>
    <constructor-arg index="2" value="123"/>
</bean>

<!--第二种,类型赋值-->
<!--不建议使用-->
<bean id="t2" class="com.xuuxxi.pojo.People">
    <constructor-arg type="java.lang.Long" value="2"/>
    <constructor-arg type="java.lang.String" value="jack"/>
    <constructor-arg type="java.lang.String" value="123"/>
</bean>

<!--第三种,通过name赋值-->
<bean id="t3" class="com.xuuxxi.pojo.People">
    <constructor-arg name="id" value="3"/>
    <constructor-arg name="name" value="Can"/>
    <constructor-arg name="password" value="123"/>
</bean>

总结

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

Spring配置

Bean的配置

<!--
	id:bean的唯一标识符,也就是相当于对象名
	class:bean对象所对应包路径
	name:别名,可以同时取多个别名(可以通过逗号、空格、分号分隔)

	> x1,xx1,xxx1都是t3的别名
-->
<bean id="t3" class="com.xuuxxi.pojo.People" name="x1,xx1,xxx1">

alias

<!--
    另一种别名的方法
    给t1取别名为tom
-->
<alias name="t1" alias="tom"/>

import

  • 一般用于团队开发使用,可以将多个配置文件导入合并成一个
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
<!--
    有多个配置文件需要合并
    最后只需要导入合并过后的applicationContext.xml文件就可以
-->

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

依赖注入

构造器注入

  • 之前的Bean就是构造器注入

Set方式注入


重点

position : demo3


    <bean id="student" class="com.xuuxxi.pojo.Student">
        <!--普通注入-->
        <property name="name" value="zhang"/>
        <!--Bean注入-->
        <property name="address" ref="address"/>
        <!--数组注入-->
        <property name="books">
            <array>
                <value>a</value>
                <value>b</value>
                <value>c</value>
                <value>d</value>
            </array>
        </property>
        <!--list注入-->
        <property name="hobbies">
            <list>
                <value>a</value>
                <value>b</value>
                <value>c</value>
                <value>d</value>
            </list>
        </property>
        <!--map-->
        <property name="cards">
            <map>
                <entry key="a" value="1"/>
                <entry key="b" value="2"/>
                <entry key="c" value="3"/>
                <entry key="d" value="4"/>
            </map>
        </property>
        <!--set-->
        <property name="games">
            <set>
                <value>a</value>
                <value>b</value>
                <value>c</value>
                <value>d</value>
            </set>
        </property>
        <!--null && empty-->
        <!--null-->
        <property name="wife">
            <null/>
        </property>
        <!--empty-->
        <!--<property name="wife" value=""/>-->
        <property name="info">
            <props>
                <prop key="a">1</prop>
                <prop key="b">2</prop>
                <prop key="c">3</prop>
                <prop key="d">4</prop>
            </props>
        </property>
    </bean>

    <bean id="address" class="com.xuuxxi.pojo.Address"/>

C空间和P空间命名注入

  • 两种拓展方式

p空间

  • 依赖于set

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

<!--需要在文件头部声明引用p空间注入-->
<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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="user" class="com.xuuxxi.pojo.User" p:name="zhang" p:age="18"/>
</beans>

c空间

  • 依赖于有参构造器

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

<!--需要在文件头部声明引用c空间注入-->
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:c="http://www.springframework.org/schema/c"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="user" class="com.xuuxxi.pojo.User" c:name="zhang" c:age="18"/>
</beans>

Bean的作用域

作用域 描述
单例(singleton) (默认)每一个Spring IoC容器都拥有唯一的一个实例对象
原型(prototype) 一个Bean定义,任意多个对象
请求(request) 一个HTTP请求会产生一个Bean对象,也就是说,每一个HTTP请求都有自己的Bean实例。只在基于web的Spring ApplicationContext中可用
会话(session) 限定一个Bean的作用域为HTTPsession的生命周期。同样,只有基于web的Spring ApplicationContext才能使用
全局会话(global session) 限定一个Bean的作用域为全局HTTPSession的生命周期。通常用于门户网站场景,同样,只有基于web的Spring ApplicationContext可用
应用(application) 限定一个Bean的作用域为ServletContext的生命周期。同样,只有基于web的Spring ApplicationContext可用

  1. singleton
    • 永远只有一个对象被创建,不管get多少次
  2. prototype
    • 一次get就创建一个新的对象
  3. 其余的作用域只能在web开发中使用到

Bean的自动装配

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

有三种装配的方式

  1. 在xml中显示的配置
  2. 在java中显示配置
  3. 隐式的自动装配 Bean

byName && byType 自动装配

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

    <!--
         autowire="byName"
         会自动在上下文中查找和自己对象 set 方法后面的值对应的 bean
		需要保证 bean 的 id 唯一性

		autowire="byType"
		会在上下文中查找符合自己对象属性类型相同的 bean
		需要保证 bean 的 class 唯一性
    -->
    <bean id="people" class="com.xuuxxi.pojo.People" autowire="byName">
        <property name="name" value="a"/>
    </bean>

注解自动装配

  • 使用注解的注意事项

    1. 导入约束
    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">
    
        <context:annotation-config/>
    
    </beans>
    

@Autowired && @Qualifier

  • 开发中最常用
@Autowired
private Cat cat;
@Autowired
private Dog dog;
private String name;
  • 自动装配,代替ref
  • 如果定义@Autowired(required = false)则注入对象可以为空
  • 若自动黄培环境比较复杂的时候,可以使用@Qualifier(value="xxx")去配置@Autowired的使用,指定一个唯一的bean进行注入

@Resource

  • 更高级,java的原生注解,功能更强大
  • 多个同类型找名字,单个类型直接注入不识别名字(byName && byType)
  • 可以用(name = xxx)来指定注入对象

小结

@Resource 和 @Autowired 的区别

  • 都是用来自动装配的,都可以放在属性字段上
  • @Autowired 通过 byType 方式实现
  • @Resource 默认是 byName 实现,实现失败则尝试进行 byType 实现
  • 开发中常用 Autowired
  • Resource 可以看成 Autowired 和 Qualifier 的集合体

使用注解开发

  • 需要导入 context 约束,增加注解的支持
<?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:component-scan base-package="com.xuuxxi.pojo"/>

</beans>

@Component

  • 等价于 <bean id="user" class="com.xuuxxi.pojo.User"/>
  • 一定要注意配置注解生效位置,否则无法生效实现自动注入

衍生注解(功能相同)

不同层下的不同注解方式,但是功能都是和 @Component 等价的


  1. dao

​ @Repository

  1. controller

​ @Controller

  1. service

​ @Service


@Value

  • 在属性上加,给属性注入值
  • 等价于 <property name="name" value="test"/>
@Component
public class User {
    @Value("test")
    private String name;
}

小结

xml 与 注解

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

最佳实践方式

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

JavaConfig方式配置

可以完全不适用Spring的xml配置,完全交给Java来做

@Configuration

  • 使用方法
@Configuration
public class AppConfig {

    @Bean
    public TransferServiceImpl transferService() {
        return new TransferServiceImpl();
    }
}

前面的配置与下面的 Spring XML 完全等效

<beans>
    <bean id="transferService" class="com.acme.TransferServiceImpl"/>
</beans>
  • 测试
  1. 编写pojo
//类被注册到容器中
@Component
public class User
  1. 编写Config文件
package com.xuuxxi.config;

import com.xuuxxi.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @Author: Xuuxxi
 * @Date: 2022/4/9
 */

//声明为配置类,相当于之前的beans.xml
@Configuration
//@ComponentScan    默认开启,也可以自己指定注解生效的包
@Import(MyConfig2.class)    //相当于引入第二个xml文件
public class MyConfig {
	
    @Bean	//注入Bean,Bean的id就是方法名(id="getUser")
    public User getUser(){
        return new User();
    }
}
  1. 获取context方法
import com.xuuxxi.config.MyConfig;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * @Author: Xuuxxi
 * @Date: 2022/4/9
 */
public class MyTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
        Object user = context.getBean("getUser");
        System.out.println(user);
    }
}

这种纯Java的配置方式,在SpringBoot中非常常见

代理模式

demo8

SpringAOP的底层!!

image-20220409155602934


优点:

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

缺点:

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

静态代理

角色分析:

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

代码步骤:

  1. 接口
package com.xuuxxi.demo1;

/**
 * @Author: Xuuxxi
 * @Date: 2022/4/9
 */
public interface Rent {
    public void rent();
}


package com.xuuxxi.demo2;

/**
 * @Author: Xuuxxi
 * @Date: 2022/4/9
 */
public interface UserService {
    public void add();
    public void delete();
    public void update();
    public void query();
}

  1. 真实角色
package com.xuuxxi.demo1;

/**
 * @Author: Xuuxxi
 * @Date: 2022/4/9
 */

//房东
public class Host implements Rent{
    @Override
    public void rent() {
        System.out.println("Host rent...");
    }
}


package com.xuuxxi.demo2;

/**
 * @Author: Xuuxxi
 * @Date: 2022/4/9
 */
public class UserServiceImpl implements UserService{
    @Override
    public void add() {
        System.out.println("add");
    }

    @Override
    public void delete() {
        System.out.println("delete");
    }

    @Override
    public void update() {
        System.out.println("update");
    }

    @Override
    public void query() {
        System.out.println("query");
    }
}

  1. 代理角色
package com.xuuxxi.demo1;

/**
 * @Author: Xuuxxi
 * @Date: 2022/4/9
 */

//中介
public class Proxy implements Rent{
    private Host host;

    public Proxy(){}

    public Proxy(Host host) {
        this.host = host;
    }


    @Override
    public void rent() {
        seeHouse();
        host.rent();
        Sign();
        fee();
    }

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

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

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


package com.xuuxxi.demo2;

/**
 * @Author: Xuuxxi
 * @Date: 2022/4/9
 */
public class UserServiceProxy implements UserService{
    private UserServiceImpl userService;

    public void setUserService(UserServiceImpl 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 query() {
        log("query");
        userService.query();
    }

    public void log(String msg){
        System.out.println("使用 " + msg + " 方法");
    }
}

  1. 客户端访问代理角色
package com.xuuxxi.demo1;

/**
 * @Author: Xuuxxi
 * @Date: 2022/4/9
 */
public class Client {
    public static void main(String[] args) {
        //房东租房
        Host host = new Host();
        //中介做附加操作
        Proxy proxy = new Proxy(host);
        proxy.rent();
    }
}

package com.xuuxxi.demo2;

/**
 * @Author: Xuuxxi
 * @Date: 2022/4/9
 */
public class Client {
    public static void main(String[] args) {
        UserServiceImpl service = new UserServiceImpl();

        UserServiceProxy proxy = new UserServiceProxy();
        proxy.setUserService(service);

        proxy.add();
    }
}

这样做的好处就是改变公共操作不用修改原有的业务代码,这是大忌

加深理解

image-20220409162622425

  • 关键就是横向开发,不改变原来的业务代码

动态代理

  • 动态代理和静态代理角色一样

  • 动态代理的代理类是动态生成的,不是写好了调用的

  • 动态代理分为两大类

    • 基于接口 --- JDK 动态代理

    • 基于类 --- cglib

java 字节码实现 :javasist (仅了解)

代码公式

  • target就是要实现的功能接口
package com.xuuxxi.demo4;

import com.xuuxxi.demo3.Rent;

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

/**
 * @Author: Xuuxxi
 * @Date: 2022/4/11
 */
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);
    }

    //处理代理实例并返回结果
    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 + " 方法");
    }
}

  • Client 使用
package com.xuuxxi.demo4;

import com.xuuxxi.demo4.ProxyInvocationHandler;

/**
 * @Author: Xuuxxi
 * @Date: 2022/4/11
 */
public class Client {
    public static void main(String[] args) {
        //真实角色
        UserServiceImpl userService = new UserServiceImpl();
        //代理角色
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        //设置代理对象
        pih.setTarget(userService);
        //动态生成代理类
        UserService proxy = (UserService) pih.getProxy();
        //开始代理
        proxy.add();
    }
}

动态代理的好处

  1. 可以使真实角色的操作更加纯粹!不用再关注一些公共的业务
  2. 公共业务就交给代理角色!实现了业务的分工
  3. 业务拓展的时候方便集中管理
  4. 一个动态代理类代理的是一个接口,一般对应的是一类业务
  5. 一个动态代理类可以代理多个类,只要是实现了同一个接口就行

AOP

什么是 AOP

image-20220411153635299

使用Spring实现AOP

  • 需要导入依赖
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.4</version>
</dependency>
  • application.xml 中引入约束
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd

方式一:API 接口

demo9

通过切入点和环绕实现代理前后的操作

方式二:使用自定义类

demo9

通过自定义切面实现

方式三:使用注解

demo9

自定义类,使用注解标记切入点

推荐使用方法二

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config/>
    <context:component-scan base-package="com.xuuxxi"/>

    <!--方式一:使用原生API接口-->
    <aop:config>
        <!--切入点-->
        <!--
            expression : 表达式,是固定的
            execution : 要执行的位置,第一个 * 表示返回所有类型,后面是路径,(..) 是方法的参数类型
        -->
        <aop:pointcut id="pointcut" expression="execution(* com.xuuxxi.service.UserServiceImpl.*(..))"/>
        <!--执行环绕增加-->
        <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
    </aop:config>


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

    <!--方式三:使用注解   JDK(DEFAULT) while proxy-target-class="false"   cglib while proxy-target-class="true"-->
    <aop:aspectj-autoproxy/>
</beans>

声明式事务

事务

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

事务ACID原则

  • 原子性
  • 一致性
  • 隔离性
  • 持久性

Mybatis && 事务问题

暂时不学

posted @ 2022-04-11 17:26  Xuuxxi  阅读(30)  评论(0编辑  收藏  举报