首页 > 技术文章 > Day24-Spring

cwtjyy 2021-06-28 01:05 原文

Spring

理念

使现有技术更加容易使用,具有强大的向后兼容性,庞大的生态系统,整合了现有的技术框架

官方文档

https://docs.spring.io/spring-framework/docs/current/reference/html/index.html

优点

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

组成

img

扩展

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

弊端

配置繁琐

IOC

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

原型

之前:程序根据用户需求主动创建对象

后来:使用注入,使程序成为被动的接受对象,大大降低耦合度,可以专注于业务的实现

package com.shaem.service;

import com.shaem.dao.User;
import com.shaem.dao.UserMysql;


public class UserServiceImp implements UserService {
    private User user;     //调用数据访问层

    public void setUser(User user){
        this.user=user;
    }

    public void getUser() {
        user.getUser();                     //业务层调用数据访问层
    }
}

import com.shaem.dao.UserMysql;
import com.shaem.dao.UserOracle;
import com.shaem.service.UserService;
import com.shaem.service.UserServiceImp;

public class Test {
    public static void main(String[] args) {
        //用户调用业务层,不需要使用Dao层
        UserService userService = new UserServiceImp();
        ((UserServiceImp)userService).setUser(new UserOracle());
        userService.getUser();
        ((UserServiceImp)userService).setUser(new UserMysql());
        userService.getUser();
    }
}

dao层中有两个实现类

本质

控制反转是一种设计思想,DI(依赖注入)为IOC的一种实现方法,获得依赖对象的方式发生改变

对象由Spring来创建、装配、管理

创建对象的方式

  1. 使用无参构造创建对象
  2. 如果使用有参构造创建对象

HelloSpring

  1. 创建Bean
package com.shaem.pojo;

public class HelloSpring {
    private String str;
    public String getStr(){
        return str;
    }
    public void setStr(String str){
        this.str=str;
    }

    @Override
    public String toString() {
        return "HelloSpring{" +
                "str='" + str + '\'' +
                '}';
    }
}
  1. 配置文件
<?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">
    <bean id="hello" class="com.shaem.pojo.HelloSpring">
        <property name="str" value="Spring" />
    </bean>

</beans>
  1. 测试
public class Test {
    public static void main(String[] args) {
        //获取Spring的上下文对象
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("Bean.xml");
        //我们的对象全交由Spring管理,使用时在Context中取出
        Object hello = applicationContext.getBean("hello");
        System.out.println(hello.toString());
    }
}

实际操作

正常的Java创建对象
    HelloSpring hello = new HelloSpring();
通过Spring来创建对象
    <bean id="hello" class="com.shaem.pojo.HelloSpring">
        <property name="str" value="Spring" />
    </bean>
            
      id=变量名
      class = new 的对象
      property给对象中的属性赋值

依赖注入

可以分为两个词来看,依赖:对象依赖于Spring容器来创建,注入:属性由Spring容器注入

注入方式

构造器注入
<bean id="" class="">
    <constructor-arg name="" value=""></constructor-arg>
</bean>

使用该标签进行对应构造器的注入

set注入
package com.shaem.pojo;

import java.util.*;

public class SetInjection {
    private String name;
    private Address address;
    private String[] books;
    private List<String> hobbys;
    private Map<String,Integer> map;
    private Set<String> set;
    private Properties properties;

    public String getName() {
        return name;
    }

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

    public Address getAddress() {
        return address;
    }

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

    public String[] getBooks() {
        return books;
    }

    public void setBooks(String[] books) {
        this.books = books;
    }

    public List<String> getHobbys() {
        return hobbys;
    }

    public void setHobbys(List<String> hobbys) {
        this.hobbys = hobbys;
    }

    public Map<String, Integer> getMap() {
        return map;
    }

    public void setMap(Map<String, Integer> map) {
        this.map = map;
    }

    public Set<String> getSet() {
        return set;
    }

    public void setSet(Set<String> set) {
        this.set = set;
    }

