spring核心知识点、简单易懂

162 阅读10分钟

spring优点

1. spring是一个免费的开源框架、容器 2. spring非侵入式.

使用spring,编写一些业务类的时候不需要继承spring特定的类,通过配置完成依赖注入后就可以使用,此时,spring就没有侵入到我业务类的代码里。侵入式让用户代码产生对框架的依赖,这些代码不能在框架外使用,不利于代码的复用。但侵入式可以使用户跟框架更好的结合,更容易更充分的利用框架提供的功能。非侵入式的代码则没有过多的依赖,可以很方便的迁移到其他地方。但是与用户代码互动的方式可能就比较复杂。

3. 控制反转.依赖注入ioc 面向切面aop 4. 对事物支持

spring是一个轻量级的控制反转ioc和面向对象的框架。

spring组成

spring由7个核心模块组成,核心模块是spring core ,其他模块作用于spring core之上,主要负责bean创建及管理。

  1. spring Core spring的核心模块, 是spring提供的基本功能,主要的组件时BeanFactory。BeanFactory使用控制反转(IOC)模式将应用程序的配置和依赖性的规范于实际应用程序的代码分开。
  2. spring ORM 提供orm的对象关系工具,
  3. spring AOP spring面向切面。
  4. spring Context spring上下文,向spring提供上下文信息。
  5. spring DAO 简化jdbc操作。
  6. spring Web 于 Web 的应用程序提供了上下文
  7. spring Web MVC springMVC框架

拓展

spring、springboot、springcloud关系

  • springboot是spring的一套快速开发脚手架。
  • springcloud是基于springboot的。
  • springboot是专注于快速开发单位微服务的个体,springcloud是专注于微服务治理的框架。
  • springboot的集成方案已经选择好了,能不配置就不配置,而springcloud是基于springboot来实现的。

IOC

IOC(控制反转)是spring的核心,是一种设计思想,而DI(依赖注入)是IOC的一种实现方式,实现IOC的方式有很多种,DI只是其中的一种,把创建对象和对象之间的关系交给spring来管理

图1 之前的方式存在对象之间的耦合关系,一层调用一层。 图2 就是用户想要调用的时候只调用中间件,这样在调用的时候只需要调用中间件,想调用那个对象不会影响到其他的对象,降低了对象之间的耦合性。ioc容器就是中间件。

IOC容器是spring的核心内容,可以使用xml实现IOC,也可以使用注解的方式实现IOC 采用xml配置bean的时候,是将配置信息和业务实现分离的,而采用注解的配置bean是将二者合二为一。

IOC理念实践

注 : spring 需要导入commons-logging进行日志记录 . 我们利用maven , 他会自动下载对应的依赖项 .

<dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-webmvc</artifactId>
   <version>5.1.10.RELEASE</version>
</dependency>

**1. 编写一个hello实体类 **

public class Hello {
  private String name;
  public String getName() {
      return name;
 }
  public void setName(String name) {
      this.name = name;
 }
  public void show(){
      System.out.println("Hello,"+ name );
 }
}

**2. 编写我们的spring文件 , 这里我们命名为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就是java对象 , 由Spring创建和管理-->
  <!--就相当于Hello hello = new Hello();现在把由用户new 创建对象,更改为 xml注入的方式,把创建hello对象交给spring管理,hello 对象的属性是由Spring容器设置的,这个过程就叫做控制反转-->
  <bean id="hello" class="com.kuang.pojo.Hello">
      <property name="name" value="Spring"/>
  </bean>

</beans>

**3. 测试 **

@Test
public void test(){
  //解析beans.xml文件 , 生成管理相应的Bean对象
  ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
  //getBean : 参数即为spring配置文件中bean的id .
  Hello hello = (Hello) context.getBean("hello");
  hello.show();
}

DI依赖注入

**构造器注入 **

<!-- name指参数名 -->
<!-- constructor-arg:构造器注入,当调用getBean()方法的时候初始化,对象中的name属性已经赋值。 -->
<bean id="hello" class="com.kuang.pojo.Hello">
   	<constructor-arg name="name" value="Spring"/>
</bean>

**set注入(重点) ** 依赖:bean对象的创建依赖于容器 注入:bean对象中的所有属性由容器来注入

Student.java
package com.kuang.pojo;

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

public class Student {

