Spring4实战学习笔记

《Spring4实战 第4版》2016年4月新出版的,之前的第三版看起来还是不错的,所以看到新版就直接买下来。

英文版源码地址:Spring in Action, Fourth Edition Covers Spring 4

1.IOC装配Bean

参考【Spring实战4 2.2】,作者提倡无XML配置化。

1.1接口只有一个现实类

可以自动装配

public interface CompactDisc {

    void play();
}
import org.springframework.stereotype.Component;

@Component
public class SgtPeppers implements CompactDisc {

    private String title = "Sgt. Pepper‘s Lonely Hearts Club Band";
    private String artist = "http://blog.csdn.net/unix21";

    public void play() {
        System.out.println("【非常醒目SgtPeppers 】>>>>>>>>>>>>>>>>>Playing " + title + " by " + artist);
    }

}

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan
public class CDPlayerConfig {
}

单元测试

import static org.junit.Assert.*;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = CDPlayerConfig.class)
public class CDPlayerTest {
    @Autowired
    private CompactDisc cd;

    @Test
    public void play() {
        cd.play();
    }
}

1.2 接口有多个实现类

【参考 Spring实战4 3.3】
故意再写一个实现类

import org.springframework.stereotype.Component;

@Component
public class SgtPeppersNew implements CompactDisc {

    private String title = "Sgt. Pepper‘s Lonely Hearts Club Band";
    private String artist = "http://blog.csdn.net/unix21";

    public void play() {
        System.out.println("【非常醒目 SgtPeppersNew】>>>>>>>>>>>>>>>>>Playing " + title + " by " + artist);
    }

}

如果这个时候运行肯定会报错NoUniqueBeanDefinitionException: No qualifying bean of type

解决方法有两种

第一种 在实现类上 标识首选的bean,使用@Primary

import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;

@Component
@Primary
public class SgtPeppers implements CompactDisc {

    private String title = "Sgt. Pepper‘s Lonely Hearts Club Band";
    private String artist = "http://blog.csdn.net/unix21";

    public void play() {
        System.out.println("【非常醒目SgtPeppers 】>>>>>>>>>>>>>>>>>Playing " + title + " by " + artist);
    }

}

但是这种方法不方便精确定义。

第二种  使用@Qualifier注解

import static org.junit.Assert.*;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = CDPlayerConfig.class)
public class CDPlayerTest {
    @Autowired
    @Qualifier("sgtPeppersNew")
    private CompactDisc cd;

    @Test
    public void play() {
        cd.play();
    }
}

需要注意的是bean id的首字母是类名小写。

spring @Qualifier注解

1.3 为组件扫描的bean命名

【参考 Spring实战4  2.2.2】

import org.springframework.stereotype.Component;

@Component("spn")
public class SgtPeppersNew implements CompactDisc {

@Autowired
    @Qualifier("spn")
    private CompactDisc cd;

也可以使用@Named效果是一样的,这是java依赖注入规范

import javax.inject.Named;

@Named("spn")
public class SgtPeppersNew implements CompactDisc {

1.4 设定组件扫描的指定包

【参考 Spring实战4  2.2.3】

如果@ComponentScan默认不设置只扫描配置类所在的包作为基础包。

@Configuration
@ComponentScan("blog.csdn.net.unix21")
public class CDPlayerConfigTest {

设置@ComponentScan的value属性就可以指明包名称。

如果想更清晰的表明设置的是基础包
@ComponentScan(basePackages="指定包")

指定多个

@ComponentScan(basePackages={"指定包1","指定包2"})

也可以将其指定为包中所包含的类或者接口

@ComponentScan(basePackages={"XXX.class","XX.class"})

1.5 自动装配

【参考 Spring实战4  2.2.4】

声明自动装配需要@Autowired注解

1.5.1 在构造方法上使用自动装配

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = CDPlayerConfigTest.class)
public class CDPlayerFunTest {

    private CompactDisc cd;

    @Autowired
    @Qualifier("spn")
    public void CDPlayer(CompactDisc cd) {
        this.cd = cd;
    }

