http://blog.csdn.net/21aspnet/article/details/51386557
1.IOC装配Bean
参考【spring实战4 2.2】,作者提倡无XML配置化。
1.1接口只有一个现实类
可以自动装配
1 package demo; 2 3 public interface CompactDisc { 4 void play(); 5 }
实现类
1 package demo; 2 3 import org.springframework.stereotype.Component; 4 5 @Component
6 public class SgtPeppers implements CompactDisc{ 7 private String title = "Sgt. Pepper's Lonely Hearts Club Band"; 8 private String artist = "http://blog.csdn.net/unix21"; 9 10 public void play() { 11 System.out.println("【非常醒目SgtPeppers 】>>>>>>>>>>>>>>>>>Playing " + title + " by " + artist); 12 } 13 }
配置类
1 package demo; 2 3 import org.springframework.context.annotation.ComponentScan; 4 import org.springframework.context.annotation.Configuration; 5 6 @Configuration 7 @ComponentScan 8 public class CDPlayerConfig { 9 10 }
单元测试类
1 package demo; 2 import org.junit.Test; 3 import org.junit.runner.RunWith; 4 import org.springframework.beans.factory.annotation.Autowired; 5 import org.springframework.beans.factory.annotation.Qualifier; 6 import org.springframework.test.context.ContextConfiguration; 7 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 8 9 @RunWith(SpringJUnit4ClassRunner.class) 10 @ContextConfiguration(classes = CDPlayerConfig.class) 11 public class CDPlayerTest { 12 @Autowired 13 @Qualifier("spn") 14 private CompactDisc cd; 15 16 @Test 17 public void play() { 18 cd.play(); 19 } 20 }
测试结果
1.2 接口有多个实现类
【参考 Spring实战4 3.3】
故意再写一个实现类
1 package demo; 2 3 import org.springframework.context.annotation.Primary; 4 import org.springframework.stereotype.Component; 5 6 7 @Component 8 public class SgtPeppersNew implements CompactDisc{ 9 private String title = "Sgt. Pepper's Lonely Hearts Club Band"; 10 private String artist = "http://blog.csdn.net/unix21"; 11 12 public void play() { 13 System.out.println("【非常醒目SgtPeppersNew 】>>>>>>>>>>>>>>>>>Playing " + title + " by " + artist); 14 } 15 }
此时有两个实现类,如果这时执行单元测试类的时候,测试类不知道注入(DI)那个实现类,所以会报错。
两种解决方案:1,加@Primary 首选标识的bean 2, 使用@Qualifier注解@Qualifier("SgtPeppersNew"), 改变@Component("SgtPeppersNew") 或者@Qualifier 注解中用实现类,但id是小写@Qualifier("sgtPeppersNew"),不需要改变@Component
1 package demo; 2 3 import org.springframework.stereotype.Component; 4 5 6 @Component 7 public class SgtPeppersNew implements CompactDisc{ 8 private String title = "Sgt. Pepper's Lonely Hearts Club Band"; 9 private String artist = "http://blog.csdn.net/unix21"; 10 11 public void play() { 12 System.out.println("【非常醒目SgtPeppersNew 】>>>>>>>>>>>>>>>>>Playing " + title + " by " + artist); 13 } 14 }
1 package demo; 2 import org.junit.Test; 3 import org.junit.runner.RunWith; 4 import org.springframework.beans.factory.annotation.Autowired; 5 import org.springframework.beans.factory.annotation.Qualifier; 6 import org.springframework.test.context.ContextConfiguration; 7 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 8 9 @RunWith(SpringJUnit4ClassRunner.class) 10 @ContextConfiguration(classes = CDPlayerConfig.class) 11 public class CDPlayerTest { 12 @Autowired 13 @Qualifier("sgtPeppersNew") 14 private CompactDisc cd; 15 16 @Test 17 public void play() { 18 cd.play(); 19 } 20 }
打印结果
1.3 为组件扫描的bean命名
【参考 Spring实战4 2.2.2】
1 import org.springframework.stereotype.Component; 2 3 @Component("spn") 4 public class SgtPeppersNew implements CompactDisc {
1 @Autowired 2 @Qualifier("spn") 3 private CompactDisc cd;
也可以使用@Named效果是一样的,这是java依赖注入规范
1 import javax.inject.Named; 2 3 @Named("spn") 4 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 在构造方法上使用自动装配
1 package demo; 2 import org.junit.Test; 3 import org.junit.runner.RunWith; 4 import org.springframework.beans.factory.annotation.Autowired; 5 import org.springframework.beans.factory.annotation.Qualifier; 6 import org.springframework.test.context.ContextConfiguration; 7 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 8 9 @RunWith(SpringJUnit4ClassRunner.class) 10 @ContextConfiguration(classes = CDPlayerConfigTest.class) 11 public class CDPlayerFunTest { 12 private CompactDisc cd; 13 @Autowired 14 @Qualifier("spn") 15 public void CDPlayer(CompactDisc cd) { 16 this.cd = cd; 17 } 18 19 @Test 20 public void play() { 21 cd.play(); 22 System.out.println("【占位符】CDPlayerFunTest"); 23 } 24 }
另一种写法
1 @Component 2 public class CDPlayer implements MediaPlayer { 3 private CompactDisc cd; 4 5 @Autowired 6 public CDPlayer(@Qualifier("spn")CompactDisc cd) { 7 this.cd = cd; 8 } 9 10 public void play() { 11 cd.play(); 12 } 13 14 }
1.5.2 在属性Setter方法上使用自动装配
1 @Component 2 public class CDPlayer implements MediaPlayer { 3 private CompactDisc cd; 4 5 @Autowired 6 @Qualifier("spn") 7 public void setCompactDisc(CompactDisc cd) { 8 this.cd = cd; 9 } 10 11 public void play() { 12 cd.play(); 13 } 14 }
避免异常声明 @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); } }
1 @RunWith(SpringJUnit4ClassRunner.class) 2 @ContextConfiguration(classes=CDPlayerConfigTest.class) 3 public class CDPlayerLogTest { 4 5 @Autowired 6 private MediaPlayer player; 7 8 @Autowired 9 @Qualifier("sp") 10 private CompactDisc cd; 11 12 @Autowired 13 @Qualifier("cold") 14 private CompactDisc cd2; 15 16 @Test 17 public void cdShouldNotBeNull() { 18 assertNotNull(cd); 19 } 20 21 @Test 22 public void play() { 23 player.play(); 24 cd.play(); 25 cd2.play(); 26 } 27 }
好处:这样做的好处限定符不耦合类名,所以可以随意重构类名。
问题:重复的限定符出现在多个类上这是不允许的,因为Java不允许同一个条目上重复出现相同类型的多个注解。
注意:此时我用本地测试时发现 private MediaPlayer player; 这个player 并不能注入到测试类中,需要以后解决。
解决:注意其实CDplayer 也是需要创建的组件类,所以也要加上@Component
1.7 使用自定义限定符注解
针对上述问题可以创建自定义的限定符注解。
1 @Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到 2 @Target({ElementType.CONSTRUCTOR,ElementType.FIELD,ElementType.METHOD,ElementType.TYPE})//定义注解的作用目标**作用范围字段、枚举的常量/方法 3 @Qualifier 4 public @interface Cold {}
1 @Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到 2 @Target({ElementType.CONSTRUCTOR,ElementType.FIELD,ElementType.METHOD,ElementType.TYPE})//定义注解的作用目标**作用范围字段、枚举的常量/方法 3 @Qualifier 4 public @interface Creamy {}
1 @Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到 2 @Target({ElementType.CONSTRUCTOR,ElementType.FIELD,ElementType.METHOD,ElementType.TYPE})//定义注解的作用目标**作用范围字段、枚举的常量/方法 3 @Qualifier 4 public @interface Fruity {}
1 @Component 2 @Cold 3 @Creamy 4 public class IceCream implements CompactDisc { 5 6 private String title = "Spring 实现 第4版 读书笔记"; 7 private String artist = "http://blog.csdn.net/unix21"; 8 9 public void play() { 10 System.out.println("【非常醒目 IceCream】>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>Playing " + title + " by " + artist); 11 } 12 }
1 @Component 2 @Cold 3 @Fruity 4 public class Popsicle implements CompactDisc { 5 6 private String title = "Spring 实现 第4版 读书笔记"; 7 private String artist = "http://blog.csdn.net/unix21"; 8 9 public void play() { 10 System.out.println("【非常醒目 Popsicle】>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>Playing " + title + " by " + artist); 11 } 12 }
1 @RunWith(SpringJUnit4ClassRunner.class) 2 @ContextConfiguration(classes = CDPlayerConfigTest.class) 3 public class CDPlayerLogTest { 4 5 @Autowired 6 private MediaPlayer player; 7 8 @Autowired 9 @Qualifier("sp") 10 private CompactDisc cd; 11 12 @Autowired 13 @Cold 14 @Creamy 15 private CompactDisc cd2; 16 17 @Autowired 18 @Cold 19 @Fruity 20 private CompactDisc cd3; 21 22 @Test 23 public void cdShouldNotBeNull() { 24 assertNotNull(cd); 25 } 26 27 @Test 28 public void play() { 29 player.play(); 30 cd.play(); 31 cd2.play(); 32 cd3.play(); 33 } 34 }
1.8 bean的作用域
Spring定义了多重作用域,singleton单例,prototype原型等
singleton单例:整个应用中,只创建bean的一个实例,默认Spring上下文中所有的bean都是单例。
prototype原型:每次注入或者通过Spring应用上下文获取的时候,都会创建一个新的bean实例。
1 @Component 2 public class Add implements AddI { 3 public int a=0; 4 5 public void Add() { 6 a++; 7 } 8 9 public void getA() { 10 System.out.println("【非常醒目 Add】>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>a= " +a+""); 11 } 12 }
1 public interface AddI { 2 void Add(); 3 void getA(); 4 }
1 @Component 2 public class CDPlayer implements MediaPlayer { 3 4 @Autowired 5 @Qualifier("sp") 6 private CompactDisc cd; 7 8 @Autowired 9 private AddI a; 10 11 public void play() { 12 System.out.println("【非常醒目 CDPlayer】>>>"); 13 cd.play(); 14 a.Add(); 15 a.getA(); 16 a.Add(); 17 a.getA(); 18 System.out.println("【非常醒目 CDPlayer】<<<"); 19 } 20 }
测试用例
1 @RunWith(SpringJUnit4ClassRunner.class) 2 @ContextConfiguration(classes = CDPlayerConfigTest.class) 3 public class CDPlayerLogTest { 4 5 @Autowired 6 private MediaPlayer player; 7 8 @Autowired 9 @Qualifier("sp") 10 private CompactDisc cd; 11 12 @Autowired 13 @Cold 14 @Creamy 15 private CompactDisc cd2; 16 17 @Autowired 18 @Cold 19 @Fruity 20 private CompactDisc cd3; 21 22 @Test 23 public void cdShouldNotBeNull() { 24 assertNotNull(cd); 25 } 26 27 @Autowired 28 private AddI a; 29 30 @Test 31 public void play() { 32 player.play(); 33 cd.play(); 34 cd2.play(); 35 cd3.play(); 36 a.getA(); 37 } 38 }
再写一个多线程
1 public class ClientThread extends Thread { 2 3 @Autowired 4 private AddI a; 5 6 @Autowired 7 public ClientThread(AddI a) { 8 this.a = a; 9 } 10 11 public void run() { 12 a.Add(); 13 a.getA(); 14 } 15 }
调用多线程
1 @RunWith(SpringJUnit4ClassRunner.class) 2 @ContextConfiguration(classes = CDPlayerConfigTest.class) 3 public class SpringScopeTest { 4 5 @Autowired 6 private AddI a; 7 8 @Test 9 public void Scope() { 10 for (int i = 0; i < 10; i++) { 11 ClientThread t = new ClientThread(a); 12 t.start(); 13 } 14 } 15 }
此时add实现类如下
@Component //@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON) @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) public class Add implements AddI{ public int a=0; public void Add() { // TODO Auto-generated method stub a++; } public void getA() { // TODO Auto-generated method stub System.out.println("【非常醒目 Add】>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>a= " +a+""); } }
如果使用单例模式则
补充说明:@Repository、@Service、@Controller 和 @Component将类标识为Bean,都是一样的,用在不同的地方而已。
2.AOP切面编程
定义接口
1 public interface PerformanceI { 2 public void perform(); 3 }
实现类
1 import org.springframework.stereotype.Component; 2 3 @Component 4 public class Performance implements PerformanceI{ 5 public void perform(){ 6 System.out.println("【非常醒目 Performance perform 调用中】 By http://blog.csdn.net/unix21"); 7 } 8 }
定义切面
1 import org.aspectj.lang.annotation.After; 2 import org.aspectj.lang.annotation.AfterThrowing; 3 import org.aspectj.lang.annotation.Aspect; 4 import org.aspectj.lang.annotation.Before; 5 6 @Aspect 7 public class MyAspect { 8 @Before("execution(* com.demo.PerformanceI.perform(..))") 9 public void before(){ 10 System.out.println("【非常醒目 [方法调用前] 】"); 11 } 12 13 @After("execution(* com.demo.PerformanceI.perform(..))") 14 public void after(){ 15 System.out.println("【非常醒目 [方法调用后] 】"); 16 } 17 18 @AfterThrowing("execution(* com.demo.PerformanceI.perform(..))") 19 public void afterThrowing(){ 20 System.out.println("【非常醒目 [方法异常后] 】"); 21 } 22 }
配置文件
1 import com.demo.*; 2 import org.springframework.context.annotation.Bean; 3 import org.springframework.context.annotation.ComponentScan; 4 import org.springframework.context.annotation.Configuration; 5 import org.springframework.context.annotation.EnableAspectJAutoProxy; 6 7 @Configuration 8 @EnableAspectJAutoProxy 9 @ComponentScan("com.demo") 10 public class AppConfig { 11 @Bean 12 public MyAspect myAspect() { 13 return new MyAspect(); 14 } 15 16 }
测试用例
1 import org.junit.Test; 2 import org.junit.runner.RunWith; 3 import org.springframework.beans.factory.annotation.Autowired; 4 import org.springframework.test.context.ContextConfiguration; 5 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 6 7 @RunWith(SpringJUnit4ClassRunner.class) 8 @ContextConfiguration(classes = AppConfig.class) 9 public class MyTest { 10 11 @Autowired 12 private PerformanceI p1; 13 14 @Test 15 public void play() { 16 p1.perform(); 17 } 18 }
此时在本地运行报错··········
后来加入了 aopalliance.jar 包后解决问题 已经写入到 博客园内 问题3.
实现了方法调用前后的AOP效果。
这个Spring官方参考做的不错:http://docs.spring.io/spring/docs/4.2.5.RELEASE/javadoc-api/
这里选不同的版本:http://docs.spring.io/spring/docs/
4.3.2 创建环绕通知
哈哈此小结个人觉得挺有意思所以也就拿上来了。
环绕通知是最为强大的通知类型。它能够让你所编写的逻辑将被通知的目标方法完全包装起 来。实际上就像在一个通知方法中同时编写前置通知和后置通知。
环绕通知,直接上代码说事情。
1 package concert; 2 3 import org.aspectj.lang.ProceedingJoinPoint; 4 import org.aspectj.lang.annotation.Around; 5 import org.aspectj.lang.annotation.Aspect; 6 import org.aspectj.lang.annotation.Pointcut; 7 @Aspect 8 public class MyAspect { 9 @Pointcut("execution(* concert.PerformanceI.perform(..))") 10 public void perform(){} 11 @Around("perform()") 12 public void watchPerformance(ProceedingJoinPoint jp){ 13 try{ 14 System.out.println("【非常醒目 [方法调用前] 】"); 15 jp.proceed(); 16 System.out.println("【非常醒目 [方法调用后] 】"); 17 } catch (Throwable e){ 18 System.out.println("【非常醒目 [方法异常后] 】"); 19 } 20 } 21 }
@Around 是环绕通知的 注释,ProceedingJoinPoint 作为参数用来通知“自己想做的业务逻辑” 个人理解就是切点。
粘贴过来一张原图,是通过XML配置来实现AOP切面功能,跟上面 通过基于AspectJ注解 @EnableAspectJAutoProxy 引入是一个意思。
3.Spring MVC
DispatcherServlet是Spring MVC的核心,每当应用接受一个HTTP请求,由DispatcherServlet负责将请求分发给应用的其他组件。
首选给出一张Spring MVC的组件图,也是Spring mvc 的流程图
在旧版本中,DispatcherServlet之类的servlet一般在web.xml文件中配置;但是Spring 3.1引入了注解就无需再使用web.xml文件。
下面配置DispatcherServlet
1 import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; 2 3 public class SpitterWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { 4 5 @Override 6 protected Class<?>[] getRootConfigClasses() { 7 return new Class<?>[]{RootConfig.class}; 8 } 9 10 @Override 11 protected Class<?>[] getServletConfigClasses() { 12 return new Class<?>[]{WebConfig.class}; 13 } 14 15 @Override 16 protected String[] getServletMappings() { 17 return new String[]{"/"}; 18 } 19 20 }