欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring5

程序员文章站 2022-03-19 12:03:34
...

Spring5

1.Spring

1.1、Spring简介

  • Srping是一个轻量级的控制反转(IoC)面向切面(AOP) 的容器框架

  • 2004年3月24日,Spring框架以interface21框架为基础,经过重新设计,发布了1.0正式版本

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

<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<!--Spring和MyBatis整合需要的jar包-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.2.8.RELEASE</version>
</dependency>

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

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

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

1.2、优点

  • 开源免费
  • 轻量级、非侵入式
  • 控制反转(IOC)、面向切面(AOP)
  • 支持事务的处理、支持其它框架整合(大杂烩)

1.3、组成

Spring5

2、IOC(控制反转)

将对象的控制权交给用户,业务层不再自己new对象。为用户提供setter器,被动等待接受用户new的对象

3、HelloSpring

3.1、配置元数据基本结构

applicationContext.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">

    <!-- dao层bean的配置 -->
    <bean id="studentDao" class="com.zmy.dao.StudentDao">
    </bean>
    <bean id="teacherDao" class="com.zmy.dao.TeacherDao">
    </bean>

    <!-- service层bean的配置 -->
    <bean id="personService" class="com.zmy.service.PersonService">
        <!--value:基本类型+String ref:复杂类类型-->
        <property name="personDao" ref="teacherDao"/>
    </bean>
</beans>

3.2、Dao层

public interface PersonDao {
    void doSomething();
}
public class StudentDao implements PersonDao{
    @Override
    public void doSomething() {
        System.out.println("学生要学习");
    }
}
public class TeacherDao implements PersonDao{
    @Override
    public void doSomething() {
        System.out.println("老师要教学");
    }
}

3.3、获取bean并使用

@Test
public void testHello(){
    //拿到容器上下文对象
    ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
    PersonService personService = (PersonService) classPathXmlApplicationContext.getBean("personService");
    personService.serviceOne();
}

4、Spring配置说明

4.1、alias(别名)

<beans>
    <bean id="student" class="com.zmy.pojo.Student">
    	<constructor-arg name="name" value="18岁的我"/>
    	<constructor-arg name="age" value="18"/>
	</bean>
    <alias name="student" alias="ppp"/>
</beans>

测试

public void testHello(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
    Student student = (Student) applicationContext.getBean("ppp");
    System.out.println(student.toString());
}

4.2、Bean

<!--
id:名字
class:类型全限定名 包名+类名
name:别名 可取多个 ',' ' ' ';'都可分割 
scope:作用域(不指定时,默认单例) singleton=单例  prototype=原型(每次get会新建一个实例)
-->
<bean id="student" class="com.zmy.pojo.Student" name="student2,student3 student4;student5" scope="singleton">
    <constructor-arg type="java.lang.String" value="名字第一个"/>
    <constructor-arg type="int" value="1"/>
    <constructor-arg type="java.lang.String" value="名字第二个"/>
</bean>

4.3、Import

合并多个xml

<import resource="beans.xml">

5、依赖注入(DI)

5.1、构造器注入

5.1.1、使用无参构造函数构造

默认使用无参构造函数构造,此时需要保证有无参构造函数,否则报错

public class Student{

    private String name;
    private int age;

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
<bean id="student" class="com.zmy.pojo.Student">
    <!--property设置的值通过set方法赋值-->
    <property name="name" value="学生set名字"/>
    <property name="age" value="18"/>
</bean>

5.1.2、使用有参构造函数构造

5.1.2.1、通过下标传参

student类,没有无参构造函数

package com.zmy.pojo;

public class Student{

    private String name;
    private int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

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

bean配置

<bean id="student" class="com.zmy.pojo.Student">
    <constructor-arg index="0" value="学生构造器名字"/>
    <constructor-arg index="1" value="0"/>
</bean>
5.1.2.2、通过类型传参

student类

package com.zmy.pojo;

public class Student{

    private String name1;
    private String name2;
    private int age;

    public Student(String name1, String name2,int age) {
        this.name1 = name1;
        this.name2 = name2;
        this.age = age;
    }

    public String getName1() {
        return name1;
    }