    @Test
    public void play() {
        cd.play();
        System.out.println("【占位符】CDPlayerFunTest");
    }
}

另一种写法

@Component
public class CDPlayer implements MediaPlayer {
  private CompactDisc cd;

  @Autowired
  public CDPlayer(@Qualifier("spn")CompactDisc cd) {
    this.cd = cd;
  }

  public void play() {
    cd.play();
  }

}

1.5.2 在属性Setter方法上使用自动装配

@Component
public class CDPlayer implements MediaPlayer {
  private CompactDisc cd;

  @Autowired
  @Qualifier("spn")
  public void setCompactDisc(CompactDisc cd) {
    this.cd = cd;
  }

  public void play() {
    cd.play();
  }
}

避免异常声明  @Autowired(required = false),如果没有匹配的bean,Spring会让这个bean处于未装配转态,但是需要谨慎对待这个设置,代码需要做null检查。

@Autowired是Spring特有的注解,可以替换为@Inject,@Inject来源自Jave依赖注入规范。

1.6 创建自定义的限定符

【参考 Spring实战4  3.3.2】

@Component
@Qualifier("cold")
public class IceCream implements CompactDisc {

    private String title = "Sgt. Pepper‘s Lonely Hearts Club Band";
    private String artist = "The Beatles";

    public void play() {
        System.out.println("【非常醒目 IceCream】>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>Playing " + title + " by " + artist);
    }
}

测试用例

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=CDPlayerConfigTest.class)
public class CDPlayerLogTest {

  @Autowired
  private MediaPlayer player;

  @Autowired
  @Qualifier("sp")
  private CompactDisc cd;

  @Autowired
  @Qualifier("cold")
  private CompactDisc cd2;

  @Test
  public void cdShouldNotBeNull() {
    assertNotNull(cd);
  }

  @Test
  public void play() {
    player.play();
    cd.play();
    cd2.play();
  }
}

好处:这样做的好处限定符不耦合类名,所以可以随意重构类名。

问题:重复的限定符出现在多个类上这是不允许的,因为Java不允许同一个条目上重复出现相同类型的多个注解

1.7 使用自定义限定符注解

针对上述问题可以创建自定义的限定符注解。

@Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到
@Target({ElementType.CONSTRUCTOR,ElementType.FIELD,ElementType.METHOD,ElementType.TYPE})//定义注解的作用目标**作用范围字段、枚举的常量/方法
@Qualifier
public @interface Cold {}

@Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到
@Target({ElementType.CONSTRUCTOR,ElementType.FIELD,ElementType.METHOD,ElementType.TYPE})//定义注解的作用目标**作用范围字段、枚举的常量/方法
@Qualifier
public @interface Creamy {}

@Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到
@Target({ElementType.CONSTRUCTOR,ElementType.FIELD,ElementType.METHOD,ElementType.TYPE})//定义注解的作用目标**作用范围字段、枚举的常量/方法
@Qualifier
public @interface Fruity {}

@Component
@Cold
@Creamy
public class IceCream implements CompactDisc {

    private String title = "Spring 实现 第4版 读书笔记";
    private String artist = "http://blog.csdn.net/unix21";

    public void play() {
        System.out.println("【非常醒目 IceCream】>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>Playing " + title + " by " + artist);
    }
}

@Component
@Cold
@Fruity
public class Popsicle implements CompactDisc {

    private String title = "Spring 实现 第4版 读书笔记";
    private String artist = "http://blog.csdn.net/unix21";

    public void play() {
        System.out.println("【非常醒目 Popsicle】>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>Playing " + title + " by " + artist);
    }
}

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = CDPlayerConfigTest.class)
public class CDPlayerLogTest {

    @Autowired
    private MediaPlayer player;

    @Autowired
    @Qualifier("sp")
    private CompactDisc cd;

    @Autowired
    @Cold
    @Creamy
    private CompactDisc cd2;

    @Autowired
    @Cold
    @Fruity
    private CompactDisc cd3;

    @Test
    public void cdShouldNotBeNull() {
        assertNotNull(cd);
    }

    @Test
    public void play() {
        player.play();
        cd.play();
        cd2.play();
        cd3.play();
    }
}

