Spring 学习笔记

什么是Spring

  • Spring是企业级开发框架,是软件设计层面的框架,优势在于可以将应用程序分层,开发者可以自助选择组件
  • Spring已经成为Java领域行业标准
  • Spring提供了各个层面的解决方案:
    MVC:struts2、Spring MVC
    ORMapping:Hibernate、MyBatis、Spring Date
  • Spring两大核心机制:IoC(控制反转)和AOP(面向切面)

Spring框架两大核心机制(IoC和AOP)

  • Ioc(控制反转)/DI(依赖注入)
  • AOP(面向切面编程)

什么是IoC(控制反转)

传统程序开发中,需要调用对象时,通常由调用者来创建被调用者的实例,即对象是由嗲用着主动new出来的
但在Spring框架中,创建对象的工作不再是由调用者来完成,而是交给IoC容器创建,再推送给调用者,整个流程完成反转,所以说控制反转

如何使用IoC

新建一个maven工程
在这里插入图片描述
在pom.xml中添加依赖
在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>aispringioc</artifactId>
    <version>1.0-SNAPSHOT</version>


    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.0.11.RELEASE</version>
        </dependency>
    </dependencies>
</project>

创建实体类
在这里插入图片描述
此时可以在xml文件中添加lombok依赖,这样实体类可以不用写get/set方法
在这里插入图片描述
实体类中添加此注解在这里插入图片描述
传统开发方式是手动new对象,并且对其进行操作
在这里插入图片描述
通过IoC创建对象,在配置文件中添加需要管理的对象,XML格式的配置文件,文件名可以自定义
新建xml文件于resources目录下
在这里插入图片描述

在这里插入图片描述

其会自动生成这些配置信息
在这里插入图片描述

给对象写如下内容,id就是类的别名,class关联的是创建对象所对应的类地址

<bean id="student" class="com.makerjack.entity.Student">
    <property name="id" value="1"></property>
    <property name="name" value="张三"></property>
    <property name="age" value="22"></property>
</bean>

这也称为属性注入
在这里插入图片描述

然后在测试中如下获取对象信息
在这里插入图片描述

配置文件

通过配置bean标签来完成对象的管理

  • id:对象名
  • class:对象的模板类。(所有交给IoC容器来管理的类,必须有无参构造函数,因为Spring底层通过反射机制来创建对象,调用的是无参构造)

对象的成员变量通过property标签完成赋值

  • name:成员变量名
  • value:成员变量值(基本数据类型,String可以直接赋值,如果是其它引用类型,不能通过value赋值)
  • ref:将IoC的另一个bean赋给当前的成员变量(DI)
    例如给Student对象加一个对象成员变量score
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

通过有参构造创建bean

以上是通过无参构造来创建对象,其本质是先创建后赋值
以下为通过有参构造函数创建对象,本质是一边创建一边赋值

  • 在实体类中创建对应有参构造
    在这里插入图片描述
    在对象前加这个注解即可

  • 配置文件中写入相关信息

<bean id="student1" class="com.makerjack.entity.Student">
    <constructor-arg name="id" value="2"></constructor-arg>
    <constructor-arg name="name" value="李四"></constructor-arg>
    <constructor-arg name="age" value="25"></constructor-arg>
    <constructor-arg name="score" ref="score"></constructor-arg>
</bean>

其中的name可以省略,其就是按照先后顺序赋值
当省略时,确保传递值的顺序不能错
name也可以换成index,其取值就是0~传递值的个数-1

<bean id="student1" class="com.makerjack.entity.Student">
    <constructor-arg index="0" value="2"></constructor-arg>
    <constructor-arg index="1" value="李四"></constructor-arg>
    <constructor-arg index="2" value="25"></constructor-arg>
    <constructor-arg index="3" ref="score"></constructor-arg>
</bean>

结果
在这里插入图片描述

给bean注入集合

如果有对象的属性为集合性质,需要存储多个同种数据
在这里插入图片描述
那么此处就不能直接引用对象
在这里插入图片描述
需要些成如下形式

<property name="scores">
    <list>
        <ref bean="score-1"></ref>
        <ref bean="score-2"></ref>
    </list>