    public void setName1(String name1) {
        this.name1 = name1;
    }

    public String getName2() {
        return name2;
    }

    public void setName2(String name2) {
        this.name2 = name2;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name1='" + name1 + '\'' +
                "name2='" + name2 + '\'' +
                ", age=" + age +
                '}';
    }
}

bean配置

<bean id="student" class="com.zmy.pojo.Student">
    <!--每种类型根据前后顺序为第一个String类型参数,第二个String类型参数...-->
    <!--第一个出现的String类型,为参数列表中的第一个String类型参数的值-->
    <constructor-arg type="java.lang.String" value="名字第一个"/>
    <constructor-arg type="int" value="1"/>
    <!--第二个出现的String类型,为参数列表中的第二个String类型参数的值-->
    <constructor-arg type="java.lang.String" value="名字第二个"/>
</bean>
5.1.2.3、通过参数名传参

student类

package com.zmy.pojo;

public class Student{

    private String name;
    private int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

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

bean配置

<bean id="student" class="com.zmy.pojo.Student">
    <constructor-arg name="name" value="18岁的我"/>
    <constructor-arg name="age" value="18"/>
</bean>

总结:在配置文件加载的时候,所有配置的bean都会被创建唯一一个对象。每次get对象都是同一个实例。

5.2、Set方式注入

实体类MyClass

import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

public class MyClass {

    private int id;
    private String name;

    private Properties props;

    Teacher teacher;

    List<Student> students;

    Map<String,Teacher> mapTeacher;

    Set<Teacher> teachers;

    public Set<Teacher> getTeachers() {
        return teachers;
    }

    public void setTeachers(Set<Teacher> teachers) {
        this.teachers = teachers;
    }

    public Map<String, Teacher> getMapTeacher() {
        return mapTeacher;
    }

    public void setMapTeacher(Map<String, Teacher> mapTeacher) {
        this.mapTeacher = mapTeacher;
    }

    public Properties getProps() {
        return props;
    }

    public void setProps(Properties props) {
        this.props = props;
    }

    public int getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public Teacher getTeacher() {
        return teacher;
    }

    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;
    }

    public List<Student> getStudents() {
        return students;
    }

    public void setStudents(List<Student> students) {
        this.students = students;
    }

    @Override
    public String toString() {
        return "MyClass{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", props=" + props +
                ", teacher=" + teacher +
                ", students=" + students +
                ", mapTeacher=" + mapTeacher +
                ", teachers=" + teachers +
                '}';
    }
}

实体类Student

public class Student{

    private String name;
    private int age;