1.8 bean的作用域

Spring定义了多重作用域,singleton单例,prototype原型等

参考:spring中scope作用域

singleton单例:整个应用中,只创建bean的一个实例,默认Spring上下文中所有的bean都是单例。

prototype原型:每次注入或者通过Spring应用上下文获取的时候,都会创建一个新的bean实例。

@Component
public class Add implements AddI {
    public  int a=0;

    public  void Add() {
        a++;
    }

    public  void getA() {
        System.out.println("【非常醒目 Add】>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>a= " +a+"");
    }
}

public interface AddI {
void Add();
    void getA();
}

@Component
public class CDPlayer implements MediaPlayer {

    @Autowired
    @Qualifier("sp")
    private CompactDisc cd;

    @Autowired
    private AddI a;

    public void play() {
        System.out.println("【非常醒目 CDPlayer】>>>");
        cd.play();
        a.Add();
        a.getA();
        a.Add();
        a.getA();
        System.out.println("【非常醒目 CDPlayer】<<<");
    }
}

测试用例

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = CDPlayerConfigTest.class)
public class CDPlayerLogTest {

    @Autowired
    private MediaPlayer player;

    @Autowired
    @Qualifier("sp")
    private CompactDisc cd;

    @Autowired
    @Cold
    @Creamy
    private CompactDisc cd2;

    @Autowired
    @Cold
    @Fruity
    private CompactDisc cd3;

    @Test
    public void cdShouldNotBeNull() {
        assertNotNull(cd);
    }

    @Autowired
    private AddI a;

    @Test
    public void play() {
        player.play();
        cd.play();
        cd2.play();
        cd3.play();
        a.getA();
    }
}

再写一个多线程

public class ClientThread extends Thread {

    @Autowired
    private AddI a;

    @Autowired
    public ClientThread(AddI a) {
        this.a = a;
    }

    public void run() {
        a.Add();
        a.getA();
    }
}

调用多线程

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = CDPlayerConfigTest.class)
public class SpringScopeTest {

    @Autowired
    private AddI a;

    @Test
    public void Scope() {
        for (int i = 0; i < 10; i++) {
            ClientThread t = new ClientThread(a);
            t.start();
        }
    }
}

改为SCOPE_PROTOTYPE

@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
//@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public class Add implements AddI {
    public  int a=0;

    public  void Add() {
        a++;
    }

    public  void getA() {
        System.out.println("【非常醒目 Add】>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>a= " +a+"");
    }
}

看到差异了吧。

补充说明:@Repository、@Service、@Controller 和 @Component将类标识为Bean,都是一样的,用在不同的地方而已。

2.AOP切面编程

定义接口

public interface PerformanceI {
    public void perform();
}

实现类

import org.springframework.stereotype.Component;

@Component
public class Performance implements PerformanceI{
    public void perform(){
    System.out.println("【非常醒目  Performance perform 调用中】 By http://blog.csdn.net/unix21");
    }
}

定义切面

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class MyAspect {
    @Before("execution(* com.demo.PerformanceI.perform(..))")
    public void before(){
    System.out.println("【非常醒目 [方法调用前] 】");
    }

    @After("execution(* com.demo.PerformanceI.perform(..))")
    public void after(){
    System.out.println("【非常醒目 [方法调用后] 】");
    }

    @AfterThrowing("execution(* com.demo.PerformanceI.perform(..))")
    public void afterThrowing(){
    System.out.println("【非常醒目 [方法异常后] 】");
    }
}

配置文件

import com.demo.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@EnableAspectJAutoProxy
@ComponentScan("com.demo")
public class AppConfig {
    @Bean
    public MyAspect myAspect() {
        return new MyAspect();
    }

}

测试用例

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AppConfig.class)
public class MyTest {

    @Autowired
    private PerformanceI p1;

    @Test
    public void play() {
        p1.perform();
    }
}

运行:

实现了方法调用前后的AOP效果。

这个Spring官方参考做的不错:http://docs.spring.io/spring/docs/4.2.5.RELEASE/javadoc-api/

