码迷,mamicode.com
首页 > 编程语言 > 详细

Spring4实战学习笔记

时间:2016-05-13 15:00:08      阅读:1821      评论:0      收藏:0      [点我收藏+]

标签:

《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

Spring4实战学习笔记

标签:

原文地址:http://blog.csdn.net/21aspnet/article/details/51386557

(0)
(1)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!