    public Student(String name,int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name1='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

实体类Teacher

public class Teacher{

    private String name;
    private int age;

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

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

bean配置

基本类型+String

引用的bean

实体类的属性名

<?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">

    <!-- pojo层bean的配置 -->
    <bean id="student" class="com.zmy.pojo.Student" scope="prototype">
        <property name="name" value="XXXX"/>
        <property name="age" value="18"/>
    </bean>

    <bean id="teacher" class="com.zmy.pojo.Teacher" scope="prototype">
        <property name="name" value="XXXX"/>
        <property name="age" value="53"/>
    </bean>

    <bean id="myClass" class="com.zmy.pojo.MyClass">
        <property name="name" value="XXX"/>
        <property name="id" value="13"/>
        <property name="teacher" ref="teacher"/>
        <property name="props">
            <props>
                <prop key="p1">v1</prop>
                <prop key="p2">v2</prop>
            </props>
        </property>
        <property name="students">
            <list>
                <ref bean="student"/>
                <ref bean="student"/>
                <ref bean="student"/>
            </list>
        </property>
        <property name="mapTeacher">
            <map>
                <entry key="teacher1" value-ref="teacher"/>
            </map>
        </property>
        <property name="teachers">
            <set>
                <ref bean="teacher"/>
            </set>
        </property>
    </bean>

</beans>

输出结果

MyClass{id=13, name='XXX', props={p2=v2, p1=v1}, teacher=Teacher{name='XXXX', age=53}, students=[Student{name1='XXXX', age=18}, Student{name1='XXXX', age=18}, Student{name1='XXXX', age=18}], mapTeacher={teacher1=Teacher{name='XXXX', age=53}}, teachers=[Teacher{name='XXXX', age=53}]}
false

5.3、扩展方式注入

使用p标签或者c标签

p标签=properties c标签=constructor-arg

使用p或者c标签需要导入对应的命名空间

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

xmlns:c="http://www.springframework.org/schema/c"
<!--需要有无参构造函数-->
<bean id="teacher" class="com.zmy.pojo.Teacher" p:name="XXX" p:age="53">

<!--需要有有参构造函数-->
<bean id="teacher" class="com.zmy.pojo.Teacher" c:name="XXX" c:age="53">

6、Bean的作用域

scope

  • singleton 单例,每次get都是同一个实例,默认为单例
  • prototype 原型,每次get都会新创建一个实例
  • request
  • session
  • application
  • websocket

7、Bean的自动装配

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

7.1、byName自动装配

<bean id="cat" class="Cat"/>
<bean id="dog" class="Dog"/>

<!--手动set注入装配-->
<bean id="people" class="People">
    <property name="name" value="XX">
    <property name="cat" ref="cat">
    <property name="dog" ref="dog">
</bean>
    
<!--自动装配-->
<bean id="people" class="People" autowire="byName">
    <property name="name" value="XX">
</bean>

byName会自动在容器上下文中寻找set方法指定的beanid

eg:public void setCat(){…} 会寻找id为Cat的bean

7.2、byType自动装配

每个类的bean只能有一个

<!--自动装配-->
<bean id="people" class="People" autowire="byType">
    <property name="name" value="XX">
</bean>

7.3、注解实现自动装配

  • Spring从2.5开始支持注解,JDK从1.5开始支持注解

  • 使用注解的前提条件

1、导入约束 (context约束)

	xmlns:context="http://www.springframework.org/schema/context"
	
	http://www.springframework.org/schema/context
    https://www.springframework.org/schema/context/spring-context.xsd

2、开启注解支持

	<context:annotation-config/>

3、@Autowired和@Qualifier

在需要自动装配的属性上 或者 set方法上添加**@Autowired**注解,自动寻找xml中的bean(根据类型而非id)

也可以增加@Qualifier(“dog”)注解,则会根据id去寻找对应的bean

4、@Resource(name=“XXX”)

等同于@Autowired + @Qualifier

  • 示例
<?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/>
    <!-- pojo层bean的配置 -->
    <bean id="cat" class="com.zmy.pojo.Cat" />
    <bean id="dog" class="com.zmy.pojo.Dog" />
    <bean id="person" class="com.zmy.pojo.Person">
        <property name="name" value="XXX"/>
    </bean>

</beans>
package com.zmy.pojo;

import org.springframework.beans.factory.annotation.Autowired;

public class Person {

    private String name;
    @Autowired
    //寻找类型为Cat的bean
    private Cat cat;
    @Autowired
    //寻找id为dog的bean
    @Qualifier("dog")
    private Dog dog;

    public Person() {
    }

    public Person(String name, Cat cat, Dog dog) {
        this.name = name;
        this.cat = cat;
        this.dog = dog;
    }

    public String getName() {
        return name;
    }

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

    public Cat getCat() {
        return cat;
    }

    public void setCat(Cat cat) {
        this.cat = cat;
    }

    public Dog getDog() {
        return dog;
    }

    public void setDog(Dog dog) {
        this.dog = dog;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", cat=" + cat +
                ", dog=" + dog +
                '}';
    }
}

8、使用注解开发

使用注解开发,需要增加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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <!--开启注解,并扫描指定包下的注解,可实现注册Bean-->
    <context:component-scan base-package="com.zmy.pojo"/>

</beans>

8.1、注册Bean

使用注解 @Component(value = “XXX”) 进行注册Bean,XXX为Bean的id

8.2、属性的注入

使用注解 @Value(“zmy”) 实现简单属性的注入

package com.zmy.pojo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

@Component
public class Person {

    @Value("zmy")
    private String name;
    @Autowired
    private Cat cat;
    @Autowired
    private Dog dog;

    public Person() {
    }

    public Person(String name, Cat cat, Dog dog) {
        this.name = name;
        this.cat = cat;
        this.dog = dog;
    }

    public String getName() {
        return name;
    }

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

    public Cat getCat() {
        return cat;
    }

    public void setCat(Cat cat) {
        this.cat = cat;
    }

    public Dog getDog() {
        return dog;
    }

    public void setDog(Dog dog) {
        this.dog = dog;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", cat=" + cat +
                ", dog=" + dog +
                '}';
    }
}
package com.zmy.pojo;

import org.springframework.stereotype.Component;

@Component
public class Dog {
    public void shout(){
        System.out.println("wang");
    }
}
package com.zmy.pojo;

import org.springframework.stereotype.Component;

@Component
public class Cat {
    public void shout(){
        System.out.println("miao");
    }
}

8.3、@Component的衍生注解

在web项目中,由于mvc三层架构,不同层会使用不同的注解注册Bean,功能一致

  • dao层

    @Repository

  • service

    @Service

  • controller

    @Controller

8.4、作用域

@Scope(“singleton”)

9、使用JavaConfig代替xml注册Bean

定义JavaConfig类来代替xml配置文件,JavaConfig类可以做所有xml能够做的配置

  • 增加 @Configuration 注解
  • 增加 @Bean 注解
package com.zmy.javaconfig;

import com.zmy.pojo.Dog;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
//配置扫描包
@ComponentScan(value = "com.zmy.pojo")
//导入另外一个配置类
@Import(JavaConfig2.class)
public class JavaConfig {

    @Bean
    public Dog dog(){
        return new Dog();
    }

}
@Test
public void testHello(){
  ApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(JavaConfig.class);
  Dog dog = annotationConfigApplicationContext.getBean("dog", Dog.class);
  dog.shout();
}

10、AOP(面向切面)

AOP底层是使用代理模式实现的,所以首先要了解代理模式。

代理模式分为:

  • 静态代理
  • 动态代理

10.1、静态代理

eg:结婚案例。

  • 结婚对象 : 实现 结婚接口,实现结婚方法
  • 婚庆公司 : 实现结婚接口,拥有结婚对象,结婚方法中调用结婚对象的结婚方法,并且有一些自己的操作
  • 结婚接口 :结婚方法

静态代理的好处:

  • 使真实对象只需关心核心业务
  • 公共业务交给代理去做

静态代理的缺点:

  • 每一个真实对象都需要一个代理,代码量翻倍。

10.2、动态代理

学习动态代理之前,先了解两个java.lang.reflect包下的类

  • Proxy 类

    • public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) throws IllegalArgumentException 该方法返回一个interfaces接口的代理类对象
  • InvocationHandler 接口

    • public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;

      实现了该接口的类的对象,作为参数传递给newProxyInstance方法

获取代理工具类

package com.zmy.pojo;

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

    //通过Proxy.newProxyInstance,返回target的代理类对象 : $Proxy0
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    }


    //1、$Proxy0的代码由JVM生成
    //2、$Proxy0有一个私有属性,InvocationHandler h,在Proxy.newProxyInstance的第三个参数,(我们传入的this,即当前类的对象)
    // $Proxy0类对象在调用接口方法时,内部实现仅调用了h的invoke方法,所以h必须有方法invoke,而我们传入的this当前类对象的类实现了InvocationHandler接口,保证了该点
    // invoke调用时传入的参数为参数为:this, 接口方法, null
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        //调用被代理的真实对象的方法method
        Object object = method.invoke(target,args);
        after();
        return object;
    }

    private void after() {
        System.out.println("结婚后的收尾!");
    }

    private void before() {
        System.out.println("结婚前的准备!");
    }

}

Marry接口

public interface Marry {
    void HappyMarry();
}

You类

public class You implements Marry{
    @Override
    public void HappyMarry() {
        System.out.println("结婚了!");
    }
}

测试类

@Test
public void test(){
    You you = new You();
    ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler();
    proxyInvocationHandler.setTarget(you);
    Marry proxy = (Marry) proxyInvocationHandler.getProxy();
    proxy.HappyMarry();
}

参考自:https://www.jb51.net/article/129142.htm