这里选不同的版本:http://docs.spring.io/spring/docs/

3.Spring MVC

DispatcherServlet是Spring MVC的核心,每当应用接受一个HTTP请求,由DispatcherServlet负责将请求分发给应用的其他组件。
在旧版本中,DispatcherServlet之类的servlet一般在web.xml文件中配置;但是Spring 3.1引入了注解就无需再使用web.xml文件。

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

public class SpitterWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[]{RootConfig.class};
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[]{WebConfig.class};
    }

    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }

}

AbstractAnnotationConfigDispatcherServletInitializer这个类负责配置DispatcherServlet、初始化Spring MVC容器和Spring容器。

正如可以通过多种方式配置DispatcherServlet一样,也可以通过多种方式启动Spring MVC特性。原来我们一般在xml文件中使用<mvc:annotation-driven>元素启动注解驱动的Spring MVC特性。这里我们使用JavaConfig配置,最简单的Spring MVC配置类代码如下:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

@Configuration
@EnableWebMvc
@ComponentScan("com.xxx.controller")
public class WebConfig extends WebMvcConfigurerAdapter{
    @Bean
    public ViewResolver viewResolver() { //配置JSP视图解析器
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix("/WEB-INF/views/");
        resolver.setSuffix(".jsp");
        //可以在JSP页面中通过${}访问beans
        resolver.setExposeContextBeansAsAttributes(true);
        return resolver;
    }

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable(); //配置静态文件处理
    }
}

@Configuration表示这是Java配置类;@EnableWebMvc注解用于启动Spring MVC特性。

通过@ComponentScan注解指定bean的自动发现机制作用的范围,被@Controller等注解修饰的web的bean将被发现并加载到spring mvc应用容器,这样就不需要在配置类中显式定义任何控制器bean了。

通过@Bean注解添加一个ViewResolverbean,具体来说是InternalResourceViewResolver。

RootConfig的配置就非常简单了,唯一需要注意的是,它在设置扫描机制的时候,将之前WebConfig设置过的那个包排除了;也就是说,这两个扫描机制作用的范围正交。RootConfig的代码如下:

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

@Configuration
@ComponentScan(basePackages = {"com.xxx.*"},
        excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, value = EnableWebMvc.class)})
public class RootConfig {
}

写一个控制器,定义之前的IOC对象PerformanceI

@Controller
public class HomeController {
    @Autowired
    private PerformanceI p1;

    @RequestMapping(value = "/home", method = RequestMethod.GET)
    public String home() {
        p1.perform();
        return "home";
    }
}

在WEB-INF/views下新增模板文件home.jsp

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>JSP Page</title>
    </head>
    <body>
        <h1>Spring4 & Sping MVC4 </h1><p>demo by http://blog.csdn.net/unix21</p>
    </body>
</html>

下面这个是【第5章】的翻译 https://segmentfault.com/a/1190000004343063?_ea=575820

时间: 2024-10-21 13:21:44

Spring4实战学习笔记的相关文章

机器学习实战学习笔记(一)

1.k-近邻算法 算法原理: 存在一个样本数据集(训练样本集),并且我们知道样本集中的每个数据与其所属分类的对应关系.输入未知类别的数据后将新数据的每个特征与样本集中数据对应的特征进行比较,然后算法提取样本集中特征最相似(最近邻)的k组数据.然后将k组数据中出现次数最多的分类,来作为新数据的分类. 算法步骤: 计算已知类别数据集中的每一个点与当前点之前的距离.(相似度度量) 按照距离递增次序排序 选取与当前点距离最小的k个点 确定k个点所在类别的出现频率 返回频率最高的类别作为当前点的分类 py

60分钟内从零起步驾驭Hive实战学习笔记

本博文的主要内容是: 1. Hive本质解析 2. Hive安装实战 3. 使用Hive操作搜索引擎数据实战 SparkSQL前身是Shark,Shark强烈依赖于Hive.Spark原来没有做SQL多维度数据查询工具,后来开发了Shark,Shark依赖于Hive的解释引擎,部分在Spark中运行,还有一部分在Hadoop中运行.所以讲SparkSQL必须讲Hive. 1. Hive本质解析 1. Hive是分布式数据仓库,同时又是查询引擎,所以SparkSQL取代的只是Hive的查询引擎,在