</property>

在这里插入图片描述
效果如下
在这里插入图片描述

scope作用域

Spring管理的bean是根据scope来生成的,表示bean的作用域,共4种,默认是单例模式

  • singleton:单例,表示通过IoC容器获取的bean是唯一的
  • prototype:原型,表示通过IoC容器获取的bean是不同的
  • request:请求,表示在一次HTTP请求内有效
  • session:会话,表示在一个用户会话内有效
    request和session只适用于Web项目,大多数情况下,使用单例和原型比较多。
    在这里插入图片描述
    测试比如我们在无参构造函数中进行截断观察
    在这里插入图片描述
    请添加图片描述
    可以观察到
    prototype模式当业务代码获取IoC容器中的bean时,Spring才去调用无参构造创建对应的bean每次获取都会重新调用(创建一个新的)
    请添加图片描述
    可以看到
    signleton模式物理业务代码是否获取IoC容器中的bean,Spring在加载sprig.xml时就会创建对象而且每次调用不会创建新的

Spring的继承

与java的继承不同,java是类层面的继承,子类可以继承父类的内部结构信息,Spring是对象层面的继承,子对象可以继承父对象的属性值
比如stu想继承student的所有属性值
在这里插入图片描述
只需要在后面添加属性“parent”即可

<bean id="stu" class="com.makerjack.entity.Student" parent="student"></bean>

效果就是可以完全继承其全部属性
若想要覆盖某些属性,直接在内部重写属性即可
在这里插入图片描述

<property name="name" value="李四"></property>

效果如下
在这里插入图片描述
即便是不同的对象,如果属性/成员变量一样或 子类属性完全包含父类属性也是可以继承的(继承属性的数值)

Spring的依赖

与继承类似,依赖也是描述bean和bean之间的一种关系,配置依赖之后,被依赖的bean一定先创建,再创建依赖的bean:A依赖于B,则先创建B,后创建A

Spring的p命名空间

p命名空间是对IoC / DI的简化操作,使用p命名空间可以更方便地完成bean的配置以及bean之间的依赖注入
需要在xml文件中添加标签库
在这里插入图片描述

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

在xml中直接在对象内部追加p,即可完成属性赋值,本质是对对象配置的简化

<bean id="student" class="com.makerjack.entity.Student" p:id="1" p:name="盖亚" p:age="23" p:scores-ref="score"></bean>

<bean id="score" class="com.makerjack.entity.Score" p:english="87" p:math="79"></bean>

Spring的工厂方法

IoC通过工厂模式创建bean的方式有两种

  • 静态工厂方法
  • 实例工厂方法

静态工厂方法

不需要实例化工厂,程序启动就会加载到内存当中,只要关注bean即可
首先建立实体类

package com.makerjack.entity;

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

import java.util.Date;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Car {
    private String brand;
    private long id;
    private String color;
    private double price;
    private Date date;
}

然后创建静态工厂方法

package com.makerjack.factory;

import com.makerjack.entity.Car;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class StaticCarFactory {
    private static Map<Long, Car> carMap;
    static {
        carMap = new HashMap<Long, Car>();
        carMap.put(1L,new Car("benz",1L,"red",17.3,new Date()));
        carMap.put(2L,new Car("beck",2L,"white",11.7,new Date()));
    }
    public static Car getCar(long id){
        return carMap.get(id);
    }
}

再编写配置文件

<!-- 配置静态工厂创建car -->
<bean id="car" class="com.makerjack.factory.StaticCarFactory" factory-method="getCar">
    <constructor-arg value="2"/>
</bean>

测试在这里插入图片描述
在这里插入图片描述

实例工厂方法

实例对象依旧如上,实例工厂换成实际的对象,以便后续创建

package com.makerjack.factory;

import com.makerjack.entity.Car;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class InstanceCarFactory {
    private Map<Long, Car> carMap;
    public InstanceCarFactory() {
        carMap = new HashMap<Long, Car>();
        carMap.put(1L,new Car("benz",1L,"red",17.3,new Date()));
        carMap.put(2L,new Car("beck",2L,"white",11.7,new Date()));
        carMap.put(3L,new Car("lenor",3L,"blue",19.5,new Date()));
    }
    public Car getCar(long id){
        return carMap.get(id);
    }
}