    private String name;
    private String[] books;
    private List<String> hobbys;
    private String wife;
    public void setName(String name) {
        this.name = name;
   }
    public void setBooks(String[] books) {
        this.books = books;
   }
    public void setHobbys(List<String> hobbys) {
        this.hobbys = hobbys;
   }
    public void setWife(String wife) {
        this.wife = wife;
   }
<!--常量注入-->
<bean id="student" class="com.kuang.pojo.Student">
     <property name="name" value="小明"/>
</bean>
 
<!--数组注入-->
 <bean id="student" class="com.kuang.pojo.Student">
     <property name="name" value="小明"/>
     <property name="books">
         <array>
             <value>西游记</value>
             <value>红楼梦</value>
             <value>水浒传</value>
         </array>
     </property>
</bean>

<!--数组注入-->
 <bean id="student" class="com.kuang.pojo.Student">
     <property name="name" value="小明"/>
     <property name="hobbys">
         <list>
			 <value>听歌</value>
			 <value>看电影</value>
			 <value>爬山</value>
     	</list>
     </property>
</bean>

**其他方式 **

Bean的作用域

bean的作用域常用的是两种,单例模式(singleton)和多例模式(prototype)。bean作用域默认是单例模式Singleton。那些由spring IOC管理的对象统称为bean。

singleton:当一个bean的作用域是单例模式的时候,spring ioc只会创建一个bean实例,所有请求bean,都是同一个bean实例。在spring ioc容器创建bean对象,就会创建bean对象,不管用不用,容器都已经创建了。

prototype:当一个bean的作用域是多例模式的时候,所有请求该bean实例的时候,spring ioc容器都会返回一个新的实例。spring ioc创建容器的时候,不会创建多例模式作用域的bean,当请求bean的时候,spring ioc容器才会创建。

注解使用@Scope来设置该bean的作用域。

单例模式(多线程的) 优点:共享一个实例,内存占用少,GC开销小,可以快速获取到bean。因为单例获取bean操作,除了第一次生成之外,其余都是从缓存里获取的,所以很快。 缺点:共享资源存在线程安全问题 多例模式(单线程的) 优点:不存在线程安全问题(静态共享资源除外:html,htm,css ,js ,jpg .txt ) 缺点:多个实例,内存占用多,GC开销大

bean的自动装配

springIOC容器DI依赖注入自动装配通常使用注解的方式进行自动装配,注解有两种形式,@Autowired和@Resource,@Autowired是按照类型进行装配,@Resource是按照名称进行装配。 #

@Autowired

1. 将User类中的set方法去除掉,使用@autowired注解

public class User {
   @Autowired
   private Cat cat;
   @Autowired
   private Dog dog;
   private String str;

   public Cat getCat() {
       return cat;
  }
   public Dog getDog() {
       return dog;
  }
   public String getStr() {
       return str;
  }
}

2. bean的配置文件

<context:annotation-config/>
<bean id="dog" class="com.kuang.pojo.Dog"/>
<bean id="cat" class="com.kuang.pojo.Cat"/>
<bean id="user" class="com.kuang.pojo.User"/>

3. 测试,成功输出结果!

public class MyTest {
   @Test
   public void testMethodAutowire() {
       ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
       User user = (User) context.getBean("user");
       user.getCat().shout();
       user.getDog().shout();
  }
}
//如果允许对象为null,设置required = false,默认为true
@Autowired(required = false)
private Cat cat;

@Resource 如果有指定的name,就先按照byname方式去查找装配,如果不成功就会按照byType的方式自动装配,如果还是不成功就会报出异常。

实体类

public class User {
   //如果允许对象为null,设置required = false,默认为true
   @Resource(name = "cat2")
   private Cat cat;
   @Resource
   private Dog dog;
   private String str;
}

bean.xml

<bean id="dog" class="com.kuang.pojo.Dog"/>
<bean id="cat1" class="com.kuang.pojo.Cat"/>
<bean id="cat2" class="com.kuang.pojo.Cat"/>

<bean id="user" class="com.kuang.pojo.User"/

测试:结果成功。

public class MyTest {
   @Test
   public void testMethodAutowire() {
       ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
       User user = (User) context.getBean("user");
       user.getCat().shout();
       user.getDog().shout();
  }
}

配置文件2:beans.xml , 删掉cat2

<bean id="dog" class="com.kuang.pojo.Dog"/>
<bean id="cat1" class="com.kuang.pojo.Cat"/>

实体类上只保留注解

@Resource
private Cat cat;
@Resource
private Dog dog;

@AutoWired和@Resource区别:建议使用@Resource @Autowired是spring提供的,autowired只能通过byTpye进行查找装配。如果无法辨别就会报出异常。当server的实现类注解上@Server,server就能找到实现类,可是当实现类存在两个或以上的时候autowired就不知道装配那个实现类了,就会报出异常,所以使用它就要确保一个server只有一个实现类 。resource是java提供的,是按照name进行查找装配,如果查找装配失败,就会按照type进行查找装配,如果还是没找到,就会报出异常。resource在使用spring框架的时候会减少耦合性。

衍生注解

@Component("user")组件
// 相当于配置文件中 
<bean id="user" class="当前注解的类"/>
为了更好的分层,@Component有三个衍生注解
@Controller:web层
@Service:service层
@Repository:dao层
这些注解都是将所注解的类交给spring来管理

bean的作用域

使用注解@Scope可以修改bean的作用域,singleton(单例模式)是默认的。还可以修改为prototype(多例模式)。

spring 代理模式

spring的代理模式分为静态代理和动态代理两种模式。springAOP(面向切面)的底层机制就是动态代理。 #E91E63

静态代理