《一头扎进Spring4》学习笔记(一)简介与helloworld实现

第一讲 问候Spring4 第一节 简介 Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson创建.简单来说,Spring是一个分层的JavaSE/EEfull-stack(一站式) 轻量级开源框架. 1.框架特征 轻量--从大小与开销两方面而言Spring都是轻量的.完整的Spring框架可以在一个大小只有1MB多的JAR文件里发布.并且Spring所需的处理开销也是微不足道的.此外,Spring是非侵入式的:典型地,Sprin

Selenium2 Python 自动化测试实战学习笔记(七)

Selenium  Grid2 利用Grid可以在不同的主机上建立主节点(hub)和分支节点(node).使主节点上的测试用例在不同的分支节点上运行,可以搭建不同的环境,从而使一份测试用例完成在不同环境下的验证.Selenium Grid2已经集成到selenium server中了(selenium-server-stanalon-xxx.jar包中). 9.1 Selenium2工作原理 Selenium2 中因为使用的WebDriver,这个技术不是靠js 驱动的,而是直接调用浏览器的原生

Spring4基础 学习笔记(1) ---- Bean

文章为博主看动力节点Spring教学视频总结的笔记,作为以后查阅使用. Spring主要作用为"解耦" 根据功能不同可以将系统中的代码分为: 主业务逻辑 系统级业务逻辑:没有具体的专业业务应用场景,为主业务提供系统级服务,例如日志.安全.事务等 Spring根据代码功能特点,将降低耦合度方式分为两类: IoC与AOP IoC使得主业务在相互调用过程中不用再自己维护关系,即不用再自己创建要使用的对象了.而是有Spring容器统一管理,自动"注入". AOP使得系统级服

Selenium2 Python 自己主动化測试实战学习笔记(五)

7.1 自己主动化測试用例 无论是功能測试.性能測试和自己主动化測试时都须要编写測试用例,測试用例的好坏能准确的体现了測试人员的经验.能力以及对项目的深度理解. 7.1.1 手工測试用例与自己主动化測试用例 手工測试用例是针对手工測试人员.自己主动化測试用例是针对自己主动化測试框架.前者是手工測试用例人员应用手工方式进行用例解析,后者是应用脚本技术进行用例解析. 前者具有较好的异常处理能力,并且可以基于測试用例,制造各种不同的逻辑推断,并且人工測试步步跟踪,可以仔细定位问题.后者全然依照測试用例

Spring4.0学习笔记(7) —— 通过FactoryBean配置Bean

1.实现Spring 提供的FactoryBean接口 package com.spring.facoryBean; import org.springframework.beans.factory.FactoryBean; public class CarFactoryBean implements FactoryBean<Car> { private String brand; public void setBrand(String brand) { this.brand = brand;

Spring4.0学习笔记(5) —— 管理bean的生命周期

Spring IOC 容器可以管理Bean的生命周期,Spring允许在Bean生命周期的特定点执行定制的任务 Spring IOC 容器对Bean的生命周期进行管理的过程: 1.通过构造器或工厂方法创建Bean的实例 2.为Bean的属性设置值和对其他Bean的引用 3.调用Bean的初始化方法 4.Bean可以使用了 5.当容器关闭时,调用Bean的销毁方法 bean文件 package com.spring.cycle; public class Car { public Car(){ S

java并发编程实战学习笔记之取消与关闭

第七章 取消与关闭 7.1 任务取消 方式一.通过volatile类型的域来保存取消状态 方式二.interrupt()方法 interrupt()可以中断目标线程 isinterrupted()方法用来检测目标线程的中断状态 interrupted()用于清除中断状态,并且返回之前的中断状态,这是唯一可以清除中断状态的方法,如果在调用该方法是返回了true,那么除非你想屏蔽这个中断,否则你必须对他进行处理,可以抛出interruptExeption异常或者重新通过interrupt来恢复中断状