    public Properties getProperties() {
        return properties;
    }

    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    @Override
    public String toString() {
        return "SetInjection{" +
                "name='" + name + '\'' +
                ", address=" + address +
                ", books=" + Arrays.toString(books) +
                ", hobbys=" + hobbys +
                ", map=" + map +
                ", set=" + set +
                ", properties=" + properties +
                '}';
    }
}

 <bean id="address" class="com.shaem.pojo.Address"/>    <bean id="setInjection" class="com.shaem.pojo.SetInjection"><!--        普通注入-->        <property name="name" value="陈琛琛"></property><!--        bean注入,使用ref-->        <property name="address" ref="address"></property><!--        数组注入-->        <property name="books">            <array>                <value>神奇的猫咪</value>                <value>奇怪的狗狗</value>            </array>        </property><!--        集合注入-->        <property name="hobbys">            <list>                <value>逛B站</value>                <value>听歌</value>            </list>        </property><!--        map注入-->        <property name="map">            <map>                <entry key="刘芮峥" value="1"></entry>            </map>        </property>        <property name="properties">            <props>                <prop key="number">418240104</prop>                <prop key="name">陈琛琛</prop>            </props>        </property>    </bean>
拓展注入(需要导入相关的依赖)
  • P命名空间
<bean id="" class="" p:xx="" p:xx=""></bean>

简化属性注入

  • C命名空间
<bean id="" class="" c:xx="" c:xx=""></bean>

构造器属性注入

Bean作用域

image-20210627013755938

  • singleton 默认为单例默认,即不管创建多少个对象,拿到的全部为同一个
  • prototype 原型 每次创建的对象都不同

自动装配

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

  • 在xml文件中进行Bean的属性装配
    <bean id="cat" class="com.shaem.pojo.Cat"></bean>    <bean id="dog" class="com.shaem.pojo.Dog"></bean>    <bean id="people" class="com.shaem.pojo.People">        <property name="name" value="陈琛琛"></property>        <property name="cat" ref="cat"></property>        <property name="dog" ref="dog"></property>    </bean>
  • 使用java进行装配(暂时不用)

  • 隐式的自动装配

    • byName(名字不同则无法自动装配)
        <bean id="cat" class="com.shaem.pojo.Cat"></bean>    <bean id="dog" class="com.shaem.pojo.Dog"></bean><!--    在容器上下文中查找,查找people中set方法名字对应的bean,如有setdog,setcat-->    <bean id="people" class="com.shaem.pojo.People" autowire="byName">        <property name="name" value="陈琛琛"></property>    </bean>
    
    • byType(必须只能有一个,不能有两个相同的class)
        <bean id="cat1" class="com.shaem.pojo.Cat"></bean>    <bean id="dog" class="com.shaem.pojo.Dog"></bean><!--    在容器上下文中查找,查找people于属性类型相同的bean,如有Dog dog,Cat cat-->    <bean id="people" class="com.shaem.pojo.People" autowire="byType">        <property name="name" value="陈琛琛"></property>    </bean>
    
通过注解进行注入
  1. 导入约束
<?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>
  1. 在对应的位置使用注解@Autowired
    private String name;    @Autowired    private Dog dog;    @Autowired    private Cat cat;

总结:

  • 先通过byType,如果类型大于1,则使用byName
  • 可以省略set方法

常用注解

@Autowired

隐性注入对象,先通过byType,如果类型大于1,则使用byName

@Resource

隐性注入对象,先通过byName,如果名字不同,则使用byType

@Component

代表该类成为一个Bean

@Componentpublic class Student {    @Value("程晨晨")    private String name;    @Override    public String toString() {        return "Student{" +                "name='" + name + '\'' +                '}';    }}
 <context:component-scan base-package="com.shaem.pojo"/>
  • dao层 @Repository
  • service @Service
  • controller @Controller

根据不同的层进行不同的注解标识,但是作用都是相等的

@scope(“”)

作用域

Java配置Spring