  1. 抽象角色:需要共同完成的事情,一般指的是接口或者抽象类。比如:房屋要出租
  2. 真实角色:就是被代理的角色,真实要求做事的角色:房东
  3. 代理角色:代理真实角色,代理真实角色后,一般会做一些附属操作:房屋中介
  4. 客户:使用代理模式来进行一些操作。

代码实现

//抽象角色:增删改查业务
public interface UserService {
   void add();
   void delete();
   void update();
   void query();
}
//真实对象,完成增删改查操作的人
public class UserServiceImpl implements UserService {
   public void add() {
       System.out.println("增加了一个用户");
  }
   public void delete() {
       System.out.println("删除了一个用户");
  }
   public void update() {
       System.out.println("更新了一个用户");
  }
   public void query() {
       System.out.println("查询了一个用户");
  }
}
//代理角色,在这里面增加日志的实现
public class UserServiceProxy implements UserService {
   private UserServiceImpl userService;
   public void setUserService(UserServiceImpl userService) {
       this.userService = userService;
  }
   public void add() {
       log("add");
       userService.add();
  }
   public void delete() {
       log("delete");
       userService.delete();
  }
   public void update() {
       log("update");
       userService.update();
  }
   public void query() {
       log("query");
       userService.query();
  }
   public void log(String msg){
       System.out.println("执行了"+msg+"方法");
  }
}
//测试实现
public class Client {
   public static void main(String[] args) {
       //真实业务
       UserServiceImpl userService = new UserServiceImpl();
       //代理类
       UserServiceProxy proxy = new UserServiceProxy();
       //使用代理类实现日志功能!
       proxy.setUserService(userService);

       proxy.add();
  }
}

代理模式的好处:可以使真实角色操作更加纯粹(房东指出租房子)。不用去关心一些公共业务(看房子、签合同)等。 公共业务(看房子、签合同)交给代理对象(房屋中介)。实现了业务分工。 公共业务发生扩展的时候变得更加方便和集中(比如扩展业务:租金) 代理模式的缺点:增加了代理环节,就增加的工作量(房屋出租过程更加繁琐)

我们在不改变原来的代码的情况下,实现了对原有功能的增强,这是AOP中最核心的思想。我们想使用代理模式的好处,又不想要代理模式的缺点,就产生了动态代理 #FF9800

动态代理

动态代理分为两大类,基于接口(JDK动态代理)和基于类(cglib),JDK动态代理核心类 : InvocationHandler和Proxy,打开JDK帮助文档看看 #03A9F4

代码实现

//抽象角色:增删改查业务
public interface UserService {
   void add();
   void delete();
   void update();
   void query();
}
//真实对象,完成增删改查操作的人
public class UserServiceImpl implements UserService {
   public void add() {
       System.out.println("增加了一个用户");
  }
   public void delete() {
       System.out.println("删除了一个用户");
  }
   public void update() {
       System.out.println("更新了一个用户");
  }
   public void query() {
       System.out.println("查询了一个用户");
  }
}
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);
  }
   // proxy : 代理类
   // method : 代理类的调用处理程序的方法对象.
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
       log(method.getName());
	   //通过反射执行下面的UserService 的方法
       Object result = method.invoke(target, args);
       return result;
  }
   public void log(String methodName){
       System.out.println("执行了"+methodName+"方法");
  }

}
//测试
public class Test {
   public static void main(String[] args) {
       //真实对象
       UserServiceImpl userService = new UserServiceImpl();
       //代理对象的调用处理程序
       ProxyInvocationHandler pih = new ProxyInvocationHandler();
       pih.setTarget(userService); //设置要代理的对象
       UserService proxy = (UserService)pih.getProxy(); //动态生成代理类!
       proxy.delete();
  }
}

静态代理需要我们在静态代理类中先声明需要代理的对象,而动态代理代理类不需要我们声明代理的真实对象,而是通过反射的机制执行真实对象方法是代理业务,真实对象调用的时候只需要在业务类中声明代理类就行了。

spring AOP

什么是aop:面向切面编程,利用AOP可以将业务的代码实现进行隔离,从而降低业务上的各部分耦合性,提高代码可重用性,提高代码开发的效率。aop把公共的业务(日志、事物、安全)进行管理,使开发人员只专注于代码实现。其本质还是动态代理。

使用注解实现

package com.kuang.config;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
@Component
public class AnnotationPointcut {
   @Before("execution(* com.kuang.service.UserServiceImpl.*(..))")
   public void before(){
       System.out.println("---------方法执行前---------");
  }

   @After("execution(* com.kuang.service.UserServiceImpl.*(..))")
   public void after(){
       System.out.println("---------方法执行后---------");
  }

   @Around("execution(* com.kuang.service.UserServiceImpl.*(..))")
   public void around(ProceedingJoinPoint jp) throws Throwable {
       System.out.println("环绕前");
       System.out.println("签名:"+jp.getSignature());
       //执行目标方法proceed
       Object proceed = jp.proceed();
       System.out.println("环绕后");
       System.out.println(proceed);
  }
}

spring声明式事务

  1. 原子性:要么完成、要么不完成。
  2. 一致性:一旦所有事务动作完成,事务就要被提交。
  3. 隔离性:多个资源存在隔离,防止数据损坏。
  4. 持久性:事务一旦完成、不管系统发生什么问题,结果都不会影响,被持久化的写道数据库。