标签:
《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();
}
}【参考 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的首字母是类名小写。
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;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是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原型等
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容器。
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 {
}@Controller
public class HomeController {
@Autowired
private PerformanceI p1;
@RequestMapping(value = "/home", method = RequestMethod.GET)
public String home() {
p1.perform();
return "home";
}
}<%@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
标签:
原文地址:http://blog.csdn.net/21aspnet/article/details/51386557