JavaConfig在Spring4之后成为推荐使用的注入方式

使用Bean注解在配置文件中创建

@Configuration
public class SHAEMConfig {
    @Bean
    public User getUser(){
        return new User();
    }
}

AOP

image-20210628004738264

代理模式

静态代理

房东出租房子,我想要租房子,我可以通过中介找不同的房子,中介对我从房东那里租房子做了一些扩张功能,比如可以看多个不同房东的房子,签约等

  • 抽象对象:抽象类或者接口,如租房这个抽象的概念
  • 真实对象:被代理的对象
  • 代理对象:代理真实对象,可以做一些功能上的扩展
  • 客户对象:访问代理对象

好处:让真实业务更加存粹,专注于自身业务,可以进行一些功能上的扩展

坏处:每次都只能实现一个类,增加代码量

动态代理

动态代理的代理类是动态生成,分成两大类:

  • 基于接口的动态代理
    • JDK的动态代理(基于反射)
package com.shaem.pojo;

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

public class ProxyInvocationHandle implements InvocationHandler {
    //被代理实现的接口
    private Object target;

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

    //生成代理类,其中三个参数分别为类加载器,被代理的接口实例,实现了InvocationHandler的类并生成了实例
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    }

    //生成实例
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = method.invoke(target,args);           //rent为接口,可以传递参数进来,实现动态改变效果,当代理对象需要执行方法
        return result;
    }
}


public class Test {
    @org.junit.Test
    public void test(){
        //真实角色
        Host host = new Host();

        //代理角色,但还未创建
        ProxyInvocationHandle pih = new ProxyInvocationHandle();
        //调用程序处理程序来处理我们要调用的接口
        pih.setRent(host);
        //动态生成代理角色
        Rent proxy = (Rent)pih.getProxy();
        //调用invoke方法,返回接口中定义的被代理对象处理好的方法
        proxy.rent();
    }
}

  • 基于类的动态代理
    • cjlib

优点

  1. 代理一类业务,可以根据接口直接动态生成代理类
  2. 公共业务扩展时,方便管理
  3. 业务分工

什么是AOP

面向切面编程,通过预编译方式和运行期动态代理实现功能的统一维护的一种技术

AOP实现方式一

使用Spring的API接口
<!--    注册bean-->
    <bean id="choose" class="com.shaem.service.ChooseIMP"></bean>
    <bean id="log" class="com.shaem.log.Log"/>

<!--    配置aop-->
    <aop:config>
<!--pointcut:切入点 execution(要执行的位置,具体到方法)-->
        <aop:pointcut id="pointcut" expression="execution(* com.shaem.service.ChooseIMP.*(..))"/>
<!--环绕增加  选择切入的方法,选择切入点-->
        <aop:advisor advice-ref="log" pointcut-ref="pointcut"></aop:advisor>
    </aop:config>
    @org.junit.Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //动态代理应该是接口类型,如果是类的话,就没有意义了
        Choose choose = (Choose) context.getBean("choose");
        choose.add();
    }

AOP实现方式二

使用自定义类

    <bean id="log2" class="com.shaem.log.Log2"/>
    <aop:config>
        <aop:aspect ref="log2">
            <aop:pointcut id="point" expression="execution(* com.shaem.service.ChooseIMP.*(..))"/>
            <aop:before method="before" pointcut-ref="point"></aop:before>
            <aop:after method="after" pointcut-ref="point"></aop:after>
        </aop:aspect>
    </aop:config>

AOP实现方式三

使用注解

@Aspect     //标注该类为一个切面
public class Log3 {
    @Before("execution(* com.shaem.service.ChooseIMP.*(..))")
    public void before(){
        System.out.println("使用注解Before");
    }
    @After("execution(* com.shaem.service.ChooseIMP.*(..))")
    public void after(){
        System.out.println("使用注解After");
    }
}
<!--    开启注解支持-->
    <bean id="log3" class="com.shaem.log.Log3"/>
    <aop:aspectj-autoproxy/>

推荐阅读