xml文件中需要写入2个bean:工厂bean和car bean

<!-- 配置实例工厂 bean -->
<bean id="carFactory" class="com.makerjack.factory.InstanceCarFactory"></bean>
<!-- 配置实例工厂创建car -->
<bean id="car" factory-bean="carFactory" factory-method="getCar">
    <constructor-arg value="3"/>
</bean>

测试
在这里插入图片描述

IoC自动装载(Autowire)

IoC负责创建对象,DI负责完成对象的依赖注入,通过配置property标签的ref属性来完成,同时Spring提供了另外一种更加简便的依赖注入方式:自动装载,不需要手动配置property,IoC容器会自动选择完成注入

自动装载有2种方式:

  • byName:通过属性名自动装载
  • byType:通过属性的数据类型自动装载

比如有个person对象
在这里插入图片描述

以往是在此处引用先前创建的对象
在这里插入图片描述
自动装载就可以自动匹配现有的

byName是匹配同名对象,当有同名就会匹配不到(不会报错)在这里插入图片描述
byType是匹配同类的,当有同种就会抛出异常
在这里插入图片描述

AOP

ASP:Aspect Oriented Programming 面向切面编程

AOP的优点

  • 降低模块之间的耦合度
  • 使系统容易扩展
  • 更好的代码复用
  • 非业务代码更加集中,便于统一管理
  • 业务代码更加简介纯粹,没有其他代码的影响

AOP是对面向对象编程的补充,在运行时,动态地把代码切入到类的指定方法、指定位置上的编程思想就是面向切面编程
将不同方法的同一个位置抽象成一个切面对象,对该切面对象进行编程就是AOP

如何使用AOP

首先创建maven工程,在pom.xml中添加依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>5.0.11.RELEASE</version>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.0.11.RELEASE</version>
</dependency>

例子
在这里插入图片描述

创建一个方法类,实现简单的四则运算
在这里插入图片描述
每个函数都要输出参数+运算+输出结果
这样整个代码的重复度就很高,后期维护也不方便

若我们创建一个代理类,执行想要的业务方法,在方法前后输出日志信息
那么就可以省去这些重复代码

以下是创建代理类的制造者

package com.makerjack.utils;

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

public class MyInvocationHandler implements InvocationHandler {
    //接受委托对象
    private Object object = null;
    //返回代理对象
    public Object bind(Object object){
        this.object = object;//委托者存入成员变量
        return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),this);
    }
    //用于嵌入日志等函数外的信息
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println(method.getName()+"方法的参数是"+ Arrays.toString(args));
        Object result = method.invoke(this.object,args);
        System.out.println(method.getName()+"结果是"+result);
        return result;
    }
}

通过它(并不是代理类本身),我们就能创建代理类,从而把业务代码和日志信息剥离开来

在测试中使用代理类来执行相关函数

package com.makerjack;

import com.makerjack.utils.Cal;
import com.makerjack.utils.MyInvocationHandler;
import com.makerjack.utils.calimpl;

public class Test {
    public static void main(String[] args) {
        Cal cal = new calimpl();
        MyInvocationHandler myInvocationHandler = new MyInvocationHandler();
        Cal calNew = (Cal) myInvocationHandler.bind(cal);
        calNew.add(1,2);
        calNew.sub(2,3);
        calNew.mul(3,4);
        calNew.div(4,2);
    }
}

效果如下
在这里插入图片描述
以上是通过动态代理实现AOP的过程,比较复杂,难以理解 Spring框架对AOP进行了封装,使用Spring框架可以用面向对象的思想来实现AOP

通过注解方式完成日志注入

Spring框架中不需要创建invocationHandler,只需要创建一个切面对象,将所有的非业务代码在切面对象中完成即可。Spring框架底层会根据切面类以及目标类自动生成一个代理对象

例如
在这里插入图片描述

package com.makerjack.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Before;

import java.util.Arrays;

public class LoggerAspect {
    @Before("execution(public int com.makerjack.utils.calimpl.*(..))")
    public void before(JoinPoint joinPoint){
        //获取方法名
        String name = joinPoint.getSignature().getName();
        //获取参数名
        String args = Arrays.toString(joinPoint.getArgs());
        System.out.println(name+"方法的参数是:"+args);
    }
}

然后需要通过注解的方式把切面类交给IoC来与目标对象相结合

在pom.xml中添加注解

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.0.11.RELEASE</version>
</dependency>

在切面类前部追加注解
在这里插入图片描述
此外,其想要切入的对象calimpl也需要添加@component注解,来交给IoC处理
在这里插入图片描述
再者,需要在spring-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:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
">

    <!-- 自动扫描 -->
    <context:component-scan base-package="com.makerjack"></context:component-scan>

    <!-- aspect注解生效,为目标类生成代理对象 -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

context:component-sancom.makerjack 包中的所有类进行扫码,如果有类添加了@commponent`` 注解,则将该类扫描到IoC容器中,即IoC管理它的对象 ``aop:aspect-autoproxy 让Spring框架结合切面类和目标类自动生成动态代理对象

最后就可以进行测试了

在这里插入图片描述
如法炮制切面对象的方法,针对函数执行后,抛异常的情况进行打印日志
在这里插入图片描述
以下为测试结果
在这里插入图片描述

  • 切面:横切关注点被模块化的抽象对象
  • 通知:切面对象完成的工作
  • 目标:被通知的对象,即被横切的对象
  • 连接点:通知要插入业务代码的具体位置
  • 切点:AOP通过切点定位到连接点

通过实现Spring API接口方式完成日志注入

代码

package com.makerjack.service;

public class UserServiceImpl implements UserService{
    public int add(int a,int b) {
        System.out.println("执行了增加操作");
        return a+b;
    }

    public void delete() {
        System.out.println("执行了删除操作");
    }

    public void update() {
        System.out.println("执行了改进操作");
    }

    public void search() {
        System.out.println("执行了查找操作");
    }
}


package com.makerjack.log;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class BeforeLog implements MethodBeforeAdvice {
    //method:待执行的目标方法
    //args:待执行的参数
    //target:目标对象
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println(target.getClass().getName()+"调用了"+method.getName()+"方法,参数为"+args.toString());
    }
}


package com.makerjack.log;

import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;

public class AfterLog implements AfterReturningAdvice {
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println(target.getClass().getName()+"调用了"+method.getName()+"方法,参数为:"+args.toString()+"结果为:"+returnValue);
    }
}



//测试函数
public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        //动态代理的是接口
        UserService userService = (UserService) applicationContext.getBean("userService");
        userService.add(1,2);
        userService.delete();
}

配置xml文件如下

<!--配置aop配置-->
<aop:config>
    <!--切入点,execution表达式,execution(要执行的位置 修饰词 返回值 类名 方法名 参数)-->
    <aop:pointcut id="pointcut" expression="execution(* com.makerjack.service.UserServiceImpl.*(..))"/>

    <!--执行环绕增强-->
    <aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"></aop:advisor>
    <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"></aop:advisor>
</aop:config>

结果如下
在这里插入图片描述
可以看到,通过接口的方式可以获取到执行对象方法的众多参数用于输出

通过自定义类完成日志注入

自定义一个通知
在这里插入图片描述
在配置文件中编辑切入配置

<!--自定义类-->
<bean id="mylog" class="com.makerjack.log.MyLog"></bean>
<aop:config>
    <aop:aspect ref="mylog">
        <!--切入点-->
        <aop:pointcut id="pointcut" expression="execution(* com.makerjack.service.UserServiceImpl.*(..))"/>
        <!--通知-->
        <aop:before method="before" pointcut-ref="pointcut"></aop:before>
        <aop:after method="after" pointcut-ref="pointcut"></aop:after>
    </aop:aspect>
</aop:config>

测试结果如下
在这里插入图片描述
可以看到,自定义类的方式操作十分简便,但功能也很有限,对于切面的信息无法